[Rails]いいね機能の作成(モデル編)
モデル
class Task < ApplicationRecord
belongs_to :user
has_many :comments, class_name: "TaskComment", dependent: :destroy
has_many :favorites, dependent: :destroy
def favorited_by?(user)
favorites.exists?(user_id: user.id)
end
end
下記部分について、疑問点が多かったので調べていく!
def favorited_by?(user)
favorites.exists?(user_id: user.id)
end
favorited_by?
はビューで「ユーザがその投稿をいいねしてたら、マークの色を変える」のに使う!
<% if post.favorited_by?(current_user) %>
<button class="favorited">★ お気に入り済み</button>
<% else %>
<button class="not-favorited">☆ お気に入り</button>
<% end %>
こうすることで、すでにいいねしている場合はボタンの見た目を変える ことができる!
なぜモデルで定義するのか?
ビューで使う機能なら、ビューやコントローラーに書いてもいい気がする。
なぜモデルに書くの?
モデルって「お気に入り機能がユーザやタスクなどほかのモデルとどのようにつながってるか?」「いいねモデルってどんな決まり・形式なのか?」を書くのであって、「ユーザがいいねしてるか」はモデルと関係なくない?
(1) ビジネスロジックをモデルにまとめるため
モデルは「ビジネスロジック」をまとめるもの。
ビジネスロジック
アプリケーションの「業務的なルール」や「データ処理のルール」。
データの取得・計算・判定など、アプリケーションの振る舞いを決定する処理。
そのアプリケーションならではのルールが「ビジネスロジック」。
例えば、今回のアプリケーションだと、ユーザはタスクを複数作成できる・タスク名は30字以内・ユーザがそのタスクをお気に入りしてるかどうか?
favorited_by?(user)
は 「この投稿がユーザーにいいねされているか?」 という データに関する判断 をするメソッド。
このような 「データベースに保存されてる情報を元に、特定の条件を満たすかどうかのチェック」 は、コントローラーではなくモデルに書くのがRailsの設計として適切 。
(Fat Modelの原則)
Fat Model
⇒モデルが持つ責任を多くする。
下記みたいにコントローラーで書くこともできるけど、
show
アクションなどに特定のアクション内に書くことになり、他のアクションやコントローラー内でも使いたくなったときに同じコードを何度も書く必要がある ので、モデルにメソッドとして定義しておく方が便利 。
@favorited = Favorite.exists?(post_id: @post.id, user_id: current_user.id)
モデル・コントローラ・ビューのそれぞれの役割
レイヤー | 役割 | 例 |
---|---|---|
ビジネスロジック (モデル) | アプリケーションのデータのルール・処理 | favorited_by?(user) |
アプリケーションロジック (コントローラー) | 画面やAPIにデータを渡す | @post = Post.find(params[:id]) |
プレゼンテーションロジック (ビュー) | 画面の表示に関する処理 | if post.favorited_by?(current_user) |
【それぞれの役割】
✅ ビジネスロジック (モデル)
- データの処理や業務ルールの実装
- 例: 「ユーザーがいいねしているか判定する」
✅ アプリケーションロジック (コントローラー)
- どのデータをどこ(ビューやAPI)に渡すか?
- 例:
@favorited = @post.favorited_by?(current_user)
✅ プレゼンテーションロジック (ビュー)
- どこにどうやって表示させるか?
- 例:
if post.favorited_by?(current_user)
「ユーザーが特定の投稿をお気に入り登録しているか?」は、そのアプリの「お気に入り機能」のルール 。
「ユーザーが投稿をお気に入り登録できる」
「お気に入り登録しているかどうか判定できる」
これらのルールはアプリの動作に関わる 業務的な判断 なので、ビジネスロジック。
(2) コントローラーがシンプルになる
モデルに favorited_by?(user)
を定義していれば、コントローラーはこんな感じでスッキリする。
(Skinny Controllerの原則)
Skinny Controller
⇒コントローラはできるだけシンプルにする
@favorited = @post.favorited_by?(current_user)
コントローラーの責務は「データの取得・画面に渡すこと」なので、ロジックはモデル側に寄せるのがベスト 。
favorited_by?(user)
部分について
by
には特別な意味はなく、単なるメソッド名の一部。
favorited_by?(user)
というメソッド名は 「このオブジェクトが指定したユーザー(user
)によってお気に入り登録されているか?」 を判定することを意図してる。
by
の役割
英語的なニュアンスとして、by
は 「~によって」 という意味を持つ前置詞です。そのため、favorited_by?
という名前は 「(特定のユーザー)によってお気に入り登録されているか?」 という意味になります。
いいね機能のモデルをlikeで作成したなら、likedと書いてもいい。
例:
def liked_by?(user)
likes.exists?(user_id: user.id)
end
アクションの過去形_by(xx)?
の形にすると、「xx(ユーザーや特定の条件)によって、そのアクションが実行されたか?」 を判定するメソッドになる。
例1: 投稿が特定のユーザーによって作成されたかを判定
def created_by?(user)
self.user_id == user.id
end
例2: ユーザーが特定の投稿を編集できるか判定
def editable_by?(user)
user.admin? || self.user_id == user.id
end
favorites.exists?(user_id: user.id)
部分について
def favorited_by?(user)
favorites.exists?(user_id: user.id)
end
favorites には この投稿をお気に入り登録したすべてのユーザー情報が入ってる。
favorites テーブルに、user_id が今ログインしている user.id のレコードが存在するか?をチェックしてる。
思ったよりモデルだけで書くことが多くなってしまったので、
コントローラ・ビューについては明日以降まとめます!
MVCの役割が学べて面白かった!
参考文献
Discussion