Create OR updating M2M from Phoenix form

Hi all

I am making a form to create a recipe. Each recipe can have multiple ingredients. I set this up as a many to many relationship, so that the same ingredients can be used in different recipes.

I can successfully create ingredients in the create form I have set up. However, in the edit form, I’d like to leave ingredients that haven’t been altered, but also add new ones, edit existing ones, and delete ones no longer present. To do this, I need not only any values in the form but the ingredient ids.

I’m not quite sure how to do this with Phoenix.

In the form, the logic looks like the example below, which renders the existing ingredients. I have some JS to add new inputs. But regardless of whether they are new or existing, in the controller, the map of values for the form only shows the string values of the ingredients, not their ids.

I’d like to know how to provide more data from the form (ingredient ids AND values) so I can look up existing ingredients, etc.

<%= for ingredient <- @recipe.ingredients do %>
	<%= text_input f, :ingredient, class: "ingredient_input", placeholder: "Ingredient", name: "recipe[ingredients][]", value: ingredient.ingredient %>
<% end %>

Coming back here to write down my solution to this.

This article by Jose Valim was very helpful.

I attempt to insert the ingredient (the string having been processed) and handle the conflict if it exists (there is a unique constraint enforced).

defp get_or_insert_ingredient(ingredient) do
	Repo.insert!(
		%Ingredient{ingredient: ingredient},
		on_conflict: [set: [ingredient: ingredient]],
		conflict_target: :ingredient
	)
end
1 Like