3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

railsで動画サイトにいいね機能実装

Last updated at Posted at 2020-03-18

動画サイトにいいね機能を実装してみた

今では当たり前になりつつあるいいね機能ですが、どのように作られているか気になりポートフォリオに試しに実装してみました。1ユーザーが複数の動画に対していいねをし、また1動画に対して複数のユーザーがいいねする、いわば多対多の関係なので、ユーザーと動画の中間テーブルとしていいねテーブルを作成していきます。

完成図

image.png

環境

rails : v5.2.4.1

〜実装済み機能〜
・動画(一覧表示、新規投稿、詳細、編集、削除)
・ユーザー(新規登録、ログイン、編集、ログアウト、詳細)
・ページネーション

実装手順

※いいね機能に関する部分のみコメントアウトで説明書きしています。

  • ターミナルからLikeモデル作成
$ rails g model Like user:references video:references
$ rails db:migrate
models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  validates :name, presence: true, uniqueness: true
  has_many :videos, dependent: :destroy
  # いいねはユーザーのdestroyに依存
  has_many :likes, dependent: :destroy
  # ユーザーがいいねしている動画
  has_many :liked_videos, through: :likes, source: :video
  # いいねしているかどうかを判定
  def already_liked?(video)
    self.likes.exists?(video_id: video.id)
  end
end
models/video.rb
class Video < ApplicationRecord
  validates :name, :work, presence: true
  belongs_to :user
  # いいねは動画のdestroyに依存
  has_many :likes, dependent: :destroy
  # 動画にいいねしているユーザー
  has_many :liked_users, through: :likes, source: :user

  mount_uploader :work, VideoUploader
end
models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :video
  # 1人が1つの動画に1いいね
  validates_uniqueness_of :video_id, scope: :user_id
end
  • ターミナルからlikesコントローラー作成
$ rails g controller likes
  • 「いいねする」「いいねを取り消す」だけなのでcreate、destroyのみ追加
config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  root "videos#index"
  resources :users, only: [:edit, :update, :show]
  # いいねを動画にネストさせる
  resources :videos do
    resources :likes, only: [:create, :destroy]
  end
end
controllers/likes_controller.rb
class LikesController < ApplicationController

  def create
    # 今ログインしているユーザーによるいいね
    @like = current_user.likes.create(video_id: params[:video_id])
    # 今いる画面にリダイレクト
    redirect_back(fallback_location: root_path)
  end

  def destroy
    # 今ログインしているユーザーがいいねしている動画を探す
    @like = Like.find_by(video_id: params[:video_id], user_id: current_user.id)
    # いいねを取り消す
    @like.destroy
    # 今いる画面にリダイレクト
    redirect_back(fallback_location: root_path)
  end

end
controllers/videos_controller.rb
class VideosController < ApplicationController
  
  def index
    @videos = Video.includes(:user).page(params[:page]).order("created_at DESC").per(12)
  end

  def new
    @video = Video.new
  end

  def create
    Video.create(video_params)
    redirect_to root_path
  end

  def show
    @video = Video.find(params[:id])
    # いいねする
    @like = Like.new
  end

  def edit
    @video = Video.find(params[:id])
  end

  def update
    video = Video.find(params[:id])
    video.update(video_params)
  end

  def destroy
    video = Video.find(params[:id])
    video.destroy
    redirect_to root_path
  end

  private

  def video_params
    params.require(:video).permit(:name, :work).merge(user_id: current_user.id)
  end

end

end

ログインしていないユーザーもいいね数を見れるようにする場合はこのように分岐が必要

show.html.haml
-# ログインしている場合
- if user_signed_in?
  -# かつ既にいいねしている場合
  - if current_user.already_liked?(@video)
    = link_to video_like_path(@video), method: :delete do
      = icon('fa', 'heart', class: 'content__show__box__top__icons__heart__already')
  -# かつまだいいねしていない場合
  - else
    = link_to video_likes_path(@video), method: :post do
      = icon('far', 'heart', class: 'content__show__box__top__icons__heart__yet')
-# ログインしていない場合
- else
  = icon('far', 'heart', class: 'content__show__box__top__icons__heart__yet')
    = @video.likes.count

videoのindexコントローラーではvideoのidカラムが取得できず、エラーでnilが返されてしまう。
そのため一覧画面では表示のみとし、いいねボタンは動作しないように設定。

index.html.haml
-# ログインしている場合、かつ既にいいねしている場合
- if user_signed_in? && current_user.already_liked?(video)
  = icon('fa', 'heart', class: 'content__box__top__icons__heart__already')
-# それ意外の場合
- else
  = icon('far', 'heart', class: 'content__box__top__icons__heart__yet')
    = video.likes.count

課題

  • 動画の一覧表示(index)、ユーザーごとの詳細ページ(users/show)でもいいねできるようにする
  • いちいち画面更新せず非同期で反映されるようにJqueryもしくはVueに書き換え

参考

3
4
1

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
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?