Skip to content

Commit 3ec08de

Browse files
committed
Add unit test for entities with a foreign root
1 parent 59b3d2d commit 3ec08de

File tree

3 files changed

+328
-5
lines changed

3 files changed

+328
-5
lines changed

lib/Gedmo/Tree/Strategy/ORM/Nested.php

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Gedmo\Tree\Strategy\ORM;
44

5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Criteria;
57
use Doctrine\ORM\Mapping\ClassMetadata;
68
use Gedmo\Exception\UnexpectedValueException;
79
use Doctrine\ORM\Proxy\Proxy;
@@ -123,7 +125,9 @@ public function processScheduledInsertion($em, $node, AdapterInterface $ea)
123125
if (isset($config['level'])) {
124126
$meta->getReflectionProperty($config['level'])->setValue($node, 0);
125127
}
126-
if (isset($config['root']) && !$meta->hasAssociation($config['root'])) {
128+
if (isset($config['root']) && !$meta->hasAssociation($config['root']) && !$config['rootIdentifierMethod']) {
129+
$meta->getReflectionProperty($config['root'])->setValue($node, 0);
130+
} else if (isset($config['rootIdentifierMethod']) && is_null($meta->getReflectionProperty($config['root'])->getValue($node))) {
127131
$meta->getReflectionProperty($config['root'])->setValue($node, 0);
128132
}
129133
}
@@ -310,7 +314,7 @@ public function updateNode(EntityManager $em, $node, $parent, $position = 'First
310314
$level = 0;
311315
$treeSize = $right - $left + 1;
312316
$newRoot = null;
313-
if ($parent) {
317+
if ($parent) { // || (!$parent && isset($config['rootIdentifierMethod']))
314318
$wrappedParent = AbstractWrapper::wrap($parent, $em);
315319

316320
$parentRoot = isset($config['root']) ? $wrappedParent->getPropertyValue($config['root']) : null;
@@ -453,8 +457,23 @@ public function updateNode(EntityManager $em, $node, $parent, $position = 'First
453457
}
454458
} else {
455459
$start = 1;
456-
457-
if ($meta->isSingleValuedAssociation($config['root'])) {
460+
if (isset($config['rootIdentifierMethod'])) {
461+
$method = $config['rootIdentifierMethod'];
462+
$newRoot = $node->$method();
463+
$repo = $em->getRepository($config['useObjectClass']);
464+
465+
$criteria = new Criteria();
466+
$criteria->andWhere(Criteria::expr()->notIn($wrapped->getMetadata()->identifier[0], [$wrapped->getIdentifier()]));
467+
$criteria->andWhere(Criteria::expr()->eq($config['root'], $node->$method()));
468+
$criteria->andWhere(Criteria::expr()->isNull($config['parent']));
469+
$criteria->andWhere(Criteria::expr()->eq($config['level'], 0));
470+
$criteria->orderBy([$config['right'] => Criteria::ASC]);
471+
$roots = $repo->matching($criteria)->toArray();
472+
$last = array_pop($roots);
473+
474+
$start = ($last) ? $meta->getFieldValue($last, $config['right']) + 1 : 1;
475+
476+
} else if ($meta->isSingleValuedAssociation($config['root'])) {
458477
$newRoot = $node;
459478
} else {
460479
$newRoot = $wrapped->getIdentifier();
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
namespace Tree\Fixture;
4+
5+
use Gedmo\Mapping\Annotation as Gedmo;
6+
use Doctrine\ORM\Mapping as ORM;
7+
8+
/**
9+
* @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
10+
* @Gedmo\Tree(type="nested")
11+
*/
12+
class ForeignRootCategory
13+
{
14+
/**
15+
* @ORM\Column(name="id", type="integer")
16+
* @ORM\Id
17+
* @ORM\GeneratedValue
18+
*/
19+
private $id;
20+
21+
/**
22+
* @ORM\Column(name="title", type="string", length=64)
23+
*/
24+
private $title;
25+
26+
/**
27+
* @Gedmo\TreeLeft
28+
* @ORM\Column(name="lft", type="integer")
29+
*/
30+
private $lft;
31+
32+
/**
33+
* @Gedmo\TreeRight
34+
* @ORM\Column(name="rgt", type="integer")
35+
*/
36+
private $rgt;
37+
38+
/**
39+
* @Gedmo\TreeParent
40+
* @ORM\ManyToOne(targetEntity="ForeignRootCategory", inversedBy="children")
41+
* @ORM\JoinColumns({
42+
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
43+
* })
44+
*/
45+
private $parent;
46+
47+
/**
48+
* @Gedmo\TreeRoot(identifierMethod="getRoot")
49+
* @ORM\Column(type="integer")
50+
*/
51+
private $root;
52+
53+
/**
54+
* @Gedmo\TreeLevel
55+
* @ORM\Column(name="lvl", type="integer")
56+
*/
57+
private $level;
58+
59+
/**
60+
* @ORM\OneToMany(targetEntity="ForeignRootCategory", mappedBy="parent")
61+
*/
62+
private $children;
63+
64+
public function getId()
65+
{
66+
return $this->id;
67+
}
68+
69+
public function setTitle($title)
70+
{
71+
$this->title = $title;
72+
}
73+
74+
public function getTitle()
75+
{
76+
return $this->title;
77+
}
78+
79+
public function setParent(ForeignRootCategory $parent = null)
80+
{
81+
$this->parent = $parent;
82+
}
83+
84+
public function getParent()
85+
{
86+
return $this->parent;
87+
}
88+
89+
public function getRoot()
90+
{
91+
return $this->root;
92+
}
93+
94+
public function getLeft()
95+
{
96+
return $this->lft;
97+
}
98+
99+
public function getRight()
100+
{
101+
return $this->rgt;
102+
}
103+
104+
public function getLevel()
105+
{
106+
return $this->level;
107+
}
108+
109+
public function getChildren()
110+
{
111+
return $this->children;
112+
}
113+
114+
public function setChildren($children)
115+
{
116+
$this->children = $children;
117+
}
118+
119+
/**
120+
* @param mixed $root
121+
*/
122+
public function setRoot($root)
123+
{
124+
$this->root = $root;
125+
}
126+
127+
128+
}

tests/Gedmo/Tree/NestedTreeRootTest.php

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
namespace Gedmo\Tree;
44

55
use Doctrine\Common\EventManager;
6+
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
67
use Tool\BaseTestCaseORM;
8+
use Tree\Fixture\ForeignRootCategory;
9+
use Tree\Fixture\RootAssociationCategory;
710
use Tree\Fixture\RootCategory;
811

912
/**
@@ -25,7 +28,7 @@ protected function setUp()
2528
$evm->addEventSubscriber(new TreeListener());
2629

2730
$this->getMockSqliteEntityManager($evm);
28-
$this->populate();
31+
// $this->populate();
2932
}
3033

3134
/**
@@ -320,13 +323,186 @@ public function testRemoval()
320323
$this->assertEquals(4, $node->getRight());
321324
}
322325

326+
327+
/**
328+
* @throws \Doctrine\ORM\OptimisticLockException
329+
*/
330+
public function testTreeWithRootPointingAtAnotherTable()
331+
{
332+
// depopulate, i don't want the other stuff in db
333+
/** @var NestedTreeRepository $repo */
334+
$repo = $this->em->getRepository(ForeignRootCategory::class);
335+
$all = $repo->findAll();
336+
foreach ($all as $one) {
337+
$this->em->remove($one);
338+
}
339+
$this->em->flush();
340+
341+
$fiction = new ForeignRootCategory();
342+
$fiction->setTitle('Fiction Books');
343+
$fiction->setRoot(1); // Lets pretend this points to another table, and root id 1 is "Books"
344+
345+
$fact = new ForeignRootCategory();
346+
$fact->setTitle('Fact Books');
347+
$fact->setRoot(1);
348+
349+
$action = new ForeignRootCategory();
350+
$action->setTitle('Action');
351+
$action->setRoot(2); // Lets pretend this points to another table, and root id 2 is "Movies"
352+
353+
$comedy = new ForeignRootCategory();
354+
$comedy->setTitle('Comedy');
355+
$comedy->setRoot(2);
356+
357+
$horror = new ForeignRootCategory();
358+
$horror->setTitle('Horror');
359+
$horror->setRoot(2);
360+
361+
// Child categories now
362+
$lotr = new ForeignRootCategory();
363+
$lotr->setTitle('Lord of the Rings');
364+
$lotr->setParent($fiction);
365+
$lotr->setRoot(1);
366+
367+
$warlock = new ForeignRootCategory();
368+
$warlock->setTitle('The Warlock of Firetop Mountain');
369+
$warlock->setParent($fiction);
370+
$warlock->setRoot(1);
371+
372+
$php = new ForeignRootCategory();
373+
$php->setTitle('PHP open source development');
374+
$php->setParent($fact);
375+
$php->setRoot(1);
376+
377+
$dracula = new ForeignRootCategory();
378+
$dracula->setTitle('Hammer Horror Dracula');
379+
$dracula->setParent($horror);
380+
$dracula->setRoot(2);
381+
382+
$frankenstein = new ForeignRootCategory();
383+
$frankenstein->setTitle('Hammer Horror Frankenstein');
384+
$frankenstein->setParent($horror);
385+
$frankenstein->setRoot(2);
386+
387+
$this->em->persist($fact);
388+
$this->em->persist($fiction);
389+
$this->em->persist($comedy);
390+
$this->em->persist($horror);
391+
$this->em->persist($action);
392+
$this->em->persist($lotr);
393+
$this->em->persist($warlock);
394+
$this->em->persist($php);
395+
$this->em->persist($dracula);
396+
$this->em->persist($frankenstein);
397+
$this->em->flush();
398+
399+
$this->assertEquals(1, $fact->getLeft());
400+
$this->assertEquals(4, $fact->getRight());
401+
$this->assertEquals(0, $fact->getLevel());
402+
$this->assertEquals(1, $fact->getRoot());
403+
$this->assertNull($fact->getParent());
404+
405+
$this->assertEquals(5, $fiction->getLeft());
406+
$this->assertEquals(10, $fiction->getRight());
407+
$this->assertEquals(0, $fiction->getLevel());
408+
$this->assertEquals(1, $fiction->getRoot());
409+
$this->assertNull($fiction->getParent());
410+
411+
$this->assertEquals(6, $lotr->getLeft());
412+
$this->assertEquals(7, $lotr->getRight());
413+
$this->assertEquals(1, $lotr->getLevel());
414+
$this->assertEquals(1, $lotr->getRoot());
415+
$this->assertEquals($fiction, $lotr->getParent());
416+
417+
$this->assertEquals(8, $warlock->getLeft());
418+
$this->assertEquals(9, $warlock->getRight());
419+
$this->assertEquals(1, $warlock->getLevel());
420+
$this->assertEquals(1, $warlock->getRoot());
421+
$this->assertEquals($fiction, $warlock->getParent());
422+
423+
$this->assertEquals(2, $php->getLeft());
424+
$this->assertEquals(3, $php->getRight());
425+
$this->assertEquals(1, $php->getLevel());
426+
$this->assertEquals(1, $php->getRoot());
427+
$this->assertEquals($fact, $php->getParent());
428+
429+
$this->assertEquals(1, $comedy->getLeft());
430+
$this->assertEquals(2, $comedy->getRight());
431+
$this->assertEquals(0, $comedy->getLevel());
432+
$this->assertEquals(2, $comedy->getRoot());
433+
$this->assertNull($comedy->getParent());
434+
435+
$this->assertEquals(3, $horror->getLeft());
436+
$this->assertEquals(8, $horror->getRight());
437+
$this->assertEquals(0, $horror->getLevel());
438+
$this->assertEquals(2, $horror->getRoot());
439+
$this->assertNull($horror->getParent());
440+
441+
$this->assertEquals(9, $action->getLeft());
442+
$this->assertEquals(10, $action->getRight());
443+
$this->assertEquals(0, $action->getLevel());
444+
$this->assertEquals(2, $action->getRoot());
445+
$this->assertNull($action->getParent());
446+
447+
$this->assertEquals(4, $dracula->getLeft());
448+
$this->assertEquals(5, $dracula->getRight());
449+
$this->assertEquals(1, $dracula->getLevel());
450+
$this->assertEquals(2, $dracula->getRoot());
451+
$this->assertEquals($horror, $dracula->getParent());
452+
453+
$this->assertEquals(6, $frankenstein->getLeft());
454+
$this->assertEquals(7, $frankenstein->getRight());
455+
$this->assertEquals(1, $frankenstein->getLevel());
456+
$this->assertEquals(2, $frankenstein->getRoot());
457+
$this->assertEquals($horror, $frankenstein->getParent());
458+
459+
// Now move the action movie category up
460+
$repo->moveUp($action);
461+
462+
$this->assertEquals(1, $comedy->getLeft());
463+
$this->assertEquals(2, $comedy->getRight());
464+
$this->assertEquals(0, $comedy->getLevel());
465+
$this->assertEquals(2, $comedy->getRoot());
466+
$this->assertNull($comedy->getParent());
467+
468+
$this->assertEquals(3, $action->getLeft());
469+
$this->assertEquals(4, $action->getRight());
470+
$this->assertEquals(0, $action->getLevel());
471+
$this->assertEquals(2, $action->getRoot());
472+
$this->assertNull($action->getParent());
473+
474+
$this->assertEquals(5, $horror->getLeft());
475+
$this->assertEquals(10, $horror->getRight());
476+
$this->assertEquals(0, $horror->getLevel());
477+
$this->assertEquals(2, $horror->getRoot());
478+
$this->assertNull($horror->getParent());
479+
480+
$this->assertEquals(6, $dracula->getLeft());
481+
$this->assertEquals(7, $dracula->getRight());
482+
$this->assertEquals(1, $dracula->getLevel());
483+
$this->assertEquals(2, $dracula->getRoot());
484+
$this->assertEquals($horror, $dracula->getParent());
485+
486+
$this->assertEquals(8, $frankenstein->getLeft());
487+
$this->assertEquals(9, $frankenstein->getRight());
488+
$this->assertEquals(1, $frankenstein->getLevel());
489+
$this->assertEquals(2, $frankenstein->getRoot());
490+
$this->assertEquals($horror, $frankenstein->getParent());
491+
492+
$this->em->clear();
493+
}
494+
323495
protected function getUsedEntityFixtures()
324496
{
325497
return array(
326498
self::CATEGORY,
499+
ForeignRootCategory::class,
327500
);
328501
}
329502

503+
/**
504+
* @throws \Doctrine\ORM\OptimisticLockException
505+
*/
330506
private function populate()
331507
{
332508
$root = new RootCategory();

0 commit comments

Comments
 (0)