2012-03-21 11 views
10

Estoy trabajando en la implementación de un hiarchy de SEO, lo que significa que tengo que anteponer los parámetros para una acción de mostrar.Parámetros personalizados en la URL para mostrar la acción

El caso de uso es un sitio de búsqueda, donde la estructura de la URL es:
/cars/(:brand)/ => una lista de páginas
/cars/(:brand)/(:model_name)?s=query_params => una búsqueda acción
/cars/:brand/:model_name/:variant/:id => una exhibición de autos acción

Mi El problema es hacer que las URL de acción de espectáculo funcionen sin tener que proporcionar:brand, :model_namey:variantcomo argumentos individuales . Siempre están disponibles como valores en el recurso.

Lo que tengo: /cars/19330-Audi-A4-3.0-TDI

Lo que quiero /cars/Audi/A4/3.0-TDI/19330

Anteriormente, esto fue como el routes.rb parecía:

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

siguiente fue mi primer intento:

# 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 

Esto hace posible hacer:
cars_path(car, brand: car.brand, model_name: car.model_name, variant: car.variant)
Pero eso obviamente no es realmente ideal.

¿Cómo es posible configurar las rutas (y tal vez el método .to_param?) De una manera que no sea una tarea tediosa cambiar todas las llamadas link_to?

¡Gracias de antemano!

- ACTUALIZACIÓN -

con la sugerencia de @ tharrisson, esto es lo que he intentado:

# 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 

La ruta funciona bien, por ejemplo, /cars/Audi/A4/3.0-TDI/19930 muestra la página correcta. Generar el enlace con to_param, sin embargo, no funciona. Ejemplo:

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 no parece saber cómo traducir el to_param en un enlace válido.

+0

Esta es una pregunta muy buena y bien escrito. Creo que tengo una idea y quiero asegurarme de estar en el camino correcto (sin juego de palabras). ¿Es el caso que quieres poder usar 'cars_path (@car)' en tu link_to's y hacer que genere la buena URL jerárquica de tus vistas, pero también hacer que el enrutador reconozca las URL "parciales" (por ejemplo, '/ cars/Audi') como una solicitud para listar Audis? ¿O es un requisito adicional para el link_to ser contextualmente consciente (por ejemplo, crea vínculos a la variante de/cars /: de algunas acciones del controlador, pero la URL completa del coche de otras acciones del controlador? –

+0

Gracias, @tharrison. Creo lo hiciste bien, quiero poder usar 'cars_path (@car)' en mi 'link_to'. La acción que se está desencadenando puede controlarse mediante restricciones en' routes.rb'. Esto significa que las URL de 'cars/Audi' no usa 'cars_path' sino algo así como' brand_path (marca: "Audi") '. –

+0

OK, llegando allí, pero no lo tengo entonces :-). Si desea usar rutas con nombre como brand_path, el argumento sería una instancia del modelo de marca ... aunque podría ser como 'brand_path (@ car.brand)'. ¿Tiene un modelo de marca y recursos en las rutas para eso? –

Respuesta

1

Bien, entonces esto es lo que tengo. Esto funciona en mi pequeño caso de prueba.Obviamente algunas composturas necesarias, y estoy seguro de que podría ser más conciso y elegante, pero mi lema es: "hacer que funcione, que sea bonita, que sea rápido" :-)

En 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 

en el modelo de coche

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

y, obviamente, frágil y no DRY, en 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 
+0

Estoy lo siento @tharrison, pero creo que lo entendiste mal. El problema no es mostrar la marca, el modelo o las páginas variantes. Eso ya funciona. El problema es vincular directamente a _un registro de un solo auto. Para que esto funcione, tengo que usar la identificación del automóvil y, además, quiero agregar la marca, el nombre del modelo y la variante a la URL (para fines de SEO). Entonces lo que quiero es: link_to cars_path (@car) para apuntar a/cars /: brand /: model_name /: variant /: id. ¿Tiene sentido? –

+0

Jonas, creo que el código que proporcioné está a un milímetro de distancia de lo que desea. Agregue o modifique las cláusulas 'match' según sea necesario agregando': id'. El argumento ': as =>: foo' es lo que le permite hacer referencia a la URL como 'foo_path', y la anulación' to_param' es lo que produce la ruta bonita: formatéela como quiera y agregue ID, o cualquier otro atributo del coche que te gusta para obtener la buena URL. ¿Derecha? –

+0

Quizás no fui lo suficientemente claro.El enrutamiento funciona perfecto de la manera que sugeriste. El problema es que Rails no puede traducir la implementación del 'to_param' en un enlace. He actualizado mi pregunta con un simple ejemplo. –

3

No veo ninguna forma de hacer esto con Rails sin ajustar el reconocimiento de URL o la generación de URL.

En su primer intento, el reconocimiento de URL funciona pero no genera. La solución que puedo ver para que funcione la generación sería anular el método de ayuda car_path.

Otra solución podría ser, como lo hizo en la ACTUALIZACIÓN, para anular el to_param método de automóvil. Tenga en cuenta que su problema no está en el método to_param, sino en la definición de ruta: debe dar los parámetros :brand, :model_name y :variant cuando desee generar la ruta. Para tratar con eso, es posible que desee utilizar un Wildcard segment en su ruta.

Por último, también puede usar el routing-filter gem que le permite agregar lógica antes y después de la generación/reconocimiento de url.

Para mí, parece que todas estas soluciones son un poco pesadas y no tan fáciles como deberían ser, pero creo que esto vino de su necesidad ya que quiere agregar algunos niveles en la URL sin seguir estrictamente el comportamiento de los rieles le dará URL como /brands/audi/models/A3/variants/19930

0

Desea que los parámetros acotados se pasen a la URL de la cual algunos parámetros son opcionales y algunos de ellos estrictamente deben estar presentes.

Las guías de rieles muestran que puede tener parámetros estrictos y opcionales y también puede dar nombre a una ruta en particular para simplificar su uso. Guía

en los carriles de enrutamiento bound parameters

Ejemplo de uso -

En ruta a continuación,

  • brand es optional parameter como su rodeado de circular bracket
  • también tenga en cuenta no puede ser opcional parámetros dentro de la ruta pero necesitan ser agregados al último /cars(/:brand)(/:make)(/:model)

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

aquí cars_url se asignarán a la acción del índice de controlador de vehículos .. nuevo cars_url("Totoya") voluntad acción index ruta de controlador de vehículos a lo largo-con params[:brand] como Toyota

Mostrar ruta URL puede ser tan por debajo de donde id es obligatoria y otros pueden ser opcionales

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

En caso anterior, id es obligatorio campo. Otros parámetros son opcionales para que pueda acceder a ella como car_url(car.id) o car_url(12, 'toyota') o car_url(12, 'toyota', 'fortuner') o car_url(12, 'toyota', 'fortuner', 'something else)

1

Sólo tiene que crear dos rutas, una para el reconocimiento, uno para la generación.

Actualizado: utilice las rutas en cuestión.

# 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 

y personalizar 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 

Muestra de ayudantes de ruta:

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 
Cuestiones relacionadas