用于显示操作的URL中的自定义参数

时间:2012-03-21 14:11:05

标签: ruby-on-rails ruby ruby-on-rails-3 routing seo

我正在努力实施SEO-hiarchy,这意味着我需要为show动作添加参数。

用例是一个搜索网站,其URL结构为:
/cars/(:brand)/ =>列表页面
/cars/(:brand)/(:model_name)?s=query_params =>搜索行动
/cars/:brand/:model_name/:variant/:id =>车展行动

我的问题是让show action URL无需提供 :brand:model_name :variant 作为个人论点。它们始终可以作为资源上的值使用。

我拥有的: /cars/19330-Audi-A4-3.0-TDI

我想要什么 /cars/Audi/A4/3.0-TDI/19330

以前,这就是routes.rb的样子:

# Before
resources :cars. only: [:show] do
  member do
  get 'favourize'
  get 'unfavourize'
end

以下是我的第一次尝试:

# First attempt
scope '/cars/:brand/:model_name/:variant' do
  match ":id" => 'cars_controller#show'
  match ":car_id/favourize" => 'cars_controller#favourize', as: :favourize_car
  match ":car_id/unfavourize" => 'cars_controller#unfavourize', as: :unfavourize_car
end

这使得可以这样做:
cars_path(car, brand: car.brand, model_name: car.model_name, variant: car.variant)
但这显然不是很理想。

如何设置路由(可能还有.to_param方法?)的方式不会让所有link_to次呼叫变得繁琐乏味?

提前致谢!

- 更新 -

根据@ tharrisson的建议,这就是我的尝试:

# routes.rb
match '/:brand/:model_name/:variant/:id' => 'cars#show', as: :car

# car.rb
def to_param
  # Replace all non-alphanumeric chars with - , then merge adjacent dashes into one
  "#{brand}/#{model_name}/#{variant.downcase.gsub(/[^[:alnum:]]/,'-').gsub(/-{2,}/,'-')}/#{id}"
end

路线工作正常,例如/cars/Audi/A4/3.0-TDI/19930显示正确的页面。但是,生成与to_param的链接不起作用。例如:

link_to "car link", car_path(@car)
#=> ActionView::Template::Error (No route matches {:controller=>"cars", :action=>"show", :locale=>"da", :brand=>#<Car id: 487143, (...)>})
link_to "car link 2", car_path(@car, brand: "Audi")
#=> ActionView::Template::Error (No route matches {:controller=>"cars", :action=>"show", :locale=>"da", :brand=>"Audi", :model_name=>#<Car id: 487143, (...)>})

Rails似乎不知道如何将to_param翻译成有效的链接。

4 个答案:

答案 0 :(得分:3)

我没有看到任何方法在没有调整URL识别或URL生成的情况下使用Rails执行此操作。

首次尝试时,您可以使用URL识别功能,但不会生成代码。我可以看到使生成工作的解决方案是覆盖car_path辅助方法。

另一种解决方案可能就像您在UPDATE中所做的那样,可以覆盖Car的to_param方法。请注意,您的问题不在to_param方法中,而在路线定义中:您需要在生成路线时提供:brand:model_name:variant参数。要解决此问题,您可能需要在路线中使用Wildcard segment

最后,您还可以使用routing-filter gem,这样您就可以在网址识别/生成之前和之后添加逻辑。

对我来说,看起来所有这些解决方案都有点沉重,并不像应该的那样容易,但我相信这是因为您需要在URL中添加一些级别而不严格遵循rails行为为您提供/brands/audi/models/A3/variants/19930

之类的网址

答案 1 :(得分:1)

好的,所以这就是我所拥有的。这适用于我的小测试用例。显然需要一些修正,我相信它可以更简洁和优雅,但我的座右铭是:“让它工作,让它变得漂亮,让它快速”: - )

routes.rb

  controller :cars do
    match 'cars', :to => "cars#index"
    match 'cars/:brand', :to => "cars#list_brand", :as => :brand
    match 'cars/:brand/:model', :to => "cars#list_model_name", :as => :model_name
    match 'cars/:brand/:model/:variant', :to => "cars#list_variant", :as => :variant
  end

在汽车模型中

  def to_param
    "#{brand}/#{model_name}/#{variant}"
  end

cars_controller.rb

中显然是脆弱的和非干的
  def index
    @cars = Car.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @cars }
    end
  end

  def list_brand
    @cars = Car.where("brand = ?", params[:brand])

    respond_to do |format|
      format.html { render :index }
    end
  end

  def list_model_name
    @cars = Car.where("brand = ? and model_name = ?", params[:brand], params[:model])

    respond_to do |format|
      format.html { render :index }
    end
  end

  def list_variant
    @cars = Car.where("brand = ? and model_name = ? and variant = ?", params[:brand], params[:model], params[:variant])

    respond_to do |format|
      format.html { render :index }
    end
  end

答案 2 :(得分:1)

您只需要创建两条路线,一条用于识别,一条用于生成。

已更新:使用相关路线。

# config/routes.rb
# this one is used for path generation
resources :cars, :only => [:index, :show] do
  member do
    get 'favourize'
    get 'unfavourize'
  end
end
# this one is used for path recognition
scope '/cars/:brand/:model_name/:variant' do
  match ':id(/:action)' => 'cars#show', :via => :get
end

并自定义to_param

# app/models/car.rb
require 'cgi'

class Car < ActiveRecord::Base
  def to_param
    parts = [brand,
             model_name,
             variant.downcase.gsub(/[^[:alnum:]]/,'-').gsub(/-{2,}/,'-'),
             id]

    parts.collect {|p| p.present? ? CGI.escape(p.to_s) : '-'}.join('/')
  end
end

路径助手示例:

link_to 'Show', car_path(@car)
link_to 'Edit', edit_car_path(@car)
link_to 'Favourize', favourize_car_path(@car)
link_to 'Unfavourize', unfavourize_car_path(@car)
link_to 'Cars', cars_path
form_for(@car) # if resources :cars is not
               # restricted to :index and :show

答案 3 :(得分:0)

您希望将有界参数传递给url,其中某些参数是可选的,并且其中一些参数必须严格存在。

Rails指南显示您可以拥有严格和可选参数,也可以按顺序为特定路线命名,以简化其使用。

铁路路线指南 bound parameters

使用示例 -

在以下路线中,

  • brand optional parametercircular bracket
  • 包围
  • 另请注意,路线内可以有可选参数,但最后需要添加/cars(/:brand)(/:make)(/:model)

    match '/cars/(:brand)', :to => 'cars#index', :as => cars

这里cars_url将映射到汽车控制器的索引动作.. 再次cars_url("Totoya")会将汽车控制器的索引操作与params[:brand]一起作为Toyota路由

显示网址​​路由可以如下所示,其中id是必需的,其他可以是可选的

match '/cars/:id(/:brand(/:model_name/)(/:variant)', :to => "cars#show", :as => car

在上述情况下,id是必填字段。其他参数是可选的。 因此您可以像car_url(car.id)car_url(12, 'toyota')car_url(12, 'toyota', 'fortuner')car_url(12, 'toyota', 'fortuner', 'something else)

那样访问它