function let(UserContext $userContext, EntityManager $em, ClassMetadata $classMetadata, EventManager $eventManager, TreeListener $treeListener, Nested $strategy) { $classMetadata->name = 'category'; $userContext->getCurrentLocaleCode()->willReturn('en_US'); $em->getEventManager()->willReturn($eventManager); $eventManager->getListeners()->willReturn([[$treeListener]]); $treeListener->getStrategy(Argument::cetera())->willReturn($strategy); $treeListener->getConfiguration(Argument::cetera())->willReturn(['parent' => 'parent', 'left' => 'left']); $this->beConstructedWith($userContext, $em, $classMetadata); }
public function testTreeMetadata() { $meta = $this->em->getClassMetadata('Mapping\\Fixture\\Xml\\ClosureTree'); $config = $this->tree->getConfiguration($this->em, $meta->name); $this->assertArrayHasKey('strategy', $config); $this->assertEquals('closure', $config['strategy']); $this->assertArrayHasKey('closure', $config); $this->assertEquals('Mapping\\Fixture\\ClosureTreeClosure', $config['closure']); $this->assertArrayHasKey('parent', $config); $this->assertEquals('parent', $config['parent']); }
public function getSubscribedEvents() { $events = parent::getSubscribedEvents(); unset($events[array_search('loadClassMetadata', $events)]); $events[] = Events::loadClassMetadata; return $events; }
/** * {@inheritDoc} */ public function buildTreeArray(array $nodes) { $meta = $this->getClassMetadata(); $config = $this->listener->getConfiguration($this->om, $meta->name); $nestedTree = array(); $l = 0; if (count($nodes) > 0) { // Node Stack. Used to help building the hierarchy $stack = array(); foreach ($nodes as $child) { $item = $child; $item[$this->childrenIndex] = array(); // Number of stack items $l = count($stack); // Check if we're dealing with different levels while ($l > 0 && $stack[$l - 1][$config['level']] >= $item[$config['level']]) { array_pop($stack); $l--; } // Stack is empty (we are inspecting the root) if ($l == 0) { // Assigning the root child $i = count($nestedTree); $nestedTree[$i] = $item; $stack[] =& $nestedTree[$i]; } else { // Add child to parent $i = count($stack[$l - 1][$this->childrenIndex]); $stack[$l - 1][$this->childrenIndex][$i] = $item; $stack[] =& $stack[$l - 1][$this->childrenIndex][$i]; } } } return $nestedTree; }
public function testTreeMetadata() { $meta = $this->em->getClassMetadata('Mapping\\Fixture\\Xml\\NestedTree'); $config = $this->tree->getConfiguration($this->em, $meta->name); $this->assertArrayHasKey('strategy', $config); $this->assertEquals('nested', $config['strategy']); $this->assertArrayHasKey('left', $config); $this->assertEquals('left', $config['left']); $this->assertArrayHasKey('right', $config); $this->assertEquals('right', $config['right']); $this->assertArrayHasKey('level', $config); $this->assertEquals('level', $config['level']); $this->assertArrayHasKey('root', $config); $this->assertEquals('root', $config['root']); $this->assertArrayHasKey('parent', $config); $this->assertEquals('parent', $config['parent']); }
function let(EntityManager $em, Connection $connection, Statement $statement, ClassMetadata $classMetadata, EventManager $eventManager, TreeListener $treeListener, Nested $strategy, \ReflectionProperty $property) { $connection->prepare(Argument::any())->willReturn($statement); $em->getClassMetadata(Argument::any())->willReturn($classMetadata); $classMetadata->name = 'channel'; $classMetadata->getReflectionProperty(Argument::any())->willReturn($property); $em->getConnection()->willReturn($connection); $em->getEventManager()->willReturn($eventManager); $em->getClassMetadata()->willReturn($classMetadata); $strategy->getName()->willReturn(Strategy::NESTED); $strategy->setNodePosition(Argument::cetera())->willReturn(null); $treeListener->getStrategy(Argument::cetera())->willReturn($strategy); $configuration = ['parent' => 'parent', 'left' => 'left']; $treeListener->getConfiguration(Argument::cetera())->willReturn($configuration); $eventManager->getListeners()->willReturn([[$treeListener]]); $this->beConstructedWith($em, $classMetadata); }
/** * Shift range of right and left values on tree * depending on tree level difference also * * @param EntityManager $em * @param string $class * @param integer $first * @param integer $last * @param integer $delta * @param integer|string $root * @param integer|string $destRoot * @param integer $levelDelta */ public function shiftRangeRL(EntityManager $em, $class, $first, $last, $delta, $root = null, $destRoot = null, $levelDelta = null) { $meta = $em->getClassMetadata($class); $config = $this->listener->getConfiguration($em, $class); $sign = $delta >= 0 ? ' + ' : ' - '; $absDelta = abs($delta); $levelSign = $levelDelta >= 0 ? ' + ' : ' - '; $absLevelDelta = abs($levelDelta); $qb = $em->createQueryBuilder(); $qb->update($config['useObjectClass'], 'node')->set('node.' . $config['left'], "node.{$config['left']} {$sign} {$absDelta}")->set('node.' . $config['right'], "node.{$config['right']} {$sign} {$absDelta}")->where($qb->expr()->gte('node.' . $config['left'], $first))->andWhere($qb->expr()->lte('node.' . $config['right'], $last)); if (isset($config['root'])) { $qb->set('node.' . $config['root'], ':drid'); $qb->setParameter('drid', $destRoot); $qb->andWhere($qb->expr()->eq('node.' . $config['root'], ':rid')); $qb->setParameter('rid', $root); } if (isset($config['level'])) { $qb->set('node.' . $config['level'], "node.{$config['level']} {$levelSign} {$absLevelDelta}"); } $qb->getQuery()->getSingleScalarResult(); // update in memory nodes increases performance, saves some IO foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $nodes) { // for inheritance mapped classes, only root is always in the identity map if ($className !== $meta->rootEntityName) { continue; } foreach ($nodes as $node) { if ($node instanceof Proxy && !$node->__isInitialized__) { continue; } $nodeMeta = $em->getClassMetadata(get_class($node)); if (!array_key_exists($config['left'], $nodeMeta->getReflectionProperties())) { continue; } $left = $meta->getReflectionProperty($config['left'])->getValue($node); $right = $meta->getReflectionProperty($config['right'])->getValue($node); $currentRoot = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null; if ($currentRoot === $root && $left >= $first && $right <= $last) { $oid = spl_object_hash($node); $uow = $em->getUnitOfWork(); $meta->getReflectionProperty($config['left'])->setValue($node, $left + $delta); $uow->setOriginalEntityProperty($oid, $config['left'], $left + $delta); $meta->getReflectionProperty($config['right'])->setValue($node, $right + $delta); $uow->setOriginalEntityProperty($oid, $config['right'], $right + $delta); if (isset($config['root'])) { $meta->getReflectionProperty($config['root'])->setValue($node, $destRoot); $uow->setOriginalEntityProperty($oid, $config['root'], $destRoot); } if (isset($config['level'])) { $level = $meta->getReflectionProperty($config['level'])->getValue($node); $meta->getReflectionProperty($config['level'])->setValue($node, $level + $levelDelta); $uow->setOriginalEntityProperty($oid, $config['level'], $level + $levelDelta); } } } } }
/** * @param EventArgs $eventArgs * @return void */ public function loadClassMetadata(EventArgs $eventArgs) { $meta = $eventArgs->getClassMetadata(); $ea = $this->getEventAdapter($eventArgs); if (is_subclass_of($meta->getName(), $ea->getHierarchyClassName(), true)) { $ea->mapHierarchy($meta); } parent::loadClassMetadata($eventArgs); }
/** * Update node and closures * * @param EntityManager $em * @param object $node * @param object $oldParent */ public function updateNode(EntityManager $em, $node, $oldParent) { $wrapped = AbstractWrapper::wrap($node, $em); $meta = $wrapped->getMetadata(); $config = $this->listener->getConfiguration($em, $meta->name); $closureMeta = $em->getClassMetadata($config['closure']); $nodeId = $wrapped->getIdentifier(); $parent = $wrapped->getPropertyValue($config['parent']); $table = $closureMeta->getTableName(); $conn = $em->getConnection(); // ensure integrity if ($parent) { $dql = "SELECT COUNT(c) FROM {$closureMeta->name} c"; $dql .= " WHERE c.ancestor = :node"; $dql .= " AND c.descendant = :parent"; $q = $em->createQuery($dql); $q->setParameters(compact('node', 'parent')); if ($q->getSingleScalarResult()) { throw new \Gedmo\Exception\UnexpectedValueException("Cannot set child as parent to node: {$nodeId}"); } } if ($oldParent) { $subQuery = "SELECT c2.id FROM {$table} c1"; $subQuery .= " JOIN {$table} c2 ON c1.descendant = c2.descendant"; $subQuery .= " WHERE c1.ancestor = :nodeId AND c2.depth > c1.depth"; $ids = $conn->fetchAll($subQuery, compact('nodeId')); if ($ids) { $ids = array_map(function ($el) { return $el['id']; }, $ids); } // using subquery directly, sqlite acts unfriendly $query = "DELETE FROM {$table} WHERE id IN (" . implode(', ', $ids) . ")"; if (!$conn->executeQuery($query)) { throw new RuntimeException('Failed to remove old closures'); } } if ($parent) { $wrappedParent = AbstractWrapper::wrap($parent, $em); $parentId = $wrappedParent->getIdentifier(); $query = "SELECT c1.ancestor, c2.descendant, (c1.depth + c2.depth + 1) AS depth"; $query .= " FROM {$table} c1, {$table} c2"; $query .= " WHERE c1.descendant = :parentId"; $query .= " AND c2.ancestor = :nodeId"; $closures = $conn->fetchAll($query, compact('nodeId', 'parentId')); foreach ($closures as $closure) { if (!$conn->insert($table, $closure)) { throw new RuntimeException('Failed to insert new Closure record'); } } } if (isset($config['level'])) { $this->pendingNodesLevelProcess[$nodeId] = $node; } }
public function testTreeMetadata() { $meta = $this->em->getClassMetadata('Mapping\\Fixture\\Xml\\MaterializedPathTree'); $config = $this->tree->getConfiguration($this->em, $meta->name); $this->assertArrayHasKey('strategy', $config); $this->assertEquals('materializedPath', $config['strategy']); $this->assertArrayHasKey('activate_locking', $config); $this->assertTrue($config['activate_locking']); $this->assertArrayHasKey('locking_timeout', $config); $this->assertEquals(10, $config['locking_timeout']); $this->assertArrayHasKey('level', $config); $this->assertEquals('level', $config['level']); $this->assertArrayHasKey('parent', $config); $this->assertEquals('parent', $config['parent']); $this->assertArrayHasKey('path_source', $config); $this->assertEquals('title', $config['path_source']); $this->assertArrayHasKey('path', $config); $this->assertEquals('path', $config['path']); $this->assertArrayHasKey('lock_time', $config); $this->assertEquals('lockTime', $config['lock_time']); }
public function testYamlMaterializedPathMapping() { $meta = $this->em->getClassMetadata(self::YAML_MATERIALIZED_PATH_CATEGORY); $config = $this->listener->getConfiguration($this->em, $meta->name); $this->assertArrayHasKey('strategy', $config); $this->assertEquals('materializedPath', $config['strategy']); $this->assertArrayHasKey('parent', $config); $this->assertEquals('parent', $config['parent']); $this->assertArrayHasKey('activate_locking', $config); $this->assertTrue($config['activate_locking']); $this->assertArrayHasKey('locking_timeout', $config); $this->assertEquals(3, $config['locking_timeout']); $this->assertArrayHasKey('level', $config); $this->assertEquals('level', $config['level']); $this->assertArrayHasKey('path', $config); $this->assertEquals('path', $config['path']); $this->assertArrayHasKey('path_separator', $config); $this->assertEquals(',', $config['path_separator']); }
/** * {@inheritdoc} */ public function persistAsNextSiblingOf(MenuItemInterface $node, MenuItemInterface $sibling) { $wrapped = new EntityWrapper($node, $this->_em); $meta = $this->getClassMetadata(); $config = $this->treeListener->getConfiguration($this->_em, $meta->name); $wrappedSibling = new EntityWrapper($sibling, $this->_em); $newParent = $wrappedSibling->getPropertyValue($config['parent']); if (null === $newParent && isset($config['root'])) { throw new UnexpectedValueException('Cannot persist sibling for a root node, tree operation is not possible'); } $node->sibling = $sibling; $sibling = $newParent; $wrapped->setPropertyValue($config['parent'], $sibling); $wrapped->setPropertyValue($config['left'], 0); $oid = spl_object_hash($node); $this->treeListener->getStrategy($this->_em, $meta->name)->setNodePosition($oid, 'NextSibling'); $this->_em->persist($node); return $this; }
public function testYamlMaterializedPathMapping() { if (!extension_loaded('apc') || !ini_get('apc.enable_cli')) { $this->markTestSkipped('APC extension is not loaded.'); } $meta = $this->em->getClassMetadata(self::YAML_MATERIALIZED_PATH_CATEGORY); $config = $this->listener->getConfiguration($this->em, $meta->name); $this->assertArrayHasKey('strategy', $config); $this->assertEquals('materializedPath', $config['strategy']); $this->assertArrayHasKey('parent', $config); $this->assertEquals('parent', $config['parent']); $this->assertArrayHasKey('activate_locking', $config); $this->assertTrue($config['activate_locking']); $this->assertArrayHasKey('locking_timeout', $config); $this->assertEquals(3, $config['locking_timeout']); $this->assertArrayHasKey('level', $config); $this->assertEquals('level', $config['level']); $this->assertArrayHasKey('path', $config); $this->assertEquals('path', $config['path']); $this->assertArrayHasKey('path_separator', $config); $this->assertEquals(',', $config['path_separator']); }
/** * @param EventManager $manager * @param EntityManagerInterface $em * @param Reader $reader */ public function addSubscribers(EventManager $manager, EntityManagerInterface $em, Reader $reader) { $subscriber = new TreeListener(); $subscriber->setAnnotationReader($reader); $manager->addEventSubscriber($subscriber); }
/** * * @return EntityManager */ public function getEntityManager() { $cache = new DoctrineCache\ArrayCache(); $annotationReader = new AnnotationReader(); $cachedAnnotationReader = new CachedReader($annotationReader, $cache); // create a driver chain for metadata reading $driverChain = new MappingDriverChain(); // load superclass metadata mapping only, into driver chain // also registers Gedmo annotations.NOTE: you can personalize it Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM($driverChain, $cachedAnnotationReader); // now we want to register our application entities, // for that we need another metadata driver used for Entity namespace $annotationDriver = new AnnotationDriver($cachedAnnotationReader, $this->paths); $driverChain->addDriver($annotationDriver, $this->namespace); // general ORM configuration $isDevMode = $this->env != "production"; $config = DoctrineSetup::createAnnotationMetadataConfiguration($this->paths, $isDevMode); $config->setMetadataCacheImpl($cache); $config->setQueryCacheImpl($cache); $config->setMetadataDriverImpl($driverChain); $config->setProxyDir($this->proxy_path); $config->setProxyNamespace($this->namespace . '\\Proxy'); $config->setAutoGenerateProxyClasses($isDevMode); // Third, create event manager and hook prefered extension listeners $evm = new EventManager(); // gedmo extension listeners // sluggable $sluggableListener = new Gedmo\Sluggable\SluggableListener(); // you should set the used annotation reader to listener, to avoid creating new one for mapping drivers $sluggableListener->setAnnotationReader($cachedAnnotationReader); $evm->addEventSubscriber($sluggableListener); // tree $treeListener = new Gedmo\Tree\TreeListener(); $treeListener->setAnnotationReader($cachedAnnotationReader); $evm->addEventSubscriber($treeListener); // loggable, not used in example $loggableListener = new Gedmo\Loggable\LoggableListener(); $loggableListener->setAnnotationReader($cachedAnnotationReader); $loggableListener->setUsername('unknown'); $evm->addEventSubscriber($loggableListener); // timestampable $timestampableListener = new Gedmo\Timestampable\TimestampableListener(); $timestampableListener->setAnnotationReader($cachedAnnotationReader); $evm->addEventSubscriber($timestampableListener); // blameable $blameableListener = new Gedmo\Blameable\BlameableListener(); $blameableListener->setAnnotationReader($cachedAnnotationReader); $blameableListener->setUserValue('unknown'); // determine from your environment $evm->addEventSubscriber($blameableListener); // translatable - buggy !!! /* $translatableListener = new Gedmo\Translatable\TranslatableListener(); // current translation locale should be set from session or hook later into the listener // most important, before entity manager is flushed $translatableListener->setTranslatableLocale('en'); $translatableListener->setDefaultLocale('en'); $translatableListener->setAnnotationReader($cachedAnnotationReader); $evm->addEventSubscriber($translatableListener); */ // sortable, not used in example $sortableListener = new Gedmo\Sortable\SortableListener(); $sortableListener->setAnnotationReader($cachedAnnotationReader); $evm->addEventSubscriber($sortableListener); // mysql set names UTF-8 if required $evm->addEventSubscriber(new \Doctrine\DBAL\Event\Listeners\MysqlSessionInit()); // Finally, create entity manager return EntityManager::create($this->dbParams, $config, $evm); }
/** * @param ObjectManager $om * @param TreeListener $listener * @param string $class * @return Strategy */ private function getClassStrategy(ObjectManager $om, TreeListener $listener, $class) { if (array_key_exists($class, $this->classStrategies)) { return $this->classStrategies[$class]; } $this->classStrategies[$class] = null; $classParents = array_merge(array($class), class_parents($class)); foreach ($classParents as $parent) { try { $this->classStrategies[$class] = $listener->getStrategy($om, $parent); break; } catch (\Exception $e) { // we don't like to throw exception because there might be a strategy for class parents } } return $this->classStrategies[$class]; }