Do I have a very simple entity (WpmMenu) that contains menu items related to each other regarding self-reference (the called list, which he named)? so in my essence I have:
protected $id protected $parent_id protected $level protected $name
with all getters / setters relationships:
protected $children; protected $parent; public function __construct() { $this->children = new ArrayCollection(); }
And everything works fine. When I render the menu tree, I get the root element from the repository, get its children, and then go through each child, get its children and do it recursively until I display each element.
What happens (and why I'm looking for a solution): At the moment I have 5 levels = 1 element, and each of these items has 3 levels = 2 items (and in the future I will use level = 3 elements). To get all the elements of my Doctrine menu tree:
- 1 query for root element +
- 1 request for 5 children (level = 1) of the root element +
- 5 requests for 3 children (level = 2) of each of the objects of level 1 +
- 15 requests (5x3) for children (level = 3) for each level 2 items
TOTAL: 22 requests
So, I need to find a solution for this, and ideally, I would like to have only 1 query.
So here is what I am trying to do: In my object repository (WpmMenuRepository) I use queryBuilder and get a flat array of all menu items sorted by level. Get the root element (WpmMenu) and manually add its children from the loaded array of elements. Then do it recursively on the children. For this, I could have the same tree, but with one query.
So this is what I have:
WpmMenuRepository:
public function setupTree() { $qb = $this->createQueryBuilder("res"); $res = $qb->select("res")->orderBy('res.level', 'DESC')->addOrderBy('res.name','DESC')->getQuery()->getResult(); $treeRoot = array_pop($res); $treeRoot->setupTreeFromFlatCollection($res); return($treeRoot); }
and in my WpmMenu object I have:
function setupTreeFromFlatCollection(Array $flattenedDoctrineCollection){ //ADDING IMMEDIATE CHILDREN for ($i=count($flattenedDoctrineCollection)-1 ; $i>=0; $i--) { /** @var WpmMenu */ $docRec = $flattenedDoctrineCollection[$i]; if (($docRec->getLevel()-1) == $this->getLevel()) { if ($docRec->getParentId() == $this->getId()) { $docRec->setParent($this); $this->addChild($docRec); array_splice($flattenedDoctrineCollection, $i, 1); } } } //CALLING CHILDREN RECURSIVELY TO ADD REST foreach ($this->children as &$child) { if ($child->getLevel() > 0) { if (count($flattenedDoctrineCollection) > 0) { $flattenedDoctrineCollection = $child->setupTreeFromFlatCollection($flattenedDoctrineCollection); } else { break; } } } return($flattenedDoctrineCollection); }
And here is what happens:
Everything works fine, BUT I get two menu items that are present twice .;) Instead of 22 queries, I now have 23. Therefore, I really worsened this business.
What really happens, I think, is that even if I add children added manually, the WpmMenu object is NOT considered in sync with the database, and as soon as I do a foreach loop on my children, loading starts when ORM loads and adding the same children that were already added "manually."
Q Is there a way to block / disable this behavior and talk about these entities that they synchronize with db, so no additional request is required?