/** * Prepares a query value and converts the php value to the database value if it is an identifier. * It also handles converting $fieldName to the database name if they are different. * * @param string $fieldName * @param string $value * @return mixed $value */ private function prepareQueryValue(&$fieldName, $value) { // Process "association.fieldName" if (strpos($fieldName, '.') !== false) { $e = explode('.', $fieldName); $mapping = $this->class->getFieldMapping($e[0]); if ($this->class->hasField($e[0])) { $name = $this->class->fieldMappings[$e[0]]['name']; if ($name !== $e[0]) { $e[0] = $name; } } if (isset($mapping['targetDocument'])) { $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); if ($targetClass->hasField($e[1])) { if ($targetClass->identifier === $e[1] || $e[1] === '$id') { $fieldName = $e[0] . '.$id'; $value = $targetClass->getDatabaseIdentifierValue($value); } } } // Process all non identifier fields } elseif ($this->class->hasField($fieldName) && !$this->class->isIdentifier($fieldName)) { $name = $this->class->fieldMappings[$fieldName]['name']; $mapping = $this->class->fieldMappings[$fieldName]; if ($name !== $fieldName) { $fieldName = $name; } // Process identifier } elseif ($fieldName === $this->class->identifier || $fieldName === '_id') { $fieldName = '_id'; $value = $this->class->getDatabaseIdentifierValue($value); } return $value; }
/** * Prepares a query value and converts the php value to the database value if it is an identifier. * It also handles converting $fieldName to the database name if they are different. * * @param string $fieldName * @param string $value * @return mixed $value */ private function prepareQueryValue(&$fieldName, $value) { // Process "association.fieldName" if (strpos($fieldName, '.') !== false) { $e = explode('.', $fieldName); $mapping = $this->class->getFieldMapping($e[0]); $name = $mapping['name']; if ($name !== $e[0]) { $e[0] = $name; } if (isset($mapping['targetDocument'])) { $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); if ($targetClass->hasField($e[1])) { if ($targetClass->identifier === $e[1]) { $e[1] = '$id'; if (is_array($value)) { foreach ($value as $k => $v) { $value[$k] = $targetClass->getDatabaseIdentifierValue($v); } } else { $value = $targetClass->getDatabaseIdentifierValue($value); } } else { $targetMapping = $targetClass->getFieldMapping($e[1]); $targetName = $targetMapping['name']; if ($targetName !== $e[1]) { $e[1] = $targetName; } } $fieldName = $e[0] . '.' . $e[1]; } } // Process all non identifier fields // We only change the field names here to the mongodb field name used for persistence } elseif ($this->class->hasField($fieldName) && !$this->class->isIdentifier($fieldName)) { $name = $this->class->fieldMappings[$fieldName]['name']; $mapping = $this->class->fieldMappings[$fieldName]; if ($name !== $fieldName) { $fieldName = $name; } // Process identifier } elseif ($this->class->hasField($fieldName) && $this->class->isIdentifier($fieldName) || $fieldName === '_id') { $fieldName = '_id'; if (is_array($value)) { foreach ($value as $k => $v) { if ($k[0] === '$' && is_array($v)) { foreach ($v as $k2 => $v2) { $value[$k][$k2] = $this->class->getDatabaseIdentifierValue($v2); } } else { $value[$k] = $this->class->getDatabaseIdentifierValue($v); } } } else { $value = $this->class->getDatabaseIdentifierValue($value); } } return $value; }
/** * Prepare where values converting document object field names to the document collection * field name. * * @param string $fieldName * @param string $value * @return string $value */ private function prepareWhereValue(&$fieldName, $value) { if (strpos($fieldName, '.') !== false) { $e = explode('.', $fieldName); $mapping = $this->class->getFieldMapping($e[0]); if ($this->class->hasField($e[0])) { $name = $this->class->fieldMappings[$e[0]]['name']; if ($name !== $e[0]) { $e[0] = $name; } } if (isset($mapping['targetDocument'])) { $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); if ($targetClass->hasField($e[1]) && $targetClass->identifier === $e[1]) { $fieldName = $e[0] . '.$id'; $value = $targetClass->getDatabaseIdentifierValue($value); } elseif ($e[1] === '$id') { $value = $targetClass->getDatabaseIdentifierValue($value); } } } elseif ($this->class->hasField($fieldName) && ! $this->class->isIdentifier($fieldName)) { $name = $this->class->fieldMappings[$fieldName]['name']; if ($name !== $fieldName) { $fieldName = $name; } } else { if ($fieldName === $this->class->identifier || $fieldName === '_id') { $fieldName = '_id'; if (is_array($value)) { if (isset($value[$this->cmd.'in'])) { foreach ($value[$this->cmd.'in'] as $k => $v) { $value[$this->cmd.'in'][$k] = $this->class->getDatabaseIdentifierValue($v); } } else { foreach ($value as $k => $v) { $value[$k] = $this->class->getDatabaseIdentifierValue($v); } } } else { $value = $this->class->getDatabaseIdentifierValue($value); } } } return $value; }
/** * Add mapping data to a translation entity. * * @param ClassMetadata $metadata */ private function mapTranslation(ClassMetadata $metadata) { // In the case A -> B -> TranslationInterface, B might not have mapping defined as it // is probably defined in A, so in that case, we just return. if (!isset($this->configs[$metadata->name])) { return; } $metadata->mapManyToOne(array('fieldName' => 'translatable', 'targetEntity' => $this->configs[$metadata->name]['model'], 'inversedBy' => 'translations', 'joinColumns' => array(array('name' => 'translatable_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE', 'nullable' => false)))); if (!$metadata->hasField('locale')) { $metadata->mapField(array('fieldName' => 'locale', 'type' => 'string', 'nullable' => false)); } // Map unique index. $columns = array($metadata->getSingleAssociationJoinColumnName('translatable'), 'locale'); if (!$this->hasUniqueConstraint($metadata, $columns)) { $constraints = isset($metadata->table['uniqueConstraints']) ? $metadata->table['uniqueConstraints'] : array(); $constraints[$metadata->getTableName() . '_uniq_trans'] = array('columns' => $columns); $metadata->setPrimaryTable(array('uniqueConstraints' => $constraints)); } }
/** * Adds support for magic finders. * * @param string $method * @param array $arguments * @throws MongoDBException * @throws \BadMethodCallException If the method called is an invalid find* method * or no find* method at all and therefore an invalid * method call. * @return array|object The found document/documents. */ public function __call($method, $arguments) { if (substr($method, 0, 6) == 'findBy') { $by = substr($method, 6, strlen($method)); $method = 'findBy'; } elseif (substr($method, 0, 9) == 'findOneBy') { $by = substr($method, 9, strlen($method)); $method = 'findOneBy'; } else { throw new \BadMethodCallException("Undefined method '{$method}'. The method name must start with " . "either findBy or findOneBy!"); } if (!isset($arguments[0])) { throw MongoDBException::findByRequiresParameter($method . $by); } $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); if ($this->class->hasField($fieldName)) { return $this->{$method}(array($fieldName => $arguments[0])); } else { throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method . $by); } }
/** * Adds support for magic finders. * * @param string $method * @param array $arguments * @throws MongoDBException * @throws \BadMethodCallException If the method called is an invalid find* method * or no find* method at all and therefore an invalid * method call. * @return array|object The found document/documents. */ public function __call($method, $arguments) { if (strpos($method, 'findBy') === 0) { $by = substr($method, 6, strlen($method)); $method = 'findBy'; } elseif (strpos($method, 'findOneBy') === 0) { $by = substr($method, 9, strlen($method)); $method = 'findOneBy'; } else { throw new \BadMethodCallException("Undefined method: '{$method}'. The method name must start with 'findBy' or 'findOneBy'!"); } if (!isset($arguments[0])) { throw MongoDBException::findByRequiresParameter($method . $by); } $fieldName = Inflector::camelize($by); if ($this->class->hasField($fieldName)) { return $this->{$method}(array($fieldName => $arguments[0])); } else { throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method . $by); } }
/** * Return the document field mapping for a property path. * * @param ClassMetadata $metadata * @param object $document * @param string $path * @return array * @throw ConstraintDefinitionException if no field mapping exists for the property path */ private function getFieldMappingForPropertyPath(ClassMetadata $metadata, $document, $path) { // Extract the first part of the property path before any dot separator $fieldName = false !== ($beforeDot = strstr($path, '.', true)) ? $beforeDot : $path; if (!$metadata->hasField($fieldName)) { throw new ConstraintDefinitionException(sprintf('Mapping for "%s" does not exist for "%s"', $path, $metadata->name)); } return $metadata->getFieldMapping($fieldName); }
/** * Add mapping data to a translation entity * * @param ClassMetadata $metadata */ private function mapTranslation(ClassMetadata $metadata) { // In the case A -> B -> TranslationInterface, B might not have mapping defined as it // is probably defined in A, so in that case, we just return; if (!isset($this->mappings[$metadata->name])) { return; } $config = $this->mappings[$metadata->name]; $mapping = $config['translation']['mapping']; $metadata->isEmbeddedDocument = true; $metadata->isMappedSuperclass = false; $metadata->setIdentifier(null); // Map locale field. if (!$metadata->hasField($mapping['translation']['locale'])) { $metadata->mapField(array('fieldName' => $mapping['translation']['locale'], 'type' => 'string')); } // Map unique index. $keys = array($mapping['translation']['translatable'] => 1, $mapping['translation']['locale'] => 1); if (!$this->hasUniqueIndex($metadata, $keys)) { $metadata->addIndex($keys, array('unique' => true)); } }
/** * @param ClassMetadata $class * @return array */ private function prepareIndexes(ClassMetadata $class) { $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name); $indexes = $class->getIndexes(); $newIndexes = array(); foreach ($indexes as $index) { $newIndex = array('keys' => array(), 'options' => $index['options']); foreach ($index['keys'] as $key => $value) { $key = $persister->prepareFieldName($key); if ($class->hasField($key)) { $mapping = $class->getFieldMapping($key); $newIndex['keys'][$mapping['name']] = $value; } else { $newIndex['keys'][$key] = $value; } } $newIndexes[] = $newIndex; } return $newIndexes; }
/** * Check if the method is a short identifier getter. * * What does this mean? For proxy objects the identifier is already known, * however accessing the getter for this identifier usually triggers the * lazy loading, leading to a query that may not be necessary if only the * ID is interesting for the userland code (for example in views that * generate links to the document, but do not display anything else). * * @param ReflectionMethod $method * @param ClassMetadata $class * @return bool */ private function isShortIdentifierGetter($method, $class) { $identifier = lcfirst(substr($method->getName(), 3)); $cheapCheck = $method->getNumberOfParameters() == 0 && substr($method->getName(), 0, 3) == "get" && $class->identifier === $identifier && $class->hasField($identifier) && $method->getEndLine() - $method->getStartLine() <= 4 && in_array($class->fieldMappings[$identifier]['type'], array('id', 'custom_id')); if ($cheapCheck) { $code = file($method->getDeclaringClass()->getFileName()); $code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1))); $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); if (preg_match($pattern, $code)) { return true; } } return false; }
/** {@inheritdoc} */ public function hasField($name) { return $this->classMetadata->hasField($name) && !$this->classMetadata->hasAssociation($name); }
/** * {@inheritdoc} */ public function hasField($fieldName) { return $this->classMetadata->hasField($fieldName); }