@@ -28,15 +28,72 @@ defmodule Geo.Resources.Changes.SlugifyName do
2828
2929 # If we have a name but no slug, generate one
3030 not is_nil ( name ) ->
31- generated_slug = slugify ( name )
32- Ash.Changeset . force_change_attribute ( changeset , :slug , generated_slug )
31+ base_slug = slugify ( name )
32+ unique_slug = ensure_unique_slug ( changeset , base_slug )
33+ Ash.Changeset . force_change_attribute ( changeset , :slug , unique_slug )
3334
3435 # Otherwise, leave as is
3536 true ->
3637 changeset
3738 end
3839 end
3940
41+ defp ensure_unique_slug ( changeset , base_slug ) do
42+ # Try base slug first
43+ if ! slug_exists? ( changeset , base_slug ) do
44+ base_slug
45+ else
46+ find_unique_slug_with_number ( changeset , base_slug )
47+ end
48+ end
49+
50+ defp find_unique_slug_with_number ( changeset , base_slug ) do
51+ # Range 1: 1-9
52+ case find_in_range ( changeset , base_slug , 1 .. 9 ) do
53+ { :found , number } -> "#{ base_slug } -#{ number } "
54+ :not_found ->
55+ # Range 2: 1-99
56+ case find_in_range ( changeset , base_slug , 1 .. 99 ) do
57+ { :found , number } -> "#{ base_slug } -#{ number } "
58+ :not_found ->
59+ # Range 3: 1-9999
60+ case find_in_range ( changeset , base_slug , 1 .. 9999 ) do
61+ { :found , number } -> "#{ base_slug } -#{ number } "
62+ :not_found -> raise "Could not find unique slug after trying up to 9999"
63+ end
64+ end
65+ end
66+ end
67+
68+ defp find_in_range ( changeset , base_slug , range ) do
69+ Enum . find_value ( range , :not_found , fn number ->
70+ slug_candidate = "#{ base_slug } -#{ number } "
71+ if ! slug_exists? ( changeset , slug_candidate ) do
72+ { :found , number }
73+ end
74+ end )
75+ end
76+
77+ defp slug_exists? ( changeset , slug ) do
78+ resource = changeset . resource
79+ query = Ash.Query . filter ( resource , slug: slug )
80+
81+ # Exclude the current record if it's an update
82+ query =
83+ case changeset . data do
84+ % { id: id } when not is_nil ( id ) ->
85+ Ash.Query . filter ( query , id != ^ id )
86+ _ ->
87+ query
88+ end
89+
90+ case Ash . read ( query ) do
91+ { :ok , [ ] } -> false
92+ { :ok , _ } -> true
93+ { :error , _ } -> false
94+ end
95+ end
96+
4097 defp slugify ( text ) when is_binary ( text ) do
4198 text
4299 |> String . downcase ( )
0 commit comments