如何有条件地在 Rails 中包含路由参数?
How to conditionally include route parameter in Rails?
我正在尝试允许一个资源 Site
有条件地拥有一个父资源 Organization
。
这是我目前拥有的:
resources :site do
resources :posts do
resources :comments
end
end
这会导致像
这样的路径
/sites/1
/sites/1/edit
/sites/1/posts
/sites/1/posts/123/comments
# etc. etc.
我希望能够拥有像
这样的路径
/parent_id/sites/1
/parent_id/sites/1/edit
/parent_id/sites/1/posts
/parent_id/sites/1/posts/123/comments
但 仅当站点属于组织。
我也不想更改已在我的网站上使用的每个路径助手(实际上有数百个地方)。
这可能吗?
这是我尝试过的方法:
scope "(:organization_id)/", defaults: { organization_id: nil } do
resources :site
end
# ...
# in application_controller.rb
def default_url_options(options = {})
options.merge(organization_id: @site.organization_id) if @site&.organization_id
end
但这没有用。 organization_id
没有设置。
# in my views
link_to "My Site", site_path(site)
# results in /sites/1 instead of /321/sites/1
我也试过在路由约束中设置 organization_id,但效果不佳。
在您的路线中添加另一个块,并围绕它提供公司资源:
resources :companies do
resources :site do
resources :posts do
resources :comments
end
end
end
resources :site do
resources :posts do
resources :comments
end
end
现在您可以像这样为您的链接创建一个助手:
# sites_helper.rb
module SitesHelper
def link_to_site(text, site)
if site.company
link_to text, company_site_path(site.company, site)
else
link_to text, site_path(site)
end
end
end
然后像这样在您的视图中使用它:
<%= link_to_site("Text of the link", variable_name) %>
注意参数中的 variable_name
。它可以是站点或@site,具体取决于您的代码。在循环中它可能是 site 但在显示页面上我猜它会是 @site.
您非常接近当前的方法。问题似乎是路由 scope
上的 defaults
优先于 default_url_options
,因此您每次都会得到一个零组织 ID。
试试看,只是:
# in routes.rb
scope "(:organization_id)" do
resources :site
...
end
# in application_controller.rb
def default_url_options(options = {})
# either nil, or a valid organization ID
options.merge(organization_id: @site&.organization_id)
end
我最终写了一个猴子补丁来覆盖为我所有相关路径生成的动态方法。我使用了一个自定义路由选项,infer_organization_from_site
,我在动态生成路由时寻找它。如果设置了该选项,我将添加 site.organization
作为助手调用的第一个参数。
# in config/routes.rb
scope "(:organization_id)", infer_organization_from_site: true do
resources :sites do
resources :posts
# etc.
end
end
# in an initializer in config/initializers/
module ActionDispatch
module Routing
# :stopdoc:
class RouteSet
class NamedRouteCollection
private
# Overridden actionpack-4.2.11/lib/action_dispatch/routing/route_set.rb
# Before this patch, we couldn't add the organization in the URL when
# we used eg. site_path(site) without changing it to site_path(organization, site).
# This patch allows us to keep site_path(site), and along with the proper
# optional parameter in the routes, allows us to not include the organization
# for sites that don't have one.
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
helper = UrlHelper.create(route, opts, route_key, url_strategy)
mod.module_eval do
define_method(name) do |*args|
options = nil
options = args.pop if args.last.is_a? Hash
if opts[:infer_organization_from_site]
args.prepend args.first.try(:organization)
end
helper.call self, args, options
end
end
end
end
end
end
end
我正在尝试允许一个资源 Site
有条件地拥有一个父资源 Organization
。
这是我目前拥有的:
resources :site do
resources :posts do
resources :comments
end
end
这会导致像
这样的路径/sites/1
/sites/1/edit
/sites/1/posts
/sites/1/posts/123/comments
# etc. etc.
我希望能够拥有像
这样的路径/parent_id/sites/1
/parent_id/sites/1/edit
/parent_id/sites/1/posts
/parent_id/sites/1/posts/123/comments
但 仅当站点属于组织。
我也不想更改已在我的网站上使用的每个路径助手(实际上有数百个地方)。
这可能吗?
这是我尝试过的方法:
scope "(:organization_id)/", defaults: { organization_id: nil } do
resources :site
end
# ...
# in application_controller.rb
def default_url_options(options = {})
options.merge(organization_id: @site.organization_id) if @site&.organization_id
end
但这没有用。 organization_id
没有设置。
# in my views
link_to "My Site", site_path(site)
# results in /sites/1 instead of /321/sites/1
我也试过在路由约束中设置 organization_id,但效果不佳。
在您的路线中添加另一个块,并围绕它提供公司资源:
resources :companies do
resources :site do
resources :posts do
resources :comments
end
end
end
resources :site do
resources :posts do
resources :comments
end
end
现在您可以像这样为您的链接创建一个助手:
# sites_helper.rb
module SitesHelper
def link_to_site(text, site)
if site.company
link_to text, company_site_path(site.company, site)
else
link_to text, site_path(site)
end
end
end
然后像这样在您的视图中使用它:
<%= link_to_site("Text of the link", variable_name) %>
注意参数中的 variable_name
。它可以是站点或@site,具体取决于您的代码。在循环中它可能是 site 但在显示页面上我猜它会是 @site.
您非常接近当前的方法。问题似乎是路由 scope
上的 defaults
优先于 default_url_options
,因此您每次都会得到一个零组织 ID。
试试看,只是:
# in routes.rb
scope "(:organization_id)" do
resources :site
...
end
# in application_controller.rb
def default_url_options(options = {})
# either nil, or a valid organization ID
options.merge(organization_id: @site&.organization_id)
end
我最终写了一个猴子补丁来覆盖为我所有相关路径生成的动态方法。我使用了一个自定义路由选项,infer_organization_from_site
,我在动态生成路由时寻找它。如果设置了该选项,我将添加 site.organization
作为助手调用的第一个参数。
# in config/routes.rb
scope "(:organization_id)", infer_organization_from_site: true do
resources :sites do
resources :posts
# etc.
end
end
# in an initializer in config/initializers/
module ActionDispatch
module Routing
# :stopdoc:
class RouteSet
class NamedRouteCollection
private
# Overridden actionpack-4.2.11/lib/action_dispatch/routing/route_set.rb
# Before this patch, we couldn't add the organization in the URL when
# we used eg. site_path(site) without changing it to site_path(organization, site).
# This patch allows us to keep site_path(site), and along with the proper
# optional parameter in the routes, allows us to not include the organization
# for sites that don't have one.
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
helper = UrlHelper.create(route, opts, route_key, url_strategy)
mod.module_eval do
define_method(name) do |*args|
options = nil
options = args.pop if args.last.is_a? Hash
if opts[:infer_organization_from_site]
args.prepend args.first.try(:organization)
end
helper.call self, args, options
end
end
end
end
end
end
end