EDIT ** The code now works after some help
I started a thread a few days ago with some general questions and since I am now focused and have some running code I thought it would make sense to post fresh with more direct questions.
I have Users which can have friendships and the converse of that is a reverse_friendship using the Friendships table (many_to_many relationships). My attempted code is below but I am having 2 problems.
- how to add the associations with timestamps. If I don’t add timestamps to the table in the Friendship migration the inserts run fine
- put_assoc works (User 1 has User 2 as a friend and User 2 has User 1 as a reverse friend). However when I go to add another I get a warning about on_replace, I think thats because put_assoc is essentially trying to overwrite the entire friends/reverse friends. How can I add friend/reverse_friend associations and have it be additive (Add User 2 then User 3, etc to User 1 as a friend).
User Schema
defmodule App.Users.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :phone_number, :string
field :username, :string
field :email, :string, null: false
many_to_many :friends, User,
join_through: "friendships",
join_keys: [from_user_id: :id, to_user_id: :id]
many_to_many :reverse_friends, User,
join_through: "friendships",
join_keys: [to_user_id: :id, from_user_id: :id]
timestamps()
end
@doc false
def changeset(user_or_changeset, attrs) do
required_fields = [:username, :name, :email, :phone_number]
user_or_changeset
|> cast(attrs, required_fields)
|> validate_required(required_fields)
end
end
User Migration
defmodule App.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string, null: false
add :username, :string, null: false
add :email, :string, null: false
add :phone_number, :string, null: false
timestamps()
end
create unique_index(:users, [:username, :email, :phone_number])
end
end
Friendship Schema
defmodule App.Users.Friendship do
use Ecto.Schema
alias App.Users.User
import Ecto.Changeset
@primary_key false
schema "friendships" do
belongs_to :from_user, User
belongs_to :to_user, User
timestamps()
end
end
Friendship Migration
defmodule App.Repo.Migrations.AddCreateFriendshipTable do
use Ecto.Migration
def change do
create table(:friendships, primary_key: false) do
add :from_user_id, references(:users)
add :to_user_id, references(:users)
add :accepted, :boolean, default: false
timestamps()
end
create unique_index(:friendships, [:from_user_id, :to_user_id])
end
end
Users Context Module
defmodule App.Users do
import Ecto.Query, warn: false
alias App.Repo
alias App.Users.User
alias App.Users.Friendship
def add_friend(user, friend_user) do
%Friendship{}
|> change()
|> put_assoc(:from_user, user)
|> put_assoc(:to_user, friend_user)
|> Repo.insert()
end
end