Rails:将逻辑移出视图
Posted
技术标签:
【中文标题】Rails:将逻辑移出视图【英文标题】:Rails: Moving logic out of Views 【发布时间】:2016-03-25 16:15:42 【问题描述】:简而言之,我的视图中有一些 if/else 逻辑,我认为它们作为控制器方法或帮助器会更有效。也就是说,我不确定将其转化为方法的最佳方式是什么,或者放置它的最佳位置。
我正在我的 Rails 应用程序中构建一个用户消息传递系统。在我的应用程序中,个人资料有许多对话(两个个人资料之间),其中包含许多消息。我正在尝试构建基本上就像收件箱一样的对话索引。作为一名 Rails 初学者,我不太确定将 if/else 部分移出视图的最有效/最佳方式是什么,因为它在 .each 循环中运行。现在我的观点是这样的:
<% @conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.profile.id %>
<% recipient = Profile.find(conversation.recipient_id) %>
<% else %>
<% recipient = Profile.find(conversation.sender_id) %>
<% end %>
<%= link_to recipient.first_name, conversation_messages_path(conversation)%>
供参考我的对话控制器索引操作:
def index
@profiles = Profile.all
@conversations = Conversation.involved(current_user.profile.id)
end
还有我的对话模式
class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, class_name: 'Profile'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'Profile'
has_many :messages, dependent: :destroy
accepts_nested_attributes_for :messages
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id,recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id =?) OR (conversations.sender_id = ? AND conversations.recipient_id =?)", sender_id,recipient_id, recipient_id, sender_id)
end
scope :involved, -> (user) do
where("(conversations.sender_id = ? OR conversations.recipient_id =?)", user, user)
end
end
这是我的第一个 *** 问题,所以如果我没有遵循此问题的任何最佳做法,请告诉我!
【问题讨论】:
【参考方案1】:欢迎来到 ***!
在这种情况下,您只是根据 conversation.sender == current_user
与否打印配置文件的名称。在您的 Conversation
模型上,您可以有一个接收用户的 :display_name
方法。
def display_name(profile)
(sender == profile ? recipient : sender).name
end
这是演示方法,因此它可能更适合 Helper、Presenter、Decorator 或许多其他解决方案,但这应该可以解决问题。在你看来叫它:link_to conversation.display_name(current_user.profile), ...
【讨论】:
感谢 Leito,仅供参考,我不得不使用link_to conversation.display_name(current_user.profile)
,因为对话连接到配置文件而不是用户对象。我想这对大多数人来说可能很明显,但它使用三元运算符创建了一些奇怪的场景,因为在某些情况下,配置文件 ID 和用户 ID 匹配,而在其他情况下则不匹配。对于像我这样的初学者来说,发生的事情变得不那么明显了。
很好,很高兴您找到了解决该问题的方法。如果您退后一步,对于 user_id 匹配 profile_ids 并抛出不想要的结果一点也不奇怪。我们问了 Ruby 一个错误的问题。假设 current_user 是 Profile 实例是我的错。【参考方案2】:
就个人而言,我会说您的 if/else 逻辑适用于视图;最好避免在每个块中进行查询。本质上,您有所谓的 n+1 查询(@conversations
中有 n 个对象,并且您在数据库中查询每个对象的 Profile 对象,以及用于填充 @conversations 的原始查询,因此为 n+1)。
在控制器中,您可以像这样加载发件人和收件人:
scope :involved_with_profiles -> (user) do
includes(:sender, :recipient).where(...same logic as your :involved scope...)
end
使用此范围而不是 :involved
来加载 @conversations
。然后,您可以将 if/else 逻辑保留在视图中,但您可以这样做:
<% recipient = conversation.recipient %>
或
<% recipient = conversation.sender %>
因为这些已经从单个查询加载到@conversations。
【讨论】:
谢谢迈克尔。如果您有机会与我原来的:involved
范围相比,您介意澄清一下范围的“包含”部分在做什么吗?它是否消除了对每个 if/else 检查的额外访问?还是我错了?
当然。它只是在原始查询中包含这些关联,以便它们与对话对象本身一起加载到内存中。然后,当您在循环中引用它时,它不必在数据库中查询相关数据 - 它已经在内存中。这称为急切加载。【参考方案3】:
它在哪里很好。
将它放在助手中不会给您带来任何好处,但可以在其他地方重用。
【讨论】:
我不得不不同意,它在哪里工作,但视图做出这个决定并不好。 我们不同意。执行该逻辑的视图没有问题。 尊重,您在保持礼貌的同时保持自己的立场,如果我们所有人都这样做,互联网可能会是一个更好的地方!我曾经/现在(你开始说服我)在思考“告诉不要问”的原则(短读:robots.thoughtbot.com/tell-dont-ask)。视图不必知道Conversation
的内部逻辑来显示用户,它应该简单地询问告诉对话要显示哪个用户。但这取决于 current_user,所以它应该在只处理持久性的模型之外。以上是关于Rails:将逻辑移出视图的主要内容,如果未能解决你的问题,请参考以下文章
UITapGestureRecognizer 在移出视图并返回后被忽略