[RUBY] [Rails] Implementation of like function

At the beginning

In case you forget it, I have briefly summarized the implementation of the like function together with devise.

Premise

environment Rails 5.2 series Ruby 2.6 series

Library used  devise  Slim

Implementation

1. Creating a Rails app

$ cd
$ rails new favorite_function
$ cd favorite_function

2. Introducing gem devise and Slim

Gemfile



gem 'slim-rails'  #Provides a Slim generator
gem 'html2slim'  #Converts ERB format files to slim format
gem 'devise'
$ bundle

3. Install devise

$ rails g devise:install
    create  config/initializers/devise.rb
    create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

4. Edit according to the 4 instructions given at the same time as devise installation

4-1. First

config/environments/development.rb



Rails.application.configure do
.
.
.
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
.
.
.
end

4-2. Second

$ rails g controller posts new index show

config/routes.rb



Rails.application.routes.draw do
  root to: 'posts#index'  #The root I want to set in the app_Specify url
  resources :posts
  .
  .
  .
end

4-3. Third

ruby:app/views/layouts/application.html.erb



<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

4-4. Fourth

$ rails g devise:views

invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      create    app/views/devise/shared/_error_messages.html.erb
      create    app/views/devise/shared/_links.html.erb
      create    app/views/devise/confirmations
      create    app/views/devise/confirmations/new.html.erb
      create    app/views/devise/passwords
      create    app/views/devise/passwords/edit.html.erb
      create    app/views/devise/passwords/new.html.erb
      create    app/views/devise/registrations
      create    app/views/devise/registrations/edit.html.erb
      create    app/views/devise/registrations/new.html.erb
      create    app/views/devise/sessions
      create    app/views/devise/sessions/new.html.erb
      create    app/views/devise/unlocks
      create    app/views/devise/unlocks/new.html.erb
      create    app/views/devise/mailer
      create    app/views/devise/mailer/confirmation_instructions.html.erb
      create    app/views/devise/mailer/email_changed.html.erb
      create    app/views/devise/mailer/password_change.html.erb
      create    app/views/devise/mailer/reset_password_instructions.html.erb
      create    app/views/devise/mailer/unlock_instructions.html.erb

5. Now convert all views to Slim

$ bundle exec erb2slim app/views/ --delete

__ * Regarding the devise directory, if you convert it to Slim, an error will occur in devise / shared / _error_messages.html.slim, so fix it. __

ruby:devise/shared/_error_messages.html.slim


- if resource.errors.any?
  #error_explanation
    h2

      #An error occurs in the following 3 lines, so fix it as follows
      = I18n.t("errors.messages.not_saved",
        count: resource.errors.count,
        resource: resource.class.model_name.human.downcase)

    ul
      - resource.errors.full_messages.each do |message|
        li
          = message

6. Create a view of the authentication mechanism

ruby:app/views/layouts/application.html.slim



html
  head
  .
  .
  .
  body
    - if user_signed_in?
      =link_to 'Log out', destroy_user_session_path, method: :delete
    - else
      =link_to 'sign up', new_user_registration_path
      =link_to 'Login', new_user_session_path
  .
  .
  .

・ If you don't know which path leads to which action in devise, check with the rails routes command.


7. Creating a model

$ rails g devise User
$ rails g model Post content:string user:references 
$ rails g model Favorite user:references post:references
$ rails g migration add_columns_to_users username:string

$ rails db:migrate

-By setting model name: references, foreign_key is automatically set in the migration file, and the association is also done in the model file.

-Since ```User and Post have a one-to-many `` `relationship, put a reference to Post to indicate that it is owned by User.

-User has multiple likes for Post` `,` `Post also has multiple likes from User` Many-to-many ``, so in Favorite On the other hand, put up a reference indicating that it is owned by Post and User.

-Since it is not possible to add a column to the default User model created by ``` devise``, execute the column addition command and create data related to User such as name.


8. Model association

8-1. Favorite model

app/models/favorite.rb


class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :post
end

8-2. Post model

app/models/post.rb


class Post < ApplicationRecord
  belongs_to :user
  has_many :favorites
end

-You can call post.favorites when you want to like a post. You can get the number of likes by using post.favorites.count.

8-3. User model

user.rb


class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :favorites
  has_many :favorite_posts, through: :favorites, source: :post
  has_many :posts
end

-It is possible to call user.favorites when `` getting the likes given by the user `.

-``` If you want to display posts that users like on your profile screen, there is `. At that time, you will be able to call user.favorite_posts. It uses the through option `` `to associate with the Post model through an intermediate table.

-The model used in through is ``` which must be associated first. In this case, favorites must be associated first.

-In source, describe the model name that is the reference source of the named model name.

has_many :Favorite model name, through: :Intermediate table, source: :The model name that is the reference source of the named model



---

### 9. Create controller

$ rails g controller posts new index show #Omitted if created when setting devise $ rails g controller users index show $ rails g controller favorites create destroy


 · (Posts,) users, favorites controller

---
### 10. Implementation of user functions

#### 10-1. Editing routing


#### **`config/routes.rb`**
```ruby


Rails.application.routes.draw do

  resources :users, only: [:index, :show]  #For user list and profile screen
  devise_for :users  #For login mechanism

end

10-2. Editing the users controller

app/controllers/users_controller.rb



class UsersController < ApplicationController

  before_action :authenticate_user!  #User authentication provided by devise

  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end

10-3. Editing the user's view

● index view

ruby:app/views/users/index.html.slim


h1 user list

- @users.each do |user|
  p
    = link_to user.email, user

● show

ruby:app/views/users/show.html.slim


h1 user profile page

p
  = @user.username
  = @user.email

10-4. Put a link to the user page in the application view

ruby:app/views/layouts/application.html.slim


  body
    - if user_signed_in?
      .
      .
      .
   +  = link_to 'My page', user_path(current_user)
   +  = link_to 'User list', users_path
    - else
      .
      .

11. Implementation of posting functions

11-1. Editing the routing

config/routes.rb


Rails.application.routes.draw do

  resources :users
  devise_for :users

  root to: 'posts#index'  #Specify the post list as the root
  resources :posts

end

11-2. Editing the posts controller

app/controllers/posts_controller.rb


class PostsController < ApplicationController

  def index
    @posts = Post.all
  end

  def new
    @post = Post.new
  end

  def create
    @post = current_user.posts.new(post_params)  # current_user represents the logged-in user provided by devise.
    if @post.save
      redirect_to @post
    end
  end

  def show
    @post = Post.find(params[:id])
  end

  private
  def post_params
    params.require(:post).permit(:content)
  end
end

-When a new post is posted, the owner of the created post is the user who posted it, so set it to `current_user.posts`. The user_id in the posts table now contains the id of current_user. (Note that this method cannot be used unless the ```User model and Post model are associated `` `)

11-3. Editing the post view

● index

ruby:app/views/posts/index.html.slim


h1 post list

- @posts.each do |post|
  p= link_to post.user.email, user_path(post.user.id)
  p= link_to post.content

● new

ruby:app/views/posts/new.html.slim


h2 new post

= form_with model: @post do |f|
  p
    = f.label :content
    = f.text_area :content
  = f.submit 'Post'

● show

ruby:app/views/posts/show.html.slim


h1 post details page

p= @post.content

11-4. Put a link to the posting page in the application view

ruby:app/views/layouts/application.html.slim


  body
    - if user_signed_in?
      .
      .
      .
      = link_to 'Post list', posts_path
    - else
      .
      .

12. Implementation of like function

12-1. Create a like button for posting

ruby:app/views/posts/index.html.slim


h1 post list

- @posts.each do |post|
  p= link_to post.user.email, user_path(post.user.id)
  p= link_to post.content

  - if user_signed_in?  #A method provided by devise to check if a user is logged in
    - if post.favorited_by?(current_user)
      p= link_to post.favorites.count, post_favorites_path(post.id), method: :delete
    - else
      p= link_to post.favorites.count, post_favorites_path(post.id), method: :post
  - else
    = post.favorites.count

-``` Use user_signed_in? `To make it visible only to the logged-in user.

-Since it is a `` mechanism that likes are added by the create action and likes disappear by the `destroy action, it is explicitly specified in the method of the link_to helper.

・ ``` If you like it, you want to display the delete link of the like ``, and if you haven't liked it yet, you want to display the link to create the like , so Check it with the favorited_by (current_user) method `.

-The favored_by (current_user) method will be created after this.

-``` Post.favorites.count shows the number of likes of the post` ``.

12-2. Define a method to check for likes

app/models/post.rb


class Post < ApplicationRecord
  belongs_to :user
  has_many :favorites

  
  + def favorited_by?(current_user)
  +   favorites.where(user_id: current_user.id).exists?
  + end
end

-Because it is a method that is called only for the Post model, it is defined as a Post instance method.

-Check if the id of the logged-in user exists in the user_id of the ``` favorites table``.

-It will be used like post.favorited_by (current_user). Let's make it a little easier

ruby:posts/index.html.slim


h1 post list

- @posts.each do |post|
  .
  .
  .
  - if post.favorited_by?(current_user) 
      #Show delete link of likes
  - else
      #Show Create Like Link

-This shows the following code.

- if post.favorites.where(user_id: current_user.id).exists?
    #Delete link display
- else
    #Show Create Like Link

-``` Get the id of the logged-in user `,` `user_id is the id of the logged-in user from the favorites table of the acquired post, and `` check if it exists I'm letting you.

-Returns true if it exists. `Like it means`, so display the like delete link.

-Returns false if it does not exist. I don't like it `` `, so display the like creation link.

12-3. Editing the routing

config/routes.rb


Rails.application.routes.draw do

  resources :users
  devise_for :users

  root to: 'posts#index'
  resources :posts do
+   resource :favorites, only: [:create, :destroy]
  end

end

-Favorites are nested in posts. This makes it easier to specify a path to like a post, such as post_favorites_path.

12-4. Editing the favorites controller

app/controllers/favorites_controller.rb


class FavoritesController < ApplicationController

  def create
    favorite = current_user.favorites.build(post_id: params[:post_id])
    favorite.save
    redirect_to posts_path
  end

  def destroy
    favorite = Favorite.find_by(post_id: params[:post_id], user_id: current_user.id)
    favorite.destroy
    redirect_to posts_path
  end
end

-``` Stores the id of `` , liked posts in the favorites table of the logged-in user `. And by saving it, it is regarded as giving a like.

-Obtain and delete the data in the favorites table where the idof the post that clicked Unlike" matches the id of the user who is ``` `. By doing so, it is regarded as a dislike.

12-5. On the user's page, display the posts that the user liked. By the way, the posts posted by the user are also displayed.

● Edit the show action of the users controller

app/controllers/users_controller.rb


class UsersController < ApplicationController

  before_action :authenticate_user!

  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @posts = @user.posts
    @favorite_posts = @user.favorite_posts
  end
end

・ In @posts, I get normal posts to display normal posts.

・ In @favorite_posts, the posts that the user likes are acquired. That's why app / models / user.rb was associated with the Post model under the name favorite_posts through the favorites table.

● Edit show.html.slim

ruby:app/views/users/show.html.slim


h1 user profile page

p
  = @user.username

h3 like
- @favorite_posts.each do |f_post|
  p
    = f_post.content

h3 post
- @posts.each do |post|
  p
    = post.content

・ The above shows posts that users like.

・ Below shows the posts posted by users.

-You can make it look like Twitter by switching by click event, but I will not do it here.

This completes the implementation of the like function.

Articles that were very helpful

https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1

Recommended Posts

[Rails] Implementation of like function
[Rails] Asynchronous implementation of like function
[Rails] About implementation of like function
[Ruby on rails] Implementation of like function
Implementation of like function (Ajax)
[Rails 6] Implementation of search function
[Rails] Implementation of category function
[Rails] Implementation of tutorial function
[Rails] Implementation of image preview function
[Rails 6] Like function (synchronous → asynchronous) implementation
Implementation of like function in Java
Implementation of search function
Rails sorting function implementation (displayed in order of number of like)
Rails [For beginners] Implementation of comment function
[Rails 6] Implementation of SNS (Twitter) sharing function
Rails search function implementation
Implementation of pagination function
[Vue.js] Implementation of menu function Implementation version rails6
[Vue.js] Implementation of menu function Vue.js introduction rails6
[Rails] Implementation of search function using gem's ransack
Implementation of Ruby on Rails login function (Session)
[Rails 6] Implementation of inquiry function using Action Mailer
[Rails] Implementation of image enlargement function using lightbox2
[Rails] Implementation of retweet function in SNS application
Rails implementation of ajax removal
Rails fuzzy search function implementation
Implementation of sequential search function
Implementation of image preview function
Implementation of category pull-down function
Login function implementation with rails
[Rails 6] Pagination function implementation (kaminari)
Ruby on Rails <2021> Implementation of simple login function (form_with)
[Rails] Implementation of drag and drop function (with effect)
Implementation of Ruby on Rails login function (devise edition)
[Ruby on Rails] Implementation of tagging function/tag filtering function
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
[Rails] Implementation of SNS authentication (Twitter, Facebook, Google) function
[Rails] Implementation of user logic deletion
Kaminari --Added pagination function of Rails
[Rails] Implementation of many-to-many category functions
[Rails] gem ancestry category function implementation
[Ruby on Rails] Comment function implementation
[Rails] Comment function implementation procedure memo
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
[Rails] Addition of Ruby On Rails comment function
I tried to implement Ajax processing of like function in Rails
Implementation of user authentication function using devise (2)
DM function implementation
Rails Addition of easy and easy login function
[Rails 6] Ranking function
[Rails] [jQuery] Asynchronous like function implementation using remote: true and js.erb
[Ruby on Rails] Follow function implementation: Bidirectional
[Rails] Category function
[Rails 6] Implementation of new registration function by SNS authentication (Facebook, Google)
Implementation of user authentication function using devise (1)
[Rails] Implementation of coupon function (with automatic deletion function using batch processing)
Implementation of GKAccessPoint
Rails follow function
Rails Basic CRUD function implementation procedure scaffold
Implementation of user authentication function using devise (3)