简体   繁体   中英

How to use Nested forms associations in phoenix?

So I'm currently learning Phoenix 1.3 and i want to learn ecto associations. I'm Using Angularjs for the frontend

I have two schema user.ex and profile.ex

Both Generated by mix phx.gen.json

user.ex

schema "users" do
field(:password_hash, :string)
field(:username, :string)
# Virtual fields:
field(:password, :string, virtual: true)
field(:password_confirmation, :string, virtual: true)
# this was added
has_one(:profiles, AuthApp.Accounts.Profile)

timestamps()end

profile.ex

schema "profiles" do
field :firstname, :string
field :lastname, :string
field :photo, :string
field :user_id, :id
belongs_to :user, AuthApp.Accounts.User  # this was added

timestamps()end

profile controller

def create(conn, %{"profile" => profile_params}) do
with {:ok, %Profile{} = profile} <- Accounts.create_profile(profile_params) do
  conn
  |> put_status(:created)
  |> put_resp_header("location", profile_path(conn, :show, profile))
  |> render("show.json", profile: profile)
endend

My problem is I don't know what controller should I add the associations and how will it look like.

This is really up to you, you can do it both ways! However, some guidance / best practice style tips for this specific scenario.

If your app feature / page / etc is working primarily with one of those domain objects in mind, use that controller.

For example, if you are rendering a profile page, and offering various routes within it, or loading various pieces of data to populate the profile and enable features related to it, than returning a nested API response representing the data's relationship makes sense with the profile at the top level.

I would typically do something like this (starting where you've left off with the defined schemas and relationships): Pick up the related data I want for my profile API request in the ProfileController . I usually do this via a Context function that handles the preloading of the relationship. In this case that combination might look something like:

def show(conn, %{"id" => id}) do
  profile = Accounts.get_profile!(id)
  render(conn, "show.html", profile: profile)
end

Assuming something like an Account context to hold the Profile schema, where Account.get_profile!/1 looks something like:

def get_profile!(id) do
  query =
    from(p in Account.Profile,
      where: p.user_id == ^id,
      select: p,
      preload: [:user]
    )

  YourApp.Repo.get(query)
end

This ultimately results in an API response looking something like:

profile : {
  photo: {},
  user: {
    id: "1"
  }
}

Which is intuitive to work with when approaching from the perspective of enabling profile features.

However, a couple caveats:

  1. I usually handle loading basic user information into the app via conn.assigns, session, and plug or some cache layer if using token based auth. If you get a chance, the Programming Phoenix book has great entry level examples of this approach, and the Guardian library has good info on the token based approach.

  2. If you're conforming to a particular API standard, there will be guidelines there and best practices, so googling around for "REST" or "GraphQL" when X will yield helpful insight.

  3. I ignored all the stuff about authentication/authorization etc, but a benefit of doing (1) is that you can safely scope API requests so a user couldn't just request a different profile ID from their own (which happens when making the basic profile request and preloading all of a users information afterwards).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM