many to many relationships management.

I couldnt find clear way but got it working. Eventually i got it working, but i would like to get some tips/tricks, if this they way, or am i missing something obvious?
# Resource
actions do
create :create do
primary? true
accept [:subject, :status, :matchers, :frequency]
argument :fetchers, {:array, :uuid}
change manage_relationship(:fetchers, type: :append_and_remove)
end

update :update do
primary? true
accept [:subject, :status, :matchers, :frequency]
argument :fetchers, {:array, :uuid}
change manage_relationship(:fetchers, type: :append_and_remove)
require_atomic? false
end
end

relationships do
many_to_many :fetchers, Scraper.Fetcher,
through: Scraper.TemplateFetcherRelationship,
source_attribute_on_join_resource: :template_id,
destination_attribute_on_join_resource: :fetcher_id
end
# Resource
actions do
create :create do
primary? true
accept [:subject, :status, :matchers, :frequency]
argument :fetchers, {:array, :uuid}
change manage_relationship(:fetchers, type: :append_and_remove)
end

update :update do
primary? true
accept [:subject, :status, :matchers, :frequency]
argument :fetchers, {:array, :uuid}
change manage_relationship(:fetchers, type: :append_and_remove)
require_atomic? false
end
end

relationships do
many_to_many :fetchers, Scraper.Fetcher,
through: Scraper.TemplateFetcherRelationship,
source_attribute_on_join_resource: :template_id,
destination_attribute_on_join_resource: :fetcher_id
end
and in
# Form component
defp assign_form(%{assigns: %{template: template}} = socket) do
prepared_context = context(socket, as: "template", forms: [auto?: true])

form =
if template do
template = template |> Ash.load!([:fetchers], context(socket))
AshPhoenix.Form.for_update(template, :update, [{:params, %{fetchers: Enum.map(template.fetchers, & &1.id)}} | prepared_context])
else
Knabis.Scraper.Template
|> AshPhoenix.Form.for_create(:create, [{:params, %{fetchers: []}} | prepared_context])
end

assign(socket, form: to_form(form))
end

<.input
field={@form[:fetchers]}
type="select"
label="Fetchers"
options={@fetcher_options}
multiple={true}
prompt="Select fetchers..."
description="Choose one or more fetchers to associate with this template"
/>
# Form component
defp assign_form(%{assigns: %{template: template}} = socket) do
prepared_context = context(socket, as: "template", forms: [auto?: true])

form =
if template do
template = template |> Ash.load!([:fetchers], context(socket))
AshPhoenix.Form.for_update(template, :update, [{:params, %{fetchers: Enum.map(template.fetchers, & &1.id)}} | prepared_context])
else
Knabis.Scraper.Template
|> AshPhoenix.Form.for_create(:create, [{:params, %{fetchers: []}} | prepared_context])
end

assign(socket, form: to_form(form))
end

<.input
field={@form[:fetchers]}
type="select"
label="Fetchers"
options={@fetcher_options}
multiple={true}
prompt="Select fetchers..."
description="Choose one or more fetchers to associate with this template"
/>
Also just wanted to document this, so after a year when i need it agin, i can find it 😄
3 Replies
ZachDaniel
ZachDaniel•4mo ago
Looks about right to me
ken-kost
ken-kost•4mo ago
Rebecca has a great blog post about this fyi 🤠: Modelling through relationships with Ash
Modelling through relationships with Ash | sevenseacat.net
They're not natively supported, but you can still make them!
roberts.gulans
roberts.gulansOP•4mo ago
If all walues are deselected, there is issue. transform_params: fn form, params, -> update_in(params, ["fetchers"], &List.wrap/1) end this helps. this helps even for selects without multiselect enabled.

Did you find this page helpful?