Маршрутизация вложенных ресурсов в 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. Однако после маршрутов все усложняется. Это условное выражение в действии indexController # index просто становится все более раздутым и делает очень много дегазации. По мере того, как приложение растет, а также количество способов, которыми я хочу показывать фотографии, это будет только ухудшаться.
Вариант 2 может состоять в том, чтобы пользовательский:: 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
, чтобы вернуть имя пользователя. Помощники по пути все еще работают так же.
Я по-прежнему ценю отзывы об этом методе. Я делаю то, что ожидал, или я злоупотребляю этой функциональностью?
Ответы
Ответ 1
Лучший способ сделать это зависит от приложения, но в моем случае это, конечно, вариант B. Использование маршрутов с расширением имен я могу использовать модуль, чтобы разделить различные проблемы с отдельными контроллерами очень просто. Я также использую контроллер, специфичный для пространства имен, для добавления общих функций ко всем контроллерам в определенном пространстве имен (добавление, например, before_filter для проверки подлинности и разрешения для всех ресурсов в пространстве имен).
Ответ 2
Рассматривали ли вы использование мелкого вложенного маршрута в этом случае?
Неверное расположение маршрута
Время от времени вложенные ресурсы могут создавать громоздкие 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)
Ответ 3
Я сделал что-то похожее на это в одном из моих приложений. Ты на правильном пути. То, что я сделал, было объявить вложенные ресурсы и построить запрос, используя гибкий синтаксис ActiveL в Active Directory в 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
Ответ 4
Вы можете определить отдельный маршрут для получения фотографий для одного пользователя:
get '(:username)/photos', :to => 'photos#index'
Но я бы посоветовал просто использовать вложенный ресурс, который Jimmy опубликовал выше, поскольку это наиболее гибкое решение.
Ответ 5
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
Просто применил это в моем текущем проекте.