Like Relationships and Global ID

Episode #223 by Teacher's Avatar David Kimura

Summary

In this episode we create a Like/Unlike system where users can like posts and use Global ID to obfuscate IDs.
rails model routes controller 13:27

Resources

Summary

# Terminal
rails g scaffold post user:belongs_to title content
rails g model users_post user:belongs_to post:belongs_to
rails g controller Posts::Likes
rails routes | grep likes

# models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  has_many :users_posts
  has_many :liked_by_users, through: :users_posts, source: :user

  # Post.first.liked_by_users.size
end

# models/users_post.rb
class UsersPost < ApplicationRecord
  belongs_to :user
  belongs_to :post
end

# models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :users_posts
  has_many :liked_posts, through: :users_posts, source: :post

  has_many :posts
end

# controllers/posts/likes_controller.rb
class Posts::LikesController < ApplicationController
  def create
    post = Post.find(params[:post_id])
    current_user.users_posts.create(post: post)
    redirect_to post, notice: "Post liked"
  end

  def destroy
    # post = Post.find(params[:post_id])
    # current_user.users_posts.find_by(post: post).destroy!
    # redirect_to post, notice: "Post unliked"

    # users_post = UsersPost.find(params[:id])
    # users_post.destroy
    # redirect_to post_path(users_post.post_id), notice: "Post unliked"

    # users_post = GlobalID::Locator.locate(params[:id])
    users_post = GlobalID::Locator.locate_signed(params[:id])
    users_post.destroy
    redirect_to post_path(users_post.post_id), notice: "Post unliked"
  end
end

# config/routes.rb
Rails.application.routes.draw do
  resources :posts do
    scope module: :posts do
      resources :likes, only: [:create, :destroy]
    end
  end
  devise_for :users
  root to: 'welcome#index'
end

# controllers/posts_controller.rb
  def show
    @liked_post = UsersPost.find_by(user: current_user, post: @post)
  end

# views/posts/show.html.erb
<%# if current_user.liked_post?(@post.id) %>
<% if @liked_post %>
  <%# link_to "unlike", post_like_path(@post, id: @liked_post.to_global_id), method: :delete %>
  <%= link_to "unlike", post_like_path(@post, id: @liked_post.to_sgid(expires_in: 1.hour)), method: :delete %>
<% else %>
  <%= link_to "like", post_likes_path(@post), method: :post %>
<% end %>