I have a ManyToMany associated field, and I'm in a situation where I'm only trying to get all the ID's without hydrating any of the sub-entities in the field.
I understand there will be a query to aquire the entity references the moment I access the field, and that's fine/expected. But I need to loop through the ID's, but I don't quite know how to get them without doing
$ids = [];
foreach($mainEntity->getSubEntities() as $subentity) {
$ids[] = $subentity->getId();
}
This also seems to hydrate the sub entity automatically, im assuming because of the foreach
loop. This results in a lot of unnecessary queries and impacts page load time.
I have the subentity field marked with EXTRALAZY
as well.
/**
* @var ArrayCollection
* @ORM\ManyToMany(targetEntity="User", fetch="EXTRA_LAZY")
* @ORM\JoinTable(name="user_friends")
*/
protected $friends;
As getKeys() returns only the sequential indexes of a PersistentCollection, I found this oneliner as a solution:
$myCollectionIds = $myCollection->map(function($obj){return $obj->getId();})->getValues();
Works smoothly in IN
contexts.
I don't believe it's possible to do what you with the entities defined the way they are. The Doctrine PersistentCollection class will always initialise itself, loading all the entities from the DB when you iterate or map over it. It does that before it runs the code in the foreach loop or closure, as you can see in the implementations of map and getIterator.
Even when the collection is marked as Extra Lazy the contents are all loaded from the DB as soon as you use any method other than contains, containsKey, count, get or slice.
However, if you don't mind adjusting your entity structure a little you can get the behaviour you want.
Reify the ManyToMany association into an entity.
Your relation is called friend
, so you might make a friendship
entity:
/** @Entity */
class Frendship
{
/**
* @var int
* @Id @Column(type="integer") @GeneratedValue
**/
protected $id;
/**
* @var User
* @ORM\@ManyToOne(targetEntity="User", inversedBy="friendships")
**/
protected $friendA
/**
* @var User
* @ORM\@ManyToOne(targetEntity="User", inversedBy="friendships")
**/
protected $friendB
public function getOtherFriend(User $friend){
if ($this->friendA === $friend)) {
return $this->friendB;
} else if ($this->friendB === $friend)) {
return $this->friendA
} else {
throw new \Exception("$friend is not part of this friendship");
}
}
}
Then you can replace your friends
collection with a frendships
collection and iterate through that. The friendships will be fully hydrated but that's OK because they only contain two integers.
You can get the other friend from each friendship, and it will be a proxy object, unless it happens to already be loaded. Since you only want to call getId()
on it doctrine won't need to retrieve the full entity from the database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With