Karsten S.
Karsten S.

Reputation: 2391

How/where to preload has_many relationship data in Phoenix 1.3

I'm learning Elixir/Phoenix on a sample project and started with Phoenix 1.2. Now I recreated the project with Phoenix 1.3 to see/learn differences and adapt my old code.

In there I have a has_many relationship between a model Position and Skill. Whereas in the 1.2 code in position_controller.ex I used

case Repo.insert(changeset) do
  {:ok, position} ->
    position = position |> Repo.preload(:skills)
    ...

to have skills preloaded for rendering I'm unsure where to put this in my 1.3 code.

Controller seems to be the wrong place now (Repo isn't even known) so I placed it into my context file inside create_position like this:

def create_position(attrs \\ %{}) do
  with {:ok, %Position{} = position} <-
    %Position{}
    |> Position.changeset(attrs)
    |> Repo.insert() do

    position = Repo.preload(position, :skills)
    {:ok, position}
  end
end

Which feels strange since it now does more than just inserting.

So what is the correct and best way to perform this task?

Upvotes: 3

Views: 478

Answers (1)

Justin Wood
Justin Wood

Reputation: 10041

You will want to separate things out depending on what your contexts / application looks like.

def insert_position(attrs) do
  attrs
  |> changeset()
  |> MyApp.Repo.insert()
end

If skills and positions are in the same context, you can do something along the lines of

def load_skills(position) do
  MyApp.Repo.preload(position, :skills)
end

If it is in a separate context, you can do the following

def get_skills_for_position(position) do
  # I'm not sure which way you have the relationship
  MyApp.Repo.all(from skill in MyApp.Skill, 
    join: position in MyApp.Position,
    on: skill.id == position.skill_id)
end

Then, in your controller, it is up to you to put these things together.

def create(conn, %{"position" => position_attrs}) do
  case create_position(position_attrs) do
    {:ok, position} ->
      # or load_skills_for_position, depending if they are in the same contexts.
      position = load_skills(position) 
      ...
    {:error, changeset} ->
      ...
  end
end

Upvotes: 3

Related Questions