チワワかわいいブログ

RUNTEQでrails勉強する日々の記録

2020/12/19 railsチュートリアル13章

マイクロポストモデルを作る

micropostモデルを作る

contentカラムはtext型にする→DB上ではstringでもtextでもパフォーマンスはほとんど変わらない、将来の変更に柔軟性が高い(データ上限が多いので)
has_many --再登場、userとmicropostを関連付けする

rails generate model Micropost content:text user:references #user_id:integerの省略形
=> 自動的にbelongs_to :userを入れてくれる

t.references :user, null: false, foreign_key: true
=> 他のモデルのIDを参照するときはnullチェックした方がいい
=> 外部キー(foreign_key)を参照するためだよということをDBに伝える  

複合キーインデックス、よく引くテーブルの高速化をする。
=>micropostの投稿とuser_idのセット

最初のテスト
  test "user id should be present" do
    @micropost.user_id = nil
    assert_not @micropost.valid?
  end

migrationのときにnull: falseとしているので、特にvalidationをかいていなくても勝手に弾かれる(assert_notがtrue)

関連付け(Association)

関連付けすると生成されるメソッドがある
user.microposts => Micropost.find_by(user_id: user.id)
user.microposts.create => Micropost.create(user_id: user.id)
user.microposts.build => Micropost.new(user_id: user.id)
user.microposts.find_by(content: ..) => Micropost.find_by(user_id: user.id, content: ...) etc..
関連付けした場合は慣習的にこちらのメソッドを使う(user_idで検索する時の間違いとかが起きないなど、色々メリットがある、current_userが投稿するときにmicropostsへのuser_idの紐付けを間違えたりとか)

@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
↓ 
@micropost = @user.microposts.build(content: "Lorem ipsum")

ここのbuildメソッドはnewメソッドと同義、関連付けしたものということがわかりやすいようにこちらを使う

micropost.user   Micropostに紐付いたUserオブジェクトを返す
user.microposts Userのマイクロポストの集合をかえす
user.microposts.create(arg) userに紐付いたマイクロポストを作成する
user.microposts.create!(arg)    userに紐付いたマイクロポストを作成する(失敗時に例外を発生)
user.microposts.build(arg)  userに紐付いた新しいMicropostオブジェクトを返す
user.microposts.find_by(id: 1)  userに紐付いていて、idが1であるマイクロポストを検索する
マイクロポストの順序付け
default_scope -> { order(created_at: :desc) }

->{...} はRubyのProcオブジェクト
ブロックを変数に渡すことができる

irb(main):001:0> a = -> { puts "Hi" }
=> #<Proc:0x00007fac3e82f248@(irb):1 (lambda)>
irb(main):002:0> a
=> #<Proc:0x00007fac3e82f248@(irb):1 (lambda)>
irb(main):003:0> a.call #callしたときにメソッドを実行する
Hi
=> nil
userの依存

Userが破棄されたらMicropostも破棄する

has_many :microposts, dependent: :destroy

User.rbのテストなので、userのテストに書こう

Micropostを表示する

userのshowテンプレートに表示しよう

コントローラ生成

表示には使わないが、micropostの投稿とか編集とか削除には使うので作ろう

表示用パーシャル
<ol class="microposts">
  <%= render @microposts %>
</ol>

前回やったやつ、renderの引数に一覧データを渡して_micropost.html.erbを呼び出す

<li id="micropost-<%= micropost.id %>">

CSSidを別々に割り振っていくことで、javascriptなど使うときとかに有効活用できるらしい

showアクションで投稿を表示
def show
    @user = User.find(params[:id])
    @microposts = @user.microposts.paginate(page: params[:page])
  end

showアクションに@micropostのインスタンス変数を追加して表示できるようにする

サンプルデータ作成
# ユーザーの一部を対象にマイクロポストを生成する
users = User.order(:created_at).take(6) #まずユーザーを抽出
50.times do
  content = Faker::Lorem.sentence(word_count: 5) #5文字程度のデータをcontentにいれる
  users.each { |user| user.microposts.create!(content: content) } #micropostを作る
end
micropostを操作する
  def create
    @micropost = current_user.microposts.build(micropost_params) #関連付けが終わっているので、user_idはデフォルトで入っているので、contentだけparamsから持ってくればいい
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      render 'static_pages/home'
    end
  end

  def micropost_params
      params.require(:micropost).permit(:content) #contentだけ持って来れればいい
  end

どこから投稿できるようにするか→今回はhomeからやる(homeにフォームを置き、ログインしてたらフォームが出るように切り替える )

<% if logged_in? %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <%= render 'shared/user_info' %> #ユーザー情報
      </section>
      <section class="micropost_form">
        <%= render 'shared/micropost_form' %> #投稿フォーム
      </section>
    </aside>
  </div>
<% else %>

投稿フォーム

<%= form_with(model: @micropost, local: true) do |f| %> #micropostモデルのオブジェクトを置くことでフォームの送信先を指定、あるかないかでリクエストは勝手に選ばれる
  <%= render 'shared/error_messages', object: f.object %> #エラーメッセージ用。
 <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

error_messagesのパーシャルは、@user用に作っているので@micropostには使えない。
パーシャルのobjectが自動で変わればいいのに・・・
f.object => フォーム作るときに参照(モデルとか)したオブジェクト
object = f.objectとすることでパーシャルの中身を動的に変更できるように修正する

投稿の表示
def feed
  Micropost.where("user_id = ?", id)
end

where("user_id = ?", id) Micropost tableの中から、次の条件に一致したデータを探す。"user_id = ?"は文字列だがSQL文。
"?"に次の引数"(self.)id"がはいる。
フィードの中身

<% if @feed_items.any? %>
  <ol class="microposts">
    <%= render @feed_items %> # each文で表示
  </ol>
  <%= will_paginate @feed_items %>
<% end %>
投稿した時に表示エラー

createアクションで失敗に分岐した時は@feed_itemがないのでエラーになる。
@feed_itemをその分岐の中でも作ってあげると解決