Rails Tutorial 第13章を見ていきます。


この章で学ぶこと


  • 投稿フォームの作成


概念


組立方


  1. Micropostデータモデル作成

  2. Userモデルと関連づけ

  3. 関連付けに伴う細かなこと

  4. 投稿内容の一覧表示

  5. 投稿フォームのview作成

  6. 画像の投稿

  7. 画像のバリデーション


1. Micropostデータモデル作成

image.png

rails generate model Micropost content:text user:references

これによって、Micropostと紐づける必要のあるuserに関する項目が色々追加されます。


app/models/micropost.rb

class Micropost < ApplicationRecord

belongs_to :user
end

=> MicropostがUserと1対1の関係であることを示すコード


db/migrate/[timestamp]_create_microposts.rb

class CreateMicroposts < ActiveRecord::Migration[5.0]

def change
create_table :microposts do |t|
t.text :content
t.references :user, foreign_key: true

t.timestamps
end
add_index :microposts, [:user_id, :created_at] #この行は追加してください
end
end


=> UserとMicropostを関連づける下準備が完了

indexをuser_idとcreated_atにつけているのは、

micropostsをuser_idに紐づくcreated_atの降順で取得することを前提にしているからです。

なお、このindexが配列になっているのは両方を使って検索することを前提としているからです。

user_idとcreated_atがセットで検索されることが前提の場合はできるだけまとめてあげたほうが高速化するようです。


2. Userモデルと関連づけ

1.でMicropostモデルを作成するのと合わせてUserモデルとの関連づける下準備をしたので、

ここではもっとその関連を強めてアクセスしやすくします。

具体的には下記のようにしたい。

image.png

必要なのはuser.rbとmicropost.rbにhas_manybelongs_toを追加してあげること。


app/models/user.rb

class User < ApplicationRecord

has_many :microposts
...
end


3. 関連付けに伴う細かなこと


▼ user.micropostsでcreated_atの降順に取り出すようにしたい


app/models/micropost.rb

...

default_scope -> { order(created_at: :desc) }
#SQL得意な人は下記のような書き方も可能
#default_scope -> { order('created_at DESC') }
...

矢印みたいなのはラムダ式という文法らしく、深い内容を探りすぎると時間かかりすぎそうなので、

ここでは「ブロックを引数に取り、Procオブジェクトというものを生成している」と理解しておきます。

ちなみに、callメソッドでProcオブジェクトの中身は見れるようです。


▼ userが削除されたらuser.micropostsも削除する


app/models/user.rb

class User < ApplicationRecord

has_many :microposts, dependent: :destory
...
end

has_manyのオプションに加えてあげるだけで終わりです。


4. 投稿内容の一覧表示

今回は、 users#show のページにmicropostsを表示するということをします。

大枠としては下記のような感じです。



  • users_controller.rbshowメソッドが呼び出される。


  • users_controller.rbの中で@microposts = user.microposts のインスタンスを作成する。


    • 3までで既にuserとmicropostを関連づけてるので実現できる。




  • users/show.html.erb内で@micropostsをレンダリングする。


    • そうすると、microposts/_micropost.html.erbが呼び出されて描画される。



  • paginationを入れる。

ポイントはMicropostのデータをUsers_controllerで表示するということです。


▼ 投稿一覧を表示


controllers/users_controller.rb

def show

@user = User.find(params[:id])
@microposts = @user.microposts
end

=> ユーザーに紐づく投稿を全て取得


views/users/show.html.erb

<%= render @microposts %>


=> _micropost.html.erbを投稿の数だけ呼び出して描画

=> その時に @microposts.each { |micropost| render micropost } が内部ではおきている。


views/microposts/_micropost.html.erb

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

<%= link_to gravatar_for(micropost.user, size:50), user_path(micropost.user) %>
<span class="user"><%= link_to micropost.user.name, user_path(micropost.user) %></span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>

=> あくまでshow.html.erbのパーシャルなので、変数はそのまま利用できる。


▼ ページング

まずは必要なGemを追加。


Gemfile

gem 'will_paginate'

gem 'bootstrap-will_paginate'

あとは、ページングしたいページで表示件数を制御し、ページングのビューを出してあげる。


controllers/users_controller.rb

@microposts = @user.microposts.paginate(page: params[:page])


=> params[:page]でページを取得している。一律30件で制御されている。


views/users/show.html.erb

<%= will_paginate @microposts %>


=> will_paginateだけではusersの一覧だと思ってしまうので引数で明示してあげる。


5. 投稿フォームのview作成



  • routes.rbの設定

  • ログイン状態の確認

  • createアクションの作成

  • フォームの作成


routes.rb

resources(:microposts, only:[:create, :destroy])


=> Micropostではviewを持たないのでcreateとdestroyだけに制御する


controllers/application_controller.rb

def logged_in_user

unless logged_in?
flash[:danger] = "Please Login"
redirect_to login_url
end
end

=> まずはログイン状態を確かめるメソッドをどこからでもアクセス可能なように作成する


controllers/micropost_controller.rb

before_action(:logged_in_user, only: [:create, :destroy])


=> 投稿に関するコントーラーが動き出すタイミングでログイン状態をチェックする


controllers/micropost_controller.rb

def create

@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created"
redirect_to root_url
else
render 'static_pages/home'
end
end

private
def micropost_params
params.require(:micropost).permit(:content)
end


=> Strong Parametersで投稿のオブジェクトを作成し、DBに保存できるのかどうかで投稿が成功したかどうかを判別


_micropost_form.html.erb

<%= form_for(@micropost) do |f| %>

<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

=> テキストを入力するtext_areaと投稿するsubmitボタンをform_forで作成


6. 画像の投稿


  • 必要なGemのインストール

  • 画像アップローダーの生成

  • Micropostにカラムを生成

  • CarrierWaveに画像と関連づけたモデルを伝える

  • ファイルアップロードボタンの作成


Gemfile

...

gem 'carrierwave'
gem 'mini_magick'
...
group :production do
...
gem 'fog'
end
...

=> 画像アップローダーと、画像圧縮と本番にあげるように必要なgemのインストール

rails generate uploader Picture

=> carrierwaveによってuploaderが使えるようになります。

rails generate migration add_picture_to_microposts picture:string

rails db:migrate

=> DBにカラムの追加


models/micropost.rb

...

mount_uploader :picture, PictureUploader
...

=> アップローダーの設定をモデルのカラムと紐づける


controllers/microposts_controller.rb

@micropost = current_user.microposts.build(micropost_params)

private
def micropost_params
params.require(:micropost).permit(:content, :picture)
end


=> 画像がアップロードされるときに :picture も許可する


_micropost_form.html.erb

<span class="picture">

<%= f.file_field :picture %>
</span>

=> 画像をアップロードするボタンを設置


views/microposts/_micropost.html.erb

<%= image_tag micropost.picture.url if micropost.picture? %>


=> 画像を表示する領域


7. 画像のバリデーション