I want to have urls like this:
http://domain.com/products/454-table-lamp
So I use friendly_id like so:
extend FriendlyId
friendly_id :slug_candidates, use: :history
def slug_candidates
[
[:id, :title]
]
end
Now, since friendly id generates the slug before the object is saved I end up with an url like so (Please note the missing id in the URL):
http://domain.com/products/table-lamp
Now while this is not too bad as such. As soon as I save another Product called "Table Lamp", I will get an URL like this:
http://domain.com/products/table-lamp-ebaf4bf5-a6fb-4824-9a07-bdda34f56973
So my question is, how can I make sure, friendly ID creates the slug containing the ID as well.
Just add an after_commit callback to your model. In this callback, set slug to nil and save:
after_commit :update_slug, on: :create
def update_slug
unless slug.include? self.id.to_s
self.slug = nil
self.save
end
end
Well, the short answer - you can't. FrienlydId generates slug in before_validation
callback here: https://git.io/vgn89. The problem is that you need to generate slug before saving to avoid conflicts, but you want to use information that available only after actual save, when record is already in db.
Take a look on this answer for details.
Of course, you can do something like if you really want it:
def slug_candidates
[ title, Model.maximum(:id) + 1 ]
end
But this code is not thread safe and can lead to some obvious problems. Another way - change the slug after save, in after_create
callback for example. You can set it to nil
and call set_slug
again.
But ask yourself - do you really need it? May be it is better to use timestamp from created_at for example as a part of slug candidates?
来源:https://stackoverflow.com/questions/35221139/friendly-id-slug-does-not-contain-the-id