Переглянути джерело

[Tree] Closure: Made "level" property optional

comfortablynumb 13 роки тому
батько
коміт
2a359df97e

+ 18 - 0
lib/Gedmo/Exception/TreeLevelFieldNotFoundException.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Gedmo\Exception;
+
+use Gedmo\Exception;
+
+/**
+ * TreeLevelFieldNotFoundException
+ * 
+ * @author Gustavo Falco <comfortablynumb84@gmail.com>
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Exception
+ * @subpackage TreeLevelFieldNotFoundException
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TreeLevelFieldNotFoundException extends RuntimeException
+{}

+ 21 - 0
lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php

@@ -8,6 +8,7 @@ use Gedmo\Tree\Strategy;
 use Gedmo\Tree\Strategy\ORM\Closure;
 use Gedmo\Tool\Wrapper\EntityWrapper;
 use Doctrine\ORM\Proxy\Proxy;
+use Gedmo\Exception\TreeLevelFieldNotFoundException;
 
 /**
  * The ClosureTreeRepository has some useful functions
@@ -292,6 +293,16 @@ class ClosureTreeRepository extends AbstractTreeRepository
     {
         $meta = $this->getClassMetadata();
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
+
+        if (!isset($config['level'])) {
+            $msg = sprintf('TreeLevel field is needed on class "%s" to be allowed to call "%s" method.',
+                $meta->name,
+                'buildTreeArray'
+            );
+
+            throw new TreeLevelFieldNotFoundException($msg);
+        }
+
         $nestedTree = array();
         $levelField = $config['level'];
         $idField = $meta->getSingleIdentifierFieldName();
@@ -331,6 +342,16 @@ class ClosureTreeRepository extends AbstractTreeRepository
     public function getNodesHierarchy($node, $direct, array $config, array $options = array())
     {
         $meta = $this->getClassMetadata();
+
+        if (!isset($config['level'])) {
+            $msg = sprintf('TreeLevel field is needed on class "%s" to be allowed to call "%s" method.',
+                $meta->name,
+                'buildTreeArray'
+            );
+
+            throw new TreeLevelFieldNotFoundException($msg);
+        }
+
         $idField = $meta->getSingleIdentifierFieldName();
 
         $q = $this->_em->createQueryBuilder()

+ 0 - 3
lib/Gedmo/Tree/Mapping/Validator.php

@@ -177,9 +177,6 @@ class Validator
         if (!isset($config['closure'])) {
             $missingFields[] = 'closure class';
         }
-        if (!isset($config['level'])) {
-            $missingFields[] = 'level';
-        }
         if ($missingFields) {
             throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
         }

+ 7 - 3
lib/Gedmo/Tree/Strategy/ORM/Closure.php

@@ -276,8 +276,10 @@ class Closure implements Strategy
                     );
                 }
 
-                $this->pendingNodesLevelProcess[$nodeId] = $node;
-            } else {
+                if (isset($config['level'])) {
+                    $this->pendingNodesLevelProcess[$nodeId] = $node;
+                }
+            } else if (isset($config['level'])) {
                 $uow->scheduleExtraUpdate($node, array($config['level'] => array(null, 1)));
                 $ea->setOriginalObjectProperty($uow, spl_object_hash($node), $config['level'], 1);
             }
@@ -444,6 +446,8 @@ class Closure implements Strategy
             }
         }
 
-        $this->pendingNodesLevelProcess[$nodeId] = $node;
+        if (isset($config['level'])) {
+            $this->pendingNodesLevelProcess[$nodeId] = $node;
+        }
     }
 }

+ 25 - 1
tests/Gedmo/Tree/ClosureTreeRepositoryTest.php

@@ -5,6 +5,8 @@ namespace Gedmo\Tree;
 use Doctrine\Common\EventManager;
 use Tool\BaseTestCaseORM;
 use Tree\Fixture\Closure\Category;
+use Tree\Fixture\Closure\CategoryWithoutLevel;
+use Tree\Fixture\Closure\CategoryWithoutLevelClosure;
 
 /**
  * These are tests for Tree behavior
@@ -19,6 +21,8 @@ class ClosureTreeRepositoryTest extends BaseTestCaseORM
 {
     const CATEGORY = "Tree\\Fixture\\Closure\\Category";
     const CLOSURE = "Tree\\Fixture\\Closure\\CategoryClosure";
+    const CATEGORY_WITHOUT_LEVEL = "Tree\\Fixture\\Closure\\CategoryWithoutLevel";
+    const CATEGORY_WITHOUT_LEVEL_CLOSURE = "Tree\\Fixture\\Closure\\CategoryWithoutLevelClosure";
 
     protected function setUp()
     {
@@ -186,11 +190,31 @@ class ClosureTreeRepositoryTest extends BaseTestCaseORM
         $this->assertEquals('Carrots', $vegitables['__children'][1]['title']);
     }
 
+    /**
+     * @expectedException Gedmo\Exception\TreeLevelFieldNotFoundException
+     */
+    public function test_buildTreeArray_IfLevelFieldIsNotPresentThrowException()
+    {
+        $repo = $this->em->getRepository(self::CATEGORY_WITHOUT_LEVEL);
+        $repo->buildTreeArray(array());
+    }
+
+    /**
+     * @expectedException Gedmo\Exception\TreeLevelFieldNotFoundException
+     */
+    public function test_getNodesHierarchy_IfLevelFieldIsNotPresentThrowException()
+    {
+        $repo = $this->em->getRepository(self::CATEGORY_WITHOUT_LEVEL);
+        $repo->getNodesHierarchy(new CategoryWithoutLevel(), true, array());
+    }
+
     protected function getUsedEntityFixtures()
     {
         return array(
             self::CATEGORY,
-            self::CLOSURE
+            self::CLOSURE,
+            self::CATEGORY_WITHOUT_LEVEL,
+            self::CATEGORY_WITHOUT_LEVEL_CLOSURE
         );
     }
 

+ 35 - 2
tests/Gedmo/Tree/ClosureTreeTest.php

@@ -8,6 +8,8 @@ use Doctrine\Common\Util\Debug;
 use Tree\Fixture\Closure\Category;
 use Tree\Fixture\Closure\News;
 use Tree\Fixture\Closure\CategoryClosure;
+use Tree\Fixture\Closure\CategoryWithoutLevel;
+use Tree\Fixture\Closure\CategoryWithoutLevelClosure;
 
 /**
  * These are tests for Tree behavior
@@ -26,13 +28,19 @@ class ClosureTreeTest extends BaseTestCaseORM
     const USER = "Tree\\Fixture\\Closure\\User";
     const PERSON_CLOSURE = "Tree\\Fixture\\Closure\\PersonClosure";
     const NEWS = "Tree\\Fixture\\Closure\\News";
+    const CATEGORY_WITHOUT_LEVEL = "Tree\\Fixture\\Closure\\CategoryWithoutLevel";
+    const CATEGORY_WITHOUT_LEVEL_CLOSURE = "Tree\\Fixture\\Closure\\CategoryWithoutLevelClosure";
+
+    protected $listener;
 
     protected function setUp()
     {
         parent::setUp();
 
+        $this->listener = new TreeListener;
+
         $evm = new EventManager;
-        $evm->addEventSubscriber(new TreeListener);
+        $evm->addEventSubscriber($this->listener);
 
         $this->getMockSqliteEntityManager($evm);
         $this->populate();
@@ -211,6 +219,29 @@ class ClosureTreeTest extends BaseTestCaseORM
         $this->em->flush();
     }
 
+    public function testIfEntityHasNotIncludedTreeLevelFieldThenDontProcessIt()
+    {
+        $listener = $this->getMock('Gedmo\Tree\TreeListener', array('getStrategy'));
+        $strategy = $this->getMock('Gedmo\Tree\Strategy\ORM\Closure', array('setLevelFieldOnPendingNodes'), array($listener));
+        $listener->expects($this->any())
+            ->method('getStrategy')
+            ->will($this->returnValue($strategy));
+
+        $strategy->expects($this->never())
+            ->method('setLevelFieldOnPendingNodes');
+
+        $evm = $this->em->getEventManager();
+
+        $evm->removeEventListener($this->listener->getSubscribedEvents(), $this->listener);
+        $evm->addEventListener($this->listener->getSubscribedEvents(), $this->listener);
+
+        $cat = new CategoryWithoutLevel();
+        $cat->setTitle('Test');
+
+        $this->em->persist($cat);
+        $this->em->flush();
+    }
+
     private function hasAncestor($closures, $name)
     {
         $result = false;
@@ -232,7 +263,9 @@ class ClosureTreeTest extends BaseTestCaseORM
             self::PERSON,
             self::PERSON_CLOSURE,
             self::USER,
-            self::NEWS
+            self::NEWS,
+            self::CATEGORY_WITHOUT_LEVEL,
+            self::CATEGORY_WITHOUT_LEVEL_CLOSURE
         );
     }
 

+ 63 - 0
tests/Gedmo/Tree/Fixture/Closure/CategoryWithoutLevel.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Tree\Fixture\Closure;
+
+use Gedmo\Mapping\Annotation as Gedmo;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @Gedmo\Tree(type="closure")
+ * @Gedmo\TreeClosure(class="Tree\Fixture\Closure\CategoryWithoutLevelClosure")
+ * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\ClosureTreeRepository")
+ */
+class CategoryWithoutLevel
+{
+    /**
+     * @ORM\Column(name="id", type="integer")
+     * @ORM\Id
+     * @ORM\GeneratedValue
+     */
+    private $id;
+
+    /**
+     * @ORM\Column(name="title", type="string", length=64)
+     */
+    private $title;
+
+    /**
+     * @Gedmo\TreeParent
+     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
+     * @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
+     */
+    private $parent;
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function setTitle($title)
+    {
+        $this->title = $title;
+    }
+
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    public function setParent(Category $parent = null)
+    {
+        $this->parent = $parent;
+    }
+
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    public function addClosure(CategoryClosure $closure)
+    {
+        $this->closures[] = $closure;
+    }
+}

+ 14 - 0
tests/Gedmo/Tree/Fixture/Closure/CategoryWithoutLevelClosure.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace Tree\Fixture\Closure;
+
+use Gedmo\Tree\Entity\MappedSuperclass\AbstractClosure;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ */
+class CategoryWithoutLevelClosure extends AbstractClosure
+{
+
+}