Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

conditional to_param method

I am using the model instance method t_param to generate a SEO-style URL

def to_param
  url
end

that way I can generate links to the model with path_to_model(model) and query the model with Model.find_by_url(url). This works fine so far.

My question: I have RESTFUL admin routes for the backend. Can I somehow make the to_param method react to the route it is called by? Because I want to create links in the backend with the ID parameter and not the URL parameter. Or what is the correct approach here?

like image 751
Mark Avatar asked Jan 10 '11 13:01

Mark


3 Answers

I had the same issue. A model really can't/shouldn't know anything about controllers. It's the controller's job to determine what resource is being requested, and to call the model to access it. If your AdminController needs to use the standard numeric ID, you're not going to want to touch to_param.

As it turns out, the solution is quite simple, and I use it in production.

Assuming you've namespaced your controllers, you'll have a MyModelController and a Admin::MyModelController, along with their helpers. In Admin, do things the standard way. In MyModelController, do the following:

In your resource actions, refer to params[:id] but treat it like a permalink (URL). For example

def get
  @my_model = MyModel.find_by_url(params[:id])
  ...
end

In your MyModelHelper

def my_model_path my_model
  super my_model.url
end

def my_model_url my_model
  super my_model.url
end

To me it felt this was the best way to "not fight" rails and get what I needed done.

There might be a more clever way to override the named routes. Also I think you can't use helper :all with this approach, unless you check state of whether you're in admin or not before generating paths/url's.

like image 169
aceofspades Avatar answered Dec 10 '22 11:12

aceofspades


Are you completely opposed to having the ID in the url? If not, what I've done in the past (that also accomplishes the goal of 'seo friendly' urls is this:

class Person
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

Thus, instead of:

http://www.example.com/users/1

you get

http://www.example.com/users/1-jerry-seinfeld

Since the String::to_i method will stop once it encounters a non-integer character (i.e. "1".to_i and "1-jerry-seinfeld".to_i both return 1), this means you can also do:

person = Person.find(params[:id])

without having to override any of your finders (with the added benefit of working on your frontend and your admin backend).

like image 21
jerhinesmith Avatar answered Dec 10 '22 11:12

jerhinesmith


Use in your model class variable @@to_param and class method for control it.

For example in model:

class ProductCategory < ActiveRecord::Base
  @@to_param = true

  attr_accessible :description, :name, :parent_id
  has_ancestry
  has_many :products
  validates :slug, :uniqueness => true, :presence => true

  before_validation :generate_slug

  #Trigger fo 'to_param' method
  def self.to_param_trigger(test)
    if (test)
      @@to_param = true
    else
      @@to_param = false
    end
  end

  def to_param
    if @@to_param
      slug
    else
      id
    end
  end

  def generate_slug
    self.slug ||= self.ancestors.map {|item| item.name.parameterize}.push(self.name.parameterize).join('/')
  end
end

In controller:

class Backend::ProductCategoriesController < Backend::BaseController
  def index
    # Disable 'to_param'
    ProductCategory.to_param_trigger false
    @categories = ProductCategory
      .select([:id, :name, :slug])
      .select("COALESCE(ancestry, '0') as anctr")
      .order :anctr, :name
  end

  def new
  end

  def edit
  end
end

before_filter and other tricks in help =)

like image 38
unixs Avatar answered Dec 10 '22 11:12

unixs