8

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 ?

  • 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. – Alex Feb 25 '16 at 11:39
  • 1
    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. – Silfers IsNobody Feb 25 '16 at 12:57
  • You've just *created* it and you expect it to be in collection you had initialized just a second before. – Alex Feb 26 '16 at 08:10

3 Answers3

10

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.

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.

auroree
  • 91
  • 3
  • 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. – Silfers IsNobody Feb 25 '16 at 13:37
  • 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. – auroree Feb 25 '16 at 13:54
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)

Jannis
  • 166
  • 1
  • 10