Creating slugs for Ecto schemas

Nikita Sobolev
wemake.services
Published in
3 min readJul 19, 2017

What is a slug?

The term “slug” comes from the world of newspaper production. If you have ever created a simple “Blog” application you have already reinvented it.

When you need to access any post at some URL you need to identify it somehow. The simplest idea is to identify them by id, but that does not seem too pretty. It would be better to identify your posts based on its title or content. So, a blog post with a title “Creating slugs for Ecto models” would be accessible via a nice-looking URL: myapp.com/posts/creating-slugs-for-ecto-models. Where creating-slugs-for-ecto-models is a slug. Pretty cool, isn’t it?

So, a slug is a good-looking web-safe unique string used to identify some data.

Creating slugs is so common, that we have actually created a utility library to generate slugs based on some other fields. We called it ecto_autoslug_field.

How does EctoAutoslugField work?

Imagine that you have a simple Article schema inside Blog context. That’s how it looks like freshly out of the generator:

lib/ecto_slugs/blog/article.ex

It has just three meaning-full fields: :title which is unique, :content, and if this article has some :breaking news in it which is set to false by default.

Now you want to generate a slug from the title.

Installation

Firstly, add {:ecto_autoslug_field, “~> 0.3”} to your mix.exs. Then fetch the dependencies with mix deps.get.

Then you will need to add a new field to your schema. Let’s call it :slug.

lib/ecto_slugs/blog/article.ex

Take a close note on this TitleSlug used everywhere. That would be a slug field module. But right now it does not exist. We will implement it later.

You will also need to add a new migration. It would be something like this:

priv/repo/migration/alter-article-with-slug.exs

Simple example

Remember, that we did not implement TitleSlug yet? Let’s decide what it should look like. The first use case is the simplest one. When an article is created, generate a slug from it’s title. Do nothing more.

lib/ecto_slugs/blog/article.ex

That’s it. It takes a value from :from field and puts changes into :to field. But maybe that’s not what you want? Let’s step it up.

Conditional example

Imagine that you have a business rule: put “breaking” in front of every breaking news article’s slug. How could you achieve that?

Note, that right now we don’t use :from option anymore, instead, we are using a get_sources/2 function. What does it do? When creating an article it will provide conditional sources for the slug. And since it has access to the changeset struct the possibilities are endless. The logic itself is also pretty simple. When changeset has :breaking key set to true prepend “breaking” to the sources list. Your business rule is now satisfied, let’s move on.

Modify the slug

Or maybe you have a different use case? The business wants your slugs to be joined differently. So you need to modify the resulting slug. How to do that?

It is possible to define a custom build_slug/2 function which accepts two arguments: the list of sources and the initial changeset. This function is designed to build and return the slug before it is saved to the database. This super() call transforms your list of sources into the slug-string.

But before the slug is returned you can do multiple things:

  • check either your slug is unique, if not — increment it somehow
  • modify your slug
  • or even build the slug yourself without this magic super() call

And of course, you can use this function alongside the get_sources/2.

Conclusion

That’s a short introduction to ecto_autoslug_field.

But that’s not even all its features covered! There are more options and possibilities. Like, recreating slug on every save with :always_change option and others. We have tried to cover everything inside documentation and it is available online.

Check ecto_autoslug_field out:

Gratis

Special thanks to @h4cc for creating slugger which we rely on.

--

--

Nikita Sobolev
wemake.services

wemake.services co-founder, ElixirLangMoscow co-organizer, open source developer