LoginSignup
0
0

More than 3 years have passed since last update.

[Rails] いいねをつける

Last updated at Posted at 2021-02-11

1人のユーザーは1つのプロフィールにいいねでき、
MVCの命名は「like」とします。

Image from Gyazo

% rails g model like user_id:integer profile_id:integer
モデルとマイグレーションファイルを作成します。

テーブル

db/migrate/〜_create_likes.rb
class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      t.integer :user_id
      t.integer :profile_id

      t.timestamps
    end
  end
end

% rails db:migrate
likesテーブルを作成します。

モデル

models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :profile
end
models/profile.rb
has_many :likes, dependent: :destroy
models/user.rb
has_many :likes, dependent: :destroy

アソシエーションを定義します。


models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :profile

  validates_uniqueness_of :profile_id, scope: :user_id
end

バリデーションを設定します。
profile_idとuser_idの組み合わせが一意性という制限です。

scopeとは、modelで使えるActive Recordの機能の一部です。
Railsガイド


models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :profile

  validates_uniqueness_of :profile_id, scope: :user_id

  def already_liked?(profile)
    self.likes.exists?(profile_id: profile.id)
  end
end

ユーザーが既にいいねしているかを確認するメソッドを作成します。
理由は、このあとviewsで条件分岐に使うからです。

exists?
該当の値があればtrue、なければfalseを返すメソッドです。

selfにはcurrent_userのデータが入ります。
データが入ってない現在のselfの中はこんな感じ↓
Image from Gyazo

要約:
current_userに紐付いたlikesテーブルの中で、profile_id:に現在いいねしようとしている
プロフィールのidが存在しているか?

ルーティング

config/routes.rb
resources :profiles do
    resource :likes, only: [:create, :destroy]
end

ルーティングを設定します。

コントローラー

likes_controller.rb
class LikesController < ApplicationController
  def create
    @like = current_user.likes.create(profile_id: params[:profile_id])
    redirect_back(fallback_location: root_path)
  end

  def destroy
    @profile = Profile.find(params[:profile_id])
    @like = current_user.likes.find_by(profile_id: @profile.id)
    @like.destroy
    redirect_back(fallback_location: root_path)
  end
end

redirect_back
直前のページにリダイレクトするメソッドです。
index, showなど、複数の画面でいいねしたいのでこう書いています。
もしshowでしかいいねしないといった場合はredirect_toで大丈夫です。

fallback_location: url
もし直前のページが見つからなかった場合に、指定のURLに遷移させます。
エラーが発生したケースを想定しての記述です。

ビュー

views/show.html.erb
 <% if user_signed_in? && current_user != @profile.user %>
      <% if current_user.already_liked?(@profile) %>
        <%= link_to profile_likes_path(@profile), method: :delete do %>
          <svg xmlns="http://www.w3.org/2000/svg" class= "unlike" width="16" height="16" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
            <path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
          </svg>
        <% end %>
      <% else %>
        <%= link_to profile_likes_path(@profile), method: :post do %>
          <svg xmlns="http://www.w3.org/2000/svg" class= "like" width="16" height="16" fill="currentColor" class="bi bi-heart" viewBox="0 0 16 16">
            <path d="M8 2.748l-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"/>
          </svg>
        <% end %>
      <% end %>
      <%= @profile.likes.count %>
  <% end %>

ビューを作成します。
少し前に作成したalready_liked?メソッドを使って、
既にいいねしてあれば削除、まだいいねしてなければ保存するという条件分岐をします。

svgはアイコンの表示です。
<%= @profile.likes.count %>はいいねの数を表示しています。


views/show.html.erb
<%= link_to profile_likes_path(@profile), method: :post do %>

<%= link_to profile_likes_path(@profile), method: :post, remote: true do %>

非同期にする場合は、link_toにremote: trueを付与します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0