/** */ public function getOptions() { $classSchema = $this->reflectionService->getClassSchema($this->getRelationClass()); if ($classSchema->getRepositoryClassName() !== NULL) { $repository = $this->objectManager->get($classSchema->getRepositoryClassName()); $query = call_user_func(array($repository, $this->settings['QueryMethod'])); } else { $query = $this->persistenceManager->createQueryForType($this->getRelationClass()); } $options = $query->execute()->toArray(); if ($this->settings['LabelPath'] !== NULL) { $options = array(); foreach ($query->execute() as $option) { $identifier = $this->persistenceManager->getIdentifierByObject($option); $label = ObjectAccess::getPropertyPath($option, $this->settings['LabelPath']); $options[$identifier] = $label; } } if ($this->settings['EmptyOption'] !== NULL) { $newOptions = array('' => $this->settings['EmptyOption']); foreach ($options as $key => $value) { $newOptions[$key] = $value; } $options = $newOptions; } return $options; }
/** * Generate a view with the given name for the given package and controller * * @param string $packageKey The package key of the controller's package * @param string $subpackage An optional subpackage name * @param string $controllerName The name of the new controller * @param string $viewName The name of the view * @param string $templateName The name of the view * @param boolean $overwrite Overwrite any existing files? * @return array An array of generated filenames */ public function generateView($packageKey, $subpackage, $controllerName, $viewName, $templateName, $overwrite = false) { $viewName = ucfirst($viewName); $templatePathAndFilename = 'resource://TYPO3.Kickstart/Private/Generator/View/' . $templateName . 'Template.html'; $contextVariables = array(); $contextVariables['packageKey'] = $packageKey; $contextVariables['subpackage'] = $subpackage; $contextVariables['isInSubpackage'] = $subpackage != ''; $contextVariables['controllerName'] = $controllerName; $contextVariables['viewName'] = $viewName; $contextVariables['modelName'] = strtolower($controllerName[0]) . substr($controllerName, 1); $contextVariables['repositoryClassName'] = '\\' . str_replace('.', '\\', $packageKey) . ($subpackage != '' ? '\\' . $subpackage : '') . '\\Domain\\Repository\\' . $controllerName . 'Repository'; $contextVariables['modelFullClassName'] = '\\' . str_replace('.', '\\', $packageKey) . ($subpackage != '' ? '\\' . $subpackage : '') . '\\Domain\\Model\\' . $controllerName; $contextVariables['modelClassName'] = ucfirst($contextVariables['modelName']); $modelClassSchema = $this->reflectionService->getClassSchema($contextVariables['modelFullClassName']); if ($modelClassSchema !== null) { $contextVariables['properties'] = $modelClassSchema->getProperties(); if (isset($contextVariables['properties']['Persistence_Object_Identifier'])) { unset($contextVariables['properties']['Persistence_Object_Identifier']); } } if (!isset($contextVariables['properties']) || $contextVariables['properties'] === array()) { $contextVariables['properties'] = array('name' => array('type' => 'string')); } $fileContent = $this->renderTemplate($templatePathAndFilename, $contextVariables); $subpackagePath = $subpackage != '' ? $subpackage . '/' : ''; $viewFilename = $viewName . '.html'; $viewPath = 'resource://' . $packageKey . '/Private/Templates/' . $subpackagePath . $controllerName . '/'; $targetPathAndFilename = $viewPath . $viewFilename; $this->generateFile($targetPathAndFilename, $fileContent, $overwrite); return $this->generatedFiles; }
/** * After returning advice, generates the value hash for the object * * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current join point * @return void * @Flow\After("classAnnotatedWith(TYPO3\Flow\Annotations\ValueObject) && method(.*->__construct()) && filter(TYPO3\Flow\Persistence\Doctrine\Mapping\Driver\FlowAnnotationDriver)") */ public function generateValueHash(JoinPointInterface $joinPoint) { $proxy = $joinPoint->getProxy(); $proxyClassName = get_class($proxy); $hashSourceParts = array(); $classSchema = $this->reflectionService->getClassSchema($proxyClassName); foreach ($classSchema->getProperties() as $property => $propertySchema) { // Currently, private properties are transient. Should this behaviour change, they need to be included // in the value hash generation if ($classSchema->isPropertyTransient($property) || $this->reflectionService->isPropertyPrivate($proxyClassName, $property)) { continue; } $propertyValue = ObjectAccess::getProperty($proxy, $property, true); if (is_object($propertyValue) === true) { // The persistence manager will return NULL if the given object is unknown to persistence $propertyValue = $this->persistenceManager->getIdentifierByObject($propertyValue) ?: $propertyValue; } $hashSourceParts[$property] = $propertyValue; } ksort($hashSourceParts); $hashSourceParts['__class_name__'] = $proxyClassName; $serializedSource = $this->useIgBinary === true ? igbinary_serialize($hashSourceParts) : serialize($hashSourceParts); $proxy = $joinPoint->getProxy(); ObjectAccess::setProperty($proxy, 'Persistence_Object_Identifier', sha1($serializedSource), true); }
/** * @test */ public function domainModelPropertyTypesAreExpandedWithUseStatementsInClassSchema() { $classSchema = $this->reflectionService->getClassSchema('TYPO3\\Flow\\Tests\\Functional\\Reflection\\Fixtures\\Model\\EntityWithUseStatements'); $expected = array('type' => 'TYPO3\\Flow\\Tests\\Functional\\Reflection\\Fixtures\\Model\\SubSubEntity', 'elementType' => NULL, 'lazy' => FALSE); $this->assertEquals($expected, $classSchema->getProperty('subSubEntity')); $expected = array('type' => 'TYPO3\\Flow\\Tests\\Functional\\Persistence\\Fixtures\\SubEntity', 'elementType' => NULL, 'lazy' => FALSE); $this->assertEquals($expected, $classSchema->getProperty('propertyFromOtherNamespace')); }
/** * @param string $model * @param object $context an arbitrary object which is available in all actions and nested functionality * @return array * @throws MissingModelTypeException * @throws \Exception */ protected function getProperties($model, $context = null) { if ($model === NULL) { throw new MissingModelTypeException('The "model" property has not been specified as parameter to the ViewHelper ' . get_class($this) . '.', 1452715128); } $classSchema = $this->reflectionService->getClassSchema($model); if ($classSchema === NULL) { throw new MissingModelTypeException('No class schema could be resolved for model ' . $model . '.', 1452715183); } $classSchema->getProperties(); $fields = []; foreach ($classSchema->getProperties() as $propertyName => $propertyDefinition) { if ($propertyName === 'Persistence_Object_Identifier') { continue; } /* @var $formFieldAnnotation FormField */ $formFieldAnnotation = $this->reflectionService->getPropertyAnnotation($model, $propertyName, FormField::class); $fields[$propertyName] = []; if ($formFieldAnnotation) { $fields[$propertyName] = get_object_vars($formFieldAnnotation); } $this->addDefaultsToFields($fields, $propertyName); } foreach (get_class_methods($model) as $methodName) { if (substr($methodName, 0, 3) === 'get') { $methodAnnotation = $this->reflectionService->getMethodAnnotation($model, $methodName, FormField::class); if ($methodAnnotation) { $propertyName = lcfirst(substr($methodName, 3)); $fields[$propertyName] = get_object_vars($methodAnnotation); $this->addDefaultsToFields($fields, $propertyName); } } } $generatorAnnotation = $this->reflectionService->getClassAnnotation($model, FieldGenerator::class); if ($generatorAnnotation !== NULL) { $generator = $this->objectManager->get($generatorAnnotation->className); if (!$generator instanceof FieldGeneratorInterface) { throw new \Exception('TODO: generator must implement FieldGeneratorInterface, ' . get_class($generator) . ' given.'); } foreach ($generator->generate($context) as $propertyName => $annotation) { $fields[$propertyName] = get_object_vars($annotation); $this->addDefaultsToFields($fields, $propertyName); } } return (new PositionalArraySorter($fields))->toArray(); }
/** * Fetch a class schema for the given class, if possible. * * @param string $className * @return ClassSchema * @throws ClassSchemaNotFoundException */ protected function getClassSchema($className) { $classSchema = $this->reflectionService->getClassSchema($className); if (!$classSchema) { throw new ClassSchemaNotFoundException('No class schema found for "' . $className . '".', 1295973082); } return $classSchema; }
/** * Fetch a class schema for the given class, if possible. * * @param string $className * @return \TYPO3\Flow\Reflection\ClassSchema * @throws \TYPO3\Flow\Persistence\Doctrine\Mapping\Exception\ClassSchemaNotFoundException */ protected function getClassSchema($className) { $className = preg_replace('/' . \TYPO3\Flow\Object\Proxy\Compiler::ORIGINAL_CLASSNAME_SUFFIX . '$/', '', $className); $classSchema = $this->reflectionService->getClassSchema($className); if (!$classSchema) { throw new \TYPO3\Flow\Persistence\Doctrine\Mapping\Exception\ClassSchemaNotFoundException('No class schema found for "' . $className . '".', 1295973082); } return $classSchema; }
/** * 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 Neos\Foo\Domain\Model\Quux, then the validator will be found if it has the * name Neos\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 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 $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== null && $classSchema->isAggregateRoot()) { $objectValidator = new AggregateBoundaryValidator(array()); } else { $objectValidator = new GenericObjectValidator([]); } $conjunctionValidator->addValidator($objectValidator); 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 (InvalidTypeException $exception) { throw new \InvalidArgumentException(sprintf(' @var annotation of ' . $exception->getMessage(), 'class "' . $targetClassName . '", property "' . $classPropertyName . '"'), 1315564744, $exception); } if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, Flow\IgnoreValidation::class)) { continue; } $propertyTargetClassName = $parsedType['type']; if (TypeHandling::isCollectionType($propertyTargetClassName) === true) { $collectionValidator = $this->createValidator(Validator\CollectionValidator::class, ['elementType' => $parsedType['elementType'], 'validationGroups' => $validationGroups]); $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator); } elseif (!TypeHandling::isSimpleType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === Configuration::SCOPE_PROTOTYPE) { $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName, $validationGroups); if (count($validatorForProperty) > 0) { $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty); } } $validateAnnotations = $this->reflectionService->getPropertyAnnotations($targetClassName, $classPropertyName, Flow\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->removeValidator($objectValidator); } } $this->addCustomValidators($targetClassName, $conjunctionValidator); }
/** * Builds methods for a single AOP proxy class for the specified class. * * @param string $targetClassName Name of the class to create a proxy class file for * @param array &$aspectContainers The array of aspect containers from the AOP Framework * @return boolean TRUE if the proxy class could be built, FALSE otherwise. */ public function buildProxyClass($targetClassName, array &$aspectContainers) { $interfaceIntroductions = $this->getMatchingInterfaceIntroductions($aspectContainers, $targetClassName); $introducedInterfaces = $this->getInterfaceNamesFromIntroductions($interfaceIntroductions); $propertyIntroductions = $this->getMatchingPropertyIntroductions($aspectContainers, $targetClassName); $methodsFromTargetClass = $this->getMethodsFromTargetClass($targetClassName); $methodsFromIntroducedInterfaces = $this->getIntroducedMethodsFromInterfaceIntroductions($interfaceIntroductions, $targetClassName); $interceptedMethods = array(); $this->addAdvicedMethodsToInterceptedMethods($interceptedMethods, array_merge($methodsFromTargetClass, $methodsFromIntroducedInterfaces), $targetClassName, $aspectContainers); $this->addIntroducedMethodsToInterceptedMethods($interceptedMethods, $methodsFromIntroducedInterfaces); if (count($interceptedMethods) < 1 && count($introducedInterfaces) < 1 && count($propertyIntroductions) < 1) { return FALSE; } $proxyClass = $this->compiler->getProxyClass($targetClassName); if ($proxyClass === FALSE) { return FALSE; } $proxyClass->addInterfaces($introducedInterfaces); /** @var $propertyIntroduction PropertyIntroduction */ foreach ($propertyIntroductions as $propertyIntroduction) { $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, 'TYPO3\\Flow\\Annotations\\Transient')) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== NULL) { $classSchema->addProperty($propertyName, $possiblePropertyTypes[0]); } } $propertyReflection = new PropertyReflection($declaringAspectClassName, $propertyName); $propertyReflection->setIsAopIntroduced(TRUE); $this->reflectionService->reflectClassProperty($targetClassName, $propertyReflection, new ClassReflection($declaringAspectClassName)); $proxyClass->addProperty($propertyName, 'NULL', $propertyIntroduction->getPropertyVisibility(), $propertyIntroduction->getPropertyDocComment()); } $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode("\t\tif (method_exists(get_parent_class(\$this), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode($this->buildMethodsAndAdvicesArrayCode($interceptedMethods)); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->overrideMethodVisibility('protected'); $callBuildMethodsAndAdvicesArrayCode = "\n\t\t\$this->Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); if (!$this->reflectionService->hasMethod($targetClassName, '__wakeup')) { $proxyClass->getMethod('__wakeup')->addPostParentCallCode("\t\tif (method_exists(get_parent_class(\$this), '__wakeup') && is_callable('parent::__wakeup')) parent::__wakeup();\n"); } // FIXME this can be removed again once Doctrine is fixed (see fixMethodsAndAdvicesArrayForDoctrineProxiesCode()) $proxyClass->getMethod('Flow_Aop_Proxy_fixMethodsAndAdvicesArrayForDoctrineProxies')->addPreParentCallCode($this->fixMethodsAndAdvicesArrayForDoctrineProxiesCode()); // FIXME this can be removed again once Doctrine is fixed (see fixInjectedPropertiesForDoctrineProxiesCode()) $proxyClass->getMethod('Flow_Aop_Proxy_fixInjectedPropertiesForDoctrineProxies')->addPreParentCallCode($this->fixInjectedPropertiesForDoctrineProxiesCode()); $this->buildGetAdviceChainsMethodCode($targetClassName); $this->buildInvokeJoinPointMethodCode($targetClassName); $this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods); $proxyClass->addProperty('Flow_Aop_Proxy_targetMethodsAndGroupedAdvices', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_groupedAdviceChains', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_methodIsInAdviceMode', 'array()'); return TRUE; }
/** * Builds methods for a single AOP proxy class for the specified class. * * @param string $targetClassName Name of the class to create a proxy class file for * @param array &$aspectContainers The array of aspect containers from the AOP Framework * @return boolean TRUE if the proxy class could be built, FALSE otherwise. */ public function buildProxyClass($targetClassName, array &$aspectContainers) { $interfaceIntroductions = $this->getMatchingInterfaceIntroductions($aspectContainers, $targetClassName); $introducedInterfaces = $this->getInterfaceNamesFromIntroductions($interfaceIntroductions); $introducedTraits = $this->getMatchingTraitNamesFromIntroductions($aspectContainers, $targetClassName); $propertyIntroductions = $this->getMatchingPropertyIntroductions($aspectContainers, $targetClassName); $methodsFromTargetClass = $this->getMethodsFromTargetClass($targetClassName); $methodsFromIntroducedInterfaces = $this->getIntroducedMethodsFromInterfaceIntroductions($interfaceIntroductions, $targetClassName); $interceptedMethods = array(); $this->addAdvicedMethodsToInterceptedMethods($interceptedMethods, array_merge($methodsFromTargetClass, $methodsFromIntroducedInterfaces), $targetClassName, $aspectContainers); $this->addIntroducedMethodsToInterceptedMethods($interceptedMethods, $methodsFromIntroducedInterfaces); if (count($interceptedMethods) < 1 && count($introducedInterfaces) < 1 && count($propertyIntroductions) < 1) { return false; } $proxyClass = $this->compiler->getProxyClass($targetClassName); if ($proxyClass === false) { return false; } $proxyClass->addInterfaces($introducedInterfaces); $proxyClass->addTraits($introducedTraits); /** @var $propertyIntroduction PropertyIntroduction */ foreach ($propertyIntroductions as $propertyIntroduction) { $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, \TYPO3\Flow\Annotations\Transient::class)) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== null) { $classSchema->addProperty($propertyName, $possiblePropertyTypes[0]); } } $propertyReflection = new PropertyReflection($declaringAspectClassName, $propertyName); $propertyReflection->setIsAopIntroduced(true); $this->reflectionService->reflectClassProperty($targetClassName, $propertyReflection, new ClassReflection($declaringAspectClassName)); $proxyClass->addProperty($propertyName, var_export($propertyIntroduction->getInitialValue(), true), $propertyIntroduction->getPropertyVisibility(), $propertyIntroduction->getPropertyDocComment()); } $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode(" if (method_exists(get_parent_class(), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode($this->buildMethodsAndAdvicesArrayCode($interceptedMethods)); $proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->overrideMethodVisibility('protected'); $callBuildMethodsAndAdvicesArrayCode = "\n \$this->Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__clone')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); if (!$this->reflectionService->hasMethod($targetClassName, '__wakeup')) { $proxyClass->getMethod('__wakeup')->addPostParentCallCode(" if (method_exists(get_parent_class(), '__wakeup') && is_callable('parent::__wakeup')) parent::__wakeup();\n"); } $proxyClass->addTraits(['\\' . AdvicesTrait::class]); $this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods); $proxyClass->addProperty('Flow_Aop_Proxy_targetMethodsAndGroupedAdvices', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_groupedAdviceChains', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_methodIsInAdviceMode', 'array()'); return true; }
/** * If $this->uriPattern is specified, this will be returned, otherwise identity properties of $this->objectType * are returned in the format {property1}/{property2}/{property3}. * If $this->objectType does not contain identity properties, an empty string is returned. * * @return string */ public function getUriPattern() { if ($this->uriPattern === null) { $classSchema = $this->reflectionService->getClassSchema($this->objectType); $identityProperties = $classSchema->getIdentityProperties(); if (count($identityProperties) === 0) { $this->uriPattern = ''; } else { $this->uriPattern = '{' . implode('}/{', array_keys($identityProperties)) . '}'; } } return $this->uriPattern; }
/** * An onFlush event listener used to validate entities upon persistence. * * @param \Doctrine\ORM\Event\OnFlushEventArgs $eventArgs * @return void */ public function onFlush(\Doctrine\ORM\Event\OnFlushEventArgs $eventArgs) { $unitOfWork = $this->entityManager->getUnitOfWork(); $entityInsertions = $unitOfWork->getScheduledEntityInsertions(); $validatedInstancesContainer = new \SplObjectStorage(); $knownValueObjects = array(); foreach ($entityInsertions as $entity) { $className = TypeHandling::getTypeForValue($entity); if ($this->reflectionService->getClassSchema($className)->getModelType() === ClassSchema::MODELTYPE_VALUEOBJECT) { $identifier = $this->getIdentifierByObject($entity); if (isset($knownValueObjects[$className][$identifier]) || $unitOfWork->getEntityPersister($className)->exists($entity)) { unset($entityInsertions[spl_object_hash($entity)]); continue; } $knownValueObjects[$className][$identifier] = true; } $this->validateObject($entity, $validatedInstancesContainer); } \TYPO3\Flow\Reflection\ObjectAccess::setProperty($unitOfWork, 'entityInsertions', $entityInsertions, true); foreach ($unitOfWork->getScheduledEntityUpdates() as $entity) { $this->validateObject($entity, $validatedInstancesContainer); } }
/** * Checks if the given value is a unique entity depending on it's identity properties or * custom configured identity properties. * * @param mixed $value The value that should be validated * @return void * @throws \TYPO3\Flow\Validation\Exception\InvalidValidationOptionsException * @api */ protected function isValid($value) { if (!is_object($value)) { throw new InvalidValidationOptionsException('The value supplied for the UniqueEntityValidator must be an object.', 1358454270); } $classSchema = $this->reflectionService->getClassSchema($value); if ($classSchema === NULL || $classSchema->getModelType() !== \TYPO3\Flow\Reflection\ClassSchema::MODELTYPE_ENTITY) { throw new InvalidValidationOptionsException('The object supplied for the UniqueEntityValidator must be an entity.', 1358454284); } if ($this->options['identityProperties'] !== NULL) { $identityProperties = $this->options['identityProperties']; foreach ($identityProperties as $propertyName) { if ($classSchema->hasProperty($propertyName) === FALSE) { throw new InvalidValidationOptionsException(sprintf('The custom identity property name "%s" supplied for the UniqueEntityValidator does not exists in "%s".', $propertyName, $classSchema->getClassName()), 1358960500); } } } else { $identityProperties = array_keys($classSchema->getIdentityProperties()); } if (count($identityProperties) === 0) { throw new InvalidValidationOptionsException('The object supplied for the UniqueEntityValidator must have at least one identity property.', 1358459831); } $identifierProperties = $this->reflectionService->getPropertyNamesByAnnotation($classSchema->getClassName(), 'Doctrine\\ORM\\Mapping\\Id'); if (count($identifierProperties) > 1) { throw new InvalidValidationOptionsException('The object supplied for the UniqueEntityValidator must only have one identifier property @ORM\\Id.', 1358501745); } $identifierPropertyName = count($identifierProperties) > 0 ? array_shift($identifierProperties) : 'Persistence_Object_Identifier'; $query = $this->persistenceManager->createQueryForType($classSchema->getClassName()); $constraints = array($query->logicalNot($query->equals($identifierPropertyName, $this->persistenceManager->getIdentifierByObject($value)))); foreach ($identityProperties as $propertyName) { $constraints[] = $query->equals($propertyName, ObjectAccess::getProperty($value, $propertyName)); } if ($query->matching($query->logicalAnd($constraints))->count() > 0) { $this->addError('Another entity with the same unique identifiers already exists', 1355785874); } }
/** * Remove objects removed from SplObjectStorage compared to * $previousSplObjectStorage. * * @param \SplObjectStorage $splObjectStorage * @param array $previousObjectStorage * @return void */ protected function removeDeletedSplObjectStorageEntries(\SplObjectStorage $splObjectStorage = null, array $previousObjectStorage) { // remove objects detached since reconstitution foreach ($previousObjectStorage as $item) { if ($splObjectStorage instanceof \TYPO3\Flow\Persistence\Generic\LazySplObjectStorage && !$this->persistenceSession->hasIdentifier($item['value']['identifier'])) { // ingore this identifier, assume it was blocked by security query rewriting upon activation continue; } $object = $this->persistenceSession->getObjectByIdentifier($item['value']['identifier']); if ($splObjectStorage === null || !$splObjectStorage->contains($object)) { if ($this->reflectionService->getClassSchema($object)->getModelType() === \TYPO3\Flow\Reflection\ClassSchema::MODELTYPE_ENTITY && $this->reflectionService->getClassSchema($object)->isAggregateRoot() === false) { $this->removeEntity($object); } elseif ($this->reflectionService->getClassSchema($object)->getModelType() === \TYPO3\Flow\Reflection\ClassSchema::MODELTYPE_VALUEOBJECT) { $this->removeValueObject($object); } } } }
/** * @test */ public function domainModelPropertyTypesAreExpandedWithUseStatementsInClassSchema() { $classSchema = $this->reflectionService->getClassSchema(\TYPO3\Flow\Tests\Functional\Reflection\Fixtures\Model\EntityWithUseStatements::class); $this->assertEquals(\TYPO3\Flow\Tests\Functional\Reflection\Fixtures\Model\SubSubEntity::class, $classSchema->getProperty('subSubEntity')['type']); $this->assertEquals(\TYPO3\Flow\Tests\Functional\Persistence\Fixtures\SubEntity::class, $classSchema->getProperty('propertyFromOtherNamespace')['type']); }
/** * Constructs a query object working on the given type * * @param string $type * @param \TYPO3\Flow\Reflection\ReflectionService $reflectionService */ public function __construct($type, \TYPO3\Flow\Reflection\ReflectionService $reflectionService) { $this->type = $type; $this->classSchema = $reflectionService->getClassSchema($type); }
/** * Sets the given properties on the object. * * @param object $object The object to set properties on * @param string $identifier The identifier of the object * @param array $objectData * @return void * @throws \TYPO3\Flow\Persistence\Exception\UnknownObjectException */ public function thawProperties($object, $identifier, array $objectData) { $classSchema = $this->reflectionService->getClassSchema($objectData['classname']); foreach ($objectData['properties'] as $propertyName => $propertyData) { if (!$classSchema->hasProperty($propertyName)) { continue; } $propertyValue = NULL; if ($propertyData['value'] !== NULL) { switch ($propertyData['type']) { case 'integer': $propertyValue = (int) $propertyData['value']; break; case 'float': $propertyValue = (double) $propertyData['value']; break; case 'boolean': $propertyValue = (bool) $propertyData['value']; break; case 'string': $propertyValue = (string) $propertyData['value']; break; case 'array': $propertyValue = $this->mapArray($propertyData['value']); break; case 'Doctrine\\Common\\Collections\\Collection': case 'Doctrine\\Common\\Collections\\ArrayCollection': $propertyValue = new \Doctrine\Common\Collections\ArrayCollection($this->mapArray($propertyData['value'])); break; case 'SplObjectStorage': $propertyMetaData = $classSchema->getProperty($propertyName); $propertyValue = $this->mapSplObjectStorage($propertyData['value'], $propertyMetaData['lazy']); break; case 'DateTime': $propertyValue = $this->mapDateTime($propertyData['value']); break; default: if ($propertyData['value'] === FALSE) { throw new \TYPO3\Flow\Persistence\Exception\UnknownObjectException('An expected object was not found by the backend. It was expected for ' . $objectData['classname'] . '::' . $propertyName, 1289509867); } $propertyValue = $this->mapToObject($propertyData['value']); break; } } else { switch ($propertyData['type']) { case 'NULL': continue; break; case 'array': $propertyValue = $this->mapArray(NULL); break; case 'Doctrine\\Common\\Collections\\Collection': case 'Doctrine\\Common\\Collections\\ArrayCollection': $propertyValue = new \Doctrine\Common\Collections\ArrayCollection(); break; case 'SplObjectStorage': $propertyValue = $this->mapSplObjectStorage(NULL); break; } } \TYPO3\Flow\Reflection\ObjectAccess::setProperty($object, $propertyName, $propertyValue, TRUE); } if (isset($objectData['metadata'])) { $object->Flow_Persistence_Metadata = $objectData['metadata']; } \TYPO3\Flow\Reflection\ObjectAccess::setProperty($object, 'Persistence_Object_Identifier', $identifier, TRUE); }