Reputation: 1072
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
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
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
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
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