1
votes

I want to POST for association one to many with JSON in Phoenix Elixir. There are many examples on the web, but I have not seen one example with one to many association. How can I pass the parameters for contact in the controller?

The schema for Costumer is

schema "customers" do
  field :email, :string
  field :name, :string
  has_many :contacts, App.Customers.Contact

  timestamps()
end

@doc false
def changeset(customer, attrs \\ %{}) do
  customer
  |> cast(attrs, [:name, :email])
  |> validate_required([:name, :email])
  |> unique_constraint(:email)
end

The schema for Contact is

schema "contacts" do
  field :phone, :string
  belongs_to :customers, App.Customers.Customer,  foreign_key: :customer_id

  timestamps()
end

@doc false
def changeset(contact, attrs \\ %{}) do
  contact
  |> cast(attrs, [:phone])
  |> validate_required([:phone])
end

This is the controller:

def create(conn, %{"email" => email, "name" => name, "phone" => phone} = customer_params) do
  with {:ok, %Customer{} = customer} <- Customers.create_customer(customer_params) do
    conn
    |> put_status(:created)
    |> put_resp_header("location", Routes.customer_path(conn, :show, customer))
    |> render("show.json", customer: customer)
  end
end
1
Are you having an issue with Ecto and the associations, or is your issue with how to get the JSON params? - Justin Wood
I think both, in the contextual module I have: def create_customer(attrs \\ %{}) do %Customer{} |> Customer.changeset(attrs) |> Ecto.Changeset.cast_assoc(:contacts, with: &Contact.changeset/2) |> Repo.insert() end - lyonTypescript

1 Answers

2
votes

In Customer, change the changeset function to:

def changeset(customer, attrs \\ %{}) do
  customer
  |> cast(attrs, [:name, :email])
  |> validate_required([:name, :email])
  |> unique_constraint(:email)
  |> cast_assoc(:contacts)
end

And then pass the parameters like this:

%{"name" => "john doe", "email" => "[email protected]", "contacts" => [
  %{"phone" => "555-555-555"},
  %{"phone" => "555-555-555"}
]}

With this, the create_customer function in the context doesn't need to be changed:

def create_customer(attrs \\ %{}) do
  %Customer{}
  |> Customer.changeset(attrs)
  |> Repo.insert()
end

Keep in mind, though, that in order to update a Customer, you would need to first preload contacts.

You can find more information in the cast_assoc documentation.