Skip to content

Commit a48fa1e

Browse files
committed
Add TreeRoot support to MaterializedPath trees
This commit allows the TreeRoot() annotation to be used with trees using the MaterializedPath type. It stores the first value in the ancestry field into the root field. The field decorated with TreeRoot() may be used as an association and thus act as a foreign key if it is configured to be a reference rather than an integer field.
1 parent 7ec4b4c commit a48fa1e

File tree

7 files changed

+314
-0
lines changed

7 files changed

+314
-0
lines changed

lib/Gedmo/Tree/Strategy/AbstractMaterializedPath.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,27 @@ public function updateNode(ObjectManager $om, $node, AdapterInterface $ea)
313313
$changes[$config['path_hash']] = array(null, $pathHash);
314314
}
315315

316+
if (isset($config['root'])) {
317+
$root = null;
318+
319+
// Define the root value by grabbing the top of the current path
320+
$rootFinderPath = explode($config['path_separator'], $path);
321+
$rootIndex = $config['path_starts_with_separator'] ? 1 : 0;
322+
$root = $rootFinderPath[$rootIndex];
323+
324+
// If it is an association, then make it an reference
325+
// to the entity
326+
if ($meta->hasAssociation($config['root'])) {
327+
$rootClass = $meta->getAssociationTargetClass($config['root']);
328+
$root = $om->getReference($rootClass, $root);
329+
}
330+
331+
$rootProp = $meta->getReflectionProperty($config['root']);
332+
$rootProp->setAccessible(true);
333+
$rootProp->setValue($node, $root);
334+
$changes[$config['root']] = array(null, $root);
335+
}
336+
316337
if (isset($config['level'])) {
317338
$level = substr_count($path, $config['path_separator']);
318339
$levelProp = $meta->getReflectionProperty($config['level']);

tests/Gedmo/Tree/Fixture/MPCategory.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ class MPCategory
4545
*/
4646
private $level;
4747

48+
/**
49+
* @Gedmo\TreeRoot
50+
* @ORM\Column(name="tree_root_value", type="string", nullable=true)
51+
*/
52+
private $treeRootValue;
53+
4854
/**
4955
* @ORM\OneToMany(targetEntity="MPCategory", mappedBy="parent")
5056
*/
@@ -94,4 +100,9 @@ public function getLevel()
94100
{
95101
return $this->level;
96102
}
103+
104+
public function getTreeRootValue()
105+
{
106+
return $this->treeRootValue;
107+
}
97108
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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\MaterializedPathRepository")
10+
* @Gedmo\Tree(type="materializedPath")
11+
*/
12+
class MPCategoryWithRootAssociation
13+
{
14+
/**
15+
* @Gedmo\TreePathSource
16+
* @ORM\Column(name="id", type="integer")
17+
* @ORM\Id
18+
* @ORM\GeneratedValue
19+
*/
20+
private $id;
21+
22+
/**
23+
* @Gedmo\TreePath
24+
* @ORM\Column(name="path", type="string", length=3000, nullable=true)
25+
*/
26+
private $path;
27+
28+
/**
29+
* @ORM\Column(name="title", type="string", length=64)
30+
*/
31+
private $title;
32+
33+
/**
34+
* @Gedmo\TreeParent
35+
* @ORM\ManyToOne(targetEntity="MPCategoryWithRootAssociation", inversedBy="children")
36+
* @ORM\JoinColumns({
37+
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
38+
* })
39+
*/
40+
private $parentId;
41+
42+
/**
43+
* @Gedmo\TreeLevel
44+
* @ORM\Column(name="lvl", type="integer", nullable=true)
45+
*/
46+
private $level;
47+
48+
/**
49+
* @Gedmo\TreeRoot
50+
* @ORM\ManyToOne(targetEntity="MPCategoryWithRootAssociation")
51+
* @ORM\JoinColumns({
52+
* @ORM\JoinColumn(name="tree_root_entity", referencedColumnName="id", onDelete="CASCADE")
53+
* })
54+
*/
55+
private $treeRootEntity;
56+
57+
/**
58+
* @ORM\OneToMany(targetEntity="MPCategory", mappedBy="parent")
59+
*/
60+
private $children;
61+
62+
/**
63+
* @ORM\OneToMany(targetEntity="Article", mappedBy="category")
64+
*/
65+
private $comments;
66+
67+
public function getId()
68+
{
69+
return $this->id;
70+
}
71+
72+
public function setTitle($title)
73+
{
74+
$this->title = $title;
75+
}
76+
77+
public function getTitle()
78+
{
79+
return $this->title;
80+
}
81+
82+
public function setParent(MPCategoryWithRootAssociation $parent = null)
83+
{
84+
$this->parentId = $parent;
85+
}
86+
87+
public function getParent()
88+
{
89+
return $this->parentId;
90+
}
91+
92+
public function setPath($path)
93+
{
94+
$this->path = $path;
95+
}
96+
97+
public function getPath()
98+
{
99+
return $this->path;
100+
}
101+
102+
public function getLevel()
103+
{
104+
return $this->level;
105+
}
106+
107+
public function getTreeRootEntity()
108+
{
109+
return $this->treeRootEntity;
110+
}
111+
}

tests/Gedmo/Tree/Fixture/MPFeaturesCategory.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ class MPFeaturesCategory
5151
*/
5252
private $level;
5353

54+
55+
/**
56+
* @Gedmo\TreeRoot
57+
* @ORM\Column(name="tree_root_value", type="string", nullable=true)
58+
*/
59+
private $treeRootValue;
60+
5461
/**
5562
* @ORM\OneToMany(targetEntity="MPFeaturesCategory", mappedBy="parent")
5663
*/
@@ -101,6 +108,11 @@ public function getLevel()
101108
return $this->level;
102109
}
103110

111+
public function getTreeRootValue()
112+
{
113+
return $this->treeRootValue;
114+
}
115+
104116
public function getPathHash()
105117
{
106118
return $this->pathHash;

tests/Gedmo/Tree/MaterializedPathORMFeaturesTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public function checkPathsAndHash()
7272
$this->assertEquals($this->generatePathHash(array('1' => $category->getId(), '2' => $category2->getId())), $category2->getPathHash());
7373
$this->assertEquals($this->generatePathHash(array('1' => $category->getId(), '2' => $category2->getId(), '3' => $category3->getId())), $category3->getPathHash());
7474
$this->assertEquals($this->generatePathHash(array('4' => $category4->getId())), $category4->getPathHash());
75+
76+
$this->assertEquals($category->getTitle(), $category->getTreeRootValue());
77+
$this->assertEquals($category->getTitle(), $category2->getTreeRootValue());
78+
$this->assertEquals($category->getTitle(), $category3->getTreeRootValue());
79+
$this->assertEquals($category4->getTitle(), $category4->getTreeRootValue());
7580
}
7681

7782
public function createCategory()
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
namespace Gedmo\Tree;
4+
5+
use Doctrine\Common\EventManager;
6+
use Tool\BaseTestCaseORM;
7+
8+
/**
9+
* These are tests for Tree behavior
10+
*
11+
* @author Gustavo Falco <[email protected]>
12+
* @author Gediminas Morkevicius <[email protected]>
13+
* @link http://www.gediminasm.org
14+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
15+
*/
16+
class MaterializedPathORMRootAssociationTest extends BaseTestCaseORM
17+
{
18+
const CATEGORY = "Tree\\Fixture\\MPCategoryWithRootAssociation";
19+
20+
protected $config;
21+
protected $listener;
22+
23+
protected function setUp()
24+
{
25+
parent::setUp();
26+
27+
$this->listener = new TreeListener();
28+
29+
$evm = new EventManager();
30+
$evm->addEventSubscriber($this->listener);
31+
32+
$this->getMockSqliteEntityManager($evm);
33+
34+
$meta = $this->em->getClassMetadata(self::CATEGORY);
35+
$this->config = $this->listener->getConfiguration($this->em, $meta->name);
36+
}
37+
38+
/**
39+
* @test
40+
*/
41+
public function insertUpdateAndRemove()
42+
{
43+
// Insert
44+
$category = $this->createCategory();
45+
$category->setTitle('1');
46+
$category2 = $this->createCategory();
47+
$category2->setTitle('2');
48+
$category3 = $this->createCategory();
49+
$category3->setTitle('3');
50+
$category4 = $this->createCategory();
51+
$category4->setTitle('4');
52+
53+
$category2->setParent($category);
54+
$category3->setParent($category2);
55+
56+
$this->em->persist($category4);
57+
$this->em->persist($category3);
58+
$this->em->persist($category2);
59+
$this->em->persist($category);
60+
$this->em->flush();
61+
62+
$this->em->refresh($category);
63+
$this->em->refresh($category2);
64+
$this->em->refresh($category3);
65+
$this->em->refresh($category4);
66+
67+
$this->assertEquals($this->generatePath(array($category->getId())), $category->getPath());
68+
$this->assertEquals($this->generatePath(array($category->getId(), $category2->getId())), $category2->getPath());
69+
$this->assertEquals($this->generatePath(array($category->getId(), $category2->getId(), $category3->getId())), $category3->getPath());
70+
$this->assertEquals($this->generatePath(array($category4->getId())), $category4->getPath());
71+
$this->assertEquals(1, $category->getLevel());
72+
$this->assertEquals(2, $category2->getLevel());
73+
$this->assertEquals(3, $category3->getLevel());
74+
$this->assertEquals(1, $category4->getLevel());
75+
76+
$this->assertEquals($category, $category->getTreeRootEntity());
77+
$this->assertEquals($category, $category2->getTreeRootEntity());
78+
$this->assertEquals($category, $category3->getTreeRootEntity());
79+
$this->assertEquals($category4, $category4->getTreeRootEntity());
80+
81+
// Update
82+
$category2->setParent(null);
83+
84+
$this->em->persist($category2);
85+
$this->em->flush();
86+
87+
$this->em->refresh($category);
88+
$this->em->refresh($category2);
89+
$this->em->refresh($category3);
90+
91+
$this->assertEquals($this->generatePath(array($category->getId())), $category->getPath());
92+
$this->assertEquals($this->generatePath(array($category2->getId())), $category2->getPath());
93+
$this->assertEquals($this->generatePath(array($category2->getId(), $category3->getId())), $category3->getPath());
94+
$this->assertEquals(1, $category->getLevel());
95+
$this->assertEquals(1, $category2->getLevel());
96+
$this->assertEquals(2, $category3->getLevel());
97+
$this->assertEquals(1, $category4->getLevel());
98+
99+
$this->assertEquals($category, $category->getTreeRootEntity());
100+
$this->assertEquals($category2, $category2->getTreeRootEntity());
101+
$this->assertEquals($category2, $category3->getTreeRootEntity());
102+
$this->assertEquals($category4, $category4->getTreeRootEntity());
103+
104+
// Remove
105+
$this->em->remove($category);
106+
$this->em->remove($category2);
107+
$this->em->flush();
108+
109+
$result = $this->em->createQueryBuilder()->select('c')->from(self::CATEGORY, 'c')->getQuery()->execute();
110+
111+
$firstResult = $result[0];
112+
113+
$this->assertCount(1, $result);
114+
$this->assertEquals('4', $firstResult->getTitle());
115+
$this->assertEquals(1, $firstResult->getLevel());
116+
$this->assertEquals($category4, $firstResult->getTreeRootEntity());
117+
}
118+
119+
public function createCategory()
120+
{
121+
$class = self::CATEGORY;
122+
123+
return new $class();
124+
}
125+
126+
protected function getUsedEntityFixtures()
127+
{
128+
return array(
129+
self::CATEGORY,
130+
);
131+
}
132+
133+
public function generatePath(array $sources)
134+
{
135+
$path = '';
136+
137+
foreach ($sources as $id) {
138+
$path .= $id.$this->config['path_separator'];
139+
}
140+
141+
return $path;
142+
}
143+
}

tests/Gedmo/Tree/MaterializedPathORMTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ public function insertUpdateAndRemove()
7373
$this->assertEquals(3, $category3->getLevel());
7474
$this->assertEquals(1, $category4->getLevel());
7575

76+
$this->assertEquals('1-4', $category->getTreeRootValue());
77+
$this->assertEquals('1-4', $category2->getTreeRootValue());
78+
$this->assertEquals('1-4', $category3->getTreeRootValue());
79+
$this->assertEquals('4-1', $category4->getTreeRootValue());
80+
7681
// Update
7782
$category2->setParent(null);
7883

@@ -91,6 +96,11 @@ public function insertUpdateAndRemove()
9196
$this->assertEquals(2, $category3->getLevel());
9297
$this->assertEquals(1, $category4->getLevel());
9398

99+
$this->assertEquals('1-4', $category->getTreeRootValue());
100+
$this->assertEquals('2-3', $category2->getTreeRootValue());
101+
$this->assertEquals('2-3', $category3->getTreeRootValue());
102+
$this->assertEquals('4-1', $category4->getTreeRootValue());
103+
94104
// Remove
95105
$this->em->remove($category);
96106
$this->em->remove($category2);
@@ -103,6 +113,7 @@ public function insertUpdateAndRemove()
103113
$this->assertCount(1, $result);
104114
$this->assertEquals('4', $firstResult->getTitle());
105115
$this->assertEquals(1, $firstResult->getLevel());
116+
$this->assertEquals('4-1', $firstResult->getTreeRootValue());
106117
}
107118

108119
/**

0 commit comments

Comments
 (0)