PHP Doctrine : Test if an object is in an ArrayCollection
Asked Answered
N

3

9

I am trying to use the method ArrayCollection::contains to find if an object is already in my Collection, but when i am doing :

//My ArrayCollection
$lesRoles = $drt->getDrtApplication()->getRoles();
$leRole = $lesRoles->first();
echo "Property appNom : ".$leRole->getRleApplication()->getAppNom()."// Property appRole : ".$leRole->getRleId()." <br>";


$role = new \Casgaya\Role(2,$drt->getDrtApplication());
echo "Property appNom : ".$role->getRleApplication()->getAppNom()."// Property appRole : ".$role->getRleId()." <br>";

var_dump($lesRoles->contains($role));

The result is :
Property appNom : CORA// Property appRole : 2
Property appNom : CORA// Property appRole : 2
bool(false)

Since appNom and rleId are the only two properties that the entity Role own i was hopping it would return true.

EDIT NEW TEST CASE :

echo "Test object role :  <br>";
var_dump($lesRoles==$role);
echo"<br>";
echo "Test integer property rleID from object role :  <br>";
var_dump($role->getRleId() == $leRole->getRleId());
echo"<br>";
echo "Test Application object property RleApplication from object role : <br> ";
var_dump($role->getRleApplication() == $leRole->getRleApplication());

The result is :

Property appNom : CORA// Property appRole : 2
Property appNom : CORA// Property appRole : 2
Test object role :
bool(false)
Test integer property rleID from object role :
bool(true)
Test Application object property RleApplication from object role :
bool(true)

Notice that when i test the equality of the two properties, both of them are true. But when i test the equality of the both whole object, it's false.

So the question is no more about ArrayCollection::contains, but it's :
On what criteria two doctrine entities are compared in the case of equality ?

Nottinghamshire answered 25/2, 2016 at 10:49 Comment(3)
Why do you think it should return true? You'd better create simple ArrayCollection, put there a role you want to be there and check if collection contains the role (if you want to check how it works). It looks like $role you just created (a new instance of role) could not be in collection - just because it's just been instantiated.Magnetism
It's just a test case, i created an object that i know is already in the arrayCollection. And both of them have exactly the same properties.Nottinghamshire
You've just created it and you expect it to be in collection you had initialized just a second before.Magnetism
N
11

I have found the solution by myself, here's for the people who have the same issue :

I am using the method ArrayCollection::exists instead of contains, so i can specify on which criteria should an equality between object be established :

In my case :

$result = $lesRoles->exists(function($key,$element) use ($role) 
{
    return ($element->getRleApplication() == $role->getRleApplication() && $role->getRleId() == $element->getRleId());
});

Note that here $key and $element are the current object tested from the collection.

Nottinghamshire answered 25/2, 2016 at 14:55 Comment(0)
A
3

contains( mixed $element ) Checks whether the given element is contained in the collection. Only element values are compared, not keys. The comparison of two elements is strict, that means not only the value but also the type must match. For objects this means reference equality.

source: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html

If you want to check if some role is contained in collection, you can retrieve it by Doctrine - it will return same object, because Doctrine usually doesn't fetch entities which are already fetched via another query.

Articulation answered 25/2, 2016 at 12:20 Comment(2)
Thank you, you helped me to focus on the real problem. Not method contains from ArrayCollection, but how the equality between two doctrine entites is established.Nottinghamshire
I think that in case of testing you could compare properties by your own or write some helper methods. As of Doctrine it's kind of magic. For example you could fetch an object, after that fetch some collection which contains this object and Doctrine should use same instance - reference equality. Doctrine uses objects existing in memory if it is possible instead of hydrating new. Easy way to check it is dumping entity with bidirectional relation: child entity will contain reference to parent entity.Articulation
M
1

I had a same issue:

$Xrepository->removeX($x);
$x->getY()->removeXRelation($x);

In removeXXXRelation() I tested if the relation was existent with ArrayCollection->contains()
This contains() would fail

After 2 hours of debugging and trying around the solution was this

$x->getY()->removeXRelation($x);
$Xrepository->removeX($x);

Simply flipping the remove calls.

As you can see Doctrine is doing magic with the object.
This magic starts with the remove call.
A colleague told me about hibernate object lifecycle states, which resolve to:
Not-persisted, Persisted, and Not-Persisted Anymore
It is possible that due to lifecycle state changes your object is generated twice, making the contains() go false (contains checks the objects identity)
That's my theory (tell me if I am wrong)

Maidservant answered 11/2, 2020 at 10:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.