Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

[Rails]お気に入り機能

実装したいこと

  • クチコミ投稿者以外がお気に入り登録をできる
  • Ajax通信でお気に入り機能を実装
  • マイページにお気に入り一覧を表示

目次

  1. お気に入りテーブルの作成
  2. アソシエーション
  3. ルーティング
  4. コントローラー
  5. ビュー

1. お気に入りテーブルの作成

どの投稿に、誰がお気に入りをしたのかを保存するために、お気に入りのテーブルを作成します。

ターミナル
rails g model like
db/migrate/20XXXXXXXXXXXX_create_likes.rb
class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      t.references :user, null: false, foreign_key: true
      t.references :post, null: false, foreign_key: true
      t.timestamps
      t.index [:user_id, :post_id], unique: true   # 同じユーザーが同じ投稿をお気に入りできないようにしている
    end
  end
end

2. アソシエーション

dependent: :destroyオプションを用いて、投稿が削除されたらお気に入りも削除されるようにします。親モデルに記述します。

app/models/likei.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy
  has_one_attached :image
  ~~
end
app/models/user.rb
class User < ApplicationRecord
 ~~
  has_many :posts
  has_many :likes
  has_one_attached :image
end

3. ルーティング

どの投稿に対してのお気に入りかを示す必要があるので、ルーティングネストを用います。
お気に入りの登録と削除ができたら良いので、「createアクション」と「destroyアクション」を設定します。

config.routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'posts#top'
  resources :posts do
    collection do
      get 'top'
    end
    resources :likes, only: [:create, :destroy]
  end
  resources :users, only: [:show]
end

4. コントローラー

コントローラーに「createアクション」と「destroyアクション」を定義します。
お気に入りの登録は投稿者以外が登録可能にしています。

app/controllers/likes_controller.rb
class LikesController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    if @post.user_id != current_user.id   # 自分の投稿以外にお気に入り登録が可能
      @like = Like.create(user_id: current_user.id, post_id: @post.id)
    end
  end
  def destroy
    @post = Post.find(params[:post_id])
    @like = Like.find_by(user_id: current_user.id, post_id: @post.id)
    @like.destroy
  end
end

マイページにユーザーがお気に入り登録をした投稿を表示します。
Likesテーブルからログイン中のユーザーのお気に入りを検索し、降順に並べ、post_idを配列の形で取り出します。
それをfindメソッドで@likesにデータを格納しています。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    birthday = @user.birthday
    date_format = "%Y%m%d"
    @age = (Date.today.strftime(date_format).to_i - birthday.strftime(date_format).to_i) / 10000
    @posts = @user.posts.order(created_at: :desc)

    likes = Like.where(user_id: current_user.id).order(created_at: :desc).pluck(:post_id)
    @likes = Post.find(likes)
  end
end

5. ビュー

お気に入りボタンを表示する部分を部分テンプレートを作成して表示します。
if文でお気に入り登録している時としていない時で分岐しています。

app/views/posts/show.html.erb
~~
<div class='star-btn' id="like_<%= @post.id %>">
  <%= render "likes/like", post: @post %>
</div>
~略~
app/views/likes/_like.html.erb
<% if !Like.exists?(user_id: current_user.id, post_id: post.id) %>
  <%= link_to post_likes_path(post.id), method: :post, remote: true, class:"like-btn" do %>
    <i class="fas fa-star star-icon1"></i>
  <% end %>
  <span class="star-count1"><%= post.likes.count %></span>
<% else %>
  <%= link_to post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true, class:"like-btn" do %>
    <i class="fas fa-star star-icon2"></i>
  <% end %>
  <span class="star-count2"><%= post.likes.count %></span>
<% end %>

お気に入りボタンがクリックされたときに「createアクション」または「destroyアクション」が実行され、部分テンプレートがAjax通信で更新されお気に入りボタンが表示されます。
※ jqueryで記述しているためインストールが必要です。

app/views/likes/create.js.erb
$('#like_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: { post: @post }) %>");
app/views/likes/destroy.js.erb
$('#like_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: { post: @post }) %>");

マイページのお気に入り一覧はeach文を用いて表示しています。

app/views/users/show.html.erb
<% if @likes.present? %>
    <ul class="relative-post-lists">
    <% @likes.each do |like| %>
      <li class='post-list'>
        <%= link_to post_path(like.id) do %>
        <div class='post-img-content'>
          <%= image_tag like.image, class: "post-img" %>
        </div>
          <div class='relative-post-name'>
            商品名:<%= like.name %>
          </div>
          <div class='relative-item-price'>
            <span><%= like.price %>円(税込み)</span>
          </div>
          <div class="relative-post-evaluation">
            <span>評価:</span>
              <span id="star-rate-<%= like.id %>"></span>
              <script>
                $('#star-rate-<%= like.id %>').raty({
                  size: 36,
                  starOff: "<%= asset_path('star-off.png') %>",
                  starOn: "<%= asset_path('star-on.png') %>",
                  starHalf: "<%= asset_path('star-half.png') %>",
                  half: true,
                  readOnly: true,
                  score: <%= like.evaluation %>,
                });
              </script>
              <%= like.evaluation %>
          </div>
          <div class='user-img-content'>
            <%= image_tag like.user.image ,class:"mini-img" if like.user.image.attached? %>
            <%= link_to like.user.nickname, user_path(like.user.id) %>
          </div>
          <div class="relative-post-date">
            更新日時:<%= like.created_at.strftime("%Y.%m.%d") %>
          </div>
        <% end %>
      </li>
    <% end %>
    </ul>
  <% end %>

参考リンク

https://qiita.com/manbolila/items/43a04e8d0d5018cf7f62
https://qiita.com/naberina/items/c6b5c8d7756cb882fb20
https://qiita.com/naota7118/items/e009eff939b5a764672d

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?