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?
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.
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).
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 =)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With