Slugy (slugy v4.1.0)

A Phoenix library to generate slug for your schema fields

Examples

Let's suppose we have a Post schema and we want to generate a slug from title field and save it to the slug field. To achieve that we need to call slugify/2 following the changeset pipeline passing the desireable field. slugify/2 generates the slug and put it to the changeset.

defmodule Post do
  use Ecto.Schema
  import Ecto.Changeset
  import Slugy

  embedded_schema do
    field(:title, :string)
    field(:slug, :string)
  end

  def changeset(post, attrs) do
    post
    |> cast(attrs, [:title, :type])
    |> slugify(:title)
  end
end

Running this code on iex console you can see the slug generated as a new change to be persisted.

iex> Post.changeset(%Post{}, %{title: "A new Post"}).changes
%{title: "A new Post", slug: "a-new-post"}

Slugy just generates a slug if the field's value passed to slugify/2 comes with a new value to persist in attrs (in update cases) or if the struct is a new record to save.

Link to this section Summary

Functions

Returns a downcased dashed string.

The slugify/2 expects a changeset as a first parameter and an atom to the second one. The function will check if there is a change on the "key" field and if affirmative generates the slug and assigns to the slug field, otherwise do nothing and just returns the changeset.

Link to this section Functions

Returns a downcased dashed string.

Examples

iex> Slugy.slugify("Vamo que vamo")
"vamo-que-vamo"
Link to this function

slugify(changeset, key)

The slugify/2 expects a changeset as a first parameter and an atom to the second one. The function will check if there is a change on the "key" field and if affirmative generates the slug and assigns to the slug field, otherwise do nothing and just returns the changeset.

iex> Post.changeset(%Post{}, %{title: "A new Post"}).changes
%{slug: "a-new-post", title: "A new Post"}

Composed slug

If you need a composed slug e.g. a post title and the type like so "how-to-use-slugy-video" you need to pass the with key that expects a list of atom keys.

defmodule Content do
  use Ecto.Schema
  import Ecto.Changeset
  import Slugy

  embedded_schema do
    field :name, :string
    field :type, :string
    field :slug, :string
  end

  def changeset(post, attrs) do
    post
    |> cast(attrs, [:name, :type])
    |> slugify(with: [:name, :type])
  end
end

iex> Content.changeset(%Content{}, %{name: "Elixir", type: "video"}).changes
%{name: "Elixir", type: "video", slug: "elixir-video"}

Slugify from a map field

In rare cases you need to generate slugs from a field inside a map field that represents a jsonb column on your database.

For example by having a struct like below and we want a slug from data -> title:

defmodule PostWithMapField do
  use Ecto.Schema
  import Ecto.Changeset
  import Slugy

  embedded_schema do
    field :data, :map
    field :slug, :string
  end

  def changeset(post, attrs) do
    post
    |> cast(attrs, [:data])
    |> slugify([:data, :title])
  end
end

Just pass a list with the keys following the path down to the desirable field.

iex> PostWithMapField.changeset(%PostWithMapField{}, %{data: %{title: "This is my AWESOME title"}}).changes
%{data: %{title: "This is my AWESOME title"}, slug: "this-is-my-awesome-title"}

Routes

And lastly if you want to have your routes with the slug as the :id param implement the Phoenix.Param protocol to your slugified schema. Phoenix.Param will extract the slug in place of the :id.

defmodule Post do
  @derive {Phoenix.Param, key: :slug}
  schema "posts" do
  # ...
  end

  def changeset(post, attrs) do
  # ...
  end
end

For more information about Phoenix.Param protocol see in https://hexdocs.pm/phoenix/Phoenix.Param.html

Installation

Add to your mix.exs file.

def deps do
  [
    {:slugy, "~> 4.1.0"}
  ]
end

Don’t forget to update your dependencies.

$ mix deps.get