Git Product home page Git Product logo

slugy's Introduction

Slugy

Build Status

A Phoenix library to generate slug for your schema fields

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
  import Slugy, only: [slugify: 2]

  schema "posts" do
    field :title, :string
    field :body, :text
    field :slug, :string
  end

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

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

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

slugify/2 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.

Usage

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

iex> slugify(changeset, :title)
%Ecto.Changeset{changes: %{slug: "content-1"}}

Slugify from an embedded struct field

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

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

%Content{
  type: "text",
  data: %{title: "Content 1", external_id: 1}
}

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

  iex> slugify(changeset, [:data, :title])
  %Ecto.Changeset{changes: %{slug: "content-1"}}

Custom slug

If you want a custom slug composed for more than one fields e.g. a post title and the type like so "how-to-use-slugy-video" you need to implement the Slug protocol that extracts the desirable fields to generate the slug.

defmodule Post do
  # ...
end

defimpl Slugy.Slug, for: Post do
  def to_slug(%{title: title, type: type}) do
    "#{title} #{type}"
  end
end

So, %Post{title: "A new Post", body: "Post body", type: "video"} with the above Slug protocol implementation will have a slug like so a-new-post-video

Without a changeset

In cases we just want to get the slug string without a changeset involved we can use slugify/1 to achieve that.

iex> slugify("Slugy is awesome")
"slugy-is-awesome"

Routes

And lastly for having our routes with the slug we just need to implement the Phoenix.Param protocol to our slugified schemas. 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

Add slug field as a unique index

To make sure slug is always unique we can add a unique constraint to our slug column

defmodule MyApp.Migrations.AddSlugToPosts do
  use Ecto.Migration

  def change do
    alter table(:posts) do
      add :slug, :string
    end

    create unique_index(:posts, [:slug])
  end
end

And add the unique_constraint/2 to check in our changeset pipeline.

defmodule Post do
  def changeset(post, attrs) do
    # ...
    |> unique_constraint(:slug)
  end
end

Installation

Add to your mix.exs file.

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

Don’t forget to update your dependencies.

$ mix deps.get

Documentation

You can also find the docs here https://hexdocs.pm/slugy.

Contribute

Feel free to contribute to this project. If you have any suggestions or bug reports just open an issue or a PR.

slugy's People

Contributors

francolucas avatar majordwarf avatar norbajunior avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.