Reputation: 14434
I'm using the friendly_id to add custom slugs to my models and their corresponding urls. Currently I have a setup where a Post
belongs to a Board
. There will undoubtedly be cases where a post will have an identical title to another but from a different board. Often times I've noticed sites (SO included) prepend a unique set of numbers before the slug to insure there are no issues with uniqueness:
https://stackoverflow.com/questions/123456/my-example-question
I was wondering what would be the best approach to accomplish this? This couldn't be done solely through the routes file because there still poses the possibility of two or more identical posts being created. Would it be a combination of altering my model, routes file, and configuration of the friendly_id gem?
My end goal is to have a url generated like this for my posts:
https://example.com/boards/example-board/123456/example-post
class Board < ApplicationRecord
extend FriendlyId
has_many :posts
friendly_id :name, use: :slugged
end
class Post < ApplicationRecord
extend FriendlyId
belongs_to :board
friendly_id :title, use: :slugged
end
resources :boards do
scope module: :boards do
resources :posts
end
end
Upvotes: 1
Views: 1585
Reputation:
You can do something like this in your routes:
resources :boards do
resources :posts, path: ':board_real_id'
end
and add params[:board_real_id]
to your query.
I don't think you need UUID for this (unless you want to). You can use candidates
and if two posts have the same name and they belong to the same board, just insert the post's id and you'll be fine, you'll have something like https://example.com/boards/example-board/123456-example-post
From: http://norman.github.io/friendly_id/file.Guide.html
Since UUIDs are ugly, FriendlyId provides a "slug candidates" functionality to let you specify alternate slugs to use in the event the one you want to use is already taken. For example:
friendly_id :slug_candidates, use: :slugged
# Try building a slug based on the following fields in
# increasing order of specificity.
def slug_candidates
[
:name,
[:id, :name]
]
end
Upvotes: 2
Reputation: 668
You need to use slug_candidates
, see the docs here.
In your case all you need is to add a uuid in the end/start of the slug, and you can achieve this by using incremental uuid. If you have the a record with the current slug, get the max uuid and increase it by 1, save it!
class Post < ApplicationRecord
extend FriendlyId
belongs_to :board
friendly_id :slug_candidates, use: :slugged
def slug_url
name
end
def slug_candidates
[:slug_url, [:slug_url, :slug_uuid]]
end
def slug_uuid
result = Post.select("COUNT(REGEXP_SUBSTR(name, '[0-9]+$')) AS cnt, MAX(REGEXP_SUBSTR(title, '[0-9]+$')) + 1 AS mx_uuid")
result.cnt == 0 ? "" : result.mx_uuid + 1
end
end
I am using MYSQL syntax to match the regex pattern.
Upvotes: 0