J'ai un cas assez commun pour les routes imbriquées, je pense, qui ressemble à ceci (dans une sorte de pseudonotation):
'/:username/photos' => Show photos for User.find_by_username
'/photos' => Show photos for User.all
En un mot: J'ai des utilisateurs. Ils ont des photos. Je veux pouvoir montrer leurs photos sur leur page. Je souhaite également pouvoir afficher toutes les photos, quel que soit l'utilisateur. J'aimerais garder mes routes RESTful et utiliser les méthodes intégrées resource
me semble être la bonne façon de le faire.
Option 1 pour cela, il est nécessaire que PhotosController # index utilise une condition pour vérifier quels paramètres sont fournis, obtenir la liste des photos et définir la vue (différente pour les photos d'un utilisateur que pour toutes les photos). C'est même facile de l'acheminer:
resources :photos, :only => [:index]
scope ':/username' do
resources :photos
end
Boom. Semblerait comme si Rails avait été configuré pour cela. Après les itinéraires, les choses se compliquent. Ce retour conditionnel dans l'action d'index PhotosController # commence à être de plus en plus gonflé et fait énormément de délayage. À mesure que l'application grandit et le nombre de façons dont je veux afficher des photos, la situation ne fera que s'aggraver.
Option 2 pourrait être d'avoir un utilisateur :: PhotosController pour gérer les photos de l'utilisateur, et un PhotosController pour gérer l'affichage de toutes les photos.
resources :photos, :only => [:index]
namespace :user, :path => '/:username' do
resources :photos
end
Cela génère les itinéraires suivants:
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"}
Cela fonctionne assez bien, je pense, mais tout est sous un module utilisateur et je sens que cela pourrait finir par causer des problèmes si je l'intègre à d'autres éléments.
Mise à jour : Je suis allé de l'avant en mettant en œuvre l'option 2, car elle semble plus propre, ce qui permet à la logique de Rails de fonctionner plutôt que de la dépasser. Jusqu'à présent, les choses se passent bien, mais je devais aussi renommer mon espace de noms en :users
et ajouter un :as => :user
pour l'empêcher d'entrer en conflit avec mon modèle User
. J'ai également remplacé la méthode to_param
sur le modèle User
pour renvoyer le nom d'utilisateur. Les auxiliaires de chemin fonctionnent toujours de cette façon aussi.
J'apprécierais tout de même les retours sur cette méthode. Est-ce que je fais les choses comme prévu ou est-ce que j'utilise mal cette fonctionnalité?
La meilleure façon de procéder dépend de l'application, mais dans mon cas, il s'agit bien de l'option B. À l'aide de routes espacées, je peux utiliser un module pour séparer les problèmes en différents contrôleurs de manière très propre. J'utilise également un contrôleur spécifique à un espace de noms pour ajouter une fonctionnalité partagée à tous les contrôleurs d'un espace de noms particulier (en ajoutant, par exemple, un before_filter pour vérifier l'authentification et l'autorisation de toutes les ressources de l'espace de noms).
Avez-vous envisagé d'utiliser un itinéraire imbriqué peu profond dans ce cas?
Imbrication dans une route peu profondeParfois, les ressources imbriquées peuvent générer des URL fastidieuses. Une solution à ce problème consiste à utiliser l’imbrication d’itinéraires peu profonds:
resources :products, :shallow => true do
resources :reviews
end
Cela permettra la reconnaissance des itinéraires suivants:
/products/1 => product_path(1)
/products/1/reviews => product_reviews_index_path(1)
/reviews/2 => reviews_path(2)
J'ai fait quelque chose de similaire à cela dans l'une de mes applications. Vous êtes sur la bonne voie. Ce que j'ai fait a été de déclarer les ressources imbriquées et de construire la requête en utilisant la syntaxe flexible basée sur arel d'Active Record dans Rails 3. Dans votre cas, cela pourrait ressembler à ceci:
# 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
Example::Application.routes.draw do
resources :accounts, :path => '' do
resources :projects, :path => '', :except => [:index]
end
end
J'ai eu l'exemple de: http://jasoncodes.com/posts/Rails-3-nested-resource-slugs
Juste appliqué cela dans mon projet actuel.
Vous pouvez définir un itinéraire distinct pour obtenir les photos d'un utilisateur, comme suit:
get '(:username)/photos', :to => 'photos#index'
Mais je vous conseillerais simplement d'utiliser la ressource imbriquée que Jimmy a publiée ci-dessus, car c'est la solution la plus flexible.