Пользовательские параметры в URL для действия показа - PullRequest
10 голосов
/ 21 марта 2012

Я работаю над реализацией SEO-hiarchy, а это значит, что мне нужно предварительно добавить параметры для действия show.

Вариант использования - это поисковый сайт с URL-структурой:
/cars/(:brand)/ => страница списка
/cars/(:brand)/(:model_name)?s=query_params => действие поиска
/cars/:brand/:model_name/:variant/:id => действие автосалона

Моя проблема заключается в создании 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 ]

3 голосов
/ 03 апреля 2012

Я не вижу способа сделать это с помощью Rails, не настроив ни распознавание URL, ни генерацию URL.

С первой попытки у вас работает распознавание URL, но не генерация. Решение, которое я вижу, чтобы заставить генерацию работать, было бы переопределить вспомогательный метод car_path.

Другое решение, как вы сделали в ОБНОВЛЕНИИ, может переопределить метод to_param Car. Обратите внимание, что ваша проблема не в методе to_param, а в определении маршрута: вам нужно указать параметры :brand, :model_name и :variant, когда вы хотите сгенерировать маршрут. Чтобы справиться с этим, вы можете использовать Подстановочный знак в вашем маршруте.

Наконец, вы также можете использовать гем 1015 * фильтра маршрутизации , который позволяет добавлять логику до и после распознавания / генерации URL.

Мне кажется, что все эти решения немного тяжелы и не так просты, как следовало бы, но я считаю, что это произошло из-за вашей потребности, так как вы хотите добавить некоторые уровни в URL без строгого следования поведению rails, которое будет дать вам URL, как /brands/audi/models/A3/variants/19930

1 голос
/ 04 апреля 2012

Вам просто нужно создать два маршрута, один для распознавания, другой для генерации.

Обновлено : использовать рассматриваемые маршруты.

# 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
1 голос
/ 21 марта 2012

ОК, вот что у меня есть.Это работает в моем маленьком тестовом случае.Очевидно, что нужны были некоторые исправления, и я уверен, что они могут быть более краткими и элегантными, но мой девиз: «заставь это работать, сделай его красивым, сделай его быстрым»: -)

In 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

В модели Car

  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
0 голосов
/ 03 апреля 2012

Вы хотите, чтобы ограниченные параметры передавались по URL-адресу, некоторые параметры которого являются необязательными, а некоторые из них обязательно должны присутствовать.

Руководства по Rails показывают, что вы можете иметь как строгие, так и необязательные параметры, а также вы можетедать имя конкретному маршруту, чтобы упростить его использование.

Руководство по маршрутизации рельсов связанные параметры

Пример использования -

На маршруте ниже,

  • brand равен optional parameter, поскольку он окружен circular bracket
  • Также обратите внимание, что в маршруте могут быть необязательные параметры, но их необходимо добавить в конце /cars(/:brand)(/:make)(/:model)

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

здесь cars_url будет отображаться для индексации действия контроллера машины .. снова cars_url("Totoya") будет направлять индексное действие контроллера машины вдоль-с params[:brand] как Toyota

Показывать URL-адрес может быть как показано ниже, где 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)

...