public function getLast($id){ $meta = $this->getClassMetadata(); $config = $this->listener->getConfiguration($this->_em, $meta->name); $qb = $this->_em->createQueryBuilder(); $qb->select('st') ->from($meta->name, 'st') ->orderBy('st.'.$config['status_field'], 'DESC') ->setMaxResults(1); $q = $qb->getQuery(); return $q->getSingleResult(); }
/** * Shift range of right and left values on tree * depending on tree level diference also * * @param EntityManager $em * @param string $class * @param integer $first * @param integer $last * @param integer $delta * @param integer $rootId * @param integer $destRootId * @param integer $levelDelta * @return void */ public function shiftRangeRL(EntityManager $em, $class, $first, $last, $delta, $rootId = null, $destRootId = 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); $dql = "UPDATE {$meta->name} node"; $dql .= " SET node.{$config['left']} = node.{$config['left']} {$sign} {$absDelta}"; $dql .= ", node.{$config['right']} = node.{$config['right']} {$sign} {$absDelta}"; if (isset($config['root'])) { $dql .= ", node.{$config['root']} = {$destRootId}"; } if (isset($config['level'])) { $dql .= ", node.{$config['level']} = node.{$config['level']} {$levelSign} {$absLevelDelta}"; } $dql .= " WHERE node.{$config['left']} >= {$first}"; $dql .= " AND node.{$config['right']} <= {$last}"; if (isset($config['root'])) { $dql .= " AND node.{$config['root']} = {$rootId}"; } $q = $em->createQuery($dql); $q->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; } $left = $meta->getReflectionProperty($config['left'])->getValue($node); $right = $meta->getReflectionProperty($config['right'])->getValue($node); $root = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null; if ($root === $rootId && $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, $destRootId); $uow->setOriginalEntityProperty($oid, $config['root'], $destRootId); } 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); } } } } }
/** * 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; } }
/** * 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 $rootId * @param integer|string $destRootId * @param integer $levelDelta * @return void */ public function shiftRangeRL(EntityManager $em, $class, $first, $last, $delta, $rootId = null, $destRootId = 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'], is_string($destRootId) ? $qb->expr()->literal($destRootId) : $destRootId); $qb->andWhere($rootId === null ? $qb->expr()->isNull('node.' . $config['root']) : $qb->expr()->eq('node.' . $config['root'], is_string($rootId) ? $qb->expr()->literal($rootId) : $rootId)); } 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; } $left = $meta->getReflectionProperty($config['left'])->getValue($node); $right = $meta->getReflectionProperty($config['right'])->getValue($node); $root = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null; if ($root === $rootId && $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, $destRootId); $uow->setOriginalEntityProperty($oid, $config['root'], $destRootId); } 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); } } } } }
/** * Process pre-locking actions * * @param ObjectManager $om * @param AdapterInterface $ea * @param object $node * @param string $action * * @return void */ public function processPostEventsActions(ObjectManager $om, AdapterInterface $ea, $node, $action) { $meta = $om->getClassMetadata(get_class($node)); $config = $this->listener->getConfiguration($om, $meta->name); if ($config['activate_locking']) { switch ($action) { case self::ACTION_INSERT: unset($this->pendingObjectsToInsert[spl_object_hash($node)]); break; case self::ACTION_UPDATE: unset($this->pendingObjectsToUpdate[spl_object_hash($node)]); break; case self::ACTION_REMOVE: unset($this->pendingObjectsToRemove[spl_object_hash($node)]); break; default: throw new \InvalidArgumentException(sprintf('"%s" is not a valid action.', $action)); } if (empty($this->pendingObjectsToInsert) && empty($this->pendingObjectsToUpdate) && empty($this->pendingObjectsToRemove)) { $this->releaseTreeLocks($om, $ea); } } }