/** * {@inheritDoc} */ public function getFieldMapping($fieldName) { if (!isset($this->fieldMappings[$fieldName])) { throw MappingException::mappingNotFound($this->name, $fieldName); } return parent::getFieldMapping($fieldName); }
/** * Gets the names of all mapped classes known to this driver. * * @return array The names of all mapped classes known to this driver. */ public function getAllClassNames() { if ($this->_classNames !== null) { return $this->_classNames; } if (!$this->_paths) { throw MappingException::pathRequired(); } $classes = array(); $includedFiles = array(); foreach ($this->_paths as $path) { if (!is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY); foreach ($iterator as $file) { if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) { continue; } $sourceFile = realpath($file->getPathName()); require_once $sourceFile; $includedFiles[] = $sourceFile; } } $declared = get_declared_classes(); foreach ($declared as $className) { $rc = new \ReflectionClass($className); $sourceFile = $rc->getFileName(); if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { $classes[] = $className; } } $this->_classNames = $classes; return $classes; }
/** * Validates and completes the mapping. * * @param array $mapping The mapping to validate and complete. * @return array The validated and completed mapping. * @override */ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); // one-side MUST be inverse (must have mappedBy) if (!isset($mapping['mappedBy'])) { throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); } $this->deleteOrphans = isset($mapping['deleteOrphans']) ? (bool) $mapping['deleteOrphans'] : false; }
protected function _findMappingFile($className) { $fileName = str_replace('\\', '.', $className) . $this->_fileExtension; $mappingFiles = array(); // Check whether file exists foreach ((array) $this->_paths as $path) { if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { $mappingFiles[] = $path . DIRECTORY_SEPARATOR . $fileName; } } if (empty($mappingFiles)) { throw MappingException::mappingFileNotFound($className, $fileName); } return $mappingFiles; }
/** * {@inheritdoc} * * @param array $mapping The mapping to validate & complete. * @return array The validated & completed mapping. * @override */ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); if ($this->isOwningSide()) { if (!isset($mapping['joinColumns'])) { throw MappingException::invalidMapping($this->_sourceFieldName); } $this->joinColumns = $mapping['joinColumns']; foreach ($mapping['joinColumns'] as $joinColumn) { $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; } $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns); } $this->deleteOrphans = isset($mapping['deleteOrphans']) ? (bool) $mapping['deleteOrphans'] : false; return $mapping; }
/** * Validates and completes the mapping. * * @param array $mapping The mapping to validate and complete. * @return array The validated and completed mapping. * @override */ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); // OneToMany-side MUST be inverse (must have mappedBy) if (!isset($mapping['mappedBy'])) { throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); } //TODO: if orphanRemoval, cascade=remove is implicit! $this->orphanRemoval = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; if (isset($mapping['orderBy'])) { if (!is_array($mapping['orderBy'])) { throw new \InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy'])); } $this->orderBy = $mapping['orderBy']; } }
/** * {@inheritdoc} */ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); if ($this->isOwningSide) { // owning side MUST have a join table if (!isset($mapping['joinTable']) || !$mapping['joinTable']) { // Apply default join table $sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1); $targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1); $mapping['joinTable'] = array('name' => $sourceShortName . '_' . $targetShortName, 'joinColumns' => array(array('name' => $sourceShortName . '_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), 'inverseJoinColumns' => array(array('name' => $targetShortName . '_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE'))); $this->joinTable = $mapping['joinTable']; } else { if (!isset($mapping['joinTable']['joinColumns'])) { throw MappingException::missingRequiredOption($this->sourceFieldName, 'joinColumns', 'Did you think of case sensitivity / plural s?'); } else { if (!isset($mapping['joinTable']['inverseJoinColumns'])) { throw MappingException::missingRequiredOption($this->sourceFieldName, 'inverseJoinColumns', 'Did you think of case sensitivity / plural s?'); } } } foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { $this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; $this->joinTableColumns[] = $joinColumn['name']; } foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { $this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; $this->joinTableColumns[] = $inverseJoinColumn['name']; } } if (isset($mapping['orderBy'])) { if (!is_array($mapping['orderBy'])) { throw new \InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy'])); } $this->orderBy = $mapping['orderBy']; } }
/** * {@inheritdoc} * * @param object $sourceEntity the entity source of this association * @param object $targetEntity the entity to load data in * @param EntityManager $em * @param array $joinColumnValues Values of the join columns of $sourceEntity. */ public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues = array()) { $targetClass = $em->getClassMetadata($this->targetEntityName); if ($this->isOwningSide) { $inverseField = isset($targetClass->inverseMappings[$this->sourceEntityName][$this->sourceFieldName]) ? $targetClass->inverseMappings[$this->sourceEntityName][$this->sourceFieldName]->sourceFieldName : false; // Mark inverse side as fetched in the hints, otherwise the UoW would // try to load it in a separate query (remember: to-one inverse sides can not be lazy). $hints = array(); if ($inverseField) { $hints['fetched'][$targetClass->name][$inverseField] = true; if ($targetClass->subClasses) { foreach ($targetClass->subClasses as $targetSubclassName) { $hints['fetched'][$targetSubclassName][$inverseField] = true; } } } /* cascade read-only status if ($em->getUnitOfWork()->isReadOnly($sourceEntity)) { $hints[Query::HINT_READ_ONLY] = true; } */ $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($joinColumnValues, $targetEntity, $this, $hints); if ($targetEntity !== null && $inverseField && !$targetClass->isCollectionValuedAssociation($inverseField)) { $targetClass->reflFields[$inverseField]->setValue($targetEntity, $sourceEntity); } } else { $conditions = array(); $sourceClass = $em->getClassMetadata($this->sourceEntityName); $owningAssoc = $targetClass->getAssociationMapping($this->mappedBy); // TRICKY: since the association is specular source and target are flipped foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { throw MappingException::joinColumnMustPointToMappedField($sourceClass->name, $sourceKeyColumn); } } $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity, $this); if ($targetEntity !== null) { $targetClass->setFieldValue($targetEntity, $this->mappedBy, $sourceEntity); } } return $targetEntity; }
/** * Sets the version field mapping used for versioning. Sets the default * value to use depending on the column type * * @param array $mapping The version field mapping array */ public function setVersionMapping(array &$mapping) { $this->isVersioned = true; $this->versionField = $mapping['fieldName']; if (!isset($mapping['default'])) { if ($mapping['type'] == 'integer') { $mapping['default'] = 1; } else { if ($mapping['type'] == 'datetime') { $mapping['default'] = 'CURRENT_TIMESTAMP'; } else { throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); } } } }
/** * {@inheritDoc} */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if (!$this->paths) { throw MappingException::pathRequired(); } $classes = array(); $includedFiles = array(); foreach ($this->paths as $path) { if (!is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY), '/^.+' . preg_quote($this->fileExtension) . '$/i', \RecursiveRegexIterator::GET_MATCH); foreach ($iterator as $file) { $sourceFile = realpath($file[0]); require_once $sourceFile; $includedFiles[] = $sourceFile; } } $declared = get_declared_classes(); foreach ($declared as $className) { $rc = new ReflectionClass($className); $sourceFile = $rc->getFileName(); if (in_array($sourceFile, $includedFiles) && $this->isExposed($className)) { $classes[] = $className; } } $this->classNames = $classes; return $classes; }
/** * Validates & completes the mapping. Mapping defaults are applied here. * * @param array $mapping * @throws MappingException If something is wrong with the mapping. */ protected function _validateAndCompleteMapping(array $mapping) { // Mandatory attributes for both sides if (!isset($mapping['fieldName'])) { throw MappingException::missingFieldName(); } $this->sourceFieldName = $mapping['fieldName']; if (!isset($mapping['sourceEntity'])) { throw MappingException::missingSourceEntity($mapping['fieldName']); } $this->sourceEntityName = $mapping['sourceEntity']; if (!isset($mapping['targetEntity'])) { throw MappingException::missingTargetEntity($mapping['fieldName']); } $this->targetEntityName = $mapping['targetEntity']; // Mandatory and optional attributes for either side if (!isset($mapping['mappedBy'])) { // Optional if (isset($mapping['joinTable']) && $mapping['joinTable']) { if ($mapping['joinTable']['name'][0] == '`') { $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); $mapping['joinTable']['quoted'] = true; } $this->joinTable = $mapping['joinTable']; } if (isset($mapping['inversedBy'])) { $this->inversedBy = $mapping['inversedBy']; } } else { $this->isOwningSide = false; $this->mappedBy = $mapping['mappedBy']; } // Optional attributes for both sides $this->fetchMode = isset($mapping['fetch']) ? $mapping['fetch'] : self::FETCH_LAZY; $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array(); if (in_array('all', $cascades)) { $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach'); } $this->isCascadeRemove = in_array('remove', $cascades); $this->isCascadePersist = in_array('persist', $cascades); $this->isCascadeRefresh = in_array('refresh', $cascades); $this->isCascadeMerge = in_array('merge', $cascades); $this->isCascadeDetach = in_array('detach', $cascades); }
/** * Validates the identifier mapping. * * @param ClassMetadata $class * * @throws MappingException When mapping does not have identifier */ protected function validateIdentifier(ClassMetadata $class) { if (!$class->hasIdentifier()) { throw MappingException::identifierRequired($class->getName()); } }
/** * Loads entities in $targetCollection using $em. * The data of $sourceEntity are used to restrict the collection * via the join table. * * @param object The owner of the collection. * @param object The collection to populate. * @param array */ public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array()) { $sourceClass = $em->getClassMetadata($this->sourceEntityName); $joinTableConditions = array(); if ($this->isOwningSide) { foreach ($this->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) { // getting id if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { throw MappingException::joinColumnMustPointToMappedField($sourceClass->name, $sourceKeyColumn); } } } else { $owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedBy]; // TRICKY: since the association is inverted source and target are flipped foreach ($owningAssoc->relationToTargetKeyColumns as $relationKeyColumn => $sourceKeyColumn) { // getting id if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { throw MappingException::joinColumnMustPointToMappedField($sourceClass->name, $sourceKeyColumn); } } } $persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName); $persister->loadManyToManyCollection($this, $joinTableConditions, $targetCollection); }
/** * Validates and completes the mapping. * * @param array $mapping * @override */ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); if ($this->isOwningSide()) { // owning side MUST have a join table if (!isset($mapping['joinTable'])) { throw MappingException::joinTableRequired($mapping['fieldName']); } // owning side MUST specify joinColumns if (!isset($mapping['joinTable']['joinColumns'])) { throw MappingException::invalidMapping($this->_sourceFieldName); } foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { $this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name']; $this->joinTableColumns[] = $joinColumn['name']; } $this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns); // owning side MUST specify inverseJoinColumns if (!isset($mapping['joinTable']['inverseJoinColumns'])) { throw MappingException::invalidMapping($this->_sourceFieldName); } foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { $this->targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name']; $this->joinTableColumns[] = $inverseJoinColumn['name']; } $this->targetKeyColumns = array_keys($this->targetToRelationKeyColumns); } }
/** * Validates & completes the mapping. Mapping defaults are applied here. * * @param array $mapping */ protected function _validateAndCompleteMapping(array $mapping) { // Mandatory attributes for both sides if (!isset($mapping['fieldName'])) { throw MappingException::missingFieldName(); } $this->sourceFieldName = $mapping['fieldName']; if (!isset($mapping['sourceEntity'])) { throw MappingException::missingSourceEntity($mapping['fieldName']); } $this->sourceEntityName = $mapping['sourceEntity']; if (!isset($mapping['targetEntity'])) { throw MappingException::missingTargetEntity($mapping['fieldName']); } $this->targetEntityName = $mapping['targetEntity']; // Mandatory and optional attributes for either side if (!isset($mapping['mappedBy'])) { // Optional if (isset($mapping['joinTable'])) { $this->joinTable = $mapping['joinTable']; } } else { $this->isOwningSide = false; $this->mappedByFieldName = $mapping['mappedBy']; } // Optional attributes for both sides $this->isOptional = isset($mapping['optional']) ? (bool) $mapping['optional'] : true; $this->cascades = isset($mapping['cascade']) ? (array) $mapping['cascade'] : array(); $this->isCascadeDelete = in_array('delete', $this->cascades); $this->isCascadeSave = in_array('save', $this->cascades); $this->isCascadeRefresh = in_array('refresh', $this->cascades); $this->isCascadeMerge = in_array('merge', $this->cascades); }