/** * Builds a data map by adding column maps for all the configured columns in the $TCA. * It also resolves the type of values the column is holding and the typo of relation the column * represents. * * @param string $className The class name you want to fetch the Data Map for * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidClassException * @return \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap The data map */ protected function buildDataMapInternal($className) { if (!class_exists($className)) { throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidClassException('Could not find class definition for name "' . $className . '". This could be caused by a mis-spelling of the class name in the class definition.', 1476045117); } $recordType = null; $subclasses = []; $tableName = $this->resolveTableName($className); $columnMapping = []; $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); $classSettings = $frameworkConfiguration['persistence']['classes'][$className]; if ($classSettings !== null) { if (isset($classSettings['subclasses']) && is_array($classSettings['subclasses'])) { $subclasses = $this->resolveSubclassesRecursive($frameworkConfiguration['persistence']['classes'], $classSettings['subclasses']); } if (isset($classSettings['mapping']['recordType']) && $classSettings['mapping']['recordType'] !== '') { $recordType = $classSettings['mapping']['recordType']; } if (isset($classSettings['mapping']['tableName']) && $classSettings['mapping']['tableName'] !== '') { $tableName = $classSettings['mapping']['tableName']; } $classHierarchy = array_merge([$className], class_parents($className)); foreach ($classHierarchy as $currentClassName) { if (in_array($currentClassName, [\TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class])) { break; } $currentClassSettings = $frameworkConfiguration['persistence']['classes'][$currentClassName]; if ($currentClassSettings !== null) { if (isset($currentClassSettings['mapping']['columns']) && is_array($currentClassSettings['mapping']['columns'])) { \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($columnMapping, $currentClassSettings['mapping']['columns'], true, false); } } } } /** @var $dataMap \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap */ $dataMap = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap::class, $className, $tableName, $recordType, $subclasses); $dataMap = $this->addMetaDataColumnNames($dataMap, $tableName); // $classPropertyNames = $this->reflectionService->getClassPropertyNames($className); $tcaColumnsDefinition = $this->getColumnsDefinition($tableName); \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($tcaColumnsDefinition, $columnMapping); // @todo Is this is too powerful? foreach ($tcaColumnsDefinition as $columnName => $columnDefinition) { if (isset($columnDefinition['mapOnProperty'])) { $propertyName = $columnDefinition['mapOnProperty']; } else { $propertyName = \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToLowerCamelCase($columnName); } // if (in_array($propertyName, $classPropertyNames)) { // @todo Enable check for property existence $columnMap = $this->createColumnMap($columnName, $propertyName); $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName); $columnMap = $this->setType($columnMap, $columnDefinition['config']); $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $propertyMetaData); $columnMap = $this->setFieldEvaluations($columnMap, $columnDefinition['config']); $dataMap->addColumnMap($columnMap); } return $dataMap; }
/** * Returns the type of a child object. * * @param string $parentClassName The class name of the object this proxy is part of * @param string $propertyName The name of the proxied property in it's parent * @throws UnexpectedTypeException * @return string The class name of the child object */ public function getType($parentClassName, $propertyName) { $propertyMetaData = $this->reflectionService->getClassSchema($parentClassName)->getProperty($propertyName); if (!empty($propertyMetaData['elementType'])) { $type = $propertyMetaData['elementType']; } elseif (!empty($propertyMetaData['type'])) { $type = $propertyMetaData['type']; } else { throw new UnexpectedTypeException('Could not determine the child object type.', 1251315967); } return $type; }
/** * Remove related objects * * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to scanned for related objects * @return void */ protected function removeRelatedObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) { $className = get_class($object); $dataMap = $this->dataMapper->getDataMap($className); $classSchema = $this->reflectionService->getClassSchema($className); $properties = $object->_getProperties(); foreach ($properties as $propertyName => $propertyValue) { $columnMap = $dataMap->getColumnMap($propertyName); $propertyMetaData = $classSchema->getProperty($propertyName); if ($propertyMetaData['cascade'] === 'remove') { if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) { foreach ($propertyValue as $containedObject) { $this->removeEntity($containedObject); } } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) { $this->removeEntity($propertyValue); } } } }
/** * Loads the model with the given identifier * * @param mixed $identifier The identifier * @param string $path The path * @return mixed|null|object */ protected function getModelWithIdentityForPath($identifier, $path) { $repository = $this->getRepositoryForPath($path); // Tries to fetch the object by UID $object = $repository->findByUid($identifier); if ($object) { return $object; } // Fetch the first identity property and search the repository for it $type = NULL; $property = NULL; try { $classSchema = $this->reflectionService->getClassSchema($this->getModelClassForPath($path)); $identityProperties = $classSchema->getIdentityProperties(); $type = reset($identityProperties); $property = key($identityProperties); } catch (\Exception $exception) { } switch ($type) { case 'string': $typeMatching = is_string($identifier); break; case 'boolean': $typeMatching = is_bool($identifier); break; case 'integer': $typeMatching = is_int($identifier); break; case 'float': $typeMatching = is_float($identifier); break; case 'array': default: $typeMatching = FALSE; } if ($typeMatching) { $findMethod = 'findOneBy' . ucfirst($property); return call_user_func(array($repository, $findMethod), $identifier); } return NULL; }
/** * Sets the data type of this argument's value * * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this * @api */ public function setDataType($dataType) { $this->dataType = $dataType; $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($dataType); return $this; }
/** * Maps the given properties to the target object WITHOUT VALIDATING THE RESULT. * If the properties could be set, this method returns TRUE, otherwise FALSE. * Returning TRUE does not mean that the target object is valid and secure! * * Only use this method if you're sure that you don't need validation! * * @param array $propertyNames Names of the properties to map. * @param mixed $source Source containing the properties to map to the target object. Must either be an array, ArrayObject or any other object. * @param object|array|string $target The target object * @param array $optionalPropertyNames Names of optional properties. If a property is specified here and it doesn't exist in the source, no error is issued. * @throws Exception\InvalidSourceException * @throws Exception\InvalidTargetException * @return boolean TRUE if the properties could be mapped, otherwise FALSE, or the object in case it could be resolved * @see mapAndValidate() * @api */ public function map(array $propertyNames, $source, $target, $optionalPropertyNames = array()) { if (!is_object($source) && !is_array($source)) { throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException('The source object must be a valid object or array, ' . gettype($target) . ' given.', 1187807099); } if (is_string($target) && strpbrk($target, '_\\') !== FALSE) { return $this->transformToObject($source, $target, '--none--'); } if (!is_object($target) && !is_array($target)) { throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('The target object must be a valid object or array, ' . gettype($target) . ' given.', 1187807100); } $this->mappingResults = new \TYPO3\CMS\Extbase\Property\MappingResults(); if (is_object($target)) { $targetClassSchema = $this->reflectionService->getClassSchema(get_class($target)); } else { $targetClassSchema = NULL; } foreach ($propertyNames as $propertyName) { $propertyValue = NULL; if (is_array($source) || $source instanceof \ArrayAccess) { if (isset($source[$propertyName])) { $propertyValue = $source[$propertyName]; } } else { $propertyValue = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getProperty($source, $propertyName); } if ($propertyValue === NULL && !in_array($propertyName, $optionalPropertyNames)) { $this->mappingResults->addError(new \TYPO3\CMS\Extbase\Error\Error("Required property '{$propertyName}' does not exist.", 1236785359), $propertyName); } else { if ($targetClassSchema !== NULL && $targetClassSchema->hasProperty($propertyName)) { $propertyMetaData = $targetClassSchema->getProperty($propertyName); if (in_array($propertyMetaData['type'], array('array', 'ArrayObject', 'TYPO3\\CMS\\Extbase\\Persistence\\ObjectStorage', 'Tx_Extbase_Persistence_ObjectStorage'), TRUE) && (strpbrk($propertyMetaData['elementType'], '_\\') !== FALSE || $propertyValue === '')) { $objects = array(); if (is_array($propertyValue)) { foreach ($propertyValue as $value) { $transformedObject = $this->transformToObject($value, $propertyMetaData['elementType'], $propertyName); if ($transformedObject !== NULL) { $objects[] = $transformedObject; } } } // make sure we hand out what is expected if ($propertyMetaData['type'] === 'ArrayObject') { $propertyValue = new \ArrayObject($objects); } elseif (in_array($propertyMetaData['type'], array('TYPO3\\CMS\\Extbase\\Persistence\\ObjectStorage', 'Tx_Extbase_Persistence_ObjectStorage'), TRUE)) { $propertyValue = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage(); foreach ($objects as $object) { $propertyValue->attach($object); } } else { $propertyValue = $objects; } } elseif ($propertyMetaData['type'] === 'DateTime' || strpbrk($propertyMetaData['type'], '_\\') !== FALSE) { $propertyValue = $this->transformToObject($propertyValue, $propertyMetaData['type'], $propertyName); if ($propertyValue === NULL) { continue; } } } elseif ($targetClassSchema !== NULL && is_subclass_of($target, 'TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity') && is_subclass_of($target, 'TYPO3\\CMS\\Extbase\\DomainObject\\AbstractValueObject')) { $this->mappingResults->addError(new \TYPO3\CMS\Extbase\Error\Error("Property '{$propertyName}' does not exist in target class schema.", 1251813614), $propertyName); } if (is_array($target)) { $target[$propertyName] = $propertyValue; } elseif (\TYPO3\CMS\Extbase\Reflection\ObjectAccess::setProperty($target, $propertyName, $propertyValue) === FALSE) { $this->mappingResults->addError(new \TYPO3\CMS\Extbase\Error\Error("Property '{$propertyName}' could not be set.", 1236783102), $propertyName); } } } return !$this->mappingResults->hasErrors(); }