#対象者
- ブックマーク機能を実装したい方
- お気に入り、いいね機能を実装したい方
#目的
- ブックマークして記事の保存や後で見るための機能を実装をする
#実際の手順と実例
###1.前提
- 投稿機能実装済
- device機能実装済
- ArticleとUserモデルがある
###2.モデルの作成
ターミナルでモデルを作成します。
$ rails g model Bookmark user:references article:references
実行後はマイグレーションファイルを編集します。
class CreateBookmarks < ActiveRecord::Migration[5.2]
def change
create_table :bookmarks do |t|
t.references :user, foreign_key: true, null: false
t.references :article, foreign_key: true, null: false #①
t.timestamps
end
end
end
①null:falseを設定したのはデータベースへの空の保存を防ぐためになります。
続いてブックマークモデルのアソシエーションとバリデーションを設定していきます。
class Bookmark < ApplicationRecord
belongs_to :user
belongs_to :article
validates :user_id, uniqueness: { scope: :article_id } #①
end
①二度連続(重複)して登録することを防いでいます。(ロード中等)
続いて各モデルにアソシエーション設定をしていきます
class Article < ApplicationRecord
belongs_to :user
has_many :bookmarks, dependent: :destroy
def bookmarked_by?(user)
bookmarks.where(user_id: user).exists? #①
end
①既にブックマークしていないか検証しています。
class User < ApplicationRecord
has_many :articles, dependent: :destroy
has_many :bookmarks, dependent: :destroy
end
###3.コントローラーの作成
$ rails g controller bookmarks
class Public::BookmarksController < ApplicationController
before_action :authenticate_user!
def create
@article = Article.find(params[:article_id])
@bookmark = @article.bookmarks.new(user_id: current_user.id) #①
if @bookmark.save
else
redirect_to request.referer #②
end
end
def destroy
@article = Article.find(params[:article_id])
@bookmark = @article.bookmarks.find_by(user_id: current_user.id)
if @bookmark.present? #③
@bookmark.destroy
else
redirect_to request.referer
end
end
end
①まずarticle_idを取得し、その後にuser_idにcurrent_userを紐付けて@bookmarkに渡しています。
②非同期処理を行うため投稿が成功した場合はリロードせずに返します。投稿失敗した場合に現在いるページにリダイレクトされるようになっています。
③既にブックマーク済かどうか判断しています。ブックマーク済なら、削除が実行されます。
ルーティングは以下の通りです。
articleにネストさせています。
resources :articles, except: [:index] do
resource :bookmarks, only: [:create, :destroy]
end
###4.Viewの作成・修正
ブックマークボタンを部分テンプレート化してどこでも使えるようにします。下記は記事の詳細ページに設置しています。
<div id="bookmark_button_<%= @article.id %>"#①
<%= render "bookmarks/bookmark", article: @article %> #②
</div>
①bookmark_buttonをキーにして非同期処理を呼び出しています。
②部分テンプレート内の変数はご自身のコントローラーで設定した値を入れてください。ここではarticles_controller.rbに@article = Article.find(params[:id])を定義してます。
以下部分テンプレートの設定です。
<% if article.bookmarked_by?(current_user) %>
<%= link_to "", article_bookmarks_path(article), method: :delete, remote: true, class: "fas fa-bookmark" %></td>
<% else %>
<%= link_to "", article_bookmarks_path(article), method: :post, remote: true, class: "far fa-bookmark" %></td>
<% end %>
````
* remote:trueでjavascript形式のリクエストを送信しています。
* fas fa-bookmarkでブックマークボタンを表しています(fontawesome)
https://fontawesome.com/v5.15/icons/bookmark?style=regular
###5.jsファイル作成
```app/views/bookmarks/create.js.erb
$('#bookmark_button_<%= @article.id %>').html("<%= j(render "bookmarks/bookmark", article: @article) %>");
```
```app/views/bookmarks/destroy.js.erb
$('#bookmark_button_<%= @article.id %>').html("<%= j(render "bookmarks/bookmark", article: @article) %>");
```
⚠シングルクオーテーションかダブルクオーテーションかでエラーになるので注意してください!
これで実装完了です!
##参照
[【Rails×Ajax】いいね機能ハンズオン](https://qiita.com/naberina/items/c6b5c8d7756cb882fb20)
####投稿者コメント
ブックマークでもいいねでもやることは変わらないので、次回実装するときはサクサク実装できそうです。
####My Profile
プログラミング学習歴3ヶ月目のアカウントです!
プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。
また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。
もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。