public function guessColumnFormatters(\Faker\Generator $generator) { $formatters = array(); $class = $this->class; $nameGuesser = new \Faker\Guesser\Name($generator); $columnTypeGuesser = new ColumnTypeGuesser($generator); foreach ($this->class->getFieldNames() as $fieldName) { if ($this->class->isIdentifier($fieldName) || !$this->class->hasField($fieldName)) { continue; } if ($formatter = $nameGuesser->guessFormat($fieldName)) { $formatters[$fieldName] = $formatter; continue; } if ($formatter = $columnTypeGuesser->guessFormat($fieldName, $this->class)) { $formatters[$fieldName] = $formatter; continue; } } foreach ($this->class->getAssociationNames() as $assocName) { if (!$this->class->isIdentifier($assocName) || !$this->class->isCollectionValuedAssociation($assocName)) { continue; } $relatedClass = $this->class->getAssociationTargetClass($assocName); $formatters[$assocName] = function ($inserted) use($relatedClass) { return isset($inserted[$relatedClass]) ? $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)] : null; }; } return $formatters; }
/** * {@inheritDoc} */ public function readExtendedMetadata(ClassMetadata $meta, array &$config) { $mapping = $this->_getMapping($meta->name); if (isset($mapping['gedmo'])) { $classMapping = $mapping['gedmo']; if (isset($classMapping['loggable'])) { $config['loggable'] = true; if (isset($classMapping['loggable']['logEntryClass'])) { if (!class_exists($classMapping['loggable']['logEntryClass'])) { throw new InvalidMappingException("LogEntry class: {$classMapping['loggable']['logEntryClass']} does not exist."); } $config['logEntryClass'] = $classMapping['loggable']['logEntryClass']; } } } if (isset($mapping['fields'])) { foreach ($mapping['fields'] as $field => $fieldMapping) { if (isset($fieldMapping['gedmo'])) { if (in_array('versioned', $fieldMapping['gedmo'])) { if ($meta->isCollectionValuedAssociation($field)) { throw new InvalidMappingException("Cannot versioned [{$field}] as it is collection in object - {$meta->name}"); } // fields cannot be overrided and throws mapping exception $config['versioned'][] = $field; } } } } if (isset($mapping['manyToOne'])) { foreach ($mapping['manyToOne'] as $field => $fieldMapping) { if (isset($fieldMapping['gedmo'])) { if (in_array('versioned', $fieldMapping['gedmo'])) { if ($meta->isCollectionValuedAssociation($field)) { throw new InvalidMappingException("Cannot versioned [{$field}] as it is collection in object - {$meta->name}"); } // fields cannot be overrided and throws mapping exception $config['versioned'][] = $field; } } } } if (isset($mapping['oneToOne'])) { foreach ($mapping['oneToOne'] as $field => $fieldMapping) { if (isset($fieldMapping['gedmo'])) { if (in_array('versioned', $fieldMapping['gedmo'])) { if ($meta->isCollectionValuedAssociation($field)) { throw new InvalidMappingException("Cannot versioned [{$field}] as it is collection in object - {$meta->name}"); } // fields cannot be overrided and throws mapping exception $config['versioned'][] = $field; } } } } }
/** * Hydrate $object with the provided $data. * * @param array $data * @param object $object * @throws \Exception * @return object */ public function hydrate(array $data, $object) { $this->metadata = $this->objectManager->getClassMetadata(get_class($object)); foreach ($data as $field => &$value) { if ($this->metadata->hasAssociation($field)) { $target = $this->metadata->getAssociationTargetClass($field); if ($this->metadata->isSingleValuedAssociation($field)) { $value = $this->toOne($value, $target); } elseif ($this->metadata->isCollectionValuedAssociation($field)) { $value = $this->toMany($value, $target); } } } return $this->hydrator->hydrate($data, $object); }
private function getAssociationString(ClassMetadata $class1, $association) { $targetClassName = $class1->getAssociationTargetClass($association); $class2 = $this->getClassByName($targetClassName); $isInverse = $class1->isAssociationInverseSide($association); $class1Count = $class1->isCollectionValuedAssociation($association) ? 2 : 1; if (null === $class2) { return $this->getClassString($class1) . ($isInverse ? '<' : '<>') . '-' . $association . ' ' . ($class1Count > 1 ? '*' : ($class1Count ? '1' : '')) . ($isInverse ? '<>' : '>') . '[' . str_replace('\\', '.', $targetClassName) . ']'; } $class1SideName = $association; $class2SideName = ''; $class2Count = 0; $bidirectional = false; if ($isInverse) { $class2SideName = (string) $class1->getAssociationMappedByTargetField($association); if ($class2SideName) { $class2Count = $class2->isCollectionValuedAssociation($class2SideName) ? 2 : 1; $bidirectional = true; } } else { foreach ($class2->getAssociationNames() as $class2Side) { if ($class2->isAssociationInverseSide($class2Side) && $class2->getAssociationMappedByTargetField($class2Side) === $association) { $class2SideName = $class2Side; $class2Count = $class2->isCollectionValuedAssociation($class2SideName) ? 2 : 1; $bidirectional = true; break; } } } $this->visitAssociation($targetClassName, $class2SideName); return $this->getClassString($class1) . ($bidirectional ? $isInverse ? '<' : '<>' : '') . ($class2SideName ? $class2SideName . ' ' : '') . ($class2Count > 1 ? '*' : ($class2Count ? '1' : '')) . '-' . $class1SideName . ' ' . ($class1Count > 1 ? '*' : ($class1Count ? '1' : '')) . ($bidirectional && $isInverse ? '<>' : '>') . $this->getClassString($class2); }
/** * {@inheritDoc} */ public function readExtendedMetadata(ClassMetadata $meta, array &$config) { $class = $meta->getReflectionClass(); // class annotations if ($annot = $this->reader->getClassAnnotation($class, self::LOGGABLE)) { $config['loggable'] = true; if ($annot->logEntryClass) { if (!class_exists($annot->logEntryClass)) { throw new InvalidMappingException("LogEntry class: {$annot->logEntryClass} does not exist."); } $config['logEntryClass'] = $annot->logEntryClass; } } // property annotations foreach ($class->getProperties() as $property) { if ($meta->isMappedSuperclass && !$property->isPrivate() || $meta->isInheritedField($property->name) || isset($meta->associationMappings[$property->name]['inherited'])) { continue; } // versioned property if ($versioned = $this->reader->getPropertyAnnotation($property, self::VERSIONED)) { $field = $property->getName(); if ($meta->isCollectionValuedAssociation($field)) { throw new InvalidMappingException("Cannot versioned [{$field}] as it is collection in object - {$meta->name}"); } // fields cannot be overrided and throws mapping exception $config['versioned'][] = $field; } } }
/** * Prepare strategies before the hydrator is used * * @throws \InvalidArgumentException * @return void */ protected function prepareStrategies() { $associations = $this->metadata->getAssociationNames(); foreach ($associations as $association) { if ($this->metadata->isCollectionValuedAssociation($association)) { // Add a strategy if the association has none set by user if (!$this->hasStrategy($association)) { if ($this->byValue) { $this->addStrategy($association, new Strategy\AllowRemoveByValue()); } else { $this->addStrategy($association, new Strategy\AllowRemoveByReference()); } } $strategy = $this->getStrategy($association); if (!$strategy instanceof Strategy\AbstractCollectionStrategy) { throw new InvalidArgumentException( sprintf( 'Strategies used for collections valued associations must inherit from ' . 'Strategy\AbstractCollectionStrategy, %s given', get_class($strategy) ) ); } $strategy->setCollectionName($association) ->setClassMetadata($this->metadata); } } }
/** * Hydrate $object with the provided $data. * * @param array $data * @param object $object * @throws \Exception * @return object */ public function hydrate(array $data, $object) { $this->metadata = $this->objectManager->getClassMetadata(get_class($object)); $object = $this->tryConvertArrayToObject($data, $object); foreach ($data as $field => &$value) { $value = $this->hydrateValue($field, $value); if ($value === null) { continue; } // @todo DateTime (and other types) conversion should be handled by doctrine itself in future if (in_array($this->metadata->getTypeOfField($field), array('datetime', 'time', 'date'))) { if (is_int($value)) { $dt = new DateTime(); $dt->setTimestamp($value); $value = $dt; } elseif (is_string($value)) { $value = new DateTime($value); } } if ($this->metadata->hasAssociation($field)) { $target = $this->metadata->getAssociationTargetClass($field); if ($this->metadata->isSingleValuedAssociation($field)) { $value = $this->toOne($value, $target); } elseif ($this->metadata->isCollectionValuedAssociation($field)) { $value = $this->toMany($value, $target); // Automatically merge collections using helper utility $propertyRefl = $this->metadata->getReflectionClass()->getProperty($field); $propertyRefl->setAccessible(true); $previousValue = $propertyRefl->getValue($object); $value = CollectionUtils::intersectUnion($previousValue, $value); } } } return $this->hydrator->hydrate($data, $object); }
/** * Adds an object to a collection. * * @param string $field * @param array $args * * @return void * * @throws \BadMethodCallException * @throws \InvalidArgumentException */ private function add($field, $args) { $this->initializeDoctrine(); if ($this->cm->hasAssociation($field) && $this->cm->isCollectionValuedAssociation($field)) { $targetClass = $this->cm->getAssociationTargetClass($field); if (!$args[0] instanceof $targetClass) { throw new \InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); } if (!$this->{$field} instanceof Collection) { $this->{$field} = new ArrayCollection($this->{$field} ?: []); } $this->{$field}->add($args[0]); $this->completeOwningSide($field, $targetClass, $args[0]); } else { throw new \BadMethodCallException("There is no method add" . $field . "() on " . $this->cm->getName()); } }
/** * {@inheritDoc} */ protected function validateExtendedMetadata(ClassMetadata $baseClassMetadata, ClassMetadataInterface $extendedClassMetadata) { if ($extendedClassMetadata->hasTranslatableProperties()) { if (!isset($extendedClassMetadata->localeProperty)) { throw new Exception\MappingException('Entity \'' . $baseClassMetadata->name . '\' has translatable properties so it must have property marked with @Translatable\\Language annotation'); } $translatableProperties = $extendedClassMetadata->getTranslatableProperties(); foreach ($translatableProperties as $translation => $properties) { if (!$baseClassMetadata->hasAssociation($translation) || !$baseClassMetadata->isCollectionValuedAssociation($translation)) { throw new Exception\MappingException('Field \'' . $translation . '\' in entity \'' . $baseClassMetadata->name . '\' has to be a OneToMany association'); } } } if (isset($extendedClassMetadata->localeProperty)) { if ($extendedClassMetadata->hasTranslatableProperties() && ($baseClassMetadata->hasField($extendedClassMetadata->localeProperty) || $baseClassMetadata->hasAssociation($extendedClassMetadata->localeProperty))) { throw new Exception\MappingException('Entity \'' . $baseClassMetadata->name . '\' seems to be a translatable entity so its \'' . $extendedClassMetadata->localeProperty . '\' field must not be persistent'); } else { if (!$extendedClassMetadata->hasTranslatableProperties() && !$baseClassMetadata->hasField($extendedClassMetadata->localeProperty) && !$baseClassMetadata->hasAssociation($extendedClassMetadata->localeProperty)) { throw new Exception\MappingException('Entity \'' . $baseClassMetadata->name . '\' seems to be a translation entity so its \'' . $extendedClassMetadata->localeProperty . '\' field must be persistent'); } } } }
/** * @param ClassMetadata $meta * @param string $field * * @return bool */ protected function isMappingValid(ClassMetadata $meta, $field) { return $meta->isCollectionValuedAssociation($field) == false; }
/** * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $baseClassMetadata * @param \FSi\DoctrineExtensions\Translatable\Mapping\ClassMetadata $translatableClassMetadata * @throws \FSi\DoctrineExtensions\Translatable\Exception\MappingException */ private function validateTranslatableProperties(ClassMetadata $baseClassMetadata, TranslatableClassMetadata $translatableClassMetadata) { $translatableProperties = $translatableClassMetadata->getTranslatableProperties(); foreach ($translatableProperties as $translation => $properties) { if (!$baseClassMetadata->hasAssociation($translation) || !$baseClassMetadata->isCollectionValuedAssociation($translation)) { throw new Exception\MappingException(sprintf("Field '%s' in entity '%s' has to be a OneToMany association", $translation, $baseClassMetadata->getName())); } } }
/** * Производит простой перебор ассоциаций, взятых из метаданных и на каждую ассоциацию вызывает callback * * @param ClassMetadata $metadata мета данные сущности * @param callable $callback метод, который будет вызван для каждой найденной ассоциации */ private function bypassAssociationsOfClass(ClassMetadata $metadata, \Closure $callback) { foreach ($metadata->getAssociationNames() as $assocName) { if (!$metadata->isCollectionValuedAssociation($assocName)) { $callback($assocName); } } }
/** * Builds the association value. * * @param ClassMetadata $metadata * @param string $propertyPath * @param string $value * * @return array|object * @throws \Exception */ private function buildAssociationValue(ClassMetadata $metadata, $propertyPath, $value) { $childMetadata = $this->manager->getClassMetadata($metadata->getAssociationTargetClass($propertyPath)); // Single association if ($metadata->isSingleValuedAssociation($propertyPath)) { if (is_string($value) && '#' === substr($value, 0, 1)) { return $this->getReference(substr($value, 1)); } elseif (is_array($value)) { return $this->buildEntity($childMetadata, $value); } throw new \Exception("Unexpected value for single association '{$propertyPath}'."); // Collection association } elseif ($metadata->isCollectionValuedAssociation($propertyPath)) { if (!is_array($value)) { throw new \Exception('Expected array.'); } $builtValue = []; foreach ($value as $childData) { if (is_string($childData) && '#' === substr($childData, 0, 1)) { array_push($builtValue, $this->getReference(substr($childData, 1))); } elseif (is_array($value)) { array_push($builtValue, $this->buildEntity($childMetadata, $childData)); } else { throw new \Exception("Unexpected value for association '{$propertyPath}'."); } } return $builtValue; } throw new \Exception("Unexpected association path '{$propertyPath}'."); }
/** * @param \Faker\Generator $generator * @return array */ public function guessColumnFormatters(\Faker\Generator $generator) { $formatters = array(); $nameGuesser = new \Faker\Guesser\Name($generator); $columnTypeGuesser = new ColumnTypeGuesser($generator); foreach ($this->class->getFieldNames() as $fieldName) { if ($this->class->isIdentifier($fieldName) || !$this->class->hasField($fieldName)) { continue; } $size = isset($this->class->fieldMappings[$fieldName]['length']) ? $this->class->fieldMappings[$fieldName]['length'] : null; if ($formatter = $nameGuesser->guessFormat($fieldName, $size)) { $formatters[$fieldName] = $formatter; continue; } if ($formatter = $columnTypeGuesser->guessFormat($fieldName, $this->class)) { $formatters[$fieldName] = $formatter; continue; } } foreach ($this->class->getAssociationNames() as $assocName) { if ($this->class->isCollectionValuedAssociation($assocName)) { continue; } $relatedClass = $this->class->getAssociationTargetClass($assocName); $unique = $optional = false; if ($this->class instanceof \Doctrine\ORM\Mapping\ClassMetadata) { $mappings = $this->class->getAssociationMappings(); foreach ($mappings as $mapping) { if ($mapping['targetEntity'] == $relatedClass) { if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_ONE) { $unique = true; $optional = isset($mapping['joinColumns'][0]['nullable']) ? $mapping['joinColumns'][0]['nullable'] : false; break; } } } } elseif ($this->class instanceof \Doctrine\ODM\MongoDB\Mapping\ClassMetadata) { $mappings = $this->class->associationMappings; foreach ($mappings as $mapping) { if ($mapping['targetDocument'] == $relatedClass) { if ($mapping['type'] == \Doctrine\ODM\MongoDB\Mapping\ClassMetadata::ONE && $mapping['association'] == \Doctrine\ODM\MongoDB\Mapping\ClassMetadata::REFERENCE_ONE) { $unique = true; $optional = isset($mapping['nullable']) ? $mapping['nullable'] : false; break; } } } } $index = 0; $formatters[$assocName] = function ($inserted) use($relatedClass, &$index, $unique, $optional) { if (isset($inserted[$relatedClass])) { if ($unique) { $related = null; if (isset($inserted[$relatedClass][$index]) || !$optional) { $related = $inserted[$relatedClass][$index]; } $index++; return $related; } return $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)]; } return null; }; } return $formatters; }