Works with @adonisjs/lucid 5.0.3 or greater.
This addon adds the functionality to generate unique slugs
for multiple fields when saving them to the database using Lucid models.
Make sure to install it using adonis install
over npm
or yarn
.
adonis install @adonisjs/lucid-slugify
# yarn
adonis install @adonisjs/lucid-slugify --yarn
Next, make sure to read the instructions.md file.
- Url friendly strings.
- Handles unicode
- Works seamlessly with updates
- Uniqueness guaranteed.
const Model = use('Model')
class Post extends Model {
static boot () {
super.boot()
this.addTrait('@provider:Lucid/Slugify', {
fields: { slug: 'title' },
strategy: 'dbIncrement',
disableUpdates: false
})
}
}
{ |
|
"fields": |
key/value pair of fields, you want to generate slugs for.. The |
"strategy": |
It can be raw function or one of the predefined strategies. |
"disableUpdates": |
Depending upon your app, you may never want slugs to change. So disable the |
} |
Let's use the post model and generate a new slug.
const post = new Post()
post.title = 'Adonis 101'
await post.save()
console.log(post.slug) // adonis-101
On update it will re-generate the slug, only if the value of the actual field has been changed.
const post = await Post().find(1)
await post.save() // noop
post.title = 'A new title'
await post.save() // slug re-generated
console.log(post.slug) // a-new-title
Generating unique slugs is a hard problem, since each application has unique requirements and constraints. So instead of defining a single strategy to generate slugs, the add-on keeps it flexible to define custom strategies.
2 different strategies are shipped by default and here's how they work.
The dbIncrement
strategy adds a counter to the slugs, when duplicate slugs are found.
Works great with mysql
and pg
, whereas sqlite
has some edge cases.
Let's assume a post with slug -> hello_world
already exists.
+----+-------------+-------------+
| id | title | slug |
+----+-------------+-------------+
| 1 | Hello world | hello-world |
+----+-------------+-------------+
Now if we will add a new slug, it will add -1
, -2
respectively to it.
+----+-------------+---------------+
| id | title | slug |
+----+-------------+---------------+
| 1 | Hello world | hello-world |
| 2 | Hello world | hello-world-1 |
| 3 | Hello world | hello-world-2 |
+----+-------------+---------------+
As mentioned above, this strategy fails in some edge cases when using sqlite
. Let's take the following database table structure.
+----+-------------------+-------------------+
| id | title | slug |
+----+-------------------+-------------------+
| 1 | Hello world | hello-world |
| 2 | Hello world | hello-world-1 |
| 3 | Hello world | hello-world-2 |
| 4 | Hello world fanny | hello-world-fanny |
+----+-------------------+-------------------+
In sqlite adding a new post with Hello world title will fail and here's why.
- The strategy will find latest row from the database matching
slug LIKE hello-world%
. - The return row will be
hello-world-fanny
and nothello-world-2
and hence the counter logic will fail. - In MySQL and PostgreSQL, we make use of Regular expressions
REGEXP ^hello-world(-[0-9*])?$
and pull the correct row.
This strategy works great regardless of the database engine in use, the only downside is, the URL's are not as pretty as dbIncrement
strategy.
Make sure to install shortid as dependency before using it
+----+-------------+----------------------+
| id | title | slug |
+----+-------------+----------------------+
| 1 | Hello world | hello-world-23TplPdS |
+----+-------------+----------------------+
The random number after the actual slug is generated using shortid and it ensures uniqueness.
You can define your strategies just by defining a function, and here's what a strategy is responsible for.
- It receives the base slug generated by the core library.
- It should return back a string ensuring the return value is unique.
this.addTrait('@provider:Lucid/Slugify', {
fields: { slug: 'title' },
strategy: async (field, value, modelInstance) => {
return `${modelInstance.author_id}-${value}`
}
})
The above is a simple implementation where we prepend the author_id
to slug and ofcourse, we assumed that author_id
will be set on the model instance.
const post = new Post()
post.author_id = auth.user.id
post.title = 'Spacex Launch'
await post.save()
console.log(post.slug) // 1-spacex-launch
You just need to find the one that fits your needs. Wordpress has been generating slugs for years and here's a screenshot of options they give.
The code is tested with mysql
, pg
and sqlite
only. Tests are written using japa.