Hotwire: Rails: ライクボタンの習作

下記のツイートを参考にした Hotwire の習作です。
likes のルーティングは todos 配下に変更しました。
ビューはスキャフォールドをベースにしています。
今回の例では Turbo Flames を使わないため  _like.html.erb のタグは turbo_frame_tag から div に変更しました。
current_user はユーザ1固定です。

完成画面

画像1

環境
ruby 3.1.2
Rails 7.0.3

実装
rails new hw_like_button && cd hw_like_button
bin/rails g model User name
bin/rails g scaffold Todo description
bin/rails g model Like description user:belongs_to todo:belongs_to
bin/rails db:migrate

ユーザ2名とTodo3件を用意する。
ユーザ1はTodo2をライクしている。
ユーザ2はTodo3をライクしている。

# users.yml
user_1:
  name: user_1
user_2:
  name: user_2

# todos.yml
todo_1:
  description: todo_1
todo_2:
  description: todo_2
todo_3:
  description: todo_3

# likes.yml
user_1_likes_todo_2:
  user: user_1
  todo: todo_2
user_2_likes_todo_3:
  user: user_2
  todo: todo_3

bin/rails db:fixtures:load

Rails.application.routes.draw do
  resources :todos do
    resources :likes, only: %i[ create destroy ]
  end
  root "todos#index"
end

class Todo
  has_many :likes
end

class TodosController < ApplicationController
# ...
  def index
    @todos = Todo.all.order(:description)
  end
# ...
end

class ApplicationController < ActionController::Base
  helper_method :current_user

  def current_user
    @current_user ||= User.find_by(name: "user_1")
  end
end

bin/rails generate controller likes --skip-routes
rm app/helpers/likes_helper.rb
rm test/controllers/likes_controller_test.rb
touch app/views/likes/_like.html.erb

<%= tag.div id: dom_id(todo, :like) do %>
  <% if like&.persisted? %>
    <%= button_to "Unlike", todo_like_path(todo, like), method: :delete %>
  <% else %>
    <%= button_to "👍 Like", todo_likes_path(todo), method: :post %>
  <% end %>
<% end %>

app/views/todos/_todo.html.erb

<div id="<%= dom_id todo %>">
  <p><%= todo.description %></p>
  <%= render partial: "likes/like", locals: { todo: todo, like: todo.likes.find_by(user: current_user) } %>
  <%= tag.div(todo.likes.count, id: dom_id(todo, :likes_count)) %>
</div>
<hr>

bin/rails s
この時点で Todoに Like/Unlike ボタンとライク数が表示される。

続けてアクションを実装する。

class LikesController < ApplicationController
  before_action :set_todo

  def create
    @like = @todo.likes.where(user: current_user).first_or_create
  end

  def destroy
    @like = Like.find(params[:id])&.destroy
    render :create
  end

  private
    def set_todo
      @todo = Todo.find(params[:todo_id])
    end
end

touch app/views/likes/create.turbo_stream.erb
"likes/like" を replace する。
dom_id(@todo, :likes_count) の中身ライク数を update する。

<%= turbo_stream.replace(dom_id(@todo, :like), partial: "likes/like", locals: { todo: @todo, like: @like} ) %>
<%= turbo_stream.update(dom_id(@todo, :likes_count), @todo.likes.count) %>

以上です。

この記事が気に入ったらサポートをしてみませんか?