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:将逻辑移出视图的主要内容,如果未能解决你的问题,请参考以下文章

Rails:留下很多代码

表格视图的最后四行不会从键盘下方移出

UITapGestureRecognizer 在移出视图并返回后被忽略

[对smartMenu.js改进] 解决右键菜单栏在边缘弹出后,移出视图区域无法操作的问题

为啥 ERB 不在 Rails 视图规范中执行?

如何在 Rails 视图中使用 AJAX