public function convertToDatabaseValue($value) { if ($value !== null && !is_array($value)) { throw MongoDBException::invalidValueForType('Hash', array('array', 'null'), $value); } return $value !== null ? (object) $value : null; }
public function convertToDatabaseValue($value) { if ($value !== null && !is_array($value)) { throw MongoDBException::invalidValueForType('Collection', array('array', 'null'), $value); } return $value !== null ? array_values($value) : null; }
/** * Resolves a registered namespace alias to the full namespace. * * @param string $documentNamespaceAlias * @return string * @throws MongoDBException */ public function getDocumentNamespace($documentNamespaceAlias) { if (!isset($this->attributes['documentNamespaces'][$documentNamespaceAlias])) { throw MongoDBException::unknownDocumentNamespace($documentNamespaceAlias); } return trim($this->attributes['documentNamespaces'][$documentNamespaceAlias], '\\'); }
/** * Resolves a registered namespace alias to the full namespace. * * @param string $alias * @return string * @throws MongoDBException */ public function getAliasNamespace($alias) { foreach (array_keys($this->getManagers()) as $name) { try { return $this->getManager($name)->getConfiguration()->getDocumentNamespace($alias); } catch (MongoDBException $e) { } } throw MongoDBException::unknownDocumentNamespace($alias); }
/** * {@inheritdoc} */ public function loadMetadataForClass($className, ClassMetadataInfo $class) { foreach ($this->drivers as $namespace => $driver) { if (strpos($className, $namespace) === 0) { $driver->loadMetadataForClass($className, $class); return; } } throw MongoDBException::classIsNotAValidDocument($className); }
public function __load() { if (!$this->__isInitialized__ && $this->__documentPersister__) { $this->__isInitialized__ = true; if ($this->__documentPersister__->load($this->__identifier__, $this) === null) { throw \Doctrine\ODM\MongoDB\MongoDBException::documentNotFound(get_class($this), $this->__identifier__); } unset($this->__documentPersister__, $this->__identifier__); } }
public function __clone() { if (!$this->__isInitialized__ && $this->__documentPersister__) { $this->__isInitialized__ = true; $class = $this->__documentPersister__->getClassMetadata(); $original = $this->__documentPersister__->load($this->__identifier__); if ($original === null) { throw \Doctrine\ODM\MongoDB\MongoDBException::documentNotFound(get_class($this), $this->__identifier__); } foreach ($class->reflFields as $field => $reflProperty) { $reflProperty->setValue($this, $reflProperty->getValue($original)); } unset($this->__documentPersister__, $this->__identifier__); } }
/** * Deletes an document as part of the current unit of work. * * This method is internally called during delete() cascades as it tracks * the already visited documents to prevent infinite recursions. * * @param object $document The document to delete. * @param array $visited The map of the already visited documents. * @throws InvalidArgumentException If the instance is a detached document. */ private function doRemove($document, array &$visited) { $oid = spl_object_hash($document); if (isset($visited[$oid])) { return; // Prevent infinite recursion } $visited[$oid] = $document; // mark visited $class = $this->dm->getClassMetadata(get_class($document)); $documentState = $this->getDocumentState($document); switch ($documentState) { case self::STATE_NEW: case self::STATE_REMOVED: // nothing to do break; case self::STATE_MANAGED: if (isset($class->lifecycleCallbacks[Events::preRemove])) { $class->invokeLifecycleCallbacks(Events::preRemove, $document); } if ($this->evm->hasListeners(Events::preRemove)) { $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($document, $this->dm)); } $this->scheduleForDelete($document); $this->cascadePreRemove($class, $document); break; case self::STATE_DETACHED: throw MongoDBException::detachedDocumentCannotBeRemoved(); default: throw MongoDBException::invalidDocumentState($documentState); } $this->cascadeRemove($document, $visited); }
/** * 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); } }
/** * Enable sharding for database which contains documents with given name. * * @param string $documentName * * @throws MongoDBException */ public function enableShardingForDbByDocumentName($documentName) { $dbName = $this->dm->getDocumentDatabase($documentName)->getName(); $adminDb = $this->dm->getConnection()->selectDatabase('admin'); $result = $adminDb->command(array('enableSharding' => $dbName)); // Error code is only available with MongoDB 3.2. MongoDB 3.0 only returns a message // Thus, check code if it exists and fall back on error message if ($result['ok'] == 1 || isset($result['code']) && $result['code'] == 23 || $result['errmsg'] == 'already enabled') { return; } throw MongoDBException::failedToEnableSharding($dbName, $result['errmsg']); }
/** * Enable sharding for database which contains documents with given name. * * @param string $documentName * * @throws MongoDBException */ public function enableShardingForDbByDocumentName($documentName) { $dbName = $this->dm->getDocumentDatabase($documentName)->getName(); $adminDb = $this->dm->getConnection()->selectDatabase('admin'); $result = $adminDb->command(array('enableSharding' => $dbName)); if ($result['ok'] != 1 && $result['errmsg'] !== 'already enabled') { throw MongoDBException::failedToEnableSharding($dbName, $result['errmsg']); } }
protected function findMappingFile($className) { $defaultFileName = str_replace('\\', '.', $className) . $this->fileExtension; foreach ($this->paths as $path) { if (!isset($this->prefixes[$path])) { if (file_exists($path . DIRECTORY_SEPARATOR . $defaultFileName)) { return $path . DIRECTORY_SEPARATOR . $defaultFileName; } continue; } $prefix = $this->prefixes[$path]; if (0 !== strpos($className, $prefix . '\\')) { continue; } $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', '.') . $this->fileExtension; if (file_exists($filename)) { return $filename; } throw MongoDBException::mappingNotFound($className, $filename); } throw MongoDBException::mappingNotFound($className, substr($className, strrpos($className, '\\') + 1) . $this->fileExtension); }
/** * Deletes a document as part of the current unit of work. * * This method is internally called during delete() cascades as it tracks * the already visited documents to prevent infinite recursions. * * @param object $document The document to delete. * @param array $visited The map of the already visited documents. * @throws MongoDBException */ private function doRemove($document, array &$visited) { $oid = spl_object_hash($document); if (isset($visited[$oid])) { return; // Prevent infinite recursion } $visited[$oid] = $document; // mark visited /* Cascade first, because scheduleForDelete() removes the entity from * the identity map, which can cause problems when a lazy Proxy has to * be initialized for the cascade operation. */ $this->cascadeRemove($document, $visited); $class = $this->dm->getClassMetadata(get_class($document)); $documentState = $this->getDocumentState($document); switch ($documentState) { case self::STATE_NEW: case self::STATE_REMOVED: // nothing to do break; case self::STATE_MANAGED: if (!empty($class->lifecycleCallbacks[Events::preRemove])) { $class->invokeLifecycleCallbacks(Events::preRemove, $document); } if ($this->evm->hasListeners(Events::preRemove)) { $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($document, $this->dm)); } $this->scheduleForDelete($document); break; case self::STATE_DETACHED: throw MongoDBException::detachedDocumentCannotBeRemoved(); default: throw MongoDBException::invalidDocumentState($documentState); } }
/** * Loads the metadata of the class in question and all it's ancestors whose metadata * is still not loaded. * * @param string $name The name of the class for which the metadata should get loaded. * @param array $tables The metadata collection to which the loaded metadata is added. */ private function loadMetadata($className) { if (!$this->initialized) { $this->initialize(); } $loaded = array(); $parentClasses = $this->getParentClasses($className); $parentClasses[] = $className; // Move down the hierarchy of parent classes, starting from the topmost class $parent = null; $visited = array(); foreach ($parentClasses as $className) { if (isset($this->loadedMetadata[$className])) { $parent = $this->loadedMetadata[$className]; if (!$parent->isMappedSuperclass && !$parent->isEmbeddedDocument) { array_unshift($visited, $className); } continue; } $class = $this->newClassMetadataInstance($className); if ($parent) { if (!$parent->isMappedSuperclass) { $class->setInheritanceType($parent->inheritanceType); $class->setDiscriminatorField($parent->discriminatorField); $class->setDiscriminatorMap($parent->discriminatorMap); } $class->setIdGeneratorType($parent->generatorType); $this->addInheritedFields($class, $parent); $this->addInheritedIndexes($class, $parent); $class->setIdentifier($parent->identifier); $class->setVersioned($parent->isVersioned); $class->setVersionField($parent->versionField); $class->setLifecycleCallbacks($parent->lifecycleCallbacks); $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); $class->setFile($parent->getFile()); } // Invoke driver try { $this->driver->loadMetadataForClass($className, $class); } catch (ReflectionException $e) { throw MongoDBException::reflectionFailure($className, $e); } if (!$class->identifier && !$class->isMappedSuperclass && !$class->isEmbeddedDocument) { throw MongoDBException::identifierRequired($className); } if ($parent && !$parent->isMappedSuperclass && !$class->isEmbeddedDocument) { if ($parent->generatorType) { $class->setIdGeneratorType($parent->generatorType); } if ($parent->generatorOptions) { $class->setIdGeneratorOptions($parent->generatorOptions); } if ($parent->idGenerator) { $class->setIdGenerator($parent->idGenerator); } } else { $this->completeIdGeneratorMapping($class); } if ($parent && $parent->isInheritanceTypeSingleCollection()) { $class->setDatabase($parent->getDatabase()); $class->setCollection($parent->getCollection()); } $db = $class->getDatabase() ?: $this->config->getDefaultDB(); $class->setDatabase($this->dm->formatDBName($db)); $class->setParentClasses($visited); if ($this->evm->hasListeners(Events::loadClassMetadata)) { $eventArgs = new \Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs($class, $this->dm); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } $this->loadedMetadata[$className] = $class; $parent = $class; if (!$class->isMappedSuperclass && !$class->isEmbeddedDocument) { array_unshift($visited, $className); } $loaded[] = $className; } return $loaded; }
/** * Execute the query and returns the results. * * @return mixed */ public function execute() { $uow = $this->dm->getUnitOfWork(); if ($this->isIndexRequired() && !$this->isIndexed()) { throw MongoDBException::queryNotIndexed($this->class->name, $this->getUnindexedFields()); } $results = parent::execute(); $hints = array(); if ($this->refresh) { $hints[self::HINT_REFRESH] = true; } if ($this->query['slaveOkay'] === true) { $hints[self::HINT_SLAVE_OKAY] = true; } // Unwrap the BaseEagerCursor if ($results instanceof BaseEagerCursor) { $results = $results->getCursor(); } // Convert the regular mongodb cursor to the odm cursor if ($results instanceof BaseCursor) { $results = $this->wrapCursor($results, $hints); } // Wrap odm cursor with EagerCursor if true if ($this->query['eagerCursor'] === true) { $results = new EagerCursor($results, $this->dm->getUnitOfWork(), $this->class); } // GeoLocationFindQuery just returns an instance of ArrayIterator so we have to // iterator over it and hydrate each object. if ($this->query['type'] === self::TYPE_GEO_LOCATION && $this->hydrate) { foreach ($results as $key => $result) { $document = $result['obj']; if ($this->class->distance) { $document[$this->class->distance] = $result['dis']; } $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $hints); } $results->reset(); } if ($this->primers) { $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); foreach ($this->primers as $fieldName => $primer) { if ($primer) { $documentPersister->primeCollection($results, $fieldName, $primer, $hints); } } } if ($this->hydrate && is_array($results) && isset($results['_id'])) { // Convert a single document array to a document object $results = $uow->getOrCreateDocument($this->class->name, $results, $hints); } return $results; }
/** * @param $document * @return array * @throws MongoDBException */ public function getShardKeyQuery($document) { $shardKeysQueryPart = array(); $dcs = $this->uow->getDocumentChangeSet($document); $data = $this->uow->getDocumentActualData($document); $md = $this->dm->getMetadataFactory()->getMetadataFor(get_class($document)); $keys = $md->shardKeys; $fieldMappings = $this->dm->getClassMetadata(get_class($document))->fieldMappings; foreach ($keys as $key) { if ($key !== 'id') { $queryKey = $fieldMappings[$key]['name']; } else { $queryKey = '_id'; } //If the document is new, we can ignore shard key value, otherwise throw exception $isUpdate = $this->uow->isScheduledForUpdate($document); if ($isUpdate && isset($dcs[$key]) && $dcs[$key][0] !== null && $dcs[$key][0] != $dcs[$key][1] && (!isset($options['upsert']) || isset($options['upsert']) && $options['upsert'] === true)) { throw MongoDBException::shardKeyChange($key); } if (!isset($data[$key])) { throw MongoDBException::shardKeyMissing($key); } $new = $data[$key]; $mapping = $fieldMappings[$key]; // @Field, @String, @Date, etc. if (!isset($mapping['association'])) { if (isset($new) || $mapping['nullable'] === true) { $shardKeysQueryPart[$queryKey] = is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new); } // @ReferenceOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { if (isset($new) || $mapping['nullable'] === true) { $shardKeysQueryPart[$queryKey] = is_null($new) ? null : $this->pb->prepareReferencedDocumentValue($mapping, $new); } // @ReferenceMany } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { // Do nothing right now } } return $shardKeysQueryPart; }
/** * Execute the query and returns the results. * * @throws \Doctrine\ODM\MongoDB\MongoDBException * @return mixed */ public function execute() { if ($this->isIndexRequired() && !$this->isIndexed()) { throw MongoDBException::queryNotIndexed($this->class->name, $this->getUnindexedFields()); } $results = parent::execute(); if (!$this->hydrate) { return $results; } $uow = $this->dm->getUnitOfWork(); /* A geoNear command returns an ArrayIterator, where each result is an * object with "dis" (computed distance) and "obj" (original document) * properties. If hydration is enabled, eagerly hydrate these results. * * Other commands results are not handled, since their results may not * resemble documents in the collection. */ if ($this->query['type'] === self::TYPE_GEO_NEAR) { foreach ($results as $key => $result) { $document = $result['obj']; if ($this->class->distance !== null) { $document[$this->class->distance] = $result['dis']; } $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints); } $results->reset(); } /* If a single document is returned from a findAndModify command and it * includes the identifier field, attempt hydration. */ if (($this->query['type'] === self::TYPE_FIND_AND_UPDATE || $this->query['type'] === self::TYPE_FIND_AND_REMOVE) && is_array($results) && isset($results['_id'])) { $results = $uow->getOrCreateDocument($this->class->name, $results, $this->unitOfWorkHints); } if (!empty($this->primers)) { $referencePrimer = new ReferencePrimer($this->dm, $uow); foreach ($this->primers as $fieldName => $primer) { $primer = is_callable($primer) ? $primer : null; $documents = $results instanceof Iterator ? $results : array($results); $referencePrimer->primeReferences($this->class, $documents, $fieldName, $this->unitOfWorkHints, $primer); } } return $results; }
/** * Prepares update array for document, using atomic operators * * @param mixed $document * @return array */ public function prepareUpdateData($document) { $oid = spl_object_hash($document); $changeset = $this->_uow->getDocumentChangeSet($document); $result = array(); foreach ($this->_class->fieldMappings as $mapping) { if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { continue; } $old = isset($changeset[$mapping['fieldName']][0]) ? $changeset[$mapping['fieldName']][0] : null; $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null; $new = $this->_prepareValue($mapping, $new); $old = $this->_prepareValue($mapping, $old); if ($this->_class->isIdentifier($mapping['fieldName']) && !$this->_equals($new, $old)) { throw MongoDBException::identifierCannotBeUpdated(); } if ($mapping['type'] === 'many' || $mapping['type'] === 'collection') { $this->_addArrayUpdateAtomicOperator($mapping, (array) $new, (array) $old, $result); } else { $this->_addFieldUpdateAtomicOperator($mapping, $new, $old, $result); } } return $result; }
/** * Gets the mapping of a field. * * @param string $fieldName The field name. * @return array The field mapping. */ public function getFieldMapping($fieldName) { if (!isset($this->fieldMappings[$fieldName])) { throw MongoDBException::mappingNotFound($this->name, $fieldName); } return $this->fieldMappings[$fieldName]; }
/** * 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); } }
/** * {@inheritDoc} */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if (!$this->paths) { throw MongoDBException::pathRequired(); } $classes = array(); $includedFiles = array(); foreach ($this->paths as $path) { if (!is_dir($path)) { throw MongoDBException::fileMappingDriversRequireConfiguredDirectoryPath(); } $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; }
/** * 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() { $classes = array(); if ($this->paths) { foreach ((array) $this->paths as $path) { if (!is_dir($path)) { throw MongoDBException::fileMappingDriversRequireConfiguredDirectoryPath(); } $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY); foreach ($iterator as $file) { if (($fileName = $file->getBasename($this->fileExtension)) == $file->getBasename()) { continue; } // NOTE: All files found here means classes are not transient! $classes[] = str_replace('.', '\\', $fileName); } } } return $classes; }
/** * Throws an exception if the DocumentManager is closed or currently not active. * * @throws ORMException If the DocumentManager is closed. */ private function _errorIfClosed() { if ($this->_closed) { throw MongoDBException::documentManagerClosed(); } }
/** * If the document is new, ignore shard key field value, otherwise throw an exception. * Also, shard key field should be present in actual document data. * * @param object $document * @param string $shardKeyField * @param array $actualDocumentData * * @throws MongoDBException */ private function guardMissingShardKey($document, $shardKeyField, $actualDocumentData) { $dcs = $this->uow->getDocumentChangeSet($document); $isUpdate = $this->uow->isScheduledForUpdate($document); $fieldMapping = $this->class->getFieldMappingByDbFieldName($shardKeyField); $fieldName = $fieldMapping['fieldName']; if ($isUpdate && isset($dcs[$fieldName]) && $dcs[$fieldName][0] != $dcs[$fieldName][1]) { throw MongoDBException::shardKeyFieldCannotBeChanged($shardKeyField, $this->class->getName()); } if (!isset($actualDocumentData[$fieldName])) { throw MongoDBException::shardKeyFieldMissing($shardKeyField, $this->class->getName()); } }
/** * Overrides an already defined type to use a different implementation. * * @static * @param string $name * @param string $className * @throws MongoDBException */ public static function overrideType($name, $className) { if (!isset(self::$typesMap[$name])) { throw MongoDBException::typeNotFound($name); } self::$typesMap[$name] = $className; }
/** * Validates the identifier mapping. * * @param ClassMetadata $class */ protected function validateIdentifier($class) { if (!$class->identifier && !$class->isMappedSuperclass && !$class->isEmbeddedDocument) { throw MongoDBException::identifierRequired($class->name); } }
/** * Sets default repository class. * * @param string $className * * @return void * * @throws MongoDBException If not is a ObjectRepository */ public function setDefaultRepositoryClassName($className) { $reflectionClass = new \ReflectionClass($className); if (!$reflectionClass->implementsInterface(ObjectRepository::class)) { throw MongoDBException::invalidDocumentRepository($className); } $this->attributes['defaultRepositoryClassName'] = $className; }
/** * Returns the MongoCollection instance for a class. * * @param string $className The class name. * @return Doctrine\ODM\MongoDB\MongoCollection */ public function getDocumentCollection($className) { $metadata = $this->_metadataFactory->getMetadataFor($className); $collection = $metadata->getCollection(); $db = $metadata->getDB(); $key = $db . '.' . $collection; if ($collection && !isset($this->_documentCollections[$key])) { if ($metadata->isFile()) { $collection = $this->_mongo->selectDB($metadata->getDB())->getGridFS($collection); } else { $collection = $this->_mongo->selectDB($metadata->getDB())->selectCollection($collection); } $mongoCollection = new MongoCollection($collection, $metadata, $this); $this->_documentCollections[$key] = $mongoCollection; } if (!isset($this->_documentCollections[$key])) { throw MongoDBException::documentNotMappedToCollection($className); } return $this->_documentCollections[$key]; }