Rails: конфликт вложенных ресурсов, как настроить действие индекса в зависимости от вызываемого маршрута

StackOverflow https://stackoverflow.com/questions/1811163

Вопрос

Представьте, что у вас есть два определенных маршрута:

map.resources articles
map.resources categories, :has_many => :articles

оба доступны помощникам / путям

articles_path # /articles
category_articles_path(1) # /category/1/articles

если вы посещаете / article , выполняется действие index из ArticlesController .

если вы посещаете / category / 1 / article , index из ArticlesController также выполняется.

Итак, каков наилучший подход для условного выбора только статей с определенными областями в зависимости от маршрута вызова?

#if coming from the nested resource route
@articles = Articles.find_by_category_id(params[:category_id])
#else
@articles = Articles.all
Это было полезно?

Решение

У вас есть два варианта выбора, в зависимости от того, насколько ваша логика и ваш взгляд связаны с областью действия. Позвольте мне объяснить подробнее.

Первый вариант - определить область действия вашего контроллера, как уже объяснено в других ответах. Я обычно устанавливаю переменную @scope, чтобы получить некоторые дополнительные преимущества в моих шаблонах.

class Articles

  before_filter :determine_scope

  def index
    @articles = @scope.all
    # ...
  end

  protected

    def determine_scope
      @scope = if params[:category_id]
        Category.find(params[:category_id]).articles
      else
        Article
      end
    end

end

Причина переменной @scope заключается в том, что вам может потребоваться узнать объем вашего запроса за пределами одного действия. Предположим, вы хотите отобразить количество записей в вашем представлении. Вы должны знать, фильтруете ли вы по категории или нет. В этом случае вам просто нужно вызывать @ scope.count или @ scope.my_named_scope.count вместо того, чтобы повторять каждый раз проверку params [: category_id] .

Этот подход хорошо работает, если ваши взгляды, с категорией и без категории, очень похожи. Но что происходит, когда список, отфильтрованный по категории, полностью отличается от списка без категории? Это происходит довольно часто: в разделе вашей категории есть некоторые виджеты, ориентированные на категории, а в разделе статей - виджеты и фильтры, связанные со статьей. Кроме того, в вашем контроллере Article есть несколько специальных before_filters, которые вы, возможно, захотите использовать, но вам не нужно использовать их, когда список статей принадлежит категории.

В этом случае вы можете разделить действия.

map.resources articles
map.resources categories, :collection => { :articles => :get }

articles_path # /articles and ArticlesController#index
category_articles_path(1) # /category/1/articles and CategoriesController#articles

Теперь список, отфильтрованный по категориям, управляется CategoriesController , и он наследует все фильтры, макеты, настройки контроллера, а нефильтрованный список управляется ArticlesController .

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

Другие советы

Мне часто нравится отделять эти действия. Когда получающиеся действия очень похожи, вы можете легко разделить области внутри контроллера, посмотрев, есть ли params [: category_id] & nbsp; и т. Д. (См. Ответ @SimoneCarletti).

Обычно разделение действий в контроллере с помощью пользовательских маршрутов обеспечивает большую гибкость и четкие результаты. Следующий код приводит к обычным именам помощников маршрутов, но маршруты направлены на определенные действия в контроллере.

В rout.rb :

resources categories do
  resources articles, :except => [:index] do
    get :index, :on => :collection, :action => 'index_articles'
  end
end
resources articles, :except => [:index] do
  get :index, :on => :collection, :action => 'index_all'
end

Тогда вы можете иметь в ArticlesController.rb

def index_all
  @articles = @articles = Articles.all
  render :index # or something else
end

def index_categories
  @articles = Articles.find_by_category_id(params[:category_id])
  render :index # or something else
end

Имея только один вложенный ресурс, проще всего использовать условные, основанные на параметрах, для определения его объема. Скорее всего, так будет и в вашем случае.

if params[:category_id]
  @articles = Category.find(params[:category_id]).articles
else
  @articles = Article.all
end

Однако, в зависимости от того, какие другие вложенные ресурсы у вас есть для модели, придерживаться этого подхода может быть довольно утомительно. В этом случае используйте плагин, например resource_controller или make_resourceful значительно упростит эту задачу.

class ArticlesController < ResourceController::Base
  belongs_to :category
end

Это на самом деле сделает все, что вы ожидаете. Он дает вам все ваши стандартные действия RESTful и автоматически устанавливает область действия для / Categories / 1 / article .

if params[:category_id].blank?
  # all
else
  # find by category_id
end

Мне нравится считать действие независимым от маршрута. Независимо от того, как они туда доберутся, примите разумное решение относительно того, что делать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top