如何在 RoR 中实现转发功能?



我正在尝试在我的应用程序上实现转发功能。

所以我在我的推文模型中有我的retweet_id

推文架构

| user_id | content | created_at | updated_at | retweet_id

tweets.rb

belongs_to :user
has_many :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'

用户.rb

has_many :tweets

在我的推文控制器中

tweets_controller.rb

...

def retweet
@retweet = Tweet.new(retweet_params)
if @retweet.save
redirect_to tweet_path, alert: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end

Private
...
def retweet_params
params.require(:retweet).permit(:retweet_id, :content).merge(user_id: current_user.id)
end

在我看来

推文/节目.html.erb

<%= link_to 'Retweet', retweet_tweet_path(@tweet.id), method: :post %>

我的路线

resources :tweets do
resources :comments
resources :likes
member do
post :retweet
end
end

所以当我尝试这个时,我收到一个错误

param is missing or the value is empty: retweet

所以我从"retweet_params"中删除了 .require,这消除了该错误(尽管我不确定这有多明智)

然后链接有效但不会转发 - 恢复到我的操作中指定的后备root_path。

Unpermitted parameters: :_method, :authenticity_token, :id
Redirected to http://localhost:3000/

我不确定我做错了什么。如何让我的转发有效?泰

引发错误的原因是retweet_params链接link_to 'Retweet', retweet_tweet_path(@tweet.id), method: :post不像新表单或编辑表单那样包含参数。相反,你应该创建一个新的推文,引用你想要转发的推文。

before_action :set_tweet, only: %i[show edit update destroy retweet]
def retweet
retweet = @tweet.retweets.build(user: current_user)
if retweet.save
redirect_to retweet, notice: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end
private
def set_tweet
@tweet = Tweet.find(params[:id])
end

以上内容应自动将新推文链接到"父推文"。如果由于某种原因这不起作用,您可以通过将上述内容更改为:

retrweet = Tweet.new(retweet_id: @tweet.id, user: current_user)

上述方法不会保存任何内容,因为这是转推。

如果不希望允许同一用户多次转发同一推文,请确保设置了适当的约束和验证。

# migration
add_index :tweets, %i[user_id retweet_id], unique: true
# model
validates :retweet_id, uniqueness: { scope: :user_id }

我们如何访问转发的内容?答案是我们从父级或源中获取内容(无论您想如何称呼它)。

目前没有允许您访问父推文或源推文的关联。您目前已经拥有:

has_many :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'

为了轻松访问源内容,我们首先添加一个额外的关联。

belongs_to :source_tweet, optional: true, inverse_of: :retweets, class_name: 'Tweet', foreign_key: 'retweet_id'
has_many :retweets, inverse_of: :source_tweet, class_name: 'Tweet', foreign_key: 'retweet_id'

通过设置上述关联,我们可以覆盖Tweet模型的contentgetter和setter。

def content
if source_tweet
source_tweet.content
else
super
end
end
def content=(content)
if source_tweet
raise 'retweets cannot have content'
else
super
end
end
# depending on preference the setter could also be written as validation
validates :content, absence: true, if: :source_tweet

请注意,在谈论查询速度时,上述方法效率不高,但它是最简单、最清晰的解决方案。解决父/子查询非常困难,如果速度成为问题,它应该得到自己的问题。

如果您想知道我为什么要设置inverse_of选项。我建议您查看活动记录关联 - 3.5 双向关联部分。

现在你看到的错误是 Rails 中强参数的错误。如果可以检查正在发送的调试器或 HTTP post 请求,则会发现retweet_params

def retweet_params
params.require(:retweet).permit(:retweet_id, :content).merge(user_id: current_user.id)
end

这实质上是说你期望像这样为参数提供一个嵌套哈希

params = { retweet: { id: 1, content: 'Tweet' } }

这不起作用,因为您只发送 ID。这样的事情怎么样?

TweetsController.rb

class TweetsController < ApplicationController
def retweet
original_tweet = Tweet.find(params[:id])
@retweet = Tweet.new(
user_id: current_user.id,
content: original_tweet.content
)
if @retweet.save
redirect_to tweet_path, alert: 'Retweeted!'
else
redirect_to root_path, alert: 'Can not retweet'
end
end
end

最新更新