タグのタイトルを動的に出力
♡app/helpers/application_helper.rbに必要な内容を記載
♡app/views/layouts/application.html.erbで全体に反映する
♡各viewに反映する
①app/helpers/application_helper.rbに必要な内容を記載
module ApplicationHelper
def page_title(page_title = '')
base_title = 'タイトル'
page_title.empty? ? base_title : "#{page_title} | #{base_title}"
end
end
以下のようにしたところ
Prefer string interpolation to string concatenation.
とRubocopで弾かれた。
def page_title(page_title = '')
base_title = 'タイトル'
page_title.empty? ? base_title : page_title + ' | ' + base_title
end
②app/views/layouts/application.html.erbで全体に反映する
<head>
<title><%= page_title(yield(:title)) %></title>
</head>
③各viewに反映する
app/views下に
<%= content_for(:title, t('.title')) %>
を追加していく。
<% content_for(:title, @board.title) %>
にする。
コメントの編集・削除ボタン表示の判定ロジック(モデルに記載)
今回の実装ではコメントの編集・削除ボタンが表示されるのは、コメントを投稿した本人のみにしたい。
判定ロジックを用いるが、今後メンテナンスをするときのためにmodelのみにまとめて記載する。
♡判定ロジックはUserモデルにまとめて記載する等、自分で決めておくと良いらしい。
♡クラスメソッドに定義して引数に判定に必要なオブジェクトをすべて記載するのではなく、インスタンスメソッドとして記載する
→クラスメソッドにしてしまうと判定用のオブジェクトを全て記載しなければいけなくなるため。
self.my_comment?(current_user, comment)
current_user == comment.user
end
とした場合、
if User.my_comment?(current_user, @comment)
としなければならなくなる。
インスタンスメソッドで記載した場合、
if current_user.my_comment?(@comment)
となり、引数の記載がシンプルになる。
♡comment.user→ belongs_to :user の定義により使えるようになるアソシエーションメソッド。
♡commentモデルにある user_id を使うよう指定してしまうと、Usersテーブルから対象のレコードを取ってきてしまい、SQLが発行される。
♡comment.user_idであれば、commentインスタンスが持っている user_id を使って比較しているに過ぎないのでSQLが発行されない。
♡selfレシーバは省略して記載するのが一般的。(Rubocopにも引っかかった)
(なので理想的な形は以下のようになるそう)
app/model/user.rbの場合
def my_comment?(comment)
self == comment.user
end
# 表示したいview側で以下のように記載
# if current_user.my_comment?(@comment)
def my_comment?(comment)
comment.user_id == id
# comment.user_id == self.id の省略形
end
最も汎用性の高い記載方法↓
def own?(object)
object.user_id == id
end
view側で以下のように記述する。
<% if current_user.own?(comment) %>
routes/controllerへの追記(コメント機能の追加)
♡config/routes.rbへの追記
♡app/boards.controller.rbへの追記(詳細画面の追加)
♡app/comments.controller.rbの設定
①config/routes.rbへの追記(ネストする)
resources :boards, only: %i[index new create show] do
resources :comments, only: %i[create], shallow: true
shallowオプションを用いてcommentsをboardsを入れ子の形にする。
あくまでも浅いネストにするため、深いネストにならないよう、idを用いないアクションのみを親のスコープ下で生成する。
idを用いるアクション→show、edit、update、destroy等
②app/boards.controller.rbへの追記(詳細画面の追加)
showアクションの追記をする。
☆N+1問題を解消するため.includes(:user)
を追記する。
belongs_toやhas_manyでアソシエーションを組んでいるため、異なるテーブルから逐一データを取得してしまう。解消して無駄をなくす。
☆作成したコメントが降順で表示されるよう、.order(created_at: :desc)
を記入する。
desc = descendingの略。
def show
@board = Board.find(params[:id])
@comment = Comment.new
@comments = @board.comments.includes(:user).order(created_at: :desc)
end
③app/comments.controller.rbの作成
rails g controller comments
のコマンドでコントローラーを作成。
class CommentsController < ApplicationController
def create
comment = current_user.comments.new(comment_params)
if comment.save
redirect_to board_path(comment.board), success: t('.success')
else
redirect_to board_path(comment.board), danger: t('.fail')
end
end
private
def comment_params
params.require(:comment).permit(:body).merge(board_id: params[:board_id])
end
end
☆ストロングパラメーター
requireの引数 = モデル名
permitの引数 = カラム名
☆merge部分について
.merge(board_id: params[:board_id])
merge = 結合
board_id: params[:board_id]はルーティングをネストしており、URLにboard.idが含まれているため。
idはユーザーが記入しているわけではないが、作成している側がDBに保存しておきたいため、mergeを用いることで同時にboard.idについても保存することができるようにしている。
※コメント登録失敗時に、renderを用いていないのは今後実装する内容に関わってくるため。
コメントのDB作成と紐付け
<手順>
♡コメントのデータベースの作成し、DB側に制約をかける
♡userとboardモデルに紐付けをする
♡コメントモデルに制約を追加する
①コメントのデータベースを作成
rails g model Comment body:text user:references board:references
外部キー制約をする際はreferencesを用いるので、作成時のコマンドに入れる
文字数制限を60000字(仮)にする→limit: 60000を追記
bodyすなわち本文の入力なしでは登録できないようにする→null:falseを追記
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.text :body, limit: 60000, null:false
t.references :user, foreign_key: true
t.references :board, foreign_key: true
t.timestamps
end
end
end
②userとboardモデルに紐付けていく
☆app/models/comment.rb
①でgenerateした際に、referencesを用いたため、
自動的にbelongs_to :user
とbelongs_to :board
が作成されている。
class Comment < ApplicationRecord
belongs_to :user
belongs_to :board
end
app/models下のuser.rbとboard.rbに
has_many :comments, dependent: :destroy
を追加し、以下のようにする。
☆app/models/user.rb
class User < ApplicationRecord
authenticates_with_sorcery!
has_many :boards, dependent: :destroy
has_many :comments, dependent: :destroy
end
☆app/models/board.rb
class Board < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
end
board.rbもuser.rbと紐付いているのでこんがらがる…。
③コメントモデルにバリデーションを追加する
class Comment < ApplicationRecord
belongs_to :user
belongs_to :board
validates :body, presence: true, length: { maximum: 60_000 }
end
エラーメッセージのパーシャル作成
ユーザーの新規登録や掲示板の作成時に、「○○を入力してください」というフラッシュメッセージを表示したいが、複数のビュー画面で表記したい。
効率的に表記するためにパーシャルを作成する。
すでにモデルにバリデーションは記載してあるので、続きからの手順をメモしておく。
<手順>
♡(今回は事前に行っている状態だが、モデルにバリデーションを設定しておく)
♡エラーメッセージのパーシャル作成
♡表示したいビューに埋め込んでいく
①モデルにバリデーションの設定がなされているか確認
app/models/board.rb
class Board < ApplicationRecord
belongs_to :user
mount_uploader :board_image, BoardImageUploader
validates :title, presence: true, length: { maximum: 255 }
validates :body, presence: true, length: { maximum: 65_535 }
end
設定できているので、次にすすむ
②エラーメッセージのパーシャルを作成する
<% if object.errors.any? %>
<div id="error_explanation" class="alert alert-warning">
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
③表示したいビューに埋め込んでいく
新規登録画面と掲示板作成画面に以下のように埋め込む。
object: f.objectとすることで、流用する際にそのまま使えて便利になる。
掲示板(作成機能)
サイトに登録している人がboardを作成できるよう機能を作る。
①ビュー側から入力された情報を保存する
app/controllers/boards_controller.rb
のcreateに以下のように定義する。
def create
@board = current_user.boards.new(board_params)
if @board.save
current_user.boards.new
で、
・初期化することができる
・viewで入力フォームに入ってきた情報のオブジェクトを作成することができる
②ストロングパラメーターの記載
boards_controller.rbのpraivate下に、
def board_params
params.require(:board).permit(:title, :body)
end
を記載する。
ストロングパラメーター(復習)
①ストロングパラメーターとは
web上から入力されるパラメーターが安全なものか検証し、取得する仕組み。
メソッドに受け付けるカラム名(id,name等列にあたる部分)を事前に指定しておく。
指定されていないカラムデータが送られてきた場合、弾くことができる。
②実際に記載してみる
適用したい、今回はapp/controllers下のusers.controller.rbに以下のように記載する。