it-roy-ru.com

Маршрутизация вложенных ресурсов в Rails 3

У меня есть довольно распространенный случай для вложенных маршрутов, я чувствую, что это выглядит примерно так (в некоторой псевдотации):

'/:username/photos' => Show photos for User.find_by_username
'/photos' => Show photos for User.all

В двух словах: У меня есть пользователи. У них есть фотографии. Я хочу, чтобы иметь возможность показывать свои фотографии на своей странице. Я также хочу, чтобы иметь возможность показывать все фотографии, независимо от пользователя. Я хотел бы, чтобы мои маршруты оставались RESTful, и использование встроенных методов resource кажется правильным способом сделать это.


Вариант 1 для этого нужно, чтобы индекс PhotosController # использовал условное выражение, чтобы проверить, какие параметры даны, получить список фотографий и задать вид (для фотографий пользователя отличается от всех фотографий). Даже легко проложить маршрут:

resources :photos, :only => [:index]
scope ':/username' do
  resources :photos
end

Boom. Кажется как будто Rails был настроен для этого. Однако после маршрутов все становится сложнее. Это условное возвращение в действии индекса PhotosController # становится все более и более раздутым и приводит к ужасной деградации. По мере роста приложения и количества способов показать фотографии все будет только ухудшаться.

Вариант 2 может быть иметь User :: PhotosController для обработки фотографий пользователя и PhotosController для обработки всех фотографий.

resources :photos, :only => [:index]
namespace :user, :path => '/:username' do
  resources :photos
end

Это генерирует следующие маршруты:

           photos GET    /photos(.:format)                    {:action=>"index", :controller=>"photos"}
      user_photos GET    /:username/photos(.:format)          {:action=>"index", :controller=>"user/photos"}
                  POST   /:username/photos(.:format)          {:action=>"create", :controller=>"user/photos"}
   new_user_photo GET    /:username/photos/new(.:format)      {:action=>"new", :controller=>"user/photos"}
  edit_user_photo GET    /:username/photos/:id/edit(.:format) {:action=>"edit", :controller=>"user/photos"}
       user_photo GET    /:username/photos/:id(.:format)      {:action=>"show", :controller=>"user/photos"}
                  PUT    /:username/photos/:id(.:format)      {:action=>"update", :controller=>"user/photos"}
                  DELETE /:username/photos/:id(.:format)      {:action=>"destroy", :controller=>"user/photos"}

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

Вопросы

  • У кого-нибудь есть опыт с чем-то подобным?
  • Кто-нибудь может поделиться лучшим способом справиться с этим?
  • Любые дополнительные плюсы и минусы, чтобы рассмотреть с любым из этих вариантов?

Обновление : я продолжил реализацию Варианта 2, потому что он чувствует себя чище, позволяя логике Rails работать, а не переопределять ее. Пока все идет хорошо, но мне также нужно было переименовать мое пространство имен в :users и добавить :as => :user, чтобы он не конфликтовал с моей моделью User. Я также переопределил метод to_param в модели User, чтобы вернуть имя пользователя. Помощники пути все еще работают таким же образом.

Я все еще буду благодарен за отзыв об этом методе. Я делаю вещи ожидаемым образом, или я неправильно использую эту функциональность?

33
coreyward

Лучший способ сделать это зависит от приложения, но в моем случае это, безусловно, вариант B. Используя маршруты с именами, я могу использовать модуль, чтобы очень четко разделить различные задачи на разные контроллеры. Я также использую специфичный для пространства имен контроллер, чтобы добавить общие функциональные возможности ко всем контроллерам в определенном пространстве имен (добавив, например, before_filter для проверки подлинности и разрешения для всех ресурсов в пространстве имен).

6
coreyward

Рассматривали ли вы использование мелкого вложенного маршрута в этом случае?

Неглубокое вложение маршрута. Временами вложенные ресурсы могут создавать громоздкие URL-адреса. Решением этой проблемы является использование вложенного маршрута:

resources :products, :shallow => true do
  resources :reviews
end

Это позволит распознавать следующие маршруты:

/products/1 => product_path(1)
/products/1/reviews => product_reviews_index_path(1)
/reviews/2 => reviews_path(2)
7
MattMcKnight

Я сделал нечто подобное в одном из моих приложений. Вы на правильном пути. Я объявил вложенные ресурсы и построил запрос, используя гибкий синтаксис на основе arel Active Record в Rails 3. В вашем случае это может выглядеть примерно так:

# config/routes.rb
resources :photos, :only => :index
resources :users do
  resources :photos
end

# app/controllers/photos_controller.rb
def index
  @photos = Photo.scoped
  @photos = @photos.by_user(params[:user_id]) if params[:user_id]
  # ...
end
1
Jimmy Cuadra
Example::Application.routes.draw do
  resources :accounts, :path => '' do
    resources :projects, :path => '', :except => [:index]
  end
end

Получил пример от: http://jasoncodes.com/posts/Rails-3-nested-resource-slugs

Просто применил это в моем текущем проекте.

0
Arthur Corenzan

Вы можете определить отдельный маршрут для получения фотографий для одного пользователя следующим образом:

get '(:username)/photos', :to => 'photos#index'

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

0
Martijn