Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Friendly Rails URLs

I have a slightly different take on a fairly common problem: SEO-friendly URLs. I have a PagesController, so my URLs currently are like (using restful routing):

/pages/some-content-title

This works just fine, but there is a hierarchical structure to the pages so I need the following:

/some-content-title routes to /pages/some-content-title

I can also get this to happen using:

match '*a', :to => 'errors#routing'

in my routes.rb and trapping it in ErrorsController as:

class ErrorsController < ApplicationController
  def routing
    Rails.logger.debug "routing error caught looking up #{params[:a]}"
    if p = Page.find_by_slug(params[:a])
      redirect_to(:controller => 'pages', :action => 'show', :id => p)
      return
    end
    render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
  end
end

My question comes in the desired SEO elimination of the "pages/" part of the URL. What the SEO-dude wants (and here is where an example is key):

/insurance => :controller=>'pages', :id=>'insurance' # but the url in the address bar is /insurance

/insurance/car :controller=>'pages', :category=>'insurance', :id=>'car' # but the url in the address bar is /insurance/car

Is there a generic way for him to get his Google love and for me to keep the routes sane?

Thanks!

like image 230
Steve Ross Avatar asked Dec 06 '25 22:12

Steve Ross


1 Answers

This is hard to do since you are redefining the parameters based on their presence (or absence) in the path. You could handle the globbed parameters in the controller, but then you don't get the URL that you want, and it requires a redirect.

Rails 3 lets you use a Rack application as an endpoint when creating routes. This (sadly underused) feature has the potential to make routing very flexible. For example:

class SeoDispatcher
  AD_KEY = "action_dispatch.request.path_parameters"

  def self.call(env)
    seopath = env[AD_KEY][:seopath]
    if seopath
      param1, param2 = seopath.split("/") # TODO handle paths with 3+ elements
      if param2.nil?
        env[AD_KEY][:id] = param1
      else
        env[AD_KEY][:category] = param1
        env[AD_KEY][:id] = param2
      end
    end
    PagesController.action(:show).call(env)
    # TODO error handling for invalid paths
  end
end
#

MyApp::Application.routes.draw do
  match '*seopath' => SeoDispatcher
end

will map as follows:

GET '/insurance'     => PagesController#show, :id => 'insurance'
GET '/insurance/car' => PagesController#show, :id => 'car', :category => 'insurance

and will retain the URL in the browser that your SEO dude is asking for.

like image 175
zetetic Avatar answered Dec 08 '25 13:12

zetetic



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!