rails db:seedの実行時にはcontrollerは経由されない

Railsチュートリアルの拡張機能を実装していて、思い通りの挙動にならない！と戸惑ったので書いておきます。

実際はただの理解不足による勘違いだったのですが、もし同じところでつまづいているひとがいたら参考になるかもしれません。

##rails db:seedの実行時にはモデルのみを経由する
`rails db:seed`コマンドで初期データを生成するとき、このデータはModelをみて生成され、controllerは経由されない。

通常、MVCでのデータの流れは
①フォームなどからユーザーによって入力される（view）
②routeを経由して適切なconrollerとアクションが呼びだされる
③controller内でデータを操作する
④対応するmodelがDBとのデータのやりとりを行い（保存・取り出し）、controllerに返す
⑤controllerからviewにデータをわたし、描画する
というふうになると思います。

はじめに挙げた`rails db:seed`（一般的には`rails db:migrate:reset`とセットで使う）コマンドは、seeds.rbをもとに初期データを生成・DBに保存します。

個人的には③と④をしてくれるんだな〜（データの操作と保存はひとつながりのセットというイメージ）と思っていたのですが、実際はそうではなく、④のモデルのみを経由してDBにデータを保存、という動きをします。

つまり、**DBに保存するまえにコントローラーでデータを操作する実装をしていた場合、seedで生成した初期データにはその操作は適用されないので注意が必要**です。

##Railsチュートリアルにおける具体例
コードを交えつつ、実際に直面した問題を説明します。

Railsチュートリアルの拡張機能として、マイクロポストに『@ユーザー名』を含めると返信できる、というリプライ機能を実装していました。

コードはこちら（不要な部分は省略・一部修正してます）。

```ruby:MicropostsController.rb
    def create
      @micropost = current_user.microposts.build(micropost_params)
      set_in_reply_to
        if @micropost.save
          flash[:success] = "Micropost created!"
          redirect_to root_url
        else
          @feed_items = []
          render 'static_pages/home'
        end
    end
    
    private
        #@userがあればその値をin_reply_toカラムにセット
        def set_in_reply_to
          #リプライはひとりにしかつけれらない仕様
          reply_user = @micropost[:content].slice(/(?<=@)[^\s]+/)
          @micropost[:in_reply_to] = reply_user unless reply_user.nil?
        end

```

```ruby:user.rb
def feed
    following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"
    Micropost.where("user_id IN (#{following_ids}) OR user_id = #{id} OR in_reply_to = #{user_name}")
end
```
さっきのMVCの流れでいうと、
①投稿フォームに@userをふくめたデータが入力される(view）
②MicropostsControllerのcreateアクションがよばれる
③createアクション内で、入力されたデータから@userを抜きだして特定のカラムにセットする（set_in_reply_toメソッド）
④modelを通じてDBに保存。そのDBから表示するべきポストをとり出し、conrollerに返す（feedメソッド）
⑤該当ユーザーのフィードに、@userを含んだポストが表示される
という感じです。

実際に画面を動かしてこれを確認するには、
1.まずログインして@userをふくめた投稿をする
2.一旦ログアウト
3.@userで指定したユーザーで再度ログイン
4.フィードにそのポストがあるか確認
という4ステップが必要になります。普通にめんどくさい。

それならseedで初期データとして@userを含んだ投稿を生成しておいて、そのユーザーでログインすれば一発じゃん（！？）。
ということで、seeds.rbを修正して、`rails db:migrate:reset`からの`rails db:seed`。

```ruby:seeds.rb
# リプライつきポスト
second_user = User.second
second_user.microposts.create!(content: "this should be displayed on @First_User 's feed")
end

```

あとは@First_Userでログインすれば、フィードにこのポストがあるはず！！→ない。

変だなとおもいつつ調査した結果は以下のとおり。
・ポスト自体はちゃんと存在している（投稿したユーザーページに出てくる）
・railsコンソールで確認すると、コントローラーでセットしたはずのin_reply_toカラムがnilになっている
・まったくおなじ内容をウェブから投稿すると、ちゃんと@First_Userのフィードの反映される（=controllerおよびフィードの取得は正しく動いている）

ここまできてやっと、`rails db:seed`ではcontrollerは呼びだされないのか...？という仮説をもち、メンターさんに質問したところ、ビンゴ。ググっても出てこず、疑問を解消するのにかなり時間を食ってしまいました。

ちなみに、railsコンソールからseeds.rbに書いたコードを実行すると、DBに新しいデータは保存されますがin_reply_toカラムはnilになります。

よく考えると、`user.microposts.create`はmicropostsモデルに直接`create`を投げているので、MicropostsControllerの`create`アクションとは別物なんですよね...

というか、そもそもコントローラーってブラウザからのリクエストを処理するところなので、そりゃ直接データを突っ込む`rails db:seed`ではよばれるわけがなかったんです。恥ずかしい。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/321004/86119326-41ee-2cf3-39b6-1ed726f2da30.png)
([Railsチュートリアル](https://railstutorial.jp/chapters/toy_app?version=5.1#sec-mvc_in_action)より引用)