Просмотр исходного кода

issue #12 fix for JOINED table inheritance TREE extension updated to use rootEntityName for updates, will have to check other extensions for consistency

gediminasm 14 лет назад
Родитель
Сommit
c0430e4324

+ 15 - 17
lib/Gedmo/Tree/TreeListener.php

@@ -123,7 +123,7 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
             }
             $diff = $rightValue - $leftValue + 1;
             if ($diff > 2) {
-                $dql = "SELECT node FROM {$entityClass} node";
+                $dql = "SELECT node FROM {$meta->rootEntityName} node";
                 $dql .= " WHERE node.{$config['left']} BETWEEN :left AND :right";
                 $q = $em->createQuery($dql);
                 // get nodes for deletion
@@ -213,9 +213,8 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
      */
     private function _processPendingNode(EntityManager $em, $entity)
     {
-        $entityClass = get_class($entity);
-        $config = $this->getConfiguration($em, $entityClass);
-        $meta = $em->getClassMetadata($entityClass);
+        $meta = $em->getClassMetadata(get_class($entity));
+        $config = $this->getConfiguration($em, $meta->name);
         $parent = $meta->getReflectionProperty($config['parent'])->getValue($entity);
         $this->_adjustNodeWithParent($parent, $entity, $em);
     }
@@ -229,11 +228,10 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
      */
     private function _prepareRoot(EntityManager $em, $entity)
     {
-        $entityClass = get_class($entity);
-        $config = $this->getConfiguration($em, $entityClass);
+        $meta = $em->getClassMetadata(get_class($entity));
+        $config = $this->getConfiguration($em, $meta->name);
         
         $edge = $this->_treeEdge ?: $this->_getTreeEdge($em, $entity);
-        $meta = $em->getClassMetadata($entityClass);
         
         $meta->getReflectionProperty($config['left'])->setValue($entity, $edge + 1);
         $meta->getReflectionProperty($config['right'])->setValue($entity, $edge + 2);
@@ -251,10 +249,10 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
      */
     private function _adjustNodeWithParent($parent, $entity, EntityManager $em)
     {
-        $entityClass = get_class($entity);
-        $config = $this->getConfiguration($em, $entityClass);
+        $meta = $em->getClassMetadata(get_class($entity));
+        $config = $this->getConfiguration($em, $meta->name);
         $edge = $this->_getTreeEdge($em, $entity);
-        $meta = $em->getClassMetadata($entityClass);
+        
         
         $leftValue = $meta->getReflectionProperty($config['left'])->getValue($entity);
         $rightValue = $meta->getReflectionProperty($config['right'])->getValue($entity);
@@ -273,7 +271,7 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
                 $this->_sync($em, $entity, 2, '+', '>= ' . $parentRightValue);
                 // cannot schedule this update if other Nodes pending
                 $qb = $em->createQueryBuilder();
-                $qb->update($entityClass, 'node')
+                $qb->update($meta->rootEntityName, 'node')
                     ->set('node.' . $config['left'], $parentRightValue)
                     ->set('node.' . $config['right'], $parentRightValue + 1);
                 $entityIdentifiers = $meta->getIdentifierValues($entity);
@@ -318,14 +316,14 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
      */
     private function _sync(EntityManager $em, $entity, $shift, $dir, $conditions, $field = 'both')
     {
-        $entityClass = get_class($entity);
-        $config = $this->getConfiguration($em, $entityClass);
+        $meta = $em->getClassMetadata(get_class($entity));
+        $config = $this->getConfiguration($em, $meta->name);
         if ($field == 'both') {
             $this->_sync($em, $entity, $shift, $dir, $conditions, $config['left']);
             $field = $config['right'];
         }
         
-        $dql = "UPDATE {$entityClass} node";
+        $dql = "UPDATE {$meta->rootEntityName} node";
         $dql .= " SET node.{$field} = node.{$field} {$dir} {$shift}";
         $dql .= " WHERE node.{$field} {$conditions}";
         $q = $em->createQuery($dql);
@@ -341,10 +339,10 @@ class TreeListener extends MappedEventSubscriber implements EventSubscriber
      */
     private function _getTreeEdge(EntityManager $em, $entity)
     {
-        $entityClass = get_class($entity);
-        $config = $this->getConfiguration($em, $entityClass);
+        $meta = $em->getClassMetadata(get_class($entity));
+        $config = $this->getConfiguration($em, $meta->name);
         
-        $query = $em->createQuery("SELECT MAX(node.{$config['right']}) FROM {$entityClass} node");
+        $query = $em->createQuery("SELECT MAX(node.{$config['right']}) FROM {$meta->rootEntityName} node");
         $right = $query->getSingleScalarResult();
         return intval($right);
     }

+ 111 - 0
tests/Gedmo/Tree/Fixture/Role.php

@@ -0,0 +1,111 @@
+<?php
+namespace Tree\Fixture;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+/**
+ * @Entity(repositoryClass="Gedmo\Tree\Repository\TreeNodeRepository")
+ * @Table(name="`role`")
+ * @InheritanceType("JOINED")
+ * @DiscriminatorColumn(name="discr", type="string")
+ * @DiscriminatorMap({"user" = "User", "usergroup" = "UserGroup"})
+ */
+abstract class Role {
+
+  /**
+   * @Column(name="id", type="integer")
+   * @Id
+   * @GeneratedValue
+   * @var int
+   */
+  private $id;
+
+  /**
+   * @gedmo:TreeParent
+   * @ManyToOne(targetEntity="UserGroup", inversedBy="children")
+   * @var UserGroup
+   */
+  private $parent;
+
+  /**
+   * @OneToMany(targetEntity="Role", mappedBy="parent")
+   * @var Doctrine\Common\Collections\ArrayCollection
+   */
+  protected $children;
+
+  /**
+   * @gedmo:TreeLeft
+   * @Column(name="lft", type="integer")
+   */
+  private $lft;
+
+  /**
+   * @gedmo:TreeRight
+   * @Column(name="rgt", type="integer")
+   */
+  private $rgt;
+
+  /**
+   * @gedmo:TreeLevel
+   * @Column(name="lvl", type="integer")
+   */
+  private $lvl;
+
+  /**
+   * @Column(name="role", type="string", length=255, nullable=false)
+   * @var string
+   */
+  private $role;
+
+  public function __construct() {
+    $this->children      = new ArrayCollection();
+  }
+
+  /**
+   * @return UserGroup
+   */
+  public function getParent() {
+    return $this->parent;
+  }
+
+  /**
+   * @param UserGroup $parent
+   * @return Role
+   */
+  public function setParent(UserGroup $parent) {
+    $this->parent = $parent;
+    return $this;
+  }
+
+  public function getRoleId() {
+    return $this->role;
+  }
+
+  protected function setRoleId($roleId) {
+    $this->role = (string)$roleId;
+    return $this;
+  }
+
+  public function __toString() {
+    return $this->getRoleId();
+  }
+
+  public function getId() {
+    return $this->id;
+  }
+  
+  public function getLeft()
+  {
+      return $this->lft;
+  }
+  
+  public function getRight()
+  {
+      return $this->rgt;
+  }
+  
+  public function getLevel()
+  {
+      return $this->lvl;
+  }
+}

+ 124 - 0
tests/Gedmo/Tree/Fixture/User.php

@@ -0,0 +1,124 @@
+<?php
+namespace Tree\Fixture;
+
+/**
+ * @Entity(repositoryClass="Gedmo\Tree\Repository\TreeNodeRepository")
+ * @Table(name="`user`")
+ */
+class User extends Role {
+
+  const PASSWORD_SALT = 'dfJko$~346958rg!DFT]AEtzserf9giq)3/TAeg;aDFa43';
+
+  /**
+   * @Column(name="email", type="string", unique="true")
+   * @var string
+   */
+  private $email;
+
+  /**
+   * @Column(name="password_hash", type="string", length=32)
+   * @var string
+   */
+  private $passwordHash;
+
+  /**
+   * @Column(name="activation_code", type="string", length=12)
+   * @var string
+   */
+  private $activationCode;
+
+  /**
+   * @param string $email
+   * @param string $password
+   */
+  public function __construct($email, $password) {
+    parent::__construct();
+    $this
+      ->setEmail($email)
+      ->setPassword($password);
+  }
+
+  public function init() {
+    $this->setActivationCode($this->generateString(12));
+  }
+
+    /**
+   * Generates a random password
+   *
+   * @param int $length
+   * @return string
+   */
+  public function generateString($length = 8) {
+    $length = (int) $length;
+    if ($length < 0) {
+      throw new \Exception("Invalid password length '$length'");
+    }
+    $set = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    $num = strlen($set);
+    $ret = '';
+    for ($i = 0; $i < $length; $i++) {
+      $ret .= $set[rand(0, $num - 1)];
+    }
+    return $ret;
+  }
+
+  /**
+   * Generates a password hash
+   *
+   * @param string $password
+   * @return string
+   */
+  public function generatePasswordHash($password) {
+    return md5($password . self::PASSWORD_SALT);
+  }
+
+  /**
+   * @return string
+   */
+  public function getEmail() {
+    return $this->email;
+  }
+
+  /**
+   * @param string $email
+   * @return User
+   */
+  public function setEmail($email) {
+    $this->email = $email;
+    $this->setRoleId($email);
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getPasswordHash() {
+    return $this->passwordHash;
+  }
+
+  /**
+   * @param string $password
+   * @return User
+   */
+  public function setPassword($password) {
+    $this->passwordHash = $this->generatePasswordHash(trim($password));
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getActivationCode() {
+    return $this->activationCode;
+  }
+
+  /**
+   * @param string $activationCode
+   * @return User
+   */
+  public function setActivationCode($activationCode) {
+    $this->activationCode = $activationCode;
+    return $this;
+  }
+
+}

+ 39 - 0
tests/Gedmo/Tree/Fixture/UserGroup.php

@@ -0,0 +1,39 @@
+<?php
+namespace Tree\Fixture;
+
+/**
+ * Group entity
+ * 
+ * @Entity(repositoryClass="Gedmo\Tree\Repository\TreeNodeRepository")
+ * @Table(name="`user_group`")
+ */
+class UserGroup extends Role {
+
+  /**
+   * @Column(name="name", type="string", length=255)
+   * @var string
+   */
+  private $name;
+
+  public function __construct($name) {
+    $this->setName($name);
+  }
+
+  /**
+   * @return string
+   */
+  public function getRoleId() {
+    return $this->name;
+  }
+
+  public function getName() {
+    return $this->name;
+  }
+
+  public function setName($name) {
+    $this->name = $name;
+    $this->setRoleId($name);
+    return $this;
+  }
+
+}

+ 132 - 0
tests/Gedmo/Tree/MultiInheritanceTest2.php

@@ -0,0 +1,132 @@
+<?php
+
+namespace Gedmo\Tree;
+
+use Doctrine\Common\Util\Debug;
+
+/**
+ * These are tests for Tree behavior
+ * Based on reported github issue #12 
+ * JOINED table inheritance mapping bug on Tree;
+ * 
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Tree
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class MultiInheritanceTest2 extends \PHPUnit_Framework_TestCase
+{
+    const TEST_ENTITY_USER_CLASS = "Tree\Fixture\User";
+    const TEST_ENTITY_USERGROUP_CLASS = "Tree\Fixture\UserGroup";
+    const TEST_ENTITY_ROLE_CLASS = "Tree\Fixture\Role";
+    
+    private $em;
+    
+    public function setUp()
+    {        
+        $config = new \Doctrine\ORM\Configuration();
+        $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
+        $config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
+        $config->setProxyDir(__DIR__ . '/Proxy');
+        $config->setProxyNamespace('Gedmo\Tree\Proxies');
+        $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
+
+        $conn = array(
+            'driver' => 'pdo_sqlite',
+            'memory' => true,
+        );
+
+        //$config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
+        
+        $evm = new \Doctrine\Common\EventManager();
+        $evm->addEventSubscriber(new \Gedmo\Tree\TreeListener());
+        $this->em = \Doctrine\ORM\EntityManager::create($conn, $config, $evm);
+
+        $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->em);
+        $schemaTool->dropSchema(array());
+        $schemaTool->createSchema(array(
+            $this->em->getClassMetadata(self::TEST_ENTITY_ROLE_CLASS),
+            $this->em->getClassMetadata(self::TEST_ENTITY_USER_CLASS),
+            $this->em->getClassMetadata(self::TEST_ENTITY_USERGROUP_CLASS)
+        ));
+        
+        $this->_populate();
+    }
+    
+    public function testConsistence()
+    {
+        $admins = $this->em->getRepository(self::TEST_ENTITY_USERGROUP_CLASS)->findOneByName('Admins');
+        $user3 = new \Tree\Fixture\User('user3@test.com', 'secret');
+        $user3->init();
+        $user3->setParent($admins);
+        
+        $this->em->persist($user3);
+        $this->em->flush();
+        $this->em->clear();
+        
+        // run tree consistence checks
+        
+        $everyBody = $this->em->getRepository(self::TEST_ENTITY_USERGROUP_CLASS)->findOneByName('Everybody');
+        $this->assertEquals(1, $everyBody->getLeft());
+        $this->assertEquals(14, $everyBody->getRight());
+        $this->assertEquals(0, $everyBody->getLevel());
+        
+        $admins = $this->em->getRepository(self::TEST_ENTITY_USERGROUP_CLASS)->findOneByName('Admins');
+        $this->assertEquals(2, $admins->getLeft());
+        $this->assertEquals(7, $admins->getRight());
+        $this->assertEquals(1, $admins->getLevel());
+        
+        $visitors = $this->em->getRepository(self::TEST_ENTITY_USERGROUP_CLASS)->findOneByName('Visitors');
+        $this->assertEquals(8, $visitors->getLeft());
+        $this->assertEquals(13, $visitors->getRight());
+        $this->assertEquals(1, $visitors->getLevel());
+        
+        $user0 = $this->em->getRepository(self::TEST_ENTITY_USER_CLASS)->findOneByEmail('user0@test.com');
+        $this->assertEquals(3, $user0->getLeft());
+        $this->assertEquals(4, $user0->getRight());
+        $this->assertEquals(2, $user0->getLevel());
+        
+        $user1 = $this->em->getRepository(self::TEST_ENTITY_USER_CLASS)->findOneByEmail('user1@test.com');
+        $this->assertEquals(9, $user1->getLeft());
+        $this->assertEquals(10, $user1->getRight());
+        $this->assertEquals(2, $user1->getLevel());
+        
+        $user2 = $this->em->getRepository(self::TEST_ENTITY_USER_CLASS)->findOneByEmail('user2@test.com');
+        $this->assertEquals(11, $user2->getLeft());
+        $this->assertEquals(12, $user2->getRight());
+        $this->assertEquals(2, $user2->getLevel());
+        
+        $user3 = $this->em->getRepository(self::TEST_ENTITY_USER_CLASS)->findOneByEmail('user3@test.com');
+        $this->assertEquals(5, $user3->getLeft());
+        $this->assertEquals(6, $user3->getRight());
+        $this->assertEquals(2, $user3->getLevel());
+    }
+    
+    private function _populate()
+    {
+        $everyBody = new \Tree\Fixture\UserGroup('Everybody');
+        $admins = new \Tree\Fixture\UserGroup('Admins');
+        $admins->setParent($everyBody);
+        $visitors = new \Tree\Fixture\UserGroup('Visitors');
+        $visitors->setParent($everyBody);
+        
+        $user0 = new \Tree\Fixture\User('user0@test.com', 'secret');
+        $user0->init();
+        $user0->setParent($admins);
+        $user1 = new \Tree\Fixture\User('user1@test.com', 'secret');
+        $user1->init();
+        $user1->setParent($visitors);
+        $user2 = new \Tree\Fixture\User('user2@test.com', 'secret');
+        $user2->init();
+        $user2->setParent($visitors);
+        
+        $this->em->persist($everyBody);
+        $this->em->persist($admins);
+        $this->em->persist($visitors);
+        $this->em->persist($user0);
+        $this->em->persist($user1);
+        $this->em->persist($user2);
+        $this->em->flush();
+        $this->em->clear();
+    }
+}

+ 95 - 0
tests/Gedmo/Tree/Proxy/TreeFixtureUserGroupProxy.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace Gedmo\Tree\Proxies;
+
+/**
+ * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
+ */
+class TreeFixtureUserGroupProxy extends \Tree\Fixture\UserGroup implements \Doctrine\ORM\Proxy\Proxy
+{
+    private $_entityPersister;
+    private $_identifier;
+    public $__isInitialized__ = false;
+    public function __construct($entityPersister, $identifier)
+    {
+        $this->_entityPersister = $entityPersister;
+        $this->_identifier = $identifier;
+    }
+    private function _load()
+    {
+        if (!$this->__isInitialized__ && $this->_entityPersister) {
+            $this->__isInitialized__ = true;
+            if ($this->_entityPersister->load($this->_identifier, $this) === null) {
+                throw new \Doctrine\ORM\EntityNotFoundException();
+            }
+            unset($this->_entityPersister, $this->_identifier);
+        }
+    }
+
+    
+    public function getRoleId()
+    {
+        $this->_load();
+        return parent::getRoleId();
+    }
+
+    public function getName()
+    {
+        $this->_load();
+        return parent::getName();
+    }
+
+    public function setName($name)
+    {
+        $this->_load();
+        return parent::setName($name);
+    }
+
+    public function getParent()
+    {
+        $this->_load();
+        return parent::getParent();
+    }
+
+    public function setParent(\Tree\Fixture\UserGroup $parent)
+    {
+        $this->_load();
+        return parent::setParent($parent);
+    }
+
+    public function __toString()
+    {
+        $this->_load();
+        return parent::__toString();
+    }
+
+    public function getId()
+    {
+        $this->_load();
+        return parent::getId();
+    }
+
+    public function getLeft()
+    {
+        $this->_load();
+        return parent::getLeft();
+    }
+
+    public function getRight()
+    {
+        $this->_load();
+        return parent::getRight();
+    }
+
+    public function getLevel()
+    {
+        $this->_load();
+        return parent::getLevel();
+    }
+
+
+    public function __sleep()
+    {
+        return array('__isInitialized__', 'id', 'parent', 'children', 'lft', 'rgt', 'lvl', 'role', 'name');
+    }
+}