** Finally, we will implement a function that allows users to display likes of posts **
The explanation of reading js is omitted!
** Articles that I referred to **
https://techtechmedia.com/favorite-function-rails/ https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3
Because users like a lot of posts and posts are also liked by a lot of users
Many-to-many table structure of users and posts with likes table as intermediate table
rails g model like
** Migration file **
class CreateLikes < ActiveRecord::Migration[5.0]
def change
create_table :likes do |t|
t.integer :user_id
t.integer :drink_id
t.timestamps
end
end
end
rails g controller likes
like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :drink, counter_cache: :likes_count
end
-Counter_cahce:: likes_count means to put the value of the number of related likes in the value of the column called likes_count of the relation destination. So let's add the likes_count column to the stories table. (It's okay if you run rails g migration AddLikes_countToStories likes_count: integer in the terminal.) Source of this text
drink.rb
class Drink < ApplicationRecord
has_many :likes
has_many :liking_users, through: :likes, source: :user
end
Since there is no liking_users model, I tell rails that I will use the likes table as an intermediate table and draw associations with the user model.
** has_many is not the essence of belongs_to to form an association, but a method that creates a method. ** **
In other words, if you do something like @ drink.liking_user, you can create a method that can get a list of users who like the post, and you can also create an association.
user.rb
has_many :likes
has_many :like_drinks, through: :likes, source: :drink
You can also get a list of posts that the user liked by doing something like user.like_drinks. This is useful when viewing posts that the user likes, which is common on Instagram and Twitter.
** has_many is not the essence of belongs_to to form an association, but a method that creates a method. ** **
Let's remember this and go home.
drinks/index.html.erb
This is the post list page
<%if @drinks%>
<% @drinks.each do |drink|%>
<li class='list'>
<%= link_to drink_path(drink.id) do %>
<%= link_to user_path(drink.user.id) do%>
<div class="user-info-timeline">
<%=image_tag drink.user.image.variant(resize: '60x60'),class: "user-img-timeline" if drink.user.image.attached?%>
<div class="username-timeline">
<%= drink.user.nickname %>
</div>
</div>
<% end %>
<div class='item-img-content'>
<%= image_tag drink.image , class: "item-img" if drink.image.attached? %>
<%# if drink.trade%>
<%# end %>
</div>
<div class='item-info'>
<h3 class='item-name'>
<%= drink.name %>
</h3>
<div class='item-price'>
<span><%= drink.price %>Circle<br>(tax included)</span>
<div class='star-btn'>
<%# image_tag "star.png ", class:"star-icon" %>
<span class='star-count'>0</span>
</div>
</div>
<div class='item-explain'>
<%= drink.explain%>
</div>
<div class='item-tag'>
<% drink.tags.each do |tag| %>
#<%=tag.tag_name%>
<%end%>
</div>
<%= render "likes/like",drink: drink%>
</div>
<% end %>
</li>
<%end%>
<%= render "likes/like",drink: drink%>
I want you to pay attention to!
First of all, to improve readability The like button of the image is cut out with a partial template,
And, for the drink: drink part,
<% @drinks.each do |drink|%>
The block variable in each statement of is passed to apply to likes/like.
Block variables are variables that can be used only within the method, such as each statement, times statement, and form_with. In other words, if it is each, a variable that can be used without a range from each to end
@There is a lot of information in drinks as an array,|drink| By doing so, each piece of information in the array is included in drink, and the number of arrays in @drinks is displayed.
likes/_like.html.erb
It is customary to name the file _like to make it easier to understand that it is a partial (partial template). However
<%= render "likes/like",drink: drink%>
You don't need an underscore when calling with
<div class="like" id="like-link-<%= drink.id %>">
<% if current_user.likes.find_by(drink_id: drink.id) %>
<%= link_to unlike_path(drink.id), method: :delete, remote: true do %>
<div class = "iine__button">❤️<%= drink.likes.count %></div>
<% end %>
<% else %>
<%= link_to like_path(drink.id), method: :post, remote: true do %>
<div class = "iine__button">♡️<%= drink.likes.count %></div>
<% end %>
<% end %>
</div>
id="like-link-<%= drink.id %>"
Is miso.
I want to switch screens asynchronously with js, so to distinguish the id for each post so that I can get the id Let's write it like this.
<%= link_to unlike_path(drink.id), method: :delete, remote: true do %>
, remote: true
By writing
Since ajax fires when you press the link, communication is performed asynchronously.
If you like it by pressing the like button, it will jump to like_path otherwise.
Since each path has not been defined yet, a routing error will occur if it is left as it is.
routes.rb
post 'like/:drink_id' ,to: 'likes#like', as: 'like'
delete 'like/:drink_id',to: 'likes#unlike', as: 'unlike'
Let's write
By setting as:'like', you would have to specify the path as likes_like_path (drink.id). You can send a post request to likes # like with like_path (drink.id)
Now that you've followed the link to send your request, let's take a look at the controller.
likes_controller
class LikesController < ApplicationController
include SessionsHelper
before_action :set_variables
def like
like = current_user.likes.new(drink_id: @drink.id)
#redirect_to drinks_path
#Since js is used, screen transition is not performed
#binding.pry
like.save
end
def unlike
like = current_user.likes.find_by(drink_id: @drink.id).destroy
#binding.pry
end
private
def set_variables
@drink = Drink.find(params[:drink_id])
@id_name = "#like-link-#{@drink.id}"
end
end
remote: Since the like and dislike actions are called from the true link, The default transition destinations are ilke.js.erb and dislike.js.erb respectively.
「⚠︎ @id_name = "#like-link-#{@drink.id}" I think that writing View processing in Controller is not very good in terms of MVC pattern. "
It's not very good, but there is no functional problem, so I'll move on.
likes/like.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>');
/likes/unlike.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>');
Go back to likes/_like.html.erb
At this time again
drink: @drink
Let's pass the variable to _like.html.erb
This @drink is
** likes_controller **
private
def set_variables
@drink = Drink.find(params[:drink_id])
@id_name = "#like-link-#{@drink.id}"
end
It's @drink.
This is the end of implementation. Thank you for your hard work.
users/show.html.erb
<%= link_to "#{@user.nickname}Liked posts",user_likes_path(@user.id)%>
Create a link like this
@userh is users # show @user = User.find (params [: id]) I define it in a common way
I haven't defined user_like_path yet
routes.rb
get 'user/likes/:id', to: 'users#likes',as: 'user_likes'
resources :users do
member do
get :following,:followers
#If you use the member method
#Will handle URl containing user id
end
end
I think that everyone will do resources: user, so get'user/likes /: id', to:'users # likes', as:'user_likes' on resources
Let's write
Now you can skip the GET request to users # likes once you follow the link
users_controller
def likes
@user = User.find(params[:id])
@drinks = @user.like_drinks.paginate(page: params[:page],per_page: 10).order("created_at DESC")
end
Let's implement it like this
.paginate(page: params[:page],per_page: 10)
You don't have to write if you haven't incorporated pagenations yet.
The .like_drinks method
has_many :like_drinks, through: :likes, source: :drink
And user.rb, so you can get a list of posts that users like.
By default, you will be redirected to users/likes.html.erb, so prepare that view as well.
users/likes.html.erb
<div class="user-profile">
<h2 class="user-profile-name"><%= current_user.nickname %></h2>
<h2><%= image_tag @user.image.variant(resize: '100x100'),class: 'user-img' if @user.image.attached? %></h2>
<div class="user-like-post">
<%= link_to "#{@user.nickname}Liked posts",user_likes_path(@user.id)%>
</div>
<div class="user-edit">
<% if current_user?(@user) %>
<%= link_to "Edit profile",edit_user_path(@user)%>
<% end %>
</div>
<% unless current_user?(@user) %>
<div id="follow_form">
<% if current_user.following?(@user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>
</div>
<% @user ||= current_user %>
<div class="stats">
<a href="<%= following_user_path(@user) %>">
<strong id="following" class="stat">
<%= @user.following.count %>
</strong>
following
</a>
<a href="<%= followers_user_path(@user) %>">
<strong id="followers" class="stat">
<%= @user.followers.count %>
</strong>
followers
</a>
</div>
<div class='main'>
<%#Product list%>
<div class='item-contents'>
<h2 class='title'><%= @user.nickname%>Post</h2>
<%= will_paginate @drinks%>
<ul class='item-lists'>
<%#If there is something in the instance variable of the product, let's make it possible to expand all the contents%>
<%if @drinks%>
<% @drinks.each do |drink|%>
<li class='list'>
<%= link_to drink_path(drink.id) do %>
<div class='item-img-content'>
<%= image_tag drink.image , class: "item-img" if drink.image.attached? %>
<%# if drink.trade%>
<%# end %>
</div>
<div class='item-info'>
<h3 class='item-name'>
<%= drink.name %>
</h3>
<div class='item-price'>
<span><%= drink.price %>Circle<br>(tax included)</span>
<div class='star-btn'>
<%# image_tag "star.png ", class:"star-icon" %>
<span class='star-count'>0</span>
</div>
</div>
<div class="item-explain">
<%= drink.explain%>
</div>
</div>
<% end %>
</li>
<%end%>
</ul>
<%= will_paginate @drinks%>
</div>
<%end%>
</div>
I'm like this
That's it. Thank you for your hard work.
Recommended Posts