Reputation: 17020
I have this resource tree:
I want to be able to access them independently wherever possible. I want to avoid redundant routes like /forum/:forum_id/topic/:topic_id/post/:id
because I can just do /post/:id
.
The ideal routes look like this:
/forums => Forums#index # Lists every forum
/forum/new => Forums#new # New forum
/forum/edit => Forums#edit # Edit forum
/forum/:id => Forums#show # Shows forum
/forum/:id/forums Forums#index # Lists nested forums
/forum/:id/topics => Topics#index # Lists topics inside forum
/forum/:id/topic/new => Topics#new # New topic
/topics => Topics#index # Lists every topic
/topic/:id => Topics#show # Shows topic
/topic/:id/posts => Posts#index # Lists posts inside topic
/topic/:id/post/new => Posts#new # New post
/posts => Posts#index # Lists every post
/post/:id => Posts#show # Shows post
What is the best way to model this situation?
Here's what I tried:
resources :forums
resources :topics
resources :posts
resources :forums do
resources :topics
end
resources :topics do
resources :posts
end
The problem is that these settings create a lot of useless routes, like:
/forums/:forum_id/topic/:id # Redundant - /topic/:id
/topics/:topic_id/post/:id # Redundant - /post/:id
/topics/new # No current forum
/posts/new # No current topic
Is there any way to specify which routes to create?
In the controllers, how do I handle multiple routes mapped to the same action? For example, inside Topics#index
how do I find out if I should handle GET /forum/:id/topics
or GET /topics
?
Upvotes: 4
Views: 691
Reputation: 17020
I solved my problem by restricting what routes each resource declaration generated:
resources :forums do
resources :topics, only: [ :index, :new, :create ]
end
scope except: [ :new, :create ] do
resources :posts
resources :topics do
resources :posts, only: [ :index, :new, :create ]
end
end
As for the controller issue, I simply check if an id
was passed:
# Topics#index
if forum_id = params[:forum_id]
@topics = Forum.find(forum_id).topics.paginate page: params[:page]
else
@topics = Topic.paginate page: params[:page]
end
Upvotes: 1
Reputation: 42865
Nested routes are only needed on index
actions where a collection of resources is found by a parent object. Otherwise it is about SEO. Most users will not notice how their urls are getting generated nor care so it's all about search engines. I see where you are going but it's going to be more work to not generate routes as the convention in this example is listing a resource with one line of code. And of course you already know this but this is just my take on things.
a) forms_path #if you want to find all forms
b) topics_path #if you want to find all topics #possible use, maybe in a tag listing.
c) posts_path #if you want to find all posts #probably never use
You will probably never want to find all topics and especially posts, but those would be the routes to use.
d) form_topics_path(form) #find all topics of a given form
e) form_topic_path(form, topic) #only find one topic on a give form
f) topic_path #find a given topic
In the last two, e and f, the form is not needed since you know which topic you want. If you are concerned about SEO and getting your urls nice for search engines then probably want to use e.
g) form_topic_posts_path(form, topic) #I'm already confused
h) form_topic_post_path(form, topic, post) #still to deep
i) topic_posts_path(topic) #most rails people think two levels is deep enough
j) topic_post_path(topic, post) #might be good for seo
It's really a matter of SEO and keeping your urls friendly besides the nested resource that need their parent id to find the associated posts such as passing the form
to find the related topics, and passing the topic
to find the related posts.
If you use topic_path
, topics_path
post_path
, post_path
you are surly missing out on better urls but in terms of having better urls for engines to read but they really are unnecessary.
In terms of not generating the routes there really isn't a demand for this because it would makes this more complicated than just declaring a resource in one line where the end goal is just housekeeping.
Upvotes: 2