/** * Checks if a Value Object equal to the given Object exists in the database * * @param AbstractValueObject $object The Value Object * @return mixed The matching uid if an object was found, else FALSE * @throws SqlErrorException */ public function getUidOfAlreadyPersistedValueObject(AbstractValueObject $object) { $dataMap = $this->dataMapper->getDataMap(get_class($object)); $tableName = $dataMap->getTableName(); $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName); if ($this->environmentService->isEnvironmentInFrontendMode()) { $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); } $whereClause = []; // loop over all properties of the object to exactly set the values of each database field $properties = $object->_getProperties(); foreach ($properties as $propertyName => $propertyValue) { // @todo We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method if ($dataMap->isPersistableProperty($propertyName) && $propertyName !== 'uid' && $propertyName !== 'pid' && $propertyName !== 'isClone') { $fieldName = $dataMap->getColumnMap($propertyName)->getColumnName(); if ($propertyValue === null) { $whereClause[] = $queryBuilder->expr()->isNull($fieldName); } else { $whereClause[] = $queryBuilder->expr()->eq($fieldName, $queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($propertyValue))); } } } $queryBuilder->select('uid')->from($tableName)->where(...$whereClause); try { $uid = (int) $queryBuilder->execute()->fetchColumn(0); if ($uid > 0) { return $uid; } else { return false; } } catch (DBALException $e) { throw new SqlErrorException($e->getPrevious()->getMessage(), 1470231748); } }
/** * Parse a Comparison into SQL and parameter arrays. * * @param Qom\ComparisonInterface $comparison The comparison to parse * @param Qom\SourceInterface $source The source * @param array &$sql SQL query parts to add to * @throws \RuntimeException * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\RepositoryException * @return void */ protected function parseComparison(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source, array &$sql) { $parameterIdentifier = $this->normalizeParameterIdentifier($comparison->getParameterIdentifier()); $operator = $comparison->getOperator(); $operand2 = $comparison->getOperand2(); if ($operator === QueryInterface::OPERATOR_IN) { $hasValue = false; foreach ($operand2 as $value) { $value = $this->dataMapper->getPlainValue($value); if ($value !== null) { $parameters[] = $value; $hasValue = true; } } if ($hasValue === false) { $sql['where'][] = '1<>1'; } else { $this->parseDynamicOperand($comparison, $source, $sql); } } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) { if ($operand2 === null) { $sql['where'][] = '1<>1'; } else { if (!$source instanceof Qom\SelectorInterface) { throw new \RuntimeException('Source is not of type "SelectorInterface"', 1395362539); } $className = $source->getNodeTypeName(); $tableName = $this->dataMapper->convertClassNameToTableName($className); $operand1 = $comparison->getOperand1(); $propertyName = $operand1->getPropertyName(); $fullPropertyPath = ''; while (strpos($propertyName, '.') !== false) { $this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath); } $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className); $dataMap = $this->dataMapper->getDataMap($className); $columnMap = $dataMap->getColumnMap($propertyName); $typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null; if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) { $relationTableName = $columnMap->getRelationTableName(); $additionalWhereForMatchFields = $this->getAdditionalMatchFieldsStatement($columnMap, $relationTableName, $relationTableName); $sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=' . $parameterIdentifier . $additionalWhereForMatchFields . ')'; } elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) { $parentKeyFieldName = $columnMap->getParentKeyFieldName(); if (isset($parentKeyFieldName)) { $childTableName = $columnMap->getChildTableName(); $sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=' . $parameterIdentifier . ')'; } else { $sql['where'][] = 'FIND_IN_SET(' . $parameterIdentifier . ', ' . $tableName . '.' . $columnName . ')'; } } else { throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745); } } } else { $this->parseDynamicOperand($comparison, $source, $sql); } }
/** * Persists the given object. * * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be inserted * @return void */ protected function persistObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) { if (isset($this->visitedDuringPersistence[$object])) { return; } $row = array(); $queue = array(); $dataMap = $this->dataMapper->getDataMap(get_class($object)); $properties = $object->_getProperties(); foreach ($properties as $propertyName => $propertyValue) { if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) { continue; } $columnMap = $dataMap->getColumnMap($propertyName); if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) { $cleanProperty = $object->_getCleanProperty($propertyName); // objectstorage needs to be persisted if the object is new, the objectstorge is dirty, meaning it has // been changed after initial build, or a empty objectstorge is present and the cleanstate objectstorage // has childelements, meaning all elements should been removed from the objectstorage if ($object->_isNew() || $propertyValue->_isDirty() || $propertyValue->count() == 0 && $cleanProperty && $cleanProperty->count() > 0) { $this->persistObjectStorage($propertyValue, $object, $propertyName, $row); $propertyValue->_memorizeCleanState(); } foreach ($propertyValue as $containedObject) { if ($containedObject instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) { $queue[] = $containedObject; } } } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface && $object instanceof ObjectMonitoringInterface) { if ($object->_isDirty($propertyName)) { if ($propertyValue->_isNew()) { $this->insertObject($propertyValue, $object, $propertyName); } // Check explicitly for NULL, as getPlainValue would convert this to 'NULL' $row[$columnMap->getColumnName()] = $propertyValue !== NULL ? $this->dataMapper->getPlainValue($propertyValue) : NULL; } $queue[] = $propertyValue; } elseif ($object->_isNew() || $object->_isDirty($propertyName)) { $row[$columnMap->getColumnName()] = $this->dataMapper->getPlainValue($propertyValue, $columnMap); } } if (!empty($row)) { $this->updateObject($object, $row); $object->_memorizeCleanState(); } $this->visitedDuringPersistence[$object] = $object->getUid(); foreach ($queue as $queuedObject) { $this->persistObject($queuedObject); } $this->emitAfterPersistObjectSignal($object); }
/** * Checks if a Value Object equal to the given Object exists in the data base * * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The Value Object * @return mixed The matching uid if an object was found, else FALSE * @todo this is the last monster in this persistence series. refactor! */ public function getUidOfAlreadyPersistedValueObject(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object) { $fields = array(); $parameters = array(); $dataMap = $this->dataMapper->getDataMap(get_class($object)); $properties = $object->_getProperties(); foreach ($properties as $propertyName => $propertyValue) { // @todo We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method if ($dataMap->isPersistableProperty($propertyName) && $propertyName !== 'uid' && $propertyName !== 'pid' && $propertyName !== 'isClone') { if ($propertyValue === NULL) { $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . ' IS NULL'; } else { $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . '=?'; $parameters[] = $this->dataMapper->getPlainValue($propertyValue); } } } $sql = array(); $sql['additionalWhereClause'] = array(); $tableName = $dataMap->getTableName(); $this->addVisibilityConstraintStatement(new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings(), $tableName, $sql); $statement = 'SELECT * FROM ' . $tableName; $statement .= ' WHERE ' . implode(' AND ', $fields); if (!empty($sql['additionalWhereClause'])) { $statement .= ' AND ' . implode(' AND ', $sql['additionalWhereClause']); } $this->replacePlaceholders($statement, $parameters, $tableName); // debug($statement,-2); $res = $this->databaseHandle->sql_query($statement); $this->checkSqlErrors($statement); $row = $this->databaseHandle->sql_fetch_assoc($res); if ($row !== FALSE) { return (int) $row['uid']; } else { return FALSE; } }
/** * Inserts an object in the storage backend * * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be insterted in the storage * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parentobject. * @param string $parentPropertyName * @return void */ protected function insertObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject = null, $parentPropertyName = '') { if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) { $result = $this->getUidOfAlreadyPersistedValueObject($object); if ($result !== false) { $object->_setProperty('uid', (int) $result); return; } } $dataMap = $this->dataMapper->getDataMap(get_class($object)); $row = array(); $properties = $object->_getProperties(); foreach ($properties as $propertyName => $propertyValue) { if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) { continue; } $columnMap = $dataMap->getColumnMap($propertyName); if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) { $row[$columnMap->getColumnName()] = 0; } elseif ($columnMap->getTypeOfRelation() !== ColumnMap::RELATION_NONE) { if ($columnMap->getParentKeyFieldName() === null) { // CSV type relation $row[$columnMap->getColumnName()] = ''; } else { // MM type relation $row[$columnMap->getColumnName()] = 0; } } elseif ($propertyValue !== null) { $row[$columnMap->getColumnName()] = $this->dataMapper->getPlainValue($propertyValue); } } $this->addCommonFieldsToRow($object, $row); if ($dataMap->getLanguageIdColumnName() !== null && $object->_getProperty('_languageUid') === null) { $row[$dataMap->getLanguageIdColumnName()] = 0; $object->_setProperty('_languageUid', 0); } if ($dataMap->getTranslationOriginColumnName() !== null) { $row[$dataMap->getTranslationOriginColumnName()] = 0; } if ($dataMap->getTranslationOriginDiffSourceName() !== null) { $row[$dataMap->getTranslationOriginDiffSourceName()] = ''; } if ($parentObject !== null && $parentPropertyName) { $parentColumnDataMap = $this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName); $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields(); if (is_array($relationTableMatchFields)) { $row = array_merge($relationTableMatchFields, $row); } if ($parentColumnDataMap->getParentKeyFieldName() !== null) { $row[$parentColumnDataMap->getParentKeyFieldName()] = (int) $parentObject->getUid(); } } $uid = $this->storageBackend->addRow($dataMap->getTableName(), $row); $object->_setProperty('uid', (int) $uid); $object->setPid((int) $row['pid']); if ((int) $uid >= 1) { $this->emitAfterInsertObjectSignal($object); } $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') { $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid); } $this->session->registerObject($object, $uid); if ((int) $uid >= 1) { $this->emitEndInsertObjectSignal($object); } }
/** * @test */ public function getPlainValueCallsGetUidOnDomainObjectInterfaceInput() { $dataMapper = new DataMapper(); $input = $this->getMock(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface::class, array(), array(), '', false); $input->expects($this->once())->method('getUid')->will($this->returnValue(23)); $this->assertSame(23, $dataMapper->getPlainValue($input)); }
/** * Parse a DynamicOperand into SQL and parameter arrays. * * @param Qom\ComparisonInterface $comparison * @param Qom\SourceInterface $source The source * @return string * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception */ protected function parseDynamicOperand(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source) { $value = $comparison->getOperand2(); $fieldName = $this->parseOperand($comparison->getOperand1(), $source); $expr = null; $exprBuilder = $this->queryBuilder->expr(); switch ($comparison->getOperator()) { case QueryInterface::OPERATOR_IN: $hasValue = false; $plainValues = []; foreach ($value as $singleValue) { $plainValue = $this->dataMapper->getPlainValue($singleValue); if ($plainValue !== null) { $hasValue = true; $plainValues[] = $plainValue; } } if ($hasValue) { $expr = $exprBuilder->comparison($fieldName, 'IN', '(' . implode(', ', $plainValues) . ')'); } else { $expr = '1<>1'; } break; case QueryInterface::OPERATOR_EQUAL_TO: if ($value === null) { $expr = $fieldName . ' IS NULL'; } else { $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value)); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::EQ, $value); } break; case QueryInterface::OPERATOR_EQUAL_TO_NULL: $expr = $fieldName . ' IS NULL'; break; case QueryInterface::OPERATOR_NOT_EQUAL_TO: if ($value === null) { $expr = $fieldName . ' IS NOT NULL'; } else { $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value)); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::NEQ, $value); } break; case QueryInterface::OPERATOR_NOT_EQUAL_TO_NULL: $expr = $fieldName . ' IS NOT NULL'; break; case QueryInterface::OPERATOR_LESS_THAN: $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value), \PDO::PARAM_INT); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::LT, $value); break; case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO: $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value), \PDO::PARAM_INT); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::LTE, $value); break; case QueryInterface::OPERATOR_GREATER_THAN: $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value), \PDO::PARAM_INT); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::GT, $value); break; case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO: $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value), \PDO::PARAM_INT); $expr = $exprBuilder->comparison($fieldName, $exprBuilder::GTE, $value); break; case QueryInterface::OPERATOR_LIKE: $value = $this->queryBuilder->createNamedParameter($this->dataMapper->getPlainValue($value)); $expr = $exprBuilder->comparison($fieldName, 'LIKE', $value); break; default: throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Unsupported operator encountered.', 1242816073); } return $expr; }