legendary-doc-site/apps/content/lib/content/slugs.ex
2020-07-24 17:10:50 -05:00

68 lines
1.7 KiB
Elixir

defmodule Content.Slugs do
@moduledoc """
Provides functions for working with post slugs and ensuring that they are unique.
"""
import Ecto.{Changeset, Query}
alias Content.{Post, Repo}
def ensure_post_has_slug(changeset) do
cond do
!is_nil(changeset |> get_field(:post_name)) ->
changeset
is_nil(changeset |> get_field(:post_title)) ->
changeset
|> put_change(
:post_name,
changeset
|> get_field(:post_date)
|> Kernel.||(NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second))
|> Timex.format!("%F", :strftime)
|> Slugger.slugify_downcase()
|> unique_slug(changeset |> get_field(:id))
)
true ->
changeset
|> put_change(
:post_name,
changeset
|> get_field(:post_title)
|> Slugger.slugify_downcase()
|> unique_slug(changeset |> get_field(:id))
)
end
end
defp unique_slug(proposed_slug, post_id, postfix_number \\ 0) do
proposed_slug_with_postfix =
if postfix_number == 0 do
proposed_slug
else
"#{proposed_slug}-#{postfix_number}"
end
competition_count =
Repo.aggregate(
(
Post
|> where([post], post.post_name == ^proposed_slug_with_postfix)
|> post_id_match(post_id)
),
:count,
:id
)
if competition_count == 0 do
proposed_slug_with_postfix
else
unique_slug(proposed_slug, post_id, postfix_number + 1)
end
end
defp post_id_match(query, nil) do
query
end
defp post_id_match(query, id) when is_number(id) do
from p in query, where: p.id != ^id
end
end