/** * Create a recursive copy of this node below $referenceNode with $nodeName. * * $detachedCopy only has an influence if we are copying from one dimension to the other, possibly creating a new * node variant: * * - If $detachedCopy is TRUE, the whole (recursive) copy is done without connecting original and copied node, * so NOT CREATING a new node variant. * - If $detachedCopy is FALSE, and the node does not yet have a variant in the target dimension, we are CREATING * a new node variant. * * As a caller of this method, $detachedCopy should be TRUE if $this->getNodeType()->isAggregate() is TRUE, and FALSE * otherwise. * * @param NodeInterface $referenceNode * @param boolean $detachedCopy * @param string $nodeName * @return NodeInterface */ protected function createRecursiveCopy(NodeInterface $referenceNode, $nodeName, $detachedCopy) { $identifier = null; $referenceNodeDimensions = $referenceNode->getDimensions(); $referenceNodeDimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($referenceNodeDimensions); $thisDimensions = $this->getDimensions(); $thisNodeDimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($thisDimensions); if ($detachedCopy === false && $referenceNodeDimensionsHash !== $thisNodeDimensionsHash && $referenceNode->getContext()->getNodeByIdentifier($this->getIdentifier()) === null) { // If the target dimensions are different than this one, and there is no node shadowing this one in the target dimension yet, we use the same // node identifier, effectively creating a new node variant. $identifier = $this->getIdentifier(); } $copiedNode = $referenceNode->createSingleNode($nodeName, null, $identifier); $copiedNode->similarize($this, true); /** @var $childNode Node */ foreach ($this->getChildNodes() as $childNode) { // Prevent recursive copy when copying into itself if ($childNode->getIdentifier() !== $copiedNode->getIdentifier()) { $childNode->copyIntoInternal($copiedNode, $childNode->getName(), $detachedCopy); } } return $copiedNode; }
/** * Build a cached array of dimension values and a hash to search for it. * * @return void */ protected function buildDimensionValues() { $dimensionValues = array(); foreach ($this->dimensions as $dimension) { /** @var NodeDimension $dimension */ $dimensionValues[$dimension->getName()][] = $dimension->getValue(); } $this->dimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($dimensionValues); $this->dimensionValues = $dimensionValues; }
/** * Saves the given array as a node data entity without using the ORM. * * If the node data already exists (same dimensions, same identifier, same workspace) * it is replaced. * * @param array $nodeData node data to save as an associative array ( $column_name => $value ) * @throws ImportException * @return void */ protected function persistNodeData($nodeData) { if ($nodeData['workspace'] !== 'live') { throw new ImportException('Saving NodeData with workspace != "live" using direct SQL not supported yet. Workspace is "' . $nodeData['workspace'] . '".'); } if ($nodeData['path'] === '/') { return; } // cleanup old data /** @var \Doctrine\DBAL\Connection $connection */ $connection = $this->entityManager->getConnection(); // prepare node dimensions $dimensionValues = $nodeData['dimensionValues']; $dimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($dimensionValues); $jsonPropertiesDataTypeHandler = JsonArrayType::getType(JsonArrayType::FLOW_JSON_ARRAY); // post-process node data $nodeData['dimensionsHash'] = $dimensionsHash; $nodeData['dimensionValues'] = $jsonPropertiesDataTypeHandler->convertToDatabaseValue($dimensionValues, $connection->getDatabasePlatform()); $nodeData['properties'] = $jsonPropertiesDataTypeHandler->convertToDatabaseValue($nodeData['properties'], $connection->getDatabasePlatform()); $nodeData['accessRoles'] = $jsonPropertiesDataTypeHandler->convertToDatabaseValue($nodeData['accessRoles'], $connection->getDatabasePlatform()); $connection->executeQuery('DELETE FROM typo3_typo3cr_domain_model_nodedimension' . ' WHERE nodedata IN (' . ' SELECT persistence_object_identifier FROM typo3_typo3cr_domain_model_nodedata' . ' WHERE identifier = :identifier' . ' AND workspace = :workspace' . ' AND dimensionshash = :dimensionsHash' . ' )', array('identifier' => $nodeData['identifier'], 'workspace' => $nodeData['workspace'], 'dimensionsHash' => $nodeData['dimensionsHash'])); /** @var \Doctrine\ORM\QueryBuilder $queryBuilder */ $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->delete()->from('TYPO3\\TYPO3CR\\Domain\\Model\\NodeData', 'n')->where('n.identifier = :identifier')->andWhere('n.dimensionsHash = :dimensionsHash')->andWhere('n.workspace = :workspace')->setParameter('identifier', $nodeData['identifier'])->setParameter('workspace', $nodeData['workspace'])->setParameter('dimensionsHash', $nodeData['dimensionsHash']); $queryBuilder->getQuery()->execute(); // insert new data // we need to use executeUpdate to execute the INSERT -- else the data types are not taken into account. // That's why we build a DQL INSERT statement which is then executed. $queryParts = array(); $queryArguments = array(); $queryTypes = array(); foreach ($this->nodeDataPropertyNames as $propertyName => $propertyConfig) { if (isset($nodeData[$propertyName])) { $queryParts[$propertyName] = ':' . $propertyName; $queryArguments[$propertyName] = $nodeData[$propertyName]; if (isset($propertyConfig['columnType'])) { $queryTypes[$propertyName] = $propertyConfig['columnType']; } } } $connection->executeUpdate('INSERT INTO typo3_typo3cr_domain_model_nodedata (' . implode(', ', array_keys($queryParts)) . ') VALUES (' . implode(', ', $queryParts) . ')', $queryArguments, $queryTypes); foreach ($dimensionValues as $dimension => $values) { foreach ($values as $value) { $nodeDimension = array('persistence_object_identifier' => Algorithms::generateUUID(), 'nodedata' => $nodeData['Persistence_Object_Identifier'], 'name' => $dimension, 'value' => $value); $connection->insert('typo3_typo3cr_domain_model_nodedimension', $nodeDimension); } } }