実装したい機能
- 投稿表示ページに、お気に入りボタン設置
- 記事投稿者でない、ログイン中のユーザーのみお気に入り登録できる。
- 記事投稿者、未ログインは、お気に入り登録できない。
- お気に入りボタンをクリックしたら、Ajax通信でデータ保存。
- ユーザーのマイページに、お気に入りリスト一覧を表示。
実装の手順
- お気に入りテーブル作成(ユーザー/投稿テーブルの中間テーブルに相当)
- ルーティング: 記事投稿のルーティングにネストの形で記述。
- お気に入りコントローラー: create、destroyアクションを定義。
- Userコントローラー: ユーザーのマイページで呼び出す
- ビュー: 投稿表示ページに、お気に入りボタン設置。マイページに、お気に入り一覧表示。
1. お気に入りテーブル作成
- 誰が、どの記事に、お気に入りしたかを管理したいので、favoriteモデル(user_id、post_idカラム)を生成。
ターミナル
% rails g model Favorite user:references post:references # Favoriteモデル作成
% rails db:migrate # 下記のマイグレーション実行
マイグレーションファイル
class CreateFavorites < ActiveRecord::Migration[5.0]
def change
create_table :favorites do |t|
t.references :user, foreign_key: true, null: false
t.references :post, foreign_key: true, null: false
t.timestamps
end
end
end
アソシエーションを組む。
ユーザー、記事が削除されたら、お気に入りも削除(dependent: :destroy オプション)。
user.rb
has_many :favorites, dependent: :destroy # ユーザー/お気に入り → 1:多
post.rb
belongs_to :user, optional: true
has_many :favorites, dependent: :destroy # 記事/お気に入り → 1:多
favorite.rb
belongs_to :user # ユーザー/お気に入り → 1:多
belongs_to :post # 記事/お気に入り → 1:多
validates_uniqueness_of :post_id, scope: :user_id # バリデーション(ユーザーと記事の組み合わせは一意)
# 同じ投稿を複数回お気に入り登録させないため。
2. ルーティング
- 記事詳細表示のルーティングにネスト(createとdestory)。
- マイページのルーティングにネスト。
routes.rb
# マイページのルーティングにネスト
resources :users, only: [:show, :edit, :update] do
get :favorites, on: :collection
end
# 記事詳細表示のルーティングにネスト
resources :posts, expect: [:index] do
resource :favorites, only: [:create, :destroy]
end
3、 4. コントローラーの定義
- 誰が?: ログイン中のユーザー(current_user)に限定。投稿者本人は除外。
- どの投稿に?: 自分が投稿していない投稿に対して。
- お気に入りコントローラー: create、destroyアクションを定義。
- Userコントローラー: ユーザーのマイページで呼び出す
favorites_controller
before_action :set_post
before_action :authenticate_user! # ログイン中のユーザーのみに許可(未ログインなら、ログイン画面へ移動)
# お気に入り登録
def create
if @post.user_id != current_user.id # 投稿者本人以外に限定
@favorite = Favorite.create(user_id: current_user.id, post_id: @post.id)
end
end
# お気に入り削除
def destroy
@favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id)
@favorite.destroy
end
private
def set_post
@post = Post.find(params[:post_id])
end
user_controller
def show
@user = User.find(params[:id])
@posts = @user.posts
favorites = Favorite.where(user_id: current_user.id).pluck(:post_id) # ログイン中のユーザーのお気に入りのpost_idカラムを取得
@favorite_list = Post.find(favorites) # postsテーブルから、お気に入り登録済みのレコードを取得
end
5. ビュー
- 投稿表示ページに、お気に入りボタン設置。
- お気に入り登録/削除でAjax通信(remote: true で実装)。
- Ajax通信で、お気に入り登録数の表示を更新。登録済みか?でボタンcssも変更させる。
favorites/_favorite.html.haml
#favorite{ id: @post.id }
- if !Favorite.exists?(user: current_user, post_id: @post.id) # お気に入り未登録の時
= link_to post_favorites_path(@post), method: :post, remote: true, class: "favorite-btn favorite-post" do
.i.far.fa-thumbs-up
%span いいね!
= @post.favorites.length # お気に入り登録数
- else # お気に入り登録済みの時
= link_to post_favorites_path(@post), method: :delete, remote: true, class: "favorite-btn favorite-delete" do
.i.far.fa-thumbs-up
%span いいね!
= @post.favorites.length # お気に入り登録数
-
remote: true : link_to や form_for/form_tag、button_to などに使えるオプション。
・ データは、コントローラーへ送信し、Ajax通信してくれる。ページ移動ではなく、JSを探してくれる(ページ移動ストップ)。
・ お気に入りボタンクリックで、発火させるJSの動作を_favorite.html.hamlと同じ階層に作成(Ajax通信で呼び出す)。
favorites/create.erb
$("#favorite_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
<!-- j メソッドで、JSとして認識してくれて、非同期で更新してくれる -->
favorites/destroy.erb
$("#favorite_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
- マイページに、お気に入り一覧表示。
users/show.html.haml
- if @favorite_list.present?
- @favorite_list.each do |post|
= post.title
= post.content