improvement: many fixes around relationship forms

This commit is contained in:
Zach Daniel 2021-03-25 12:55:21 -04:00
parent e726fb2ee8
commit 162383c750
3 changed files with 156 additions and 56 deletions

View file

@ -241,22 +241,15 @@ defmodule AshPhoenix do
[^outer_form_name, key | path] = decode_path(path)
{argument, argument_manages} =
if changeset.action do
{nil, nil}
with action when not is_nil(action) <- changeset.action,
argument when not is_nil(argument) <-
Enum.find(changeset.action.arguments, &(to_string(&1.name) == key)),
manage_change when not is_nil(manage_change) <-
find_manage_change(argument, changeset.action) do
{argument, manage_change}
else
# This is some magic to avoid having to pass in the relationship name
# when we can figure it out from the action
argument =
changeset.action.arguments
|> Enum.find(&(to_string(&1.name) == key))
if argument do
manage_change = find_manage_change(argument, changeset.action)
if manage_change do
{argument, manage_change}
end
end
_ ->
{nil, nil}
end
changeset.resource
@ -350,22 +343,15 @@ defmodule AshPhoenix do
[^outer_form_name, key | path] = decode_path(path)
{argument, argument_manages} =
if changeset.action do
{nil, nil}
with action when not is_nil(action) <- changeset.action,
argument when not is_nil(argument) <-
Enum.find(changeset.action.arguments, &(to_string(&1.name) == key)),
manage_change when not is_nil(manage_change) <-
find_manage_change(argument, changeset.action) do
{argument, manage_change}
else
# This is some magic to avoid having to pass in the relationship name
# when we can figure it out from the action
argument =
changeset.action.arguments
|> Enum.find(&(to_string(&1.name) == key))
if argument do
manage_change = find_manage_change(argument, changeset.action)
if manage_change do
{argument, manage_change}
end
end
_ ->
{nil, nil}
end
changeset.resource
@ -455,6 +441,8 @@ defmodule AshPhoenix do
{Map.update!(changeset.data, rel, &hide/1), nil}
end
end
else
{changeset.data, []}
end
changeset = mark_removed(changeset, new_value, rel)
@ -470,20 +458,15 @@ defmodule AshPhoenix do
end
defp find_manage_change(argument, action) do
Enum.find(action.changes, fn
{Ash.Resource.Change.ManageRelationship, opts} ->
opts[:argument] == argument.name
Enum.find_value(action.changes, fn
%{change: {Ash.Resource.Change.ManageRelationship, opts}} ->
if opts[:argument] == argument.name do
opts[:relationship]
end
_ ->
false
end)
|> case do
nil ->
nil
{_, opts} ->
opts[:relationship_name]
end
end)
end
@doc """
@ -670,13 +653,21 @@ defmodule AshPhoenix do
end
defp add_to_path(nil, [], add) do
add
List.wrap(add)
end
defp add_to_path(value, [], add) when is_list(value) do
value ++ List.wrap(add)
end
defp add_to_path(value, [], add) when value == %{} do
[value] ++ List.wrap(add)
end
defp add_to_path(value, [key | rest], add) when is_integer(key) and value == %{} do
add_to_path([value], [key | rest], add)
end
defp 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
@ -706,6 +697,8 @@ defmodule AshPhoenix do
%{key => add_to_path(nil, rest, add)}
end
defp add_to_path(_, _, add), do: add
defp remove_from_path(value, [key]) when is_integer(key) and is_list(value) do
List.delete_at(value, key)
end
@ -754,7 +747,15 @@ defmodule AshPhoenix do
defp remove_from_path(value, _), do: value
defp decode_path(path) do
@doc """
A utility for decoding the path of a form into a list.
For example:
change[posts][0][comments][1]
["change", "posts", 0, "comments", 1]
"""
def decode_path(path) do
path = Plug.Conn.Query.decode(path)
do_decode_path(path)
end

View file

@ -95,6 +95,13 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
rel = Ash.Resource.Info.relationship(changeset.resource, field) ->
data = relationship_data(changeset, rel, use_data?, opts[:id] || rel.name)
data =
if rel.cardinality == :many && data do
List.wrap(data)
else
data
end
{rel, rel.destination, data}
attr = Ash.Resource.Info.attribute(changeset.resource, field) ->
@ -171,11 +178,25 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
[] ->
nil
data when is_list(data) ->
List.last(data)
value ->
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
Ash.Changeset.new(data, value)
else
value
end
else
value
end
end
end
end
@ -190,10 +211,29 @@ defimpl Phoenix.HTML.FormData, for: Ash.Changeset do
end
{manage, _opts} ->
manage
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, manage) |> 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] || []

View file

@ -61,7 +61,17 @@ defmodule AshPhoenix.FormData.Helpers do
update_action = Ash.Resource.Info.primary_action(resource, :update)
if update_action do
Ash.Changeset.for_update(data, update_action.name, %{})
accepted_relationships =
resource
|> accepted_relationships(update_action)
|> Enum.map(fn relationship ->
{relationship.name, {:manage, [meta: [id: relationship.name]]}}
end)
Ash.Changeset.for_update(data, update_action.name, %{},
relationships: accepted_relationships,
actor: opts[:actor]
)
else
Ash.Changeset.new(data)
end
@ -69,7 +79,17 @@ defmodule AshPhoenix.FormData.Helpers do
create_action = Ash.Resource.Info.primary_action(resource, :create)
if create_action do
Ash.Changeset.for_create(resource, create_action.name, data)
accepted_relationships =
resource
|> accepted_relationships(create_action)
|> Enum.map(fn relationship ->
{relationship.name, {:manage, [meta: [id: relationship.name]]}}
end)
Ash.Changeset.for_create(resource, create_action.name, data,
relationships: accepted_relationships,
actor: opts[:actor]
)
else
Ash.Changeset.new(resource, data)
end
@ -128,7 +148,17 @@ defmodule AshPhoenix.FormData.Helpers do
cond do
is_struct(data) ->
if update_action do
Ash.Changeset.for_update(data, update_action.name, %{})
accepted_relationships =
resource
|> accepted_relationships(update_action)
|> Enum.map(fn relationship ->
{relationship.name, {:manage, [meta: [id: relationship.name]]}}
end)
Ash.Changeset.for_update(data, update_action.name, %{},
relationships: accepted_relationships,
actor: opts[:actor]
)
else
data
|> Ash.Changeset.new()
@ -140,7 +170,17 @@ defmodule AshPhoenix.FormData.Helpers do
true ->
if create_action do
Ash.Changeset.for_create(resource, create_action.name, data)
accepted_relationships =
resource
|> accepted_relationships(create_action)
|> Enum.map(fn relationship ->
{relationship.name, {:manage, [meta: [id: relationship.name]]}}
end)
Ash.Changeset.for_create(resource, create_action.name, data,
relationships: accepted_relationships,
actor: opts[:actor]
)
else
resource
|> Ash.Changeset.new(data)
@ -202,9 +242,9 @@ defmodule AshPhoenix.FormData.Helpers do
data
|> Enum.map(fn data ->
if is_struct(data) do
Ash.Changeset.for_update(data, update_action, %{})
Ash.Changeset.for_update(data, update_action, %{}, actor: opts[:actor])
else
Ash.Changeset.for_create(resource, create_action, data)
Ash.Changeset.for_create(resource, create_action, data, actor: opts[:actor])
end
end)
@ -263,13 +303,13 @@ defmodule AshPhoenix.FormData.Helpers do
changeset =
cond do
is_struct(data) ->
Ash.Changeset.for_update(data, update_action, %{})
Ash.Changeset.for_update(data, update_action, %{}, actor: opts[:actor])
is_nil(data) ->
nil
true ->
Ash.Changeset.for_create(resource, create_action, data)
Ash.Changeset.for_create(resource, create_action, data, actor: opts[:actor])
end
if changeset do
@ -303,4 +343,23 @@ defmodule AshPhoenix.FormData.Helpers do
}
end
end
defp accepted_relationships(resource, action) do
accepted =
if action.accept do
resource
|> Ash.Resource.Info.relationships()
|> Enum.filter(&(&1.name in action.accept))
else
Ash.Resource.Info.relationships(resource)
end
if action.reject do
resource
|> Ash.Resource.Info.relationships()
|> Enum.reject(&(&1.name in action.reject))
else
accepted
end
end
end