I have a pretty common case for nested routes, I feel like, that looks something like this (in some sort of pseudonotation):
'/:username/photos' => Show photos for User.find_by_username '/photos' => Show photos for User.all
In a nutshell: I have users. They have photos. I want to be able to show their photos on their page. I also want to be able to show all photos, regardless of the user. I'd like to keep my routes RESTful and using the built-in resource
methods feels like the right way to do it.
Option 1 for doing this is to have PhotosController#index use a conditional to check which params are given and get the list of photos and set the view (different for a user's photos than for all photos). It's even easy to route it:
resources :photos, :only => [:index] scope ':/username' do resources :photos end
Boom. It'd seem like Rails was setup for this. After the routes, though, things get more complicated. That conditional back in the PhotosController#index action is just getting more and more bloated and is doing an awful lot of delgation. As the application grows and so do the number of ways I want to show photos, it is only going to get worse.
Option 2 might be to have a User::PhotosController to handle user photos, and a PhotosController to handle showing all photos.
resources :photos, :only => [:index] namespace :user, :path => '/:username' do resources :photos end
That generates the following routes:
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"}
This works pretty well, I think, but everything is under a User module and I feel like that might end up causing problems when I integrate it with other things.
Update: I've gone ahead implementing Option 2 because it feels cleaner allowing Rails' logic to work rather than overriding it. So far things are going well, but I also needed to rename my namespace to :users
and add an :as => :user
to keep it from clashing with my User
model. I've also overridden the to_param
method on the User
model to return the username. Path helpers still work this way, too.
I'd still appreciate feedback on this method. Am I doing things the expected way, or am I misusing this functionality?
Rails Nested Resources Nested routes are another way of capturing these relationships through your routing. This means that our URLs will look aesthetically pleasing and not a random string of numbers and characters. resources :coffees do. resources :reviews, only: [:new, :index, :show]
Any object that you want users to be able to access via URI and perform CRUD (or some subset thereof) operations on can be thought of as a resource. In the Rails sense, it is generally a database table which is represented by a model, and acted on through a controller.
Rails RESTful Design which creates seven routes all mapping to the user controller. Rails also allows you to define multiple resources in one line.
Have you considered using a shallow nested route in this case?
Shallow Route Nesting At times, nested resources can produce cumbersome URLs. A solution to this is to use shallow route nesting:
resources :products, :shallow => true do resources :reviews end
This will enable the recognition of the following routes:
/products/1 => product_path(1) /products/1/reviews => product_reviews_index_path(1) /reviews/2 => reviews_path(2)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With