Question about Contexts and Pivot tables

Curiously Yes I can…

I was doing something similar with 2 contexts, Accounting and Social.

This one allows to create, update User

defmodule Chat.Accounting.User do
  @moduledoc false
  
  use Ecto.Schema
  import Ecto.Changeset
  alias __MODULE__
  alias Chat.Accounting.Phone

  schema "users" do
    field :email, :string
    field :name, :string
    field :password, :string, virtual: true
    field :password_hash, :string
    
    has_one :phone, Phone, on_delete: :delete_all

    timestamps()
  end
  
  @required_fields ~w(name email)a
  @registration_fields ~w(password)a  
  
  @doc false
  
  # Because of Postgres constraints errors
  # You will receive only the first constraint error, not all of them!
  
  def changeset(%User{} = user, attrs \\ %{}) do
    user
    |> cast(attrs, @required_fields)
    |> validate_required(@required_fields)
    |> validate_length(:name, min: 1, max: 32)
    |> validate_format(:email, ~r/@/)
    |> unique_constraint(:name, message: "Name already taken")
    |> unique_constraint(:email, message: "Email already taken")
  end
  
  @doc false
  def registration_changeset(user, attrs \\ %{}) do
    user
    |> changeset(attrs)
    |> cast(attrs, @registration_fields)
    |> validate_required(@registration_fields)
    |> validate_length(:password, min: 6, max: 32)
    |> generate_encrypted_password()
  end

  # PRIVATE

  defp generate_encrypted_password(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: password}} ->
        put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(password))
      _ ->
        changeset
    end
  end
end

This one allows to manage User association, inside Social context.

defmodule Chat.Social.User do
  @moduledoc false
  
  use Ecto.Schema
  import Ecto.Changeset
  alias __MODULE__
  alias Chat.Social.{Group, Post, Comment}
  
  schema "users" do
    field :email, :string
    field :name, :string
    
    many_to_many :followeds, User, 
      join_through: "followings", 
      join_keys: [follower_id: :id, followed_id: :id], 
      on_delete: :delete_all,
      on_replace: :delete
    
    many_to_many :followers, User, 
      join_through: "followings", 
      join_keys: [followed_id: :id, follower_id: :id], 
      on_delete: :delete_all,
      on_replace: :delete
      
    many_to_many :groups, Group, 
      join_through: "subscriptions",
      on_delete: :delete_all,
      on_replace: :delete
    
    has_many :owned_groups, Group
    has_many :posts, Post
    has_many :comments, Comment
    
    timestamps()
  end
  
  def follow_changeset(%User{} = user, attrs \\ %{}) do
    user
    |> cast(attrs, [])
    |> put_assoc(:followeds, attrs[:followeds])
  end
  
  def group_changeset(%User{} = user, attrs \\ %{}) do
    user
    |> cast(attrs, [])
    |> put_assoc(:groups, attrs[:groups])
  end
end

As You can see they refer to the same table, but they both represent a User, inside their respective contexts.

Social User cannot be created, they don’t know what a password is, but they can join group, and follow other User as well.

5 Likes