/** * @param string $className * @return array */ public function findModelProperties($className) { $modelDefinition = array(); foreach ($this->reflectionService->getClassPropertyNames($className) as $propertyName) { if (is_array($this->ignoredProperties) && in_array($propertyName, $this->ignoredProperties)) { continue; } $propertyType = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var'); $type = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType[0]); // if (class_exists($type['type']) && !isset($classNames[$type['type']])) { // $this->readClass($type['type'], $classNames); // } // if (class_exists($type['elementType']) && !isset($classNames[$type['elementType']])) { // if ($this->reflectionService->isClassAbstract($type['elementType'])) { // $implementations = $this->reflectionService->getAllSubClassNamesForClass($type['elementType']); // foreach ($implementations as $implementationClassName) { // if (isset($classNames[$implementationClassName])) { // continue; // } // $this->readClass($implementationClassName, $classNames); // } // } else { // $this->readClass($type['elementType'], $classNames); // } // } // TODO: Add lookup for relations and add them to the modelImplementations $modelDefinition[$propertyName] = array('type' => \TYPO3\Flow\Utility\TypeHandling::isCollectionType($type['type']) ? $type['elementType'] : $type['type']); } return $modelDefinition; }
/** * Convert raw property values to the correct type according to a node type configuration * * @param NodeType $nodeType * @param string $propertyName * @param string $rawValue * @param Context $context * @return mixed */ public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context) { $propertyType = $nodeType->getPropertyType($propertyName); switch ($propertyType) { case 'string': return $rawValue; case 'reference': return $this->convertReference($rawValue, $context); case 'references': return $this->convertReferences($rawValue, $context); case 'DateTime': return $this->convertDateTime($rawValue); case 'integer': return $this->convertInteger($rawValue); case 'boolean': return $this->convertBoolean($rawValue); case 'array': return $this->convertArray($rawValue); default: $innerType = $propertyType; if ($propertyType !== null) { try { $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { } } if (is_string($rawValue) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') { return $this->propertyMapper->convert(json_decode($rawValue, true), $propertyType, $configuration); } } }
/** * Convert raw property values to the correct type according to a node type configuration * * @param NodeType $nodeType * @param string $propertyName * @param string $rawValue * @param Context $context * @return mixed */ public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context) { $propertyType = $nodeType->getPropertyType($propertyName); switch ($propertyType) { case 'string': return $rawValue; case 'reference': return $this->convertReference($rawValue, $context); case 'references': return $this->convertReferences($rawValue, $context); case 'DateTime': return $this->convertDateTime($rawValue); case 'integer': return $this->convertInteger($rawValue); case 'boolean': return $this->convertBoolean($rawValue); case 'array': return $this->convertArray($rawValue); default: $innerType = $propertyType; if ($propertyType !== null) { try { $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { } } if ((is_string($rawValue) || is_array($rawValue)) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') { $propertyMappingConfiguration = new MvcPropertyMappingConfiguration(); $propertyMappingConfiguration->allowOverrideTargetType(); $propertyMappingConfiguration->allowAllProperties(); $propertyMappingConfiguration->skipUnknownProperties(); $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true); $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true); return $this->propertyMapper->convert($rawValue, $propertyType, $propertyMappingConfiguration); } else { return $rawValue; } } }
/** * @param ClassSchema $classSchema * @param string $propertyName * @return boolean * @throws InvalidPropertyTypeException * @throws \InvalidArgumentException */ protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classSchema, $propertyName) { $skipArtificialIdentity = false; $className = $classSchema->getClassName(); if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Transient::class)) { return false; } if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Inject::class)) { return false; } if (!$this->isPropertyTaggedWith($className, $propertyName, 'var')) { return false; } $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var'); if (count($varTagValues) > 1) { throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366); } $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t"); try { TypeHandling::parseType($declaredType); } catch (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475); } if ($this->isPropertyAnnotatedWith($className, $propertyName, ORM\Id::class)) { $skipArtificialIdentity = true; } $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, Flow\Lazy::class), $this->isPropertyAnnotatedWith($className, $propertyName, Flow\Transient::class)); if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Identity::class)) { $classSchema->markAsIdentityProperty($propertyName); } return $skipArtificialIdentity; }
private function buildNodeProperty(NodeInterface $node, $propertyName, $dataType) { if (substr($propertyName, 0, 1) === '_') { $propertyValue = ObjectAccess::getProperty($node, substr($propertyName, 1)); } else { $propertyValue = $node->getProperty($propertyName); } // Enforce an integer value for integer properties as otherwise javascript will give NaN and VIE converts it to an array containing 16 times 'NaN' if ($dataType === 'integer') { $propertyValue = (int) $propertyValue; } // Serialize boolean values to String if ($dataType === 'boolean') { return $propertyValue ? 'true' : 'false'; } // Serialize array values to String if ($dataType === 'array') { return $propertyValue; } // Serialize date values to String if ($dataType === 'DateTime') { if (!$propertyValue instanceof \DateTimeInterface) { return ''; } $value = clone $propertyValue; return $value->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::W3C); } // Serialize node references to node identifiers if ($dataType === 'references') { $nodeIdentifiers = array(); if (is_array($propertyValue)) { /** @var $subNode NodeInterface */ foreach ($propertyValue as $subNode) { $nodeIdentifiers[] = $subNode->getIdentifier(); } } return $nodeIdentifiers; } // Serialize node reference to node identifier if ($dataType === 'reference') { if ($propertyValue instanceof NodeInterface) { return $propertyValue->getIdentifier(); } else { return ''; } } if ($propertyValue instanceof \TYPO3\Media\Domain\Model\ImageInterface) { $propertyMappingConfiguration = new \TYPO3\Flow\Property\PropertyMappingConfiguration(); return $this->entityToIdentityConverter->convertFrom($propertyValue, 'array', array(), $propertyMappingConfiguration); } // Serialize an Asset to JSON (the NodeConverter expects JSON for object type properties) if ($dataType === ltrim('TYPO3\\Media\\Domain\\Model\\Asset', '\\') && $propertyValue !== null) { if ($propertyValue instanceof \TYPO3\Media\Domain\Model\Asset) { return $this->persistenceManager->getIdentifierByObject($propertyValue); } } // Serialize an array of Assets to JSON if (is_array($propertyValue)) { $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($dataType); if ($parsedType['elementType'] === ltrim('TYPO3\\Media\\Domain\\Model\\Asset', '\\')) { $convertedValues = array(); foreach ($propertyValue as $singlePropertyValue) { if ($singlePropertyValue instanceof \TYPO3\Media\Domain\Model\Asset) { $convertedValues[] = $this->persistenceManager->getIdentifierByObject($singlePropertyValue); } } return $convertedValues; } } return $propertyValue === null ? '' : $propertyValue; }
/** * Iterates through the given $properties setting them on the specified $node using the appropriate TypeConverters. * * @param object $nodeLike * @param NodeType $nodeType * @param array $properties * @param TYPO3CRContext $context * @param PropertyMappingConfigurationInterface $configuration * @return void * @throws TypeConverterException */ protected function setNodeProperties($nodeLike, NodeType $nodeType, array $properties, TYPO3CRContext $context, PropertyMappingConfigurationInterface $configuration = null) { $nodeTypeProperties = $nodeType->getProperties(); unset($properties['_lastPublicationDateTime']); foreach ($properties as $nodePropertyName => $nodePropertyValue) { if (substr($nodePropertyName, 0, 2) === '__') { continue; } $nodePropertyType = isset($nodeTypeProperties[$nodePropertyName]['type']) ? $nodeTypeProperties[$nodePropertyName]['type'] : null; switch ($nodePropertyType) { case 'reference': $nodePropertyValue = $context->getNodeByIdentifier($nodePropertyValue); break; case 'references': $nodeIdentifiers = json_decode($nodePropertyValue); $nodePropertyValue = array(); if (is_array($nodeIdentifiers)) { foreach ($nodeIdentifiers as $nodeIdentifier) { $referencedNode = $context->getNodeByIdentifier($nodeIdentifier); if ($referencedNode !== null) { $nodePropertyValue[] = $referencedNode; } } } elseif ($nodeIdentifiers !== null) { throw new TypeConverterException(sprintf('node type "%s" expects an array of identifiers for its property "%s"', $nodeType->getName(), $nodePropertyName), 1383587419); } break; case 'DateTime': if ($nodePropertyValue !== '' && ($nodePropertyValue = \DateTime::createFromFormat(\DateTime::W3C, $nodePropertyValue)) !== false) { $nodePropertyValue->setTimezone(new \DateTimeZone(date_default_timezone_get())); } else { $nodePropertyValue = null; } break; case 'integer': $nodePropertyValue = intval($nodePropertyValue); break; case 'boolean': if (is_string($nodePropertyValue)) { $nodePropertyValue = $nodePropertyValue === 'true' ? true : false; } break; case 'array': $nodePropertyValue = json_decode($nodePropertyValue, true); break; } if (substr($nodePropertyName, 0, 1) === '_') { $nodePropertyName = substr($nodePropertyName, 1); ObjectAccess::setProperty($nodeLike, $nodePropertyName, $nodePropertyValue); continue; } if (!isset($nodeTypeProperties[$nodePropertyName])) { if ($configuration !== null && $configuration->shouldSkipUnknownProperties()) { continue; } else { throw new TypeConverterException(sprintf('Node type "%s" does not have a property "%s" according to the schema', $nodeType->getName(), $nodePropertyName), 1359552744); } } $innerType = $nodePropertyType; if ($nodePropertyType !== null) { try { $parsedType = TypeHandling::parseType($nodePropertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (InvalidTypeException $exception) { } } if (is_string($nodePropertyValue) && $this->objectManager->isRegistered($innerType) && $nodePropertyValue !== '') { $nodePropertyValue = $this->propertyMapper->convert(json_decode($nodePropertyValue, true), $nodePropertyType, $configuration); } $nodeLike->setProperty($nodePropertyName, $nodePropertyValue); } }
/** * The type of a property is determined by the reflection service. * * @param string $targetType * @param string $propertyName * @param \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration * @return string * @throws \TYPO3\Flow\Property\Exception\InvalidTargetException */ public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration) { $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', self::CONFIGURATION_TARGET_TYPE); if ($configuredTargetType !== NULL) { return $configuredTargetType; } $varAnnotations = $this->reflectionService->getPropertyTagValues($targetType, $propertyName, 'var'); if ($varAnnotations === array()) { // No @var annotation found for property, use string as default // TODO: Read this from some kind of configuration return 'string'; } $propertyInformation = \TYPO3\Flow\Utility\TypeHandling::parseType($varAnnotations[0]); return $propertyInformation['type'] . ($propertyInformation['elementType'] !== NULL ? '<' . $propertyInformation['elementType'] . '>' : ''); }
/** * Builds a base validator conjunction for the given data type. * * The base validation rules are those which were declared directly in a class (typically * a model) through some validate annotations on properties. * * If a property holds a class for which a base validator exists, that property will be * checked as well, regardless of a validate annotation * * Additionally, if a custom validator was defined for the class in question, it will be added * to the end of the conjunction. A custom validator is found if it follows the naming convention * "Replace '\Model\' by '\Validator\' and append 'Validator'". * * Example: $targetClassName is TYPO3\Foo\Domain\Model\Quux, then the validator will be found if it has the * name TYPO3\Foo\Domain\Validator\QuuxValidator * * @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups * @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name. * @param array $validationGroups The validation groups to build the validator for * @return void * @throws \TYPO3\Flow\Validation\Exception\NoSuchValidatorException * @throws \InvalidArgumentException */ protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups) { $conjunctionValidator = new ConjunctionValidator(); $this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator; if (!TypeHandling::isSimpleType($targetClassName) && class_exists($targetClassName)) { // Model based validator $objectValidator = new GenericObjectValidator(array()); foreach ($this->reflectionService->getClassPropertyNames($targetClassName) as $classPropertyName) { $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($targetClassName, $classPropertyName); if (!isset($classPropertyTagsValues['var'])) { throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104); } try { $parsedType = TypeHandling::parseType(trim(implode('', $classPropertyTagsValues['var']), ' \\')); } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf(' @var annotation of ' . $exception->getMessage(), 'class "' . $targetClassName . '", property "' . $classPropertyName . '"'), 1315564744, $exception); } if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, \TYPO3\Flow\Annotations\IgnoreValidation::class)) { continue; } $propertyTargetClassName = $parsedType['type']; if (TypeHandling::isCollectionType($propertyTargetClassName) === TRUE) { $collectionValidator = $this->createValidator(\TYPO3\Flow\Validation\Validator\CollectionValidator::class, array('elementType' => $parsedType['elementType'], 'validationGroups' => $validationGroups)); $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator); } elseif (!TypeHandling::isSimpleType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_PROTOTYPE) { $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName, $validationGroups); if (count($validatorForProperty) > 0) { $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty); } } $validateAnnotations = $this->reflectionService->getPropertyAnnotations($targetClassName, $classPropertyName, \TYPO3\Flow\Annotations\Validate::class); foreach ($validateAnnotations as $validateAnnotation) { if (count(array_intersect($validateAnnotation->validationGroups, $validationGroups)) === 0) { // In this case, the validation groups for the property do not match current validation context continue; } $newValidator = $this->createValidator($validateAnnotation->type, $validateAnnotation->options); if ($newValidator === NULL) { throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validateAnnotation->type . '".', 1241098027); } $objectValidator->addPropertyValidator($classPropertyName, $newValidator); } } if (count($objectValidator->getPropertyValidators()) > 0) { $conjunctionValidator->addValidator($objectValidator); } } $this->addCustomValidators($targetClassName, $conjunctionValidator); }
/** * The type of a property is determined by the reflection service. * * @param string $targetType * @param string $propertyName * @param PropertyMappingConfigurationInterface $configuration * @return string * @throws InvalidTargetException */ public function getTypeOfChildProperty($targetType, $propertyName, PropertyMappingConfigurationInterface $configuration) { $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE); if ($configuredTargetType !== null) { return $configuredTargetType; } $methodParameters = $this->reflectionService->getMethodParameters($targetType, '__construct'); if (isset($methodParameters[$propertyName]) && isset($methodParameters[$propertyName]['type'])) { return $methodParameters[$propertyName]['type']; } elseif ($this->reflectionService->hasMethod($targetType, ObjectAccess::buildSetterMethodName($propertyName))) { $methodParameters = $this->reflectionService->getMethodParameters($targetType, ObjectAccess::buildSetterMethodName($propertyName)); $methodParameter = current($methodParameters); if (!isset($methodParameter['type'])) { throw new InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $targetType . '".', 1303379158); } else { return $methodParameter['type']; } } else { $targetPropertyNames = $this->reflectionService->getClassPropertyNames($targetType); if (in_array($propertyName, $targetPropertyNames)) { $varTagValues = $this->reflectionService->getPropertyTagValues($targetType, $propertyName, 'var'); if (count($varTagValues) > 0) { // This ensures that FQCNs are returned without leading backslashes. Otherwise, something like @var \DateTime // would not find a property mapper. It is needed because the ObjectConverter doesn't use class schemata, // but reads the annotations directly. $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t"); try { $parsedType = TypeHandling::parseType($declaredType); } catch (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $targetType . '" for property "' . $propertyName . '"'), 1467699674); } return $parsedType['type'] . ($parsedType['elementType'] !== null ? '<' . $parsedType['elementType'] . '>' : ''); } else { throw new InvalidTargetException(sprintf('Public property "%s" had no proper type annotation (i.e. "@var") in target object of type "%s".', $propertyName, $targetType), 1406821818); } } } throw new InvalidTargetException(sprintf('Property "%s" has neither a setter or constructor argument, nor is public, in target object of type "%s".', $propertyName, $targetType), 1303379126); }
/** * Adds (defines) a specific property and its type. * * @param string $name Name of the property * @param string $type Type of the property * @param boolean $lazy Whether the property should be lazy-loaded when reconstituting * @param boolean $transient Whether the property should not be considered for persistence * @return void * @throws \InvalidArgumentException */ public function addProperty($name, $type, $lazy = false, $transient = false) { try { $type = TypeHandling::parseType($type); } catch (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $name . '"'), 1315564474); } $this->properties[$name] = ['type' => $type['type'], 'elementType' => $type['elementType'], 'lazy' => $lazy, 'transient' => $transient]; }
/** * Adds properties of the class at hand to the class schema. * * Only non-transient properties annotated with a var annotation will be added. * Invalid annotations will cause an exception to be thrown. Properties pointing * to existing classes will only be added if the target type is annotated as * entity or valueobject. * * @param \TYPO3\Flow\Reflection\ClassSchema $classSchema * @return void * @throws \InvalidArgumentException * @throws \TYPO3\Flow\Reflection\Exception\InvalidPropertyTypeException */ protected function addPropertiesToClassSchema(ClassSchema $classSchema) { // those are added as property even if not tagged with entity/valueobject $propertyTypeWhiteList = array('DateTime', 'SplObjectStorage', 'Doctrine\\Common\\Collections\\Collection', 'Doctrine\\Common\\Collections\\ArrayCollection'); $className = $classSchema->getClassName(); $needsArtificialIdentity = TRUE; foreach ($this->getClassPropertyNames($className) as $propertyName) { if ($this->isPropertyTaggedWith($className, $propertyName, 'var') && !$this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Transient')) { $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var'); if (count($varTagValues) > 1) { throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366); } else { $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t"); } if ($this->isPropertyAnnotatedWith($className, $propertyName, 'Doctrine\\ORM\\Mapping\\Id')) { $needsArtificialIdentity = FALSE; } try { $parsedType = TypeHandling::parseType($declaredType); } catch (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475); } if (!in_array($parsedType['type'], $propertyTypeWhiteList) && (class_exists($parsedType['type']) || interface_exists($parsedType['type'])) && !($this->isClassAnnotatedWith($parsedType['type'], 'TYPO3\\Flow\\Annotations\\Entity') || $this->isClassAnnotatedWith($parsedType['type'], 'Doctrine\\ORM\\Mapping\\Entity') || $this->isClassAnnotatedWith($parsedType['type'], 'TYPO3\\Flow\\Annotations\\ValueObject'))) { continue; } $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')); if ($this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Identity')) { $classSchema->markAsIdentityProperty($propertyName); } } } if ($needsArtificialIdentity === TRUE) { $classSchema->addProperty('Persistence_Object_Identifier', 'string'); } }
/** * Adds properties of the class at hand to the class schema. * * Properties will be added if they have a var annotation && (!transient-annotation && !inject-annotation) * * Invalid annotations will cause an exception to be thrown. * * @param \TYPO3\Flow\Reflection\ClassSchema $classSchema * @return void * @throws Exception\InvalidPropertyTypeException */ protected function addPropertiesToClassSchema(ClassSchema $classSchema) { $className = $classSchema->getClassName(); $needsArtificialIdentity = true; foreach ($this->getClassPropertyNames($className) as $propertyName) { if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Transient::class)) { continue; } if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Inject::class)) { continue; } if ($this->isPropertyTaggedWith($className, $propertyName, 'var')) { $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var'); if (count($varTagValues) > 1) { throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366); } else { $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t"); } try { TypeHandling::parseType($declaredType); } catch (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475); } if ($this->isPropertyAnnotatedWith($className, $propertyName, 'Doctrine\\ORM\\Mapping\\Id')) { $needsArtificialIdentity = false; } $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Lazy::class), $this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Transient::class)); if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Identity::class)) { $classSchema->markAsIdentityProperty($propertyName); } } if ($needsArtificialIdentity === true) { $classSchema->addProperty('Persistence_Object_Identifier', 'string'); } } }
/** * Traverses the given object structure in order to transform it into an * array structure. * * @param object $object Object to traverse * @param array $configuration Configuration for transforming the given object * @param boolean $onlyIdentifier * @param boolean $enableSideLoading * @return array Object structure as an array * @todo implement sideloading */ protected function transformObject($object, array $configuration, $onlyIdentifier = TRUE, $enableSideLoading = TRUE) { if ($object instanceof \DateTime) { return $object->format('Y-m-d\\TH:i:s'); } elseif ($onlyIdentifier === TRUE) { return $this->persistenceManager->getIdentifierByObject($object); } else { $transformedObject = array('id' => $this->persistenceManager->getIdentifierByObject($object)); $objectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($object)); foreach (ObjectAccess::getGettablePropertyNames($object) as $propertyName) { if ($this->isPropertyIgnored($objectName, $propertyName)) { continue; } $propertyValue = ObjectAccess::getProperty($object, $propertyName); if (is_array($propertyValue) || $propertyValue instanceof \ArrayAccess) { $propertyValue = $this->transformValue($propertyValue, array(), TRUE, $enableSideLoading); if ($this->isRelation($objectName, $propertyName)) { $this->addLink($transformedObject, $propertyName, $propertyValue); if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) { $propertyTags = $this->reflectionService->getPropertyTagValues($objectName, $propertyName, 'var'); $propertyObjectType = TypeHandling::parseType($propertyTags[0]); $this->sideLoad($propertyObjectType['elementType'], $propertyValue); } $propertyValue = NULL; } elseif (empty($propertyValue)) { $propertyValue = NULL; } } elseif ($this->isSimpleValue($propertyValue)) { if (is_object($propertyValue)) { $propertyValue = $this->transformObject($propertyValue, $configuration, FALSE, $enableSideLoading); } } elseif (is_object($propertyValue)) { $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue)); if (!$this->isObjectIgnored($propertyObjectName)) { $propertyValue = $this->transformObject($propertyValue, $configuration, TRUE, $enableSideLoading); if ($this->isRelation($objectName, $propertyName)) { $this->addLink($transformedObject, $propertyName, $propertyValue); if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) { $this->sideLoad($propertyObjectName, $propertyValue); } $propertyValue = NULL; } elseif (empty($propertyValue)) { $propertyValue = NULL; } } } if ($propertyValue !== NULL) { if (is_object($propertyValue)) { $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue)); if (!$this->isObjectIgnored($propertyObjectName)) { $transformedObject[$propertyName] = $propertyValue; } } else { $transformedObject[$propertyName] = $propertyValue; } } } return $transformedObject; } }
/** * Return the type of a given sub-property inside the $targetType * * @param string $targetType * @param string $propertyName * @param PropertyMappingConfigurationInterface $configuration * @return string */ public function getTypeOfChildProperty($targetType, $propertyName, PropertyMappingConfigurationInterface $configuration) { $parsedTargetType = TypeHandling::parseType($targetType); return $parsedTargetType['elementType']; }
/** * Adds (defines) a specific property and its type. * * @param string $name Name of the property * @param string $type Type of the property * @param boolean $lazy Whether the property should be lazy-loaded when reconstituting * @return void * @throws \InvalidArgumentException */ public function addProperty($name, $type, $lazy = FALSE) { try { $type = \TYPO3\Flow\Utility\TypeHandling::parseType($type); } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $name . '"'), 1315564474); } $this->properties[$name] = array('type' => $type['type'], 'elementType' => $type['elementType'], 'lazy' => $lazy); }
/** * @test * @dataProvider types */ public function parseTypeReturnsArrayWithInformation($type, $expectedResult) { $this->assertEquals($expectedResult, TypeHandling::parseType($type), 'Failed for ' . $type); }
/** * Create a property mapping configuration for the given dataType to convert a Node property value from the given dataType to a simple type. * * @param string $dataType * @return PropertyMappingConfigurationInterface */ protected function createConfiguration($dataType) { if (!isset($this->generatedPropertyMappingConfigurations[$dataType])) { $propertyMappingConfiguration = new PropertyMappingConfiguration(); $propertyMappingConfiguration->allowAllProperties(); $parsedType = ['elementType' => null, 'type' => $dataType]; // Special handling for "reference(s)", should be deprecated and normlized to array<NodeInterface> if ($dataType !== 'references' && $dataType !== 'reference') { $parsedType = TypeHandling::parseType($dataType); } if ($this->setTypeConverterForType($propertyMappingConfiguration, $dataType) === false) { $this->setTypeConverterForType($propertyMappingConfiguration, $parsedType['type']); } $elementConfiguration = $propertyMappingConfiguration->forProperty('*'); $this->setTypeConverterForType($elementConfiguration, $parsedType['elementType']); $this->generatedPropertyMappingConfigurations[$dataType] = $propertyMappingConfiguration; } return $this->generatedPropertyMappingConfigurations[$dataType]; }