简体   繁体   中英

POST and DELETE in one http request and from one controller (rails)

I am building an api with rails and currently trying to implement heart/unheart feature for posts. The way this feature works is if user heart the post, a new entry shows up in the database (one user can heart a post only once) and if user unheart it, the entry gets deleted. Currently, I check on the front-end if user hearts it or not and send the right HTTP request for POST or DELETE. However, it's not very efficient and I was wondering if there is a better way to get it done automatically on a back-end (Controller or Model) and by having only one http request (ex. POST)?

heart.rb Model:

class Heart < ApplicationRecord
  belongs_to :user
  belongs_to :post

  validates :post_id, presence: true
  validates :user_id, presence: true

  validates :user_id, uniqueness: { scope: :post_id }

end 

user.rb Model:

class User < ApplicationRecord
    has_many :posts, dependent: :destroy
    has_many :hearts, dependent: :destroy

    def heart!(post)
        self.hearts.create!(post_id: post.id)
    end

    def unheart!(post)
        heart = self.hearts.find_by_post_id(post.id)
        heart.destroy!
    end
end  

hearts_controller.rb

class HeartsController < ApplicationController

    def heart
        @user = current_user
        @post = Post.find(params[:post_id])
       if @user.heart!(@post)
       end
    end

    def unheart
         @user = current_user
         @heart = @user.hearts.find_by_post_id(params[:post_id])
         @post = Post.find(params[:post_id])
        if @heart.destroy!
        end
    end


end

routes.rb

Rails.application.routes.draw do
 match 'heart', to: 'hearts#heart', via: :post
 match 'unheart', to: 'hearts#unheart', via: :delete

end

As you can see, I need to check first if it was hearted or not and only then I need to send the right POST or DELETE request. Is there a more efficient way to send only one request and then check it inside of rails if it hearted or not and post/delete from one controller?

I would advise you to have only one route that listens for a PATCH method for posts. Something like:

patch '/posts/:id/heart' => 'posts#heart_post', as: :heart_post

And instead of HeartsController , implement heart_post action in PostsController , take your post and current user and heart your post (regardless of the current state, whether is hearted or not).

def heart_post
    @post = Post.find(params[:id])
    @post.heart_action(@current_user.id)

    # whatever you want to render
end

Then, your Post model would contain the logic for itself and it would know whether it needs to perform a heart or unheart action.

def heart_action (user_id)
    heart = Heart.find_by(user_id: user_id, post_id: self.id)

    if heart
        heart.destroy
    else
        Heart.create(user_id: user_id, link_id: self.id)
    end
end

There should be only one route for this action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM