18
15

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 5 years have passed since last update.

【Rails】いいね機能の実装(.js.erbを用いない方法)【プログラミング学習165日目】

Last updated at Posted at 2019-02-07

はじめに

今回は、ポートフォリオにいいね機能を実装してみて色々と苦労したので備忘録も兼ねて記事にしていきたいと思います。

「Rails いいね 実装」などでググると以下の記事を始めとして色々な方が解説してくれている記事を見つけることができます。

Railsチュートリアルライクな実装方法はこれらを見れば理解できたので、これらの記事と少し方法を変えてみたいなというモチベーションから.js.erbを用いない方法でいいね機能を実装してみました。

前提

  • スパイスカレー屋の検索・共有アプリを作成しており、UserがShopにいいねできるようにしたいという状況を想定しています。
  • 既にUserリソースとShopリソースは作成されている状態から始めます。
  • 認証系にはDeviseを用いており、current_userメソッドやuser_signed_in?メソッド等が使用できる状態であるとします。
  • ビューは.erbではなく、.slimを使って書きます。
  • 本記事ではいいねボタンはBootstrap4で簡単にスタイリングします。
  • 最終的には以下のように動くことを狙います。

ダウンロード (1).gif

開発環境

  • Ruby 2.5.1
  • Rails 5.2.2

いいね実装の具体的な流れ

必要なGemのインストール

jQueryが使えるようにあらかじめ'jquery-rails'を入れておきます。

Gemfile
gem 'jquery-rails'

そしてapplication.jsを以下のようにしておきます。
(順番に気を付けないとコンソールでエラーが消えてくれないので注意です。)

app/assets/javascripts/application.js
・・・
//= require jquery3
//= require rails-ujs
//= require popper
//= require turbolinks
//= require bootstrap-sprockets
//= require_tree .

Likeモデルの作成・編集

まずLikeモデルを作成していきます。

>_ターミナル
$ rails g model Like user:references shop:references

モデル生成時にデータ型をreferencesとしておくことで、以下のように何もしなくてもLikeモデルがUserとShopと関連付いていると認識してくれるようになるので便利です。

app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :shop
end

いいね機能において、UserとShopの関係は1:1なので、マイグレーションファイルにadd_index :likes, [:user_id, :shop_id], unique: trueを追記しておきます。

その後、$ rails db:migrateを実行します。

db/migrate/[timestamp]_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.2]
  def change
    create_table :likes do |t|
      t.references :shop, foreign_key: true
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :likes, [:user_id, :shop_id], unique: true
  end
end

次に各モデルに関連付けを行います。
Shopモデルにおけるlike_usersは後で使うのでひとまずこのような形で記述しておいて下さい。

app/models/user.rb
class User < ApplicationRecord
  ・・・
  has_many :likes, dependent: :destroy
  has_many :like_shops, through: :likes, source: :shop
  ・・・
end
app/models/shop.rb
class Shop < ApplicationRecord
  ・・・
  has_many :likes, dependent: :destroy
  has_many :like_users, through: :likes, source: :user
  ・・・
end

Likesコントローラの作成・ルーティング設定

コントローラを作成します。

>_ターミナル
$ rails g controller Likes

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

今回はcreateアクションのみで実装しようと思うのでcreateだけあればOKです。

Rails.application.routes.draw do
  ・・・
  resources :likes, only: [:create]
  ・・・
end

Likesコントローラ実装

さて、コントローラの中身を実装していきます。

createアクション+toggleメソッドでいいねといいねの解除を表現しています。

いいねの保存や削除が上手く行かなかったら422のレスポンスを返して問題なく機能すれば200のレスポンスを返すというとてもシンプルな構造です。

app/controllers/likes_controller.rb
class LikesController < ApplicationController
  def create
    @shop = Shop.find(params[:shop_id])
    @like = current_user.likes.find_by(shop: @shop)
    toggle
  end

  private

    def toggle
      if @like
        return head :unprocessable_entity unless @like.destroy
      else
        @like = current_user.likes.build(shop: @shop)
        return head :unprocessable_entity unless @like.save
      end
      head :ok
    end
end

ビューの編集

いいねボタンといいね解除ボタンについて記述していきます。
今回はlikes/_like.html.slimというパーシャルを作ってそこにボタンのビューを書きました。

ポイントを箇条書きします。

  • ユーザーがログインしている時だけいいねボタンが表示されるようにuser_signed_in?メソッドを用いて表現しています。
  • いいねボタンを押した時にJavaScript(jQuery)が動くようにid="link-mark"を用意しています。
  • Bootstrap4でボタンのスタイリングをしています。
  • data-shop_id="#{@shop.id}"は後程出てくるlike.jsに対象店舗のid情報を送信するために必要です。
  • like?メソッドでユーザーが対象の店舗に既にいいねしているかの判断をしています。
app/views/likes/_like.html.slim
- if user_signed_in?
  - unless @shop.like?(current_user)
    a#link-mark.btn.btn-primary.text-white data-shop_id="#{@shop.id}" いいね!
  - else
    a#link-mark.btn.btn-secondary.text-white data-shop_id="#{@shop.id}" いいね!を取り消す

そして、like?メソッドは自作しなければならないので、Shopモデルで実装していきます。

各モデルを関連付けしたことによってinclude?メソッドが使用できるようになっているので、それを用いてユーザーが対象店舗に既にいいねをしているかしていないかをtrueまたはfalseで返しています。

app/models/shop.rb
class Shop < ApplicationRecord
  ・・・
  has_many :likes, dependent: :destroy
  has_many :like_users, through: :likes, source: :user
  ・・・
  def like?(user)
    like_users.include?(user)
  end
end

like.jsの作成

さて、これでいいねボタンとLikesコントローラのcreateアクションの実装まで終えました。

あとは、いいねボタンを押したらLikesコントローラのcreateアクションで処理が行われるようにJavaScript(jQuery)の実装をしてビューとコントローラを繋いでいけば狙い通りの動きをしてくれるはずです。

そこで、先程のlikeパーシャルの最後に<script> タグを生やすためにjavascript_include_tagヘルパーを用います。

app/views/likes/_like.html.slim
・・・
= javascript_include_tag('like')

javascript_include_tag(like)app/assets/javascriptsフォルダの中にあるlike.jsファイルを呼出しますよという意味なので、$ touch app/assets/javascripts/like.jsのようにしてファイルを作成します。

そして以下のようにAjax処理を記述してあげれば完成です。
実装内容についてはコメントを付けているのでそちらを参照して下さい。

app/assets/javascripts/like.js
$(function(){
  // id="link-mark"の箇所(いいねボタン)をクリックしたら
  $('#link-mark').on('click', function(){
    // 非同期でlikes#createに処理を送信+その時に店舗情報(shop_id)を渡す
    $.ajax({
      url: '/likes',
      type: 'POST',
      data: {shop_id: $(this).data('shop_id')}
    })
    // 処理が上手く行ったらボタンを切り替えて
    .done((data) => {
      if ($(this).text() === 'いいね!') {
        $(this).text('いいね!を取り消す').removeClass('btn-primary').addClass('btn-secondary');
      } else if ($(this).text() === 'いいね!を取り消す') {
        $(this).text('いいね!').removeClass('btn-secondary').addClass('btn-primary');
      }
    })
    // 処理が上手く行かなかったら失敗の旨を伝えるアラートを表示
    .fail((data) => {
      alert('いいね!に失敗しました');
    })
  });
});

最終的にはきちんとボタンを押したら非同期でボタンが切り替わって、Likesテーブルにいいねが追加されたり削除されたりするようになります。(このgifではLikesテーブルにいいねが追加されたり削除されたりする様子は分かりませんが。。。笑)

ダウンロード (1).gif

まとめ

  • .js.erbを用いない方法でいいね機能を実装してみました。
  • 色々調べたり聞いたりしながら、RailsにおけるAjax処理の実装方法のパターンを増やせるようになったので大きな収穫でした。

参考URL

18
15
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
18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?