在*不同*控制器的方法之间传递参数

问题描述

已经有关于如何在同一控制器的方法之间传递参数的线程。

但是有没有一种方法可以在不同控制器的方法之间传递参数?

例如,有时您有一篇文章,当您对其调用#update动作时,您还希望同时通过TagsController的#update动作来更新与其关联的标签

这个习惯用法就像在ArticlesController#actions中实例化TagsController,然后在执行tags_controller_instance.send(:update)时传递一个新的Rack :: Response实例,仅将整个params-hash的:tags部分传递给它。 / p>

您将只需要发送ArticlesController收到的params-hash的特殊部分,因为TagsController将具有不同的StrongParameters!

我认为这可以归结为以下问题:如何创建一个Rack :: Request来复制一方面的RequestsController以及如何不将整个params-hash传递给它,而仅将其中有意义的部分传递给它。 >

然后应该可以在ArticlesController#update中使用this.tags来请求更新的标记列表,对吗?

谢谢

冯·斯波茨

解决方法

这个习惯用法就像在ArticlesController#actions中实例化TagsController,然后在执行tags_controller_instance.send(:update)时将仅带有整个参数哈希的:tags部分的Rack :: Response新实例传递给它。

请不要那样做!很难理解和维护。您可能甚至还没有想到其他副作用。

问题是,您不想在TagsController#update中复制的ArticlesController是什么?如果是复杂的逻辑,我认为您应该对此进行抽象,例如在(service) object中,然后调用它。

类似这样的东西:

class UpdateTags
  def self.run(params)
    new(params).run
  end

  def initialize(params)
    @params = params
  end

  def run
    # copy logic from TagsController
  end
end

然后您可以在控制器中使用/重复使用此服务

class TagsController
  def update
    UpdateTags.run(params)
  end
end

class ArticlesController
  def update
    # update Article or move this in a dedicated service too
    UpdateTags.run(params)
  end
end

另一种方法可能是让Articlenested attributes接受Tags的属性。

编辑

详细说明为什么实例化另一个控制器不是一个好主意。

  • 过滤器之前呢? (再次)执行它们可以吗?
  • 如何渲染视图?您显然不想渲染视图,因此这是额外的工作,并且可能会有副作用吗?
  • 其他副作用,例如缓存,日志记录,数据分析。
  • 实例化控制器不是公共API,因此可以在Rails版本之间进行更改,从而导致更新困难。
  • 这不是常见的模式,因此很难理解

我只是再次强调,这不是一个好主意。复制代码比错误的抽象要好。

,

这听起来像是疯狂的Wile E. Coyote解决方案,对于用nested attributes解决的问题却微不足道。

在Rails中,控制器的唯一公共方法应该是控制器的 action ,它们是响应HTTP请求的方法,并且这些只能通过http调用来调用 >。

任何其他方法should be private/protected。没有有效的方案,您需要从另一个控制器上调用TagsController#update来更新标签,因为该方法为should do only one thing-更新标签以响应PATCH /tags/:id

如果要在一个请求中更新文章及其标签,请使用accepts_nested_attributes_for :tags

class Article < ApplicationRecord
  has_many :tags
  accepts_nested_attibutes_for :tags
end

这将创建一个tags_attributes=设置器,您可以使用该设置器与父级一起更新嵌套记录。

如果您想在不适合经典继承的类之间共享功能,请使用水平继承。在Ruby中,这意味着模块:

module Taggable
  private 
  def tag_attributes
    [:foo,:bar,:baz]
  end
end
class TagsController < ApplicationController
  include Taggable
  # PATCH /tags/:id
  def update
    @tag = Tag.find(params[:id])
    if @tag.update(tag_params)
      redirect_to @tag,success: 'Tag updated'
    else
      render :edit
    end
  end

  private
  def tag_params
    params.require(:tag).permit(*tag_attibutes)
  end
end
class ArticlesController < ApplicationController
  include Taggable
  def update
    if @article.update(article_params)
      redirect_to @article,success: 'Article updated'
    else
      render :edit
    end
  end

  private
  def article_params
    params.require(:article).permit(:title,:body,tags_attributes: tag_attibutes)
  end
end
<%= form_with(model: @article) do |f| %>
  ...
  <%= f.fields_for :tags do |tag| %>
  <div class="field">
    <%= tag.label :foo %>
    <%= tag.text_field :foo %>
  </div>
  <% end %>
  
  <% f.submit %>
<% end %>

在某些情况下,您可能会选择使用AJAX来通过发送确实调用另一个控制器的异步HTTP请求(而不是在同一请求中)来“即时”创建/更新/删除嵌套资源。但是,这确实超出了这个问题的范围。