Nested.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <?php
  2. namespace Gedmo\Tree\Strategy\ORM;
  3. use Doctrine\ORM\Proxy\Proxy;
  4. use Gedmo\Tree\Strategy,
  5. Doctrine\ORM\EntityManager,
  6. Gedmo\Tree\TreeListener,
  7. Doctrine\ORM\Mapping\ClassMetadataInfo,
  8. Doctrine\ORM\Query;
  9. /**
  10. * This strategy makes tree act like
  11. * nested set.
  12. *
  13. * This behavior can inpact the performance of your application
  14. * since nested set trees are slow on inserts and updates.
  15. *
  16. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  17. * @package Gedmo.Tree.Strategy.ORM
  18. * @subpackage Nested
  19. * @link http://www.gediminasm.org
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. class Nested implements Strategy
  23. {
  24. /**
  25. * Previous sibling position
  26. */
  27. const PREV_SIBLING = 'prevSibling';
  28. /**
  29. * Next sibling position
  30. */
  31. const NEXT_SIBLING = 'nextSibling';
  32. /**
  33. * Last child position
  34. */
  35. const LAST_CHILD = 'lastChild';
  36. /**
  37. * First child position
  38. */
  39. const FIRST_CHILD = 'firstChild';
  40. /**
  41. * TreeListener
  42. *
  43. * @var AbstractTreeListener
  44. */
  45. protected $listener = null;
  46. /**
  47. * The max number of "right" field of the
  48. * tree in case few root nodes will be persisted
  49. * on one flush for node classes
  50. *
  51. * @var array
  52. */
  53. private $treeEdges = array();
  54. /**
  55. * List of pending Nodes, which needs to
  56. * be post processed because of having a parent Node
  57. * which requires some additional calculations
  58. *
  59. * @var array
  60. */
  61. private $pendingChildNodeInserts = array();
  62. /**
  63. * List of persisted nodes for specific
  64. * class to know when to process pending
  65. * inserts
  66. *
  67. * @var array
  68. */
  69. private $persistedNodes = array();
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function __construct(TreeListener $listener)
  74. {
  75. $this->listener = $listener;
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function getName()
  81. {
  82. return Strategy::NESTED;
  83. }
  84. /**
  85. * {@inheritdoc}
  86. */
  87. public function processScheduledInsertion($em, $node)
  88. {
  89. $meta = $em->getClassMetadata(get_class($node));
  90. $config = $this->listener->getConfiguration($em, $meta->name);
  91. $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
  92. if ($parent instanceof Proxy && !$parent->__isInitialized__) {
  93. $em->refresh($parent);
  94. }
  95. if ($parent === null) {
  96. $this->prepareRoot($em, $node);
  97. if (isset($config['level'])) {
  98. $meta->getReflectionProperty($config['level'])->setValue($node, 0);
  99. }
  100. } else {
  101. $meta->getReflectionProperty($config['left'])->setValue($node, 0);
  102. $meta->getReflectionProperty($config['right'])->setValue($node, 0);
  103. if (isset($config['level'])) {
  104. $meta->getReflectionProperty($config['level'])->setValue(
  105. $node,
  106. $meta->getReflectionProperty($config['level'])->getValue($parent) + 1
  107. );
  108. }
  109. $this->pendingChildNodeInserts[$meta->name][] = $node;
  110. }
  111. $this->persistedNodes[spl_object_hash($node)] = null;
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function processScheduledUpdate($em, $node)
  117. {
  118. $meta = $em->getClassMetadata(get_class($node));
  119. $config = $this->listener->getConfiguration($em, $meta->name);
  120. $uow = $em->getUnitOfWork();
  121. $changeSet = $uow->getEntityChangeSet($node);
  122. if (isset($config['root']) && isset($changeSet[$config['root']])) {
  123. throw new \Gedmo\Exception\UnexpectedValueException("Root cannot be changed manualy, change parent instead");
  124. }
  125. if (isset($changeSet[$config['parent']])) {
  126. $this->updateNode($em, $node, $changeSet[$config['parent']][1]);
  127. }
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. public function processPostPersist($em, $node)
  133. {
  134. $meta = $em->getClassMetadata(get_class($node));
  135. $config = $this->listener->getConfiguration($em, $meta->name);
  136. if (isset($config['root'])) {
  137. $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
  138. if ($parent instanceof Proxy && !$parent->__isInitialized__) {
  139. $em->refresh($parent);
  140. }
  141. $identifierField = $meta->getSingleIdentifierFieldName();
  142. $nodeId = $meta->getReflectionProperty($identifierField)->getValue($node);
  143. if ($parent) {
  144. $rootId = $meta->getReflectionProperty($config['root'])->getValue($parent);
  145. } else {
  146. $rootId = $nodeId;
  147. }
  148. $meta->getReflectionProperty($config['root'])->setValue($node, $rootId);
  149. $oid = spl_object_hash($node);
  150. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['root'], $rootId);
  151. $dql = "UPDATE {$meta->rootEntityName} node";
  152. $dql .= " SET node.{$config['root']} = {$rootId}";
  153. $dql .= " WHERE node.{$identifierField} = {$nodeId}";
  154. $em->createQuery($dql)->getSingleScalarResult();
  155. }
  156. unset($this->persistedNodes[spl_object_hash($node)]);
  157. if (!$this->persistedNodes && $this->pendingChildNodeInserts) {
  158. $pendingChildNodeInserts = $this->pendingChildNodeInserts;
  159. foreach ($pendingChildNodeInserts as $class => &$nodes) {
  160. while ($node = array_shift($nodes)) {
  161. $this->insertChild($em, $node);
  162. }
  163. }
  164. $this->pendingChildNodeInserts = array();
  165. }
  166. }
  167. /**
  168. * {@inheritdoc}
  169. */
  170. public function processScheduledDelete($em, $node)
  171. {
  172. $meta = $em->getClassMetadata(get_class($node));
  173. $config = $this->listener->getConfiguration($em, $meta->name);
  174. $uow = $em->getUnitOfWork();
  175. $leftValue = $meta->getReflectionProperty($config['left'])->getValue($node);
  176. $rightValue = $meta->getReflectionProperty($config['right'])->getValue($node);
  177. if (!$leftValue || !$rightValue) {
  178. return;
  179. }
  180. $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
  181. $diff = $rightValue - $leftValue + 1;
  182. if ($diff > 2) {
  183. $dql = "SELECT node FROM {$meta->rootEntityName} node";
  184. $dql .= " WHERE node.{$config['left']} BETWEEN :left AND :right";
  185. if (isset($config['root'])) {
  186. $dql .= " AND node.{$config['root']} = {$rootId}";
  187. }
  188. $q = $em->createQuery($dql);
  189. // get nodes for deletion
  190. $q->setParameter('left', $leftValue + 1);
  191. $q->setParameter('right', $rightValue - 1);
  192. $nodes = $q->getResult();
  193. foreach ((array)$nodes as $removalNode) {
  194. $uow->scheduleForDelete($removalNode);
  195. }
  196. }
  197. $this->shiftRL($em, $meta->rootEntityName, $rightValue + 1, -$diff, $rootId);
  198. }
  199. /**
  200. * {@inheritdoc}
  201. */
  202. public function onFlushEnd($em)
  203. {
  204. // reset values
  205. $this->treeEdges = array();
  206. $this->updatesOnNodeClasses = array();
  207. }
  208. /**
  209. * {@inheritdoc}
  210. */
  211. public function processPreRemove($em, $node)
  212. {}
  213. /**
  214. * {@inheritdoc}
  215. */
  216. public function processPrePersist($em, $node)
  217. {
  218. }
  219. /**
  220. * Insert a node which requires
  221. * parent synchronization
  222. *
  223. * @param EntityManager $em
  224. * @param object $node
  225. * @param string $position
  226. * @return void
  227. */
  228. public function insertChild(EntityManager $em, $node, $position = 'firstChild')
  229. {
  230. $meta = $em->getClassMetadata(get_class($node));
  231. $config = $this->listener->getConfiguration($em, $meta->name);
  232. $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
  233. if ($parent instanceof Proxy && !$parent->__isInitialized__) {
  234. $em->refresh($parent);
  235. }
  236. if (isset($config['level'])) {
  237. $level = $parent ? ($meta->getReflectionProperty($config['level'])->getValue($parent) + 1) : 0;
  238. $meta->getReflectionProperty($config['level'])->setValue($node, $level);
  239. }
  240. $identifierField = $meta->getSingleIdentifierFieldName();
  241. $nodeId = $meta->getReflectionProperty($identifierField)->getValue($node);
  242. $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($parent) : null;
  243. $parentRight = $meta->getReflectionProperty($config['right'])->getValue($parent);
  244. $this->shiftRL($em, $meta->rootEntityName, $parentRight, 2, $rootId);
  245. $meta->getReflectionProperty($config['left'])->setValue($node, $parentRight);
  246. $meta->getReflectionProperty($config['right'])->setValue($node, $parentRight + 1);
  247. $oid = spl_object_hash($node);
  248. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $parentRight);
  249. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $parentRight + 1);
  250. $dql = "UPDATE {$meta->rootEntityName} node";
  251. $dql .= " SET node.{$config['left']} = " . ($parentRight) . ', ';
  252. $dql .= " node.{$config['right']} = " . ($parentRight + 1);
  253. if (isset($level)) {
  254. $dql .= ", node.{$config['level']} = " . $level;
  255. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['level'], $level);
  256. }
  257. $dql .= " WHERE node.{$identifierField} = {$nodeId}";
  258. $em->createQuery($dql)->getSingleScalarResult();
  259. }
  260. /**
  261. * Update the $node with a diferent $parent
  262. * destination
  263. *
  264. * @todo consider $position configurable through listener
  265. * @param EntityManager $em
  266. * @param object $node - target node
  267. * @param object $parent - destination node
  268. * @param string $position
  269. * @throws Gedmo\Exception\UnexpectedValueException
  270. * @return void
  271. */
  272. public function updateNode(EntityManager $em, $node, $parent, $position = 'firstChild')
  273. {
  274. $meta = $em->getClassMetadata(get_class($node));
  275. $config = $this->listener->getConfiguration($em, $meta->name);
  276. $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
  277. $identifierField = $meta->getSingleIdentifierFieldName();
  278. $nodeId = $meta->getReflectionProperty($identifierField)->getValue($node);
  279. $left = $meta->getReflectionProperty($config['left'])->getValue($node);
  280. $right = $meta->getReflectionProperty($config['right'])->getValue($node);
  281. $level = 0;
  282. $treeSize = $right - $left + 1;
  283. $newRootId = null;
  284. if ($parent) {
  285. if ($parent instanceof Proxy && !$parent->__isInitialized__) {
  286. $em->refresh($parent);
  287. }
  288. $parentRootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($parent) : null;
  289. $parentLeft = $meta->getReflectionProperty($config['left'])->getValue($parent);
  290. $parentRight = $meta->getReflectionProperty($config['right'])->getValue($parent);
  291. if ($rootId === $parentRootId && $parentLeft >= $left && $parentRight <= $right) {
  292. throw new \Gedmo\Exception\UnexpectedValueException("Cannot set child as parent to node: {$nodeId}");
  293. }
  294. if (isset($config['level'])) {
  295. $level = $meta->getReflectionProperty($config['level'])->getValue($parent);
  296. }
  297. switch ($position) {
  298. case self::PREV_SIBLING:
  299. $start = $parentLeft;
  300. break;
  301. case self::NEXT_SIBLING:
  302. $start = $parentRight + 1;
  303. break;
  304. case self::LAST_CHILD:
  305. $start = $parentRight;
  306. $level++;
  307. case self::FIRST_CHILD:
  308. default:
  309. $start = $parentLeft + 1;
  310. $level++;
  311. break;
  312. }
  313. $this->shiftRL($em, $meta->rootEntityName, $start, $treeSize, $parentRootId);
  314. if ($rootId === $parentRootId && $left >= $start) {
  315. $left += $treeSize;
  316. $meta->getReflectionProperty($config['left'])->setValue($node, $left);
  317. }
  318. if ($rootId === $parentRootId && $right >= $start) {
  319. $right += $treeSize;
  320. $meta->getReflectionProperty($config['right'])->setValue($node, $right);
  321. }
  322. $newRootId = $parentRootId;
  323. } elseif (!isset($config['root'])) {
  324. $start = $this->max($em, $meta->rootEntityName);
  325. } else {
  326. $start = 1;
  327. $newRootId = $nodeId;
  328. }
  329. $diff = $start - $left;
  330. /*$qb = $em->createQueryBuilder();
  331. $qb->update($meta->rootEntityName, 'node');
  332. if (isset($config['root'])) {
  333. //$qb->set('node.' . $config['root'], $newRootId);
  334. }
  335. if (isset($config['level'])) {
  336. $qb->set('node.' . $config['level'], $level);
  337. }
  338. if ($treeSize > 2 && !$isNewNode) {*/
  339. $levelDiff = isset($config['level']) ? $level - $meta->getReflectionProperty($config['level'])->getValue($node) : null;
  340. $this->shiftRangeRL(
  341. $em,
  342. $meta->rootEntityName,
  343. $left,
  344. $right,
  345. $diff,
  346. $rootId,
  347. $newRootId,
  348. $levelDiff
  349. );
  350. $this->shiftRL($em, $meta->rootEntityName, $left, -$treeSize, $rootId);
  351. /*} else {
  352. $qb->set('node.' . $config['left'], $left + $diff);
  353. $qb->set('node.' . $config['right'], $right + $diff);
  354. $qb->where("node.{$identifierField} = {$nodeId}");
  355. $qb->getQuery()->getSingleScalarResult();
  356. }*/
  357. }
  358. /**
  359. * Get the edge of tree
  360. *
  361. * @param EntityManager $em
  362. * @param string $class
  363. * @param integer $rootId
  364. * @return integer
  365. */
  366. public function max(EntityManager $em, $class, $rootId = 0)
  367. {
  368. $meta = $em->getClassMetadata($class);
  369. $config = $this->listener->getConfiguration($em, $meta->name);
  370. $dql = "SELECT MAX(node.{$config['right']}) FROM {$meta->rootEntityName} node";
  371. if (isset($config['root']) && $rootId) {
  372. $dql .= " WHERE node.{$config['root']} = {$rootId}";
  373. }
  374. $query = $em->createQuery($dql);
  375. $right = $query->getSingleScalarResult();
  376. return intval($right);
  377. }
  378. /**
  379. * Shift tree left and right values by delta
  380. *
  381. * @param EntityManager $em
  382. * @param string $class
  383. * @param integer $first
  384. * @param integer $delta
  385. * @param integer $rootId
  386. * @return void
  387. */
  388. public function shiftRL(EntityManager $em, $class, $first, $delta, $rootId = null)
  389. {
  390. $meta = $em->getClassMetadata($class);
  391. $config = $this->listener->getConfiguration($em, $class);
  392. $sign = ($delta >= 0) ? ' + ' : ' - ';
  393. $absDelta = abs($delta);
  394. $dql = "UPDATE {$meta->rootEntityName} node";
  395. $dql .= " SET node.{$config['left']} = node.{$config['left']} {$sign} {$absDelta}";
  396. $dql .= " WHERE node.{$config['left']} >= {$first}";
  397. if (isset($config['root'])) {
  398. $dql .= " AND node.{$config['root']} = {$rootId}";
  399. }
  400. $q = $em->createQuery($dql);
  401. $q->getSingleScalarResult();
  402. $dql = "UPDATE {$meta->rootEntityName} node";
  403. $dql .= " SET node.{$config['right']} = node.{$config['right']} {$sign} {$absDelta}";
  404. $dql .= " WHERE node.{$config['right']} >= {$first}";
  405. if (isset($config['root'])) {
  406. $dql .= " AND node.{$config['root']} = {$rootId}";
  407. }
  408. $q = $em->createQuery($dql);
  409. $q->getSingleScalarResult();
  410. // update in memory nodes increases performance, saves some IO
  411. foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $nodes) {
  412. if ($className !== $meta->name && !$this->inDistriminatorMap($meta, $className)) {
  413. continue;
  414. }
  415. if ($className !== $meta->name) {
  416. $meta = $em->getClassMetadata($className);
  417. }
  418. foreach ($nodes as $node) {
  419. if ($node instanceof Proxy && !$node->__isInitialized__) {
  420. continue;
  421. }
  422. $oid = spl_object_hash($node);
  423. $left = $meta->getReflectionProperty($config['left'])->getValue($node);
  424. $root = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
  425. if ($root === $rootId && $left >= $first) {
  426. $meta->getReflectionProperty($config['left'])->setValue($node, $left + $delta);
  427. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $left + $delta);
  428. }
  429. $right = $meta->getReflectionProperty($config['right'])->getValue($node);
  430. if ($root === $rootId && $right >= $first) {
  431. $meta->getReflectionProperty($config['right'])->setValue($node, $right + $delta);
  432. $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $right + $delta);
  433. }
  434. }
  435. }
  436. }
  437. /**
  438. * Shift range of right and left values on tree
  439. * depending on tree level diference also
  440. *
  441. * @param EntityManager $em
  442. * @param string $class
  443. * @param integer $first
  444. * @param integer $last
  445. * @param integer $delta
  446. * @param integer $rootId
  447. * @param integer $destRootId
  448. * @param integer $levelDelta
  449. * @return void
  450. */
  451. public function shiftRangeRL(EntityManager $em, $class, $first, $last, $delta, $rootId = null, $destRootId = null, $levelDelta = null)
  452. {
  453. $meta = $em->getClassMetadata($class);
  454. $config = $this->listener->getConfiguration($em, $class);
  455. $sign = ($delta >= 0) ? ' + ' : ' - ';
  456. $absDelta = abs($delta);
  457. $levelSign = ($levelDelta >= 0) ? ' + ' : ' - ';
  458. $absLevelDelta = abs($levelDelta);
  459. $dql = "UPDATE {$meta->rootEntityName} node";
  460. $dql .= " SET node.{$config['left']} = node.{$config['left']} {$sign} {$absDelta}";
  461. $dql .= ", node.{$config['right']} = node.{$config['right']} {$sign} {$absDelta}";
  462. if (isset($config['root'])) {
  463. $dql .= ", node.{$config['root']} = {$destRootId}";
  464. }
  465. if (isset($config['level'])) {
  466. $dql .= ", node.{$config['level']} = node.{$config['level']} {$levelSign} {$absLevelDelta}";
  467. }
  468. $dql .= " WHERE node.{$config['left']} >= {$first}";
  469. $dql .= " AND node.{$config['right']} <= {$last}";
  470. if (isset($config['root'])) {
  471. $dql .= " AND node.{$config['root']} = {$rootId}";
  472. }
  473. $q = $em->createQuery($dql);
  474. $q->getSingleScalarResult();
  475. // update in memory nodes increases performance, saves some IO
  476. foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $nodes) {
  477. if ($className !== $meta->name && !$this->inDistriminatorMap($meta, $className)) {
  478. continue;
  479. }
  480. if ($className !== $meta->name) {
  481. $meta = $em->getClassMetadata($className);
  482. }
  483. foreach ($nodes as $node) {
  484. if ($node instanceof Proxy && !$node->__isInitialized__) {
  485. continue;
  486. }
  487. $left = $meta->getReflectionProperty($config['left'])->getValue($node);
  488. $right = $meta->getReflectionProperty($config['right'])->getValue($node);
  489. $root = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
  490. if ($root === $rootId && $left >= $first && $right <= $last) {
  491. $oid = spl_object_hash($node);
  492. $uow = $em->getUnitOfWork();
  493. $meta->getReflectionProperty($config['left'])->setValue($node, $left + $delta);
  494. $uow->setOriginalEntityProperty($oid, $config['left'], $left + $delta);
  495. $meta->getReflectionProperty($config['right'])->setValue($node, $right + $delta);
  496. $uow->setOriginalEntityProperty($oid, $config['right'], $right + $delta);
  497. if (isset($config['root'])) {
  498. $meta->getReflectionProperty($config['root'])->setValue($node, $destRootId);
  499. $uow->setOriginalEntityProperty($oid, $config['root'], $destRootId);
  500. }
  501. if (isset($config['level'])) {
  502. $level = $meta->getReflectionProperty($config['level'])->getValue($node);
  503. $meta->getReflectionProperty($config['level'])->setValue($node, $level + $levelDelta);
  504. $uow->setOriginalEntityProperty($oid, $config['level'], $level + $levelDelta);
  505. }
  506. }
  507. }
  508. }
  509. }
  510. /**
  511. * Check if given class is in inheritance discriminator map
  512. *
  513. * @param ClassMetadataInfo $meta
  514. * @param string $className
  515. * @return boolean
  516. */
  517. public function inDistriminatorMap(ClassMetadataInfo $meta, $className)
  518. {
  519. foreach ($meta->discriminatorMap as $class) {
  520. if ($className === $class) {
  521. return true;
  522. }
  523. }
  524. return false;
  525. }
  526. /**
  527. * If Node does not have parent, set it as root
  528. *
  529. * @param EntityManager $em
  530. * @param object $entity
  531. * @return void
  532. */
  533. private function prepareRoot(EntityManager $em, $node)
  534. {
  535. $meta = $em->getClassMetadata(get_class($node));
  536. $config = $this->listener->getConfiguration($em, $meta->name);
  537. if (isset($config['root'])) {
  538. $meta->getReflectionProperty($config['root'])->setValue($node, null);
  539. $meta->getReflectionProperty($config['left'])->setValue($node, 1);
  540. $meta->getReflectionProperty($config['right'])->setValue($node, 2);
  541. } else {
  542. $edge = isset($this->treeEdges[$meta->name]) ?
  543. $this->treeEdges[$meta->name] : $this->max($em, $meta->rootEntityName);
  544. $meta->getReflectionProperty($config['left'])->setValue($node, $edge + 1);
  545. $meta->getReflectionProperty($config['right'])->setValue($node, $edge + 2);
  546. $this->treeEdges[$meta->name] = $edge + 2;
  547. }
  548. }
  549. }