MrEvers
MrEvers

Reputation: 1072

accessing relation on object variable changes the variable

I was comparing objects, and ran across unexpected behavior.
The following code is not how I encountered it, but is the simplest way to explain/reproduce the behavior/error:

$obj1 = MyModel::find($id);
$obj2 = MyModel::find($id);
dd($obj1 == $obj2);

will of course return true.
However, if I use one of the objects to get a collection of related objects (with a simply hasMany in the model), the object seems altered.

$users = $obj1->users;
dd($obj1 == $obj2);

now returns false instead.

Both objects still have the same attributes, so I can simply check the ids, but I do not understand why the objects are not the same anymore, can someone explain to me how and why the object is altered by accessing the collection.

Upvotes: 2

Views: 53

Answers (4)

Aman Pawar
Aman Pawar

Reputation: 26

The relations do not get inherited unless you call them. So, when you call the users in the $obj1 the data for the users gets inherited which makes it not the same as the $obj2.

Still, need more clarification then do this

dump($obj1);
dump($obj2);
die();

and check in the $obj1 you will find the users data or the users array whereas in the $obj2 you do not find anything as such as they still need to be called in it.

Upvotes: 0

IlGala
IlGala

Reputation: 3419

Your question is more related to PHP rather than Laravel. If you have a look at the Comparing objects documentation you will see that:

When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values (values are compared with ==), and are instances of the same class.

So... In your case you loaded $obj1 and $obj2

$obj1 = MyModel::find($id);
$obj2 = MyModel::find($id);

If we follow the guidelines when running the dd PHP will look for:

1. Instance of the same class

It's true => MyModel::class == MyModel::class

2. Attributes: It's true as well since same class equals same attributes.

3. Values

You looked for the same database id so Laravel mapped the query result inside two different instances... but retrieving the same values.


Now, when you perform a lazy load, Laravel will push inside the relations array a new value. So the comparison will succed for point 1 and 2, but fails on point 3 since

dd($obj1->relations)

/* RESULT
 * [...]
 * #relations: array:1[
 *  "users" => Collection { ... }
 * ],
 */

dd($obj2->relations)

/* RESULT
 * [...]
 * #relations: array:0[],
 */

The values inside this attributes are different, and the comparison fails.

Upvotes: 1

Fernando
Fernando

Reputation: 127

The objets are not the same, because when you run a query like $obj1->users; the $obj1 is altereded, If you dd() the $obj1 you´ll see that the object has a new attribute, call users which is a collection of users in this case.

Now if you try to compare those two objects, you will get false, because they have differents attributes.

Now if you call $obj2->users and compare $obj1 == $obj2 again, you´ll get true.

Upvotes: 0

Tim Lewis
Tim Lewis

Reputation: 29316

Using $obj1->users loads the users Collection into the Model's relationships array. This does alter it enough for loose comparison to return false Compare dd($obj1, $obj2)

MyModel {
  ...
  #attributes: array:2[...],
  #original: array:2[...],
  #relations: array:2[...],
  ...
}

MyModel {
  ...
  #attributes: array:2[...],
  #original: array:2[...],
  #relations: array:0[...],
  ...
}

This is what your first example looks like. Now, if you use $users = $model2->users, it looks like this:

MyModel {
  ...
  #attributes: array:2[...],
  #original: array:2[...],
  #relations: array:0[...],
  ...
}

MyModel {
  ...
  #attributes: array:2[...],
  #original: array:2[...],
  #relations: array:1[
    "users" => Collection { ... }
  ],
  ...
}

If you want them to be the same, you would have to eager load the relationship as such:

$obj1 = MyModel::with(["users"])->find($id);
$obj2 = MyModel::with(["users"])->find($id);
$users = $obj2->users; // With or without this line, the comparison returns `true`
dd($obj1 == $obj2); // true

At that point, both $obj1 and $obj2 have the same relations: array:1[] attribute.

Upvotes: 3

Related Questions