1
votes

Phoenix n00b here.

So I have a logged in current user (using Coherence, but I don't think this has impact here):

user = Coherence.current_user(conn)

And I have an object that has a relationship with a user:

object = Repo.get(Object, XX) |> Repo.preload(:user)
owner = object.user

I simply want to check that user is the same as owner.

  • Writing user == owner is not correct because - I guess - structs are references so the two will be difference despite being the same DB object.

  • Writing user.id == owner.id shall work but since user can be nil it will fail in that case, while the equality remains true.

  • I tried user[:id] == owner[:id] since this doesn't fail if user is nil but then if it is a user, I get a User does not implement the Access behaviour error. :(

  • Writing not is_nil(user) && user.id == owner.id works but is ugly (and thanks god owner is not also nillable)

I guess I'll need a helper function here, but ain't it already built-in? If so, where? In Ecto? In Coherence?

Ain't there a way to override struct equality for some specific structs?

3
user == owner should work assuming it is actually being pulled from the same table and are coming from the same module, with the caveat that the data could have changed between asking for the user and asking for the owner, so it is theoretically possible to have it be false, even if they have the same id's. - Justin Wood
If one of the two instances has a Repo.preload somewhere, it fails, which is not acceptable (as those remain the same objects). - Augustin Riedinger
You have to remember that structs are implemented as maps. The problem with just comparing id's is that you could compare different kinds of structs and if the id's match, it will return true. I think your best option will be to create your own compare function. - Justin Wood
So there isn't any built in function, like in Ecto.Schema? - Augustin Riedinger

3 Answers

1
votes

Why not put the condition in the query?

owner = some_struct

from(
  o in Object, 
  where: o.user_id == ^owner.id and not is_nil(o.user_id), 
  preload: [:user]
) 
|> Repo.one # !only works if db returns max one row!
|> case do
  nil ->    # no matches in db
  object -> # object.user.id == object.user_id == owner.id here
end

Edit: changed to hopefully something closer to what the poster wants

0
votes

I would suggest: owner = Map.get(object, :user, %{}). You will have now 2 maps to compare. You can then use user[:id] == owner[:id]

0
votes

As I said in the comments, comparing only the id's is probably a bad idea because it would return true even if you compare different kinds of structs. Your best bet would be to create a compare function. Something like the following will probably work for you.

def compare_structs(%mod{id: id}, %mod{id: id}), do: true
def compare_structs(_, _), do: false

This uses pattern matching to make sure that both arguments are structs, as well as coming from the same module and that they have the same id. Everything else passed to this function will be false.