Cameron
Cameron

Reputation: 28853

Rails nested routing for dynamic pages

In my Rails app I have pages which are stored in the database.

So for example:

id: 1
name: 'About'
slug: 'about'
parent_id: null

id: 2
name: 'Team'
slug: 'team'
parent_id: 1

id: 3
name: 'Cameron'
slug: 'cameron'
parent_id: 2

The slug is used to access them via the routing like so:

match '/:slug' => 'pages#show', :via => :get, :as => :page

So I could access those pages at:

/about
/team
/cameron

What I want to do is use the parent_id so that the routing becomes:

/about/team/cameron

Can this be achieved using routing alone? Or do I need to do something else as well?

Upvotes: 4

Views: 380

Answers (2)

Tushar Pal
Tushar Pal

Reputation: 523

Take a help at Friendly gem, it simplifies routing with slugs a lot.

Alternative options to define routes like

As far as I know there's no good way to routes with dynamic paramlinks, you can create a named route to capture pretty nested pages path:

 get '/p/*id', :to => 'pages#show', :as => :nested_pages

Also, make sure you update slug of your page object to have nested urls, i.e.: append parent pages' slug to it. For example:

 page1.slug = '/about'
 page2.slug = '/about/team' # team is a child of about
 page3.slug = '/about/team/cameron' # cameron is a child of team

so that you can be made it like this

 get '/p/*id', :to => 'pages#show', :via => :get, :as => :nested_pages or pages

So, to make this work, you can probably change generate_slug method in your Page model class:

 def generate_slug
   name_as_slug = name.parameterize
   if parent.present?
     self.slug = [parent.slug, (slug.blank? ? name_as_slug : slug.split('/').last)].join('/')
   else
     self.slug = name_as_slug if slug.blank?
   end
 end

Upvotes: 0

Robin
Robin

Reputation: 8518

You can't achieve this with nested routes, but you can with route globbing and wildcards. Let me outline the steps it probably takes to realize it, for you:

  1. Add a wild card route like get '*path', to: 'pages#show' to the end of your routes file, otherwise it will match all other get requests.
  2. Get the record for the params['path'] in the pages#show action.

Start with the last segment and try to find it. If you don't find it, the page doesn't exist. If you get results for the last slug segment, check out their parants, if they match with the next slug segment and so on.

If it's going to be a huge tree, this solution probably creates a performance problem. Then you could implement something that stores the path somewhere or use solutions like the PostgreSQL ltree.

Upvotes: 1

Related Questions