Pictweetの作成 その3
13:ツイートの詳細画面を表示
現在の実装では、ツイートをフィールドに一覧に表示させることはできてもツイッターやフェイスブックのようにその投稿の詳細画面(=個別ページ)はない。そこで、ツイートに詳細ボタンを追加して、ツイートの詳細ページに遷移できるようにする。手順はユーザーのマイページを実装した時とほぼ同じで、以下のとおり。
作業内容
1.ツイート詳細画面のルーティングを記述する
2.tweets controllerにアクションを作成する
3.ツイート詳細画面のビューを追加する
4.ツイート詳細画面へのリンクを作成する
1.ツイート詳細画面へのルーティングを記述
まずはルーティングから記述していく。ツイート詳細画面のルーティングはマイページのルーティングに非常に似ている。こちらもまたツイート毎にパスが異なり、idが1のツイートの詳細画面のパスは/tweets/1のようなパスになる。ツイートに関するアクションなのでtweetsコントローラを使う。アクション名はマイページ同様showアクション
を使う。
1 2 3 |
Rails.application.routes.draw do
get '/コントローラ名/:id' => 'コントローラ名#show'
end
|
11行目を追記する。
1 2 3 4 5 6 7 8 9 10 11 12 |
Rails.application.routes.draw do
devise_for :users
root 'tweets#index'
get 'tweets' => 'tweets#index'
get 'tweets/new' => 'tweets#new'
post 'tweets' => 'tweets#create'
get 'users/:id' => 'users#show'
delete 'tweets/:id' => 'tweets#destroy'
get 'tweets/:id/edit' => 'tweets#edit'
patch 'tweets/:id' => 'tweets#update'
get 'tweets/:id' => 'tweets#show' #ツイート詳細画面
end
|
マイページへのルーティング同様ハッシュ形式でツイートのidの値を受け取っている。
2.tweets_controllerにアクションを定義
次はツイート詳細画面に必要な情報をデータベースから取ってくるためのshowアクションを定義していく。
usersコントローラのshowアクションで実装したことを思い出しながら、showアクションを定義する。
問題14:ツイート詳細ページに表示したい情報をshowアクションに定義する
作業ファイル:app/controllers/tweets_controller.rb
これでツイート詳細画面に遷移するためのアクションを作ることができた。アクションを定義した後はそれに対応するビューを作っていく。
3.ツイート詳細画面のビューを追加
showアクションを定義したら、次はツイート詳細ページのビューを作っていく。ここではtweetsコントローラのshowアクションに対応するビューファイルを作っていく。
まずは必要なビューファイルを作る。
新規作成するファイル
- app/views/tweets/show.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<div class ="contents row">
<div class="content_post" style="background-image: url(<%= @tweet.image %>);">
<% if user_signed_in? && current_user.id == @tweet.user_id %>
<div class="more">
<span><%= image_tag 'arrow_top.png' %></span>
<ul class="more_list">
<li>
<%= link_to '編集', "/tweets/#{@tweet.id}/edit", method: :get %>
</li>
<li>
<%= link_to '削除', "/tweets/#{@tweet.id}", method: :delete %>
</li>
</ul>
</div>
<% end %>
<%= simple_format(@tweet.text) %>
<span class="name">
<a href="/users/<%= @tweet.user.id %>">
<span>投稿者</span><%= @tweet.user.nickname %>
</a>
</span>
</div>
</div>
|
基本的にはviews/tweets/index.html.erbと同じ構造をしている。each文がないことに注意する。
4.ツイート詳細画面へのリンクを作成
次はツイート詳細ページに遷移するためのボタンをビューに追加していく。
今回はログインしていないユーザでも詳細ページに遷移できるようにするために、if文の条件分岐の外に詳細ボタンを追加していく。
ツイートの詳細ページはツイート作成者ではなくても見られるようにしたいので、ツイート右上にあるドロップダウンボタンはツイート投稿者でなくとも表示させるようにする必要がある。
あとはlink_toメソッド
を使いながらshowアクションへのルーティングが走るボタンを作成する。
問題15:ツイート詳細ページに行くボタンを作る
作業ファイル:app/views/tweets/index.html.erb
ヒント:showアクションへのパスをterminalで確認し、link_toメソッドを使いながら実装していく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<div class="content_post" style="background-image: url();">
<div class="more">
<span><%= image_tag 'arrow_top.png' %></span>
<ul class="more_list">
<li>
<ツイート詳細画面を呼び出すためのルーティング>
</li>
<% if user_signed_in? && current_user.id == tweet.user_id %>
<li>
<%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %>
</li>
<li>
<%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %>
</li>
<% end %>
</ul>
</div>
/ 中略
</div>
|
できたらツイート詳細ボタンをクリックして、ツイート詳細画面を確認する。一見ツイート一覧と変わらないが、ツイートが一つしか表示されないような画面が出てきたら成功。
14:ツイートにコメントできるようにする
ツイート詳細画面からツイートに対してコメントができるように実装する。下記のようなものを作っていく。
【例】
コメントはツイートとは別のテーブルで管理しなくてはならない。なので、commentsテーブル
を作る必要がある。さらに、コメントはどのツイートに対してのコメントなのか、そして誰のコメントなのかが明示的でなくてはならない。だとすると、userモデルとtweetモデルの2つとアソシエーションを組む必要がある。
テーブルを作り、ルーティング、コントローラ、ビューを作る流れはこれまでとほぼ同じである。
1点、ルーティングの仕方だけ少し違うので、そこだけ注意する。手順は以下のとおり。
作業内容
1.commentsテーブルを作る
2.ルーティングを記述する
3.フォームを作る
4.コントローラを作り、createアクションを記述する
5.ビューを編集する
1.commentsテーブルを作る
ツイートに対してのコメントをDBに保存するには、新しいテーブルとモデルを作らなければならない。commentを保存するテーブルなのでcommentsテーブル、モデルはcommentモデルという名前になる。テーブルの名前は複数形、モデル名は単数形になっていることに注意。
① commentモデルを作る
1 |
$ rails g model comment
|
新規作成されるファイル
- app/models/comment.rb
- db/migrate/2016XXXXXXXXXXX_create_comments.rb
commentモデルファイルとcommentsテーブルのマイグレーションファイル(=設計図)が作成された。
② マイグレーションファイルを編集
モデルを作った際にマイグレーションファイルができた。このマイグレーションファイルを編集して、commentsテーブルに必要なカラムの情報を追加する。
1 2 3 4 5 6 7 8 9 10 |
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.integer :user_id
t.integer :tweet_id
t.text :text
t.timestamps
end
end
end
|
コメントの本文はtextカラムに保存していく。ここで注意したいのは、カラムにuser_id
とtweet_id
があること。
コメントはまず誰が投稿したコメントなのか分かる必要があるので、結びつくユーザーのidを保存する必要がある。コメントを投稿したユーザーのidを保存するカラムがuser_idとなる。
そしてもうひとつ、コメントはどのツイートに対してのコメントなのか明示する必要がある。結びつくツイートのidを保存するカラムがtweet_idとなる。
③ rake db:migrateをしてテーブルを作成
マイグレーションファイルの記述は完成したので、次はrake db:migrate
を実行してテーブルを作る。
1 |
$ rake db:migrate
|
マイグレーションファイルが実行されたことにより、commentsテーブルができた。Sequel Proを見てテーブルが追加されたことを確認。
④アソシエーションを定義
commentsテーブルのカラムにuser_idとtweet_idを作成したことによって、コメントに結びつくユーザーとツイートのidが保存できるカラムができた。しかし実際にツイートに結びつくコメントを呼び出したり、コメントに結びつくツイートを呼び出したりするにはアソシエーション
というものを定義しなければならない。
なので、次はcommentsテーブル、tweetsテーブル、usersテーブルの3つのテーブルを結びつけるアソシエーションを記述する。やり方はtweetsテーブルとusersテーブルを結びつけた時と同様。
アソシエーションの図は少し複雑ですが、以下の通り。
テーブルとテーブルをつなぐ棒線は一対多の関係を表している。例えばtweetsテーブルからusersテーブルに伸びる棒線は一人のユーザーが複数のツイートを持つことができる関係を表している。
【テーブル図】
では実際にアソシエーションを組んでいく。
1 2 3 4 |
class Comment < ApplicationRecord
belongs_to :tweet #tweetsテーブルとのアソシエーション
belongs_to :user #usersテーブルとのアソシエーション
end
|
コメントは一人のユーザーと一つのツイートに従属するのでbelongs_to :モデル単数形
を使う。
1 2 3 4 |
class Tweet < ApplicationRecord
belongs_to :user
has_many :comments #commentsテーブルとのアソシエーション
end
|
ツイートは複数のコメントを持つことができるので、has_many :モデル複数形
を使ってアソシエーションを組む。
1 2 3 4 5 6 7 8 |
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :tweets
has_many :comments #commentsテーブルとのアソシエーション
end
|
ユーザーは複数のコメントを投稿することができるので、has_many: モデル複数形
を使ってアソシエーションを組む。
これで、3つのテーブルのアソシエーションを組むことができた。
2.ルーティングを記述
コメントを投稿するためのルーティングを記述していく。
ルーティングを簡単に作成するためにresourcesメソッド
を使って定義する。
resourcesメソッド
Railsの基本となるコントローラの7つのアクションで記載した7つのアクション名に対してのルーティングを自動で生成するメソッド。
tweetsテーブルにとっての1ツイート、usersテーブルにとっての1ユーザー、つまり、テーブルに保存されるレコード1つの情報のことをリソース
と呼ぶ。
以下の例のような記述では、book
というリソースに対して、基本となる7つのアクションをリクエストするルーティングが設定される。
【例】
1 2 3 |
Rails.application.routes.draw do
resources :books
end
|
ターミナルにて rake routes
コマンドを実行すると以下のように7つのルーティングが設定されているのが確認できる。
【例】
1 2 3 4 5 6 7 8 9 |
Prefix Verb URI Pattern Controller#Action
books GET /books(.:format) books#index
POST /books(.:format) books#create
new_book GET /books/new(.:format) books#new
edit_book GET /books/:id/edit(.:format) books#edit
book GET /books/:id(.:format) books#show
PATCH /books/:id(.:format) books#update
PUT /books/:id(.:format) books#update
DELETE /books/:id(.:format) books#destroy
|
またcontrollerでbefore_action
の編集をした時と同様に、resourcesメソッドでもexceptやonlyオプションを組み合わせて特定のアクションへのルーティングのみを指定することができる。
コメントについてのルーティングを定義する前に、まずは今まで定義していたツイートとユーザーに関するルーティングをresourcesメソッドを使って書き換える。
1 2 3 4 5 6 |
Rails.application.routes.draw do
devise_for :users
root 'tweets#index'
resources :tweets #tweets_controllerに対してのresourcesメソッド
resources :users, only: [:show] #users_controllerに対してのresourcesメソッド
end
|
tweetsコントローラは7つのアクション全て使っているが、usersコントローラはshowアクションしか使っていないので、only
オプションでアクションを指定する。
できたら、rake routes
コマンドを使ってどのようにルーティングが定義されているか確認。
1 |
$ rake routes
|
コマンドを実行して以下のようなルーティングが表示されれば成功。
1 2 3 4 5 6 7 8 9 10 |
Prefix Verb URI Pattern Controller#Action
# 中略
root GET / tweets#index
tweets_new GET /tweets/new(.:format) tweets#new
tweets POST /tweets(.:format) tweets#create
GET /users/:id(.:format) users#show
DELETE /tweets/:id(.:format) tweets#destroy
GET /tweets/:id/edit(.:format) tweets#edit
PATCH /tweets/:id(.:format) tweets#update
GET /tweets/:id(.:format) tweets#show
|
次はcommentsコントローラのルーティングを記述する。
今まで通りに記述すると、下記のようなルーティングになる。
1 2 3 4 5 6 7 |
Rails.application.routes.draw do
devise_for :users
root 'tweets#index'
resources :tweets
resources :comments, only: [:create]
resources :users, only: [:show]
end
|
このルーティングによって発行されるパスは以下の通り。
1 2 3 4 |
Prefix Verb URI Pattern Controller#Action
# 中略
comments POST /comments(.:format) comments#create
# 中略
|
コメントには必ずコメント先というものが存在している。
今回の場合だと、コメント先のツイートがなければどのツイートに対してのコメントなのか分からなくなってしまう。
しかし今のままではパスの中にどのツイートのコメントなのかということを示す情報がない。
コメントを投稿する際には、どのツイートに対するコメントなのかをパスから判断できるようにしたいので、
ここではネスト
という方法を使っていく。
ルーティングのネスト
ネスト
とは入れ子構造とも呼ばれ、ある記述の中に入れ子構造で別の記述をする方法。
ルーティングでいうと、あるコントローラへのルーティングの記述の中に、別のコントローラへのルーティングを記述するということを指す。
今回の場合では、tweets_controllerのルーティングの中にcomments_controllerのルーティングを記述する。
こうすることによって、commentsのルーティングのパスの中にコメントに結びつくツイートのidの情報が含まれるようになる。
今回はcommentを投稿する(=作る)機能を実装するのでcreate
アクションのルーティングを記述する。
1 2 3 4 5 6 7 8 |
Rails.application.routes.draw do
devise_for :users
root 'tweets#index'
resources :tweets do
resources :comments, only: [:create]
end
resources :users, only: [:show]
end
|
do
とend
で挟むことで中の記述をネストさせることができる。この記述によって、どのようなルーティングになるのか、ターミナルでrake routes
コマンドを実行し、ルーティングを確認してみる。
1 |
$ rake routes
|
コマンドを実行して以下のようなルーティングが表示されれば成功。
1 2 3 4 |
URLに注目すると、ネストによって、/tweets/:tweet_id
/comments(.:format)となっている。リンクを飛ばすときは、この:tweet_id
のところにコメントと結びつくツイートのidを記述する。そうするとparamsのなかにtweet_id
というキーが追加され、コントローラで扱うことができる。
3.フォームを作成
コメントを投稿するためのフォームを作る。コメントはツイートの詳細画面から投稿できると便利なのでviews/tweets/show.html.erbにフォームを記述する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<div class ="contents row">
<div class="content_post" style="background-image: url(<%= @tweet.image %>);">
<% if user_signed_in? && current_user.id == @tweet.user_id %>
<div class="more">
<span><%= image_tag 'arrow_top.png' %></span>
<ul class="more_list">
<li>
<%= link_to '編集', "/tweets/#{@tweet.id}/edit", method: :get %>
</li>
<li>
<%= link_to '削除', "/tweets/#{@tweet.id}", method: :delete %>
</li>
</ul>
</div>
<% end %>
<%= simple_format(@tweet.text) %>
<span class="name">
<a href="/users/<%= @tweet.user.id %>">
<span>投稿者</span><%= @tweet.user.nickname %>
</a>
</span>
</div>
<div class="container">
<!-- ここからフォーム -->
<% if current_user %>
<%= form_tag("/tweets/#{@tweet.id}/comments", method: :post) do %>
<textarea cols="30" name="text" placeholder="コメントする" rows="2"></textarea>
<input type="submit" value="SENT">
<% end %>
<% end %>
</div>
</div>
|
25行目でif current_user
とすることで、ログインしていない状態では投稿フォームを出さないようにしている。これは、ログインしていない状態ではコメントを投稿できないようにするため。
また、form_tagのurlのところに注目すると、
24 |
<%= form_tag("/tweets/#{@tweet.id}/comments", method: :post) do %>
|
HTTPメソッドがpostメソッドになっている。これはツイートのフォームを作った際と同じ。ここで重要なところはform_tagの後に続く括弧の中のurlです。このurlの中に式展開させながら@tweet.idを記述することによって、paramsの中にコメントと結びつくツイートのidが追加される。
4.コントローラを作り、createアクションを記述
コントローラとアクションを作ります。まずはcommentsコントローラを作る必要がある。rails gコマンドでcommentsコントローラを作る。
1 |
$ rails g controller comments
|
モデルと対応しているコントローラの名前は必ず複数形にする。
新規作成されるファイル
- app/controllers/comments_controller.rb
- app/views/comments
これでcommentsコントローラを作ることができた。次はcommentsコントローラ内でcreateアクションを作る。
createアクションは以下の手順で進めていく。
1.createアクションを定義する
2.ストロングパラメーターを指定する
3.リダイレクト先をtweets/show.htmlにする
① createアクションを定義
まずはcommentsコントローラにcreateアクションを定義する。
1 2 3 4 5 |
クラスに対してcreateメソッド
を使ってインスタンスを作成し、データベースに保存。引数では、カラムと、カラムの値を指定している。まずtextカラムにはparamsの中の"text"という情報を保存する。user_idカラムにはcurrent_user.id(=ログインしているユーザーのid)を保存している。
次にtweet_idカラムだが、ネストしてurlに@tweet.id(ツイートのid)を記述してあるのでparams[:tweet_id]
とするだけで簡単に情報を得ることができる。
ネストはなぜ必要なのか?
もしネストしていなかったらどうなるのか。
【例】
↓ネストさせた場合
1 2 3 4 |
↓ネストさせなかった場合
1 2 3 4 |
Prefix Verb URI Pattern Controller#Action
# 中略
comments POST /comments(.:format) comments#create
# 中略
|
ネストをさせない場合だと、:tweet_id
となっている部分が無くなっている。そうすると、コメントと結びつくツイートのid情報が送れなくなる。先ほど、コントローラでtweet_idカラムの値を指定した。しかしネストしていない場合だとツイートのidの情報をコントローラに送れていないので、tweet_idカラムの値を保存することができなくなってしまう(厳密に言えば他の方法はあるのだが、この方法が一番良いとされている)。
まとめると、ルーティングをネストさせる一番の理由はアソシエーション先のレコードのidをparamsに追加してコントローラに送るためである。今回の実装だと、コメントと結びつくツイートのidをparamsに追加している。
② ストロングパラメーターを指定
先程までのアクションの記述でコメントの保存はできる。しかしストロングパラメーターを指定していないので、不正な情報を受け取ってしまう可能性がある。なので次はストロングパラメーターを指定して、必要なparamsだけを取ってこれるようにする。
1 2 3 4 5 6 7 8 9 10 |
tweetsコントローラのcreateアクションを作った際とほぼ同じ。privateメソッドを使って、comment_paramsメソッドをコントローラ以外のところで呼び出せないようにしていて、permitメソッドで受け取るparamsのキーを指定している。
③ リダイレクト先をtweets/show.html.erbにする
ルーティングを記述し、アクションを作成したら次に行うことはビューを作ること。しかし今回は新しくビューファイルを作るのではなく、既にあるツイートの詳細画面に戻る処理を記述する。redirect_toメソッド
を使って画面遷移の記述をしていく。
1 2 3 4 5 6 7 8 9 10 11 |
class CommentsController < ApplicationController
def create
comment = Comment.create(text: comment_params[:text], tweet_id: comment_params[:tweet_id], user_id: current_user.id)
redirect_to "/tweets/#{comment.tweet.id}" #コメントと結びつくツイートの詳細画面に遷移する
end
private
def comment_params
params.permit(:text, :tweet_id)
end
end
|
redirect_toの後にはルーティングのurlやprefixを記述することでそのアクションを実行することができた。
tweetsコントローラのshowアクションを実行するにはツイートのidが必要。ここではアソシエーションを使って、commentと結びつくツイートのidを記述している。
5.ビューを編集
① コメントをビューに表示させる
コメントを投稿した後は、自分が投稿したコメントや他のユーザーのコメントが読めたりするといい。コメントを表示する場所はツイートの詳細画面とする。このようにするとコメントを投稿した後に、先ほど記述したredirect_toメソッドによってツイートの詳細画面に遷移してコメントの確認ができる。
しかしその前に、ツイートの詳細画面でツイートと結びつくコメントを表示するためには、その前のコントローラの段階で、コメントのレコードをデータベースから取得する必要がある。
なのでまずはtweetsコントローラのshowアクションを編集する。
1 2 3 4 5 6 7 8 9 10 11 12 |
表示させたいツイートのレコード@tweet
は既に取得できているので、ツイートと結びつくコメントのレコードをデータベースから取得する。
tweetsテーブルとcommentsテーブルはアソシエーションが組まれているので、@tweet.commentsとすることで、@tweetについて投稿された全てのコメントのレコードを取得することができる。
また、ビューでは誰のコメントかを明らかにするために、アソシエーションを使ってユーザーのレコードを取得する処理をする。その時にN + 1問題
が発生してしまうので、includesメソッド
を使って、N + 1問題を解決している点に注意する。
コントローラで、あるツイートについて投稿された全てのコメントのレコードを取得することができたので、それをビューで表示する。@comments
には複数のコメントのレコードが入っているので配列の形をとっている。なのでビューに表示させるためにはeachメソッド
を使って、ひとつひとつのレコードに分解してから表示させる。
さらに、コメントをしたユーザー名をクリックしたらそのユーザーページに遷移するようにしたいので、名前のところにlink_toメソッド
を使ってリンクを作る(リンクはまだクリックしないように)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 中略
<div class="container">
<% if current_user %>
<%= form_tag("/tweets/#{@tweet.id}/comments", method: :post) do %>
<textarea cols="30" name="text" placeholder="コメントする" rows="2"></textarea>
<input type="submit" value="SENT">
<% end %>
<% end %>
<!-- ここから追記してください -->
<div class="comments">
<h4><コメント一覧></h4>
<% if @comments %>
<% @comments.each do |comment| %>
<p>
<strong><%= link_to comment.user.nickname, "/users/#{comment.user_id}" %>:</strong>
<%= comment.text %>
</p>
<% end %>
<% end %>
</div>
</div>
</div>
|
こちらではif @comments
とすることで、もし@commentsが空だった場合でもエラーが起こらないようにしている。
eachメソッドで繰り返しコメントを表示する。名前のところはlink_toメソッドを使ってリンクを作る。ユーザーのidはcomment.user_id
と記述することで、コメントを投稿したユーザーのidをparamsとして送る。
16行目で「comment.user」と記述し、コメントしたユーザーのレコードを取得しているが、includesメソッドで既にデータベースからレコードを取得しているのでSQL文は発行されず、N + 1問題は発生しない。
② cssファイルを編集する
コメントの一覧画面を綺麗に表示させるために、cssファイルを編集する。
style.scssの318行目以降のコードを全て削除して、以下のコードをコピー&ペーストをして編集しましょう。
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
.success {
@include boxBase(20px 0 ,30px);
box-shadow: 0 0 10px rgba($black,0.2);
background-color: $white;
box-sizing: border-box;
text-align: center;
h3 {
@include baseMargin;
}
}
}
form {
h3 {
@include baseMargin(0 0 20px);
text-align: center;
font-weight: normal;
font-size: 20px;
color: $dark;
}
input,textarea {
width: 100%;
@include boxBase(5px 0 15px,10px);
border: 1px solid $gray;
border-radius: 5px;
font-family: "游ゴシック", "YuGothic";
}
input[type="submit"] {
@extend .transition;
background-color: $accent;
border-radius: 20px;
color: #fff;
border: 0;
font-size: 18px;
&:hover {
background-color: lighten($accent, 10%);
}
}
}
.container {
@include boxBase(20px 0 ,30px);
box-shadow: 0 0 10px rgba($black,0.2);
background-color: $white;
box-sizing: border-box;
}
.comments {
padding: 5px;
margin-top: 15px;
a {
color: $accent;
&:hover {
text-decoration: underline;
}
}
}
footer {
@include boxBase;
color: $gray;
p {
text-align: center;
}
}
|
少しcssのクラスを編集したので、他のビューファイルも編集する必要がある。以下のようにtweets/new.html.erbを編集する
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="contents row">
<div class="container">
<%= form_tag('/tweets', method: :post) do %>
<h3>
投稿する
</h3>
<input placeholder="Image Url" type="text" name="image">
<textarea cols="30" name="text" placeholder="text" rows="10"></textarea>
<input type="submit" value="SENT">
<% end %>
</div>
</div>
|
③ コントローラーを編集することでそれぞれのユーザーのマイページのビューの表示を正しくする
今、users_controller.rb
は以下のようになっている。
1 2 3 4 5 6 |
class UsersController < ApplicationController
def show
@nickname = current_user.nickname
@tweets = current_user.tweets.page(params[:page]).per(5).order("created_at DESC")
end
end
|
今までユーザーページは自分自身のツイートしか見られない仕様になっていたので、current_userを使うことでログインしているユーザー、即ち自分自身のツイートを見ることが出来た。しかし、今はツイートの詳細ページからコメントをしたユーザーのページに遷移出来るような仕様になっている。なのでcurrent_userを使ったままでは、どのユーザー名をクリックしても自分自身のユーザーページしか表示されない(実際に試してみると、そのようになっているはず)。クリックしたユーザーのページに遷移できるように、users_controller
のshowアクション編集する。
1 2 3 4 5 6 7 |
class UsersController < ApplicationController
def show
user = User.find(params[:id])
@nickname = user.nickname
@tweets = user.tweets.page(params[:page]).per(5).order("created_at DESC")
end
end
|
3行目では、コメント欄に表示されるユーザーをクリックすることで送られたidをparams
で取得して、そのユーザーのレコードをuser
という変数に代入している。
4行目では、current_user
からuser
変数に変えて、nickname
の値を取り出している。
5 行目ではアソシエーションを利用することで、そのユーザーが投稿したツイートを取得して、@tweets
に代入している。
15:部分テンプレートを使ってみる
以前書いたコードを「部分テンプレート」を利用して書き換えてみる。
部分テンプレート
ビューを作成する際に同じようなレイアウトの部分が複数存在する場合がある。例えば、Twitterでは複数のツイートが表示されているが、これらのHTML構造は全て同じである。このような場合に同じHTML構造の部分を共通化することによって、無駄なくビューファイルを作成することが出来る。この共通化された部分を、 部分テンプレートという。 部分テンプレートを使用した場合、そのHTML構造を他のビューでも使いまわすことができるという利点も存在する。
部分テンプレートのファイル名は必ずアンダーバー「_」から始まる。
Pictweetもツイートを複数表示している箇所があった。以下のツイートの一覧画面である。
この箇所を部分テンプレートに書き換えてみたい。
ピンクの枠で囲まれた部分がひとつのツイートで、この構造の繰り返しでツイートの一覧が出来ている。
ここでこの部分を別のファイルに切り出して、そのファイルを呼び出して使う。
このページのビューファイル(tweets/index.html.erb)は以下のようになっている。この中のeachメソッドで囲まれた3~28行目がひとつのツイートのテンプレートになっている。この部分を別のファイルに切り出す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<div class="contents row">
<% @tweets.each do |tweet| %>
<div class="content_post" style="background-image: url(<%= tweet.image %>);">
<div class="more">
<span><%= image_tag 'arrow_top.png' %></span>
<ul class="more_list">
<li>
<%= link_to "詳細", tweet_path(tweet.id), method: :get %>
</li>
<% if user_signed_in? && current_user.id == tweet.user_id %>
<li>
<%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %>
</li>
<li>
<%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %>
</li>
<% end %>
</ul>
</div>
<%= simple_format(tweet.text) %>
<span class="name">
<a href="/users/<%= tweet.user_id %>">
<span>投稿者</span><%= tweet.user.nickname %>
</a>
</span>
</div>
<% end %>
<%= paginate(@tweets) %>
</div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<div class="content_post" style="background-image: url(<%= tweet.image %>);">
<div class="more">
<span><%= image_tag 'arrow_top.png' %></span>
<ul class="more_list">
<li>
<%= link_to "詳細", tweet_path(tweet.id), method: :get %>
</li>
<% if user_signed_in? && current_user.id == tweet.user_id %>
<li>
<%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %>
</li>
<li>
<%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %>
</li>
<% end %>
</ul>
</div>
<%= simple_format(tweet.text) %>
<span class="name">
<a href="/users/<%= tweet.user_id %>">
<span>投稿者</span><%= tweet.user.nickname %>
</a>
</span>
</div>
|
ここではコピー&ペーストのショートカットも使えるが、より作業がスムーズなカット&ペーストのショートカットを使ってみる。カットとは選択した部分をコピーして削除することであり、command + x
がショートカットキーになる。
あとはcommand + v
を使って選択した部分を適切な場所に貼り付ける。
この_tweet.html.erb
ファイルがツイートひとつ分のHTML構造を表す部分テンプレートになる。
続いて、こちらの部分テンプレートを呼び出す。そのためには、renderメソッドを利用する。
render
renderメソッドは、部分テンプレートを呼び出す際に利用するメソッド。
render partialオプション
renderメソッドに :partial
というオプションをつけることで、明示的に部分テンプレート名を指定し、部分テンプレートを表示することができる。
下の例では、_sample.html.erbという部分テンプレートを呼び出している。
1 |
render partial: "sample"
|
render localsオプション
また、localsというオプションを用いると部分テンプレート内でその変数を使えるようになる。
1 |
render partial: "sample", locals: { tweet: "hello!" }
|
これで部分テンプレート内においてhello!
という文字列の代入されたtweet
という変数が使えるようになる。
では、renderメソッドを利用して_tweet.html.erbを呼び出す。
1 2 3 4 5 6 |
ここでのrenderメソッドのlocalsオプションに注目してみる。{ tweet: tweet }
の右側の tweet
はeachメソッド内の変数としてのtweet
でtweetのインスタンスを示している。一方、左側の tweet
は部分テンプレート内での変数の名前を表している。
例えば、
と名前を変更すると、tweetというインスタンスを部分テンプレートの中ではpartial_tweet
として使えるということである。
その後サーバーを立ち上げた状態で「localhost:3000/tweets」にアクセスすると、これまでと同様にツイートの一覧が表示されたら成功。
※表示自体は変わらないため、エラーが起きなければ問題ない。
表示自体が変わらないにも関わらず、部分テンプレートを用いるのは繰り返し再利用出来るから。
再利用できるメリットとして
・繰り返し書くコードを一回で済ます
・修正するときに修正箇所が少なく済む
などがある。
では、実際に先ほど作成した部分テンプレートを別の箇所にも適用させてみる。
マイページの一覧画面にも部分テンプレートを適用出来る。
1 2 3 4 5 6 7 |
※違うフォルダ内の部分テンプレートを呼び出すときは tweets/tweet
のようにどのフォルダの部分テンプレートを使用しているかを明示的に記載する。
その後サーバーを立ち上げた状態でマイページにアクセスして、これまでと同様にツイートの一覧が表示されたら成功。
このように部分テンプレートを使うと同じHTML構造を使い回すことが出来る。