How to get a nice url hierarchy with slugs and acts_as_tree?


#1

I have a model Page in my application that acts_as_tree. I want this
model to keep whole structure of website. I have some root nodes: home,
contact, articles, etc. and the possibility of adding children to them.

I want to access these pages using slugs (Page model and new/edit forms
have such attribute/field). In show action of pages controller:

if params[:slug]
  @page = Page.find_by_slug(params[:slug])
  raise ActiveRecord::RecordNotFound, "Page not found" if @page.nil?
else
  @page = Page.find(params[:id])
end

And routing:

map.connect ‘:slug’, :controller => ‘pages’, :action => ‘show’

Everything works well at the first level but how to access children
pages using hierarchy of the tree and slugs?

Example structure:

Home (pages/1)
Articles(pages/2)
----Article1 (pages/5)
----Article2 (pages/6)
Contact(pages/3)
About (pages/4)

I need this kind of access especially in ‘Articles’ node. I’d like to
access several article using mydomain.com/articles_slug/article1_slug.

What is the nice way to do it? Could you post some helpful links or
write solution? Any help appreciated.


#2

Hi Emmek,

I never found anything really convincing to handle that, but facing the
same problem I found some workaround.

So first to get a slug, I use the plugin “ActsAsUrlParam”
http://github.com/caring/acts_as_url_param

I use it on a page “model” that uses a custom acts_as_tree (with dotted
ids, pretty convenient to boost performances)
http://github.com/xavier/acts-as-tree-with-dotted-ids

Then I created special routes (yeah, to use with caution, I wanted to
avoid the “/pages/” prefix )

map.resources :pages
map.page_first_level ‘/:slug.:format’,
:controller=>:pages,:action=>:show
map.page_second_level ‘/:parent_slug/:slug.:format’,
:controller=>:pages,:action=>:show
map.page_third_level ‘/:ancestor_slug/:parent_slug/:slug.:format’,
:controller=>:pages,:action=>:show

finally in my page helper

module PagesHelper
def smart_page_path(page,format=nil)
case page.depth
when 0
page_first_level_path(page.slug,:format=>format)
when 1
page_second_level_path(page.parent.slug,page.slug,:format=>format)
when 2
page_third_level_path(page.parent.parent.slug,
page.parent.slug,page.slug,:format=>format)
else
page_path(page)
end
end
end

it uses the page depth to generate my nice formatted URL

as result, using smart_page_path(@page)

-Home -> /home.html
-About -> /about.html
—History -> /about/history.html
—Team -> /about/team.html
-Projects -> /projects.html
—Global -> /projects/global.html
-----Ecology -> /projects/global/ecology.html
-----Human rights -> /projects/global/human-rights.html
—Local -> /projects/local.html
-Contact -> /contact.html

BUT!!

  • the extra params are just meant for SEO, you can use any routes, only
    the last slug will be used (/my-dummy-param/about.html)
  • maybe it’s not that good to use routes that starts from the root… I
    always use an “admin” namespace fro my admin sections, so I never have
    much at the root anyway.

If someone has a better idea I’ll be delighted to hear it.


#3

Emmek on Rails wrote:

I have a model Page in my application that acts_as_tree. I want this
model to keep whole structure of website.

Then I would strongly suggest awesome_nested_set instead of
acts_as_tree. It makes it possible to use the DB much more
efficiently.

I have some root nodes: home,
contact, articles, etc. and the possibility of adding children to them.

I want to access these pages using slugs (Page model and new/edit forms
have such attribute/field). In show action of pages controller:

if params[:slug]
  @page = Page.find_by_slug(params[:slug])
  raise ActiveRecord::RecordNotFound, "Page not found" if @page.nil?
else
  @page = Page.find(params[:id])
end

And routing:

map.connect ‘:slug’, :controller => ‘pages’, :action => ‘show’

Everything works well at the first level but how to access children
pages using hierarchy of the tree and slugs?

I’m not sure. You might need to use a *glob route for the page path,
then do your own parsing.

Best,

Marnen Laibow-Koser
http://www.marnen.org
removed_email_address@domain.invalid


#4

Marnen Laibow-Koser wrote:

Emmek on Rails wrote:

I have a model Page in my application that acts_as_tree. I want this
model to keep whole structure of website.

Then I would strongly suggest awesome_nested_set instead of
acts_as_tree. It makes it possible to use the DB much more
efficiently.

Actually, on second thought, this might be a good use case for a
materialized path in the DB. Then you could just do find_by_path to
retrieve any page at any depth.

Best,

Marnen Laibow-Koser
http://www.marnen.org
removed_email_address@domain.invalid