Browse Source

Merge remote-tracking branch 'xanf/InMemoryUpdatesWithInheritance'

gedi 13 năm trước cách đây
mục cha
commit
2e7c1ff5a5

+ 22 - 0
lib/Gedmo/Tree/Strategy/ORM/Nested.php

@@ -72,6 +72,13 @@ class Nested implements Strategy
      */
     private $nodePositions = array();
 
+    /**
+     * Stores a list of delayed nodes for correct order of updates
+     *
+     * @var array
+     */
+    private $delayedNodes = array();
+
     /**
      * {@inheritdoc}
      */
@@ -314,8 +321,18 @@ class Nested implements Strategy
             $wrappedParent = AbstractWrapper::wrap($parent, $em);
 
             $parentRootId = isset($config['root']) ? $wrappedParent->getPropertyValue($config['root']) : null;
+            $parentOid = spl_object_hash($parent);
             $parentLeft = $wrappedParent->getPropertyValue($config['left']);
             $parentRight = $wrappedParent->getPropertyValue($config['right']);
+            if (empty($parentLeft) && empty($parentRight)) {
+                // parent node is a new node, but wasn't processed yet (due to Doctrine commit order calculator redordering)
+                // We delay processing of node to the moment parent node will be processed
+                if (!isset($this->delayedNodes[$parentOid])) {
+                    $this->delayedNodes[$parentOid] = array();
+                }
+                $this->delayedNodes[$parentOid][] = array('node' => $node, 'position' => $position);
+                return;
+            }
             if (!$isNewNode && $rootId === $parentRootId && $parentLeft >= $left && $parentRight <= $right) {
                 throw new UnexpectedValueException("Cannot set child as parent to node: {$nodeId}");
             }
@@ -417,6 +434,11 @@ class Nested implements Strategy
             $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $left + $diff);
             $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $right + $diff);
         }
+        if (isset($this->delayedNodes[$oid])) {
+            foreach($this->delayedNodes[$oid] as $nodeData) {
+                $this->updateNode($em, $nodeData['node'], $node, $nodeData['position']);
+            }
+        }
     }
 
     /**

+ 10 - 0
tests/Gedmo/Tree/Fixture/Genealogy/Man.php

@@ -0,0 +1,10 @@
+<?php
+namespace Tree\Fixture\Genealogy;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
+ */
+class Man extends Person {
+}

+ 98 - 0
tests/Gedmo/Tree/Fixture/Genealogy/Person.php

@@ -0,0 +1,98 @@
+<?php
+namespace Tree\Fixture\Genealogy;
+
+use Doctrine\Common\Collections\ArrayCollection;
+use Gedmo\Mapping\Annotation as Gedmo;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
+ * @ORM\Table(name="genealogy")
+ * @ORM\InheritanceType("SINGLE_TABLE")
+ * @ORM\DiscriminatorColumn(name="discr", type="string")
+ * @ORM\DiscriminatorMap({"man" = "Man", "woman" = "Woman"})
+ * @Gedmo\Tree(type="nested")
+ */
+abstract class Person {
+
+  /**
+   * @ORM\Column(name="id", type="integer")
+   * @ORM\Id
+   * @ORM\GeneratedValue
+   * @var int
+   */
+  private $id;
+
+  /**
+   * @Gedmo\TreeParent
+   * @ORM\ManyToOne(targetEntity="Person", inversedBy="children")
+   * @var Person
+   */
+  private $parent;
+
+  /**
+   * @ORM\OneToMany(targetEntity="Person", mappedBy="parent")
+   * @var Doctrine\Common\Collections\ArrayCollection
+   */
+  protected $children;
+
+  /**
+   * @Gedmo\TreeLeft
+   * @ORM\Column(name="lft", type="integer")
+   */
+  private $lft;
+
+  /**
+   * @Gedmo\TreeRight
+   * @ORM\Column(name="rgt", type="integer")
+   */
+  private $rgt;
+
+  /**
+   * @Gedmo\TreeLevel
+   * @ORM\Column(name="lvl", type="integer")
+   */
+  private $lvl;
+
+  /**
+   * @ORM\Column(name="name", type="string", length=255, nullable=false)
+   * @var string
+   */
+  private $name;
+
+  /**
+   * @param string $name
+   */
+  public function __construct($name) {
+    $this->name = $name;
+    $this->children = new ArrayCollection();
+  }
+
+  /**
+   * @param Person $parent
+   * @return Person
+   */
+  public function setParent(Person $parent) {
+    $this->parent = $parent;
+    return $this;
+  }
+
+  public function getName() {
+    return $this->name;
+  }
+
+  public function getLeft()
+  {
+      return $this->lft;
+  }
+
+  public function getRight()
+  {
+      return $this->rgt;
+  }
+
+  public function getLevel()
+  {
+      return $this->lvl;
+  }
+}

+ 10 - 0
tests/Gedmo/Tree/Fixture/Genealogy/Woman.php

@@ -0,0 +1,10 @@
+<?php
+namespace Tree\Fixture\Genealogy;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
+ */
+class Woman extends Person {
+}

+ 97 - 0
tests/Gedmo/Tree/InMemoryUpdatesWithInheritanceTest.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Gedmo\Tree;
+
+use Doctrine\Common\EventManager;
+use Tool\BaseTestCaseORM;
+use Tree\Fixture\Genealogy\Man;
+use Tree\Fixture\Genealogy\Woman;
+
+/**
+ * Additional tests for tree inheritance and in-memory updates
+ *
+ * @author Illya Klymov <xanf@xanf.me>
+ * @package Gedmo.Tree
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class InMemoryUpdatesWithInheritanceTest extends BaseTestCaseORM
+{
+
+    const PERSON = "Tree\\Fixture\\Genealogy\\Person";
+    const MAN = "Tree\\Fixture\\Genealogy\\Man";
+    const WOMAN = "Tree\\Fixture\\Genealogy\\Woman";
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $evm = new EventManager;
+        $evm->addEventSubscriber(new TreeListener);
+
+        $this->getMockSqliteEntityManager($evm);
+    }
+
+    public function testInMemoryTreeInsertsWithInheritance()
+    {
+        $nodes = array();
+
+        $man1 = new Man('Root - Man1');
+        $this->em->persist($man1);
+
+        $woman1 = new Woman('Level 1 - Woman1');
+        $this->em->persist($woman1);
+        $woman1->setParent($man1);
+
+        $man2 = new Man('Level 2 - Man2');
+        $this->em->persist($man2);
+        $man2->setParent($woman1);
+
+        $woman2 = new Woman('Level 3 - Woman2');
+        $this->em->persist($woman2);
+        $woman2->setParent($man2);
+
+        $this->em->flush();
+
+        $this->em->refresh($man1);
+        $left = $man1->getLeft();
+        $right = $man1->getRight();
+        $level = $man1->getLevel();
+        $this->assertEquals(1, $left);
+        $this->assertEquals(8, $right);
+        $this->assertEquals(0, $level);
+
+        $this->em->refresh($woman1);
+        $left = $woman1->getLeft();
+        $right = $woman1->getRight();
+        $level = $woman1->getLevel();
+        $this->assertEquals(2, $left);
+        $this->assertEquals(7, $right);
+        $this->assertEquals(1, $level);
+
+        $this->em->refresh($man2);
+        $left = $man2->getLeft();
+        $right = $man2->getRight();
+        $level = $man2->getLevel();
+        $this->assertEquals(3, $left);
+        $this->assertEquals(6, $right);
+        $this->assertEquals(2, $level);
+
+        $this->em->refresh($woman2);
+        $left = $woman2->getLeft();
+        $right = $woman2->getRight();
+        $level = $woman2->getLevel();
+        $this->assertEquals(4, $left);
+        $this->assertEquals(5, $right);
+        $this->assertEquals(3, $level);
+    }
+
+    protected function getUsedEntityFixtures()
+    {
+        return array(
+            self::PERSON,
+            self::MAN,
+            self::WOMAN
+        );
+    }
+}