mirror of
https://github.com/ash-project/ash_phoenix.git
synced 2024-09-20 07:12:49 +12:00
improvement: various improvements to relationship manipulation functions
fix: add removed embeds to hidden fields
This commit is contained in:
parent
4eca3c8333
commit
98e26a9544
3 changed files with 215 additions and 172 deletions
|
@ -241,12 +241,15 @@ defmodule AshPhoenix do
|
|||
#{Ash.OptionsHelpers.docs(@add_value_opts)}
|
||||
"""
|
||||
@spec add_value(Ash.Changeset.t(), String.t(), String.t(), Keyword.t()) :: Ash.Changeset.t()
|
||||
def add_value(changeset, path, outer_form_name, opts \\ []) do
|
||||
def add_value(changeset, original_path, outer_form_name, opts \\ []) do
|
||||
opts = Ash.OptionsHelpers.validate!(opts, @add_value_opts)
|
||||
add = opts[:add]
|
||||
|
||||
[^outer_form_name, key | path] = decode_path(path)
|
||||
[^outer_form_name, key | path] = decode_path(original_path)
|
||||
|
||||
if match?({x, y} when not is_nil(x) and not is_nil(y), argument_and_manages(changeset, key)) do
|
||||
add_related(changeset, original_path, outer_form_name, add: add)
|
||||
else
|
||||
attribute_or_argument =
|
||||
Ash.Resource.Info.attribute(changeset.resource, key) ||
|
||||
find_argument(changeset, key)
|
||||
|
@ -276,18 +279,23 @@ defmodule AshPhoenix do
|
|||
changeset
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
A utility to support "remove" buttons on list attributes and arguments used in forms.
|
||||
"""
|
||||
@spec remove_value(Ash.Changeset.t(), String.t(), String.t()) :: Ash.Changeset.t()
|
||||
def remove_value(changeset, path, outer_form_name) do
|
||||
[^outer_form_name, key | path] = decode_path(path)
|
||||
def remove_value(changeset, original_path, outer_form_name) do
|
||||
[^outer_form_name, key | path] = decode_path(original_path)
|
||||
|
||||
attribute_or_argument =
|
||||
Ash.Resource.Info.attribute(changeset.resource, key) ||
|
||||
find_argument(changeset, key)
|
||||
|
||||
if match?({x, y} when not is_nil(x) and not is_nil(y), argument_and_manages(changeset, key)) do
|
||||
{_, changeset} = remove_related(changeset, original_path, outer_form_name)
|
||||
changeset
|
||||
else
|
||||
if attribute_or_argument do
|
||||
value =
|
||||
case attribute_or_argument do
|
||||
|
@ -315,6 +323,7 @@ defmodule AshPhoenix do
|
|||
changeset
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp find_argument(changeset, key) do
|
||||
changeset.action && Enum.find(changeset.action.arguments, &(to_string(&1.name) == key))
|
||||
|
@ -362,9 +371,14 @@ defmodule AshPhoenix do
|
|||
@spec add_related(Ash.Changeset.t(), String.t(), String.t(), Keyword.t()) :: Ash.Changeset.t()
|
||||
def add_related(changeset, path, outer_form_name, opts \\ []) do
|
||||
opts = Ash.OptionsHelpers.validate!(opts, @add_related_opts)
|
||||
add = opts[:add] || %{}
|
||||
add = Keyword.get(opts, :add, %{})
|
||||
|
||||
[^outer_form_name, key | path] = decode_path(path)
|
||||
[^outer_form_name, key | path] =
|
||||
if is_list(path) do
|
||||
path
|
||||
else
|
||||
decode_path(path)
|
||||
end
|
||||
|
||||
{argument, argument_manages} = argument_and_manages(changeset, key)
|
||||
|
||||
|
@ -534,14 +548,17 @@ defmodule AshPhoenix do
|
|||
Enum.map(related, &hide/1)
|
||||
end), []}
|
||||
|
||||
match?([i] when is_integer(i), path) and is_list(value) ->
|
||||
[i] = path
|
||||
match?([i | _] when is_integer(i), path) and is_list(value) ->
|
||||
[i | _] = path
|
||||
new_value = hide_at_not_hidden(Map.get(changeset.data, rel), i)
|
||||
|
||||
{Map.put(changeset.data, rel, new_value), new_value}
|
||||
|
||||
path == [] || match?([i] when is_integer(i), path) ->
|
||||
{Map.update!(changeset.data, rel, &hide/1), nil}
|
||||
|
||||
true ->
|
||||
{changeset.data, new_value}
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -770,23 +787,23 @@ defmodule AshPhoenix do
|
|||
|> Enum.all?(&hidden?/1)
|
||||
end
|
||||
|
||||
defp add_to_path(nil, [], nil) do
|
||||
def add_to_path(nil, [], nil) do
|
||||
[nil]
|
||||
end
|
||||
|
||||
defp add_to_path(nil, [], add) do
|
||||
def add_to_path(nil, [], add) do
|
||||
add
|
||||
end
|
||||
|
||||
defp add_to_path(value, [], nil) when is_list(value) do
|
||||
def add_to_path(value, [], nil) when is_list(value) do
|
||||
value ++ [nil]
|
||||
end
|
||||
|
||||
defp add_to_path(value, [], add) when is_list(value) do
|
||||
def add_to_path(value, [], add) when is_list(value) do
|
||||
value ++ List.wrap(add)
|
||||
end
|
||||
|
||||
defp add_to_path(value, [], add) when is_map(value) do
|
||||
def add_to_path(value, [], add) when is_map(value) do
|
||||
case last_index(value) do
|
||||
:error ->
|
||||
%{"0" => value, "1" => add}
|
||||
|
@ -796,15 +813,15 @@ defmodule AshPhoenix do
|
|||
end
|
||||
end
|
||||
|
||||
defp add_to_path(value, [key | rest], add) when is_integer(key) and is_list(value) do
|
||||
def add_to_path(value, [key | rest], add) when is_integer(key) and is_list(value) do
|
||||
List.update_at(value, key, &add_to_path(&1, rest, add))
|
||||
end
|
||||
|
||||
defp add_to_path(empty, [key | rest], add) when is_integer(key) and empty in [nil, []] do
|
||||
def add_to_path(empty, [key | rest], add) when is_integer(key) and empty in [nil, []] do
|
||||
[add_to_path(nil, rest, add)]
|
||||
end
|
||||
|
||||
defp add_to_path(value, [key | rest], add)
|
||||
def add_to_path(value, [key | rest], add)
|
||||
when (is_binary(key) or is_atom(key)) and is_map(value) do
|
||||
cond do
|
||||
Map.has_key?(value, key) ->
|
||||
|
@ -821,15 +838,15 @@ defmodule AshPhoenix do
|
|||
end
|
||||
end
|
||||
|
||||
defp add_to_path(nil, [key | rest], add) when is_binary(key) or is_atom(key) do
|
||||
def add_to_path(nil, [key | rest], add) when is_binary(key) or is_atom(key) do
|
||||
%{key => add_to_path(nil, rest, add)}
|
||||
end
|
||||
|
||||
defp add_to_path([item], [key | _] = path, add) when is_binary(key) do
|
||||
def add_to_path([item], [key | _] = path, add) when is_binary(key) do
|
||||
[add_to_path(item, path, add)]
|
||||
end
|
||||
|
||||
defp add_to_path(_, _, add), do: add
|
||||
def add_to_path(_, _, add), do: add
|
||||
|
||||
defp last_index(map) do
|
||||
{:ok,
|
||||
|
|
|
@ -50,7 +50,7 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
|
|||
id = Keyword.get(opts, :id) || name
|
||||
|
||||
hidden =
|
||||
if changeset.action_type in [:update] do
|
||||
if changeset.action_type in [:update, :destroy] do
|
||||
changeset.data
|
||||
|> Map.take(Ash.Resource.Info.primary_key(changeset.resource))
|
||||
|> Enum.to_list()
|
||||
|
@ -58,6 +58,20 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
|
|||
[]
|
||||
end
|
||||
|
||||
hidden =
|
||||
changeset.resource
|
||||
|> Ash.Resource.Info.attributes()
|
||||
|> Enum.filter(&Ash.Type.embedded_type?(&1.type))
|
||||
|> Enum.reduce(hidden, fn attribute, hidden ->
|
||||
case Ash.Changeset.fetch_change(changeset, attribute.name) do
|
||||
{:ok, empty} when empty in [nil, []] ->
|
||||
Keyword.put(hidden, attribute.name, nil)
|
||||
|
||||
_ ->
|
||||
hidden
|
||||
end
|
||||
end)
|
||||
|
||||
removed_embed_values =
|
||||
changeset.context[:private][:removed_keys]
|
||||
|> Kernel.||(%{})
|
||||
|
@ -196,110 +210,6 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
|
|||
defp unwrap([value | _]), do: value
|
||||
defp unwrap(value), do: value
|
||||
|
||||
defp relationship_data(changeset, %{cardinality: :one} = rel, use_data?, id) do
|
||||
case get_managed(changeset, rel.name, id) do
|
||||
nil ->
|
||||
if use_data? do
|
||||
changeset_data(changeset, rel)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
{manage, _opts} ->
|
||||
case manage do
|
||||
[] ->
|
||||
nil
|
||||
|
||||
value ->
|
||||
value =
|
||||
if is_list(value) do
|
||||
List.last(value)
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
if use_data? do
|
||||
data = changeset_data(changeset, rel)
|
||||
|
||||
if data do
|
||||
data
|
||||
|> Ash.Changeset.new(take_attributes(value, rel.destination))
|
||||
|> Map.put(:params, value)
|
||||
else
|
||||
value
|
||||
end
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp relationship_data(changeset, rel, use_data?, id) do
|
||||
case get_managed(changeset, rel.name, id) do
|
||||
nil ->
|
||||
if use_data? do
|
||||
changeset_data(changeset, rel)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{manage, _opts} ->
|
||||
if use_data? do
|
||||
changeset
|
||||
|> changeset_data(rel)
|
||||
|> zip_changes(manage)
|
||||
else
|
||||
manage
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp zip_changes([], manage) do
|
||||
manage
|
||||
end
|
||||
|
||||
defp zip_changes([record | rest_data], [manage | rest_manage]) do
|
||||
[
|
||||
Ash.Changeset.new(record, take_attributes(manage, record.__struct__))
|
||||
|> Map.put(:params, manage)
|
||||
] ++
|
||||
zip_changes(rest_data, rest_manage)
|
||||
end
|
||||
|
||||
defp zip_changes(records, []) do
|
||||
records
|
||||
end
|
||||
|
||||
defp get_managed(changeset, relationship_name, id) do
|
||||
manage = changeset.relationships[relationship_name] || []
|
||||
|
||||
Enum.find(manage, fn {_, opts} -> opts[:meta][:id] == id end)
|
||||
end
|
||||
|
||||
defp changeset_data(changeset, rel) do
|
||||
data = Map.get(changeset.data, rel.name)
|
||||
|
||||
case data do
|
||||
%Ash.NotLoaded{} ->
|
||||
default_data(rel)
|
||||
|
||||
data ->
|
||||
if is_list(data) do
|
||||
Enum.reject(data, &hidden?/1)
|
||||
else
|
||||
if hidden?(data) do
|
||||
nil
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp default_data(%{cardinality: :many}), do: []
|
||||
defp default_data(%{cardinality: :one}), do: nil
|
||||
|
||||
@impl true
|
||||
def input_validations(changeset, _, field) do
|
||||
attribute_or_argument =
|
||||
|
|
|
@ -68,6 +68,113 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
|
||||
def get_embedded(_), do: nil
|
||||
|
||||
def relationship_data(changeset, %{cardinality: :one} = rel, use_data?, id) do
|
||||
case get_managed(changeset, rel.name, id) do
|
||||
nil ->
|
||||
if use_data? do
|
||||
changeset_data(changeset, rel)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
{manage, _opts} ->
|
||||
case manage do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
[] ->
|
||||
nil
|
||||
|
||||
value ->
|
||||
value =
|
||||
if is_list(value) do
|
||||
List.last(value)
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
if use_data? do
|
||||
data = changeset_data(changeset, rel)
|
||||
|
||||
if data do
|
||||
data
|
||||
|> Ash.Changeset.new()
|
||||
|> Map.put(:params, value)
|
||||
else
|
||||
value
|
||||
end
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def relationship_data(changeset, rel, use_data?, id) do
|
||||
case get_managed(changeset, rel.name, id) do
|
||||
nil ->
|
||||
if use_data? do
|
||||
changeset_data(changeset, rel)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{manage, _opts} ->
|
||||
if use_data? do
|
||||
changeset
|
||||
|> changeset_data(rel)
|
||||
|> zip_changes(manage)
|
||||
else
|
||||
manage
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp zip_changes([], manage) do
|
||||
manage
|
||||
end
|
||||
|
||||
defp zip_changes([record | rest_data], [manage | rest_manage]) do
|
||||
[
|
||||
Ash.Changeset.new(record, %{})
|
||||
|> Map.put(:params, manage)
|
||||
] ++
|
||||
zip_changes(rest_data, rest_manage)
|
||||
end
|
||||
|
||||
defp zip_changes(records, []) do
|
||||
records
|
||||
end
|
||||
|
||||
defp changeset_data(changeset, rel) do
|
||||
data = Map.get(changeset.data, rel.name)
|
||||
|
||||
case data do
|
||||
%Ash.NotLoaded{} ->
|
||||
default_data(rel)
|
||||
|
||||
data ->
|
||||
if is_list(data) do
|
||||
Enum.reject(data, &hidden?/1)
|
||||
else
|
||||
if hidden?(data) do
|
||||
nil
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp default_data(%{cardinality: :many}), do: []
|
||||
defp default_data(%{cardinality: :one}), do: nil
|
||||
|
||||
defp get_managed(changeset, relationship_name, id) do
|
||||
manage = changeset.relationships[relationship_name] || []
|
||||
|
||||
Enum.find(manage, fn {_, opts} -> opts[:meta][:id] == id end)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def to_nested_form(
|
||||
data,
|
||||
|
@ -156,7 +263,7 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
id: id,
|
||||
name: name,
|
||||
errors: form_for_errors(changeset, opts),
|
||||
data: data,
|
||||
data: changeset.data,
|
||||
params: changeset.params,
|
||||
hidden: hidden,
|
||||
options: opts
|
||||
|
@ -183,9 +290,9 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
data
|
||||
|> Enum.map(fn data ->
|
||||
if is_struct(data) do
|
||||
Ash.Changeset.for_update(data, update_action, %{}, actor: opts[:actor])
|
||||
Ash.Changeset.for_update(data, update_action, params(data), actor: opts[:actor])
|
||||
else
|
||||
Ash.Changeset.for_create(resource, create_action, data, actor: opts[:actor])
|
||||
Ash.Changeset.for_create(resource, create_action, params(data), actor: opts[:actor])
|
||||
end
|
||||
end)
|
||||
|
||||
|
@ -241,13 +348,13 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
changeset =
|
||||
cond do
|
||||
is_struct(data) ->
|
||||
Ash.Changeset.for_update(data, update_action, %{}, actor: opts[:actor])
|
||||
Ash.Changeset.for_update(data, update_action, params(data), actor: opts[:actor])
|
||||
|
||||
is_nil(data) ->
|
||||
nil
|
||||
|
||||
true ->
|
||||
Ash.Changeset.for_create(resource, create_action, data, actor: opts[:actor])
|
||||
Ash.Changeset.for_create(resource, create_action, params(data), actor: opts[:actor])
|
||||
end
|
||||
|
||||
if changeset do
|
||||
|
@ -273,7 +380,7 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
id: id,
|
||||
name: name,
|
||||
errors: form_for_errors(changeset, opts),
|
||||
data: data,
|
||||
data: changeset.data,
|
||||
params: changeset.params,
|
||||
hidden: hidden,
|
||||
options: opts
|
||||
|
@ -303,9 +410,15 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
update_action = action!(resource, :update, opts[:update_action])
|
||||
|
||||
data
|
||||
|> Ash.Changeset.new()
|
||||
|> case do
|
||||
%Ash.Changeset{} = changeset ->
|
||||
changeset
|
||||
|
||||
other ->
|
||||
Ash.Changeset.new(other)
|
||||
end
|
||||
|> set_source_context({relationship, source_changeset})
|
||||
|> Ash.Changeset.for_update(update_action.name, %{}, actor: opts[:actor])
|
||||
|> Ash.Changeset.for_update(update_action.name, params(data), actor: opts[:actor])
|
||||
end
|
||||
else
|
||||
if opts[:create_action] == :_raw do
|
||||
|
@ -324,6 +437,9 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
end
|
||||
end
|
||||
|
||||
defp params(%Ash.Changeset{params: params}), do: params
|
||||
defp params(_), do: nil
|
||||
|
||||
defp set_source_context(changeset, {relationship, original_changeset}) do
|
||||
case original_changeset.context[:manage_relationship_source] do
|
||||
nil ->
|
||||
|
|
Loading…
Reference in a new issue