Matheus Moreira
Matheus Moreira

Reputation: 17020

Nested resources - How to avoid redundant routes?

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

Answers (2)

Matheus Moreira
Matheus Moreira

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

Sam 山
Sam 山

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

Related Questions