Exemple #1
0
 /**
  * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
  * connected to the given <tt>DocumentManager</tt>.
  *
  * @param \CosmoW\ODM\Riak\DocumentManager $documentManager The DocumentManager the new factory works for.
  * @param string                                $proxyDir        The directory to use for the proxy classes. It
  *                                                               must exist.
  * @param string                                $proxyNamespace  The namespace to use for the proxy classes.
  * @param integer                               $autoGenerate    Whether to automatically generate proxy classes.
  */
 public function __construct(DocumentManager $documentManager, $proxyDir, $proxyNamespace, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
 {
     $this->metadataFactory = $documentManager->getMetadataFactory();
     $this->uow = $documentManager->getUnitOfWork();
     $this->proxyNamespace = $proxyNamespace;
     $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNamespace);
     $proxyGenerator->setPlaceholder('baseProxyInterface', 'CosmoW\\ODM\\Riak\\Proxy\\Proxy');
     parent::__construct($proxyGenerator, $this->metadataFactory, $autoGenerate);
 }
 /**
  * Create a new repository instance for a document class.
  *
  * @param DocumentManager $documentManager The DocumentManager instance.
  * @param string          $documentName    The name of the document.
  *
  * @return \Doctrine\Common\Persistence\ObjectRepository
  */
 protected function createRepository(DocumentManager $documentManager, $documentName)
 {
     $metadata = $documentManager->getClassMetadata($documentName);
     $repositoryClassName = $metadata->customRepositoryClassName;
     if ($repositoryClassName === null) {
         $configuration = $documentManager->getConfiguration();
         $repositoryClassName = $configuration->getDefaultRepositoryClassName();
     }
     return new $repositoryClassName($documentManager, $documentManager->getUnitOfWork(), $metadata);
 }
 /**
  * INTERNAL:
  * Sets the collection's owning entity together with the AssociationMapping that
  * describes the association between the owner and the elements of the collection.
  *
  * @param object $document
  * @param array $mapping
  */
 public function setOwner($document, array $mapping)
 {
     $this->owner = $document;
     $this->mapping = $mapping;
     if (!empty($this->mapping['targetDocument'])) {
         $this->typeClass = $this->dm->getClassMetadata($this->mapping['targetDocument']);
     }
 }
Exemple #4
0
 /**
  * Create the document database for a mapped class.
  *
  * @param string $documentName
  * @throws \InvalidArgumentException
  */
 public function createDocumentDatabase($documentName)
 {
     $class = $this->dm->getClassMetadata($documentName);
     if ($class->isMappedSuperclass || $class->isEmbeddedDocument) {
         throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes or embedded documents.');
     }
     $this->dm->getDocumentDatabase($documentName)->execute("function() { return true; }");
 }
 /** @inheritDoc */
 public function generate(DocumentManager $dm, $document)
 {
     $className = get_class($document);
     $db = $dm->getDocumentDatabase($className);
     $coll = $this->collection ?: 'doctrine_increment_ids';
     $key = $this->key ?: $dm->getDocumentCollection($className)->getName();
     $query = array('_id' => $key);
     $newObj = array('$inc' => array('current_id' => 1));
     $command = array();
     $command['findandmodify'] = $coll;
     $command['query'] = $query;
     $command['update'] = $newObj;
     $command['upsert'] = true;
     $command['new'] = true;
     $result = $db->command($command);
     return $result['value']['current_id'];
 }
Exemple #6
0
 /**
  * Prepare the Cursor returned by {@link Query::execute()}.
  *
  * This method will wrap the base Cursor with an ODM Cursor or EagerCursor,
  * and set the hydrate option and UnitOfWork hints. This occurs in addition
  * to any preparation done by the base Query class.
  *
  * @see \CosmoW\Riak\Cursor::prepareCursor()
  * @param BaseCursor $cursor
  * @return CursorInterface
  */
 protected function prepareCursor(BaseCursor $cursor)
 {
     $cursor = parent::prepareCursor($cursor);
     // Convert the base Cursor into an ODM Cursor
     $cursorClass = !empty($this->query['eagerCursor']) ? 'CosmoW\\ODM\\Riak\\EagerCursor' : 'CosmoW\\ODM\\Riak\\Cursor';
     $cursor = new $cursorClass($cursor, $this->dm->getUnitOfWork(), $this->class);
     $cursor->hydrate($this->hydrate);
     $cursor->setHints($this->unitOfWorkHints);
     if (!empty($this->primers)) {
         $referencePrimer = new ReferencePrimer($this->dm, $this->dm->getUnitOfWork());
         $cursor->enableReferencePriming($this->primers, $referencePrimer);
     }
     return $cursor;
 }
Exemple #7
0
 /**
  * Get Discriminator Values
  *
  * @param \Iterator|array $classNames
  * @return array an array of discriminatorValues (mixed type)
  * @throws \InvalidArgumentException if the number of found collections > 1
  */
 private function getDiscriminatorValues($classNames)
 {
     $discriminatorValues = array();
     $collections = array();
     foreach ($classNames as $className) {
         $class = $this->dm->getClassMetadata($className);
         $discriminatorValues[] = $class->discriminatorValue;
         $key = $this->dm->getDocumentDatabase($className)->getName() . '.' . $class->getCollection();
         $collections[$key] = $key;
     }
     if (count($collections) > 1) {
         throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.');
     }
     return $discriminatorValues;
 }
 /**
  * Executes a query updating the given document.
  *
  * @param object $document
  * @param array $newObj
  * @param array $options
  */
 private function executeQuery($document, array $newObj, array $options)
 {
     $className = get_class($document);
     $class = $this->dm->getClassMetadata($className);
     $id = $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document));
     $query = array('_id' => $id);
     if ($class->isVersioned) {
         $query[$class->versionField] = $class->reflFields[$class->versionField]->getValue($document);
     }
     $collection = $this->dm->getDocumentCollection($className);
     $result = $collection->update($query, $newObj, $options);
     if ($class->isVersioned && !$result['n']) {
         throw LockException::lockFailed($document);
     }
 }
Exemple #9
0
 /**
  * Adds identifiers from a PersistentCollection to $groupedIds.
  *
  * If the relation contains simple references, the mapping is assumed to
  * have a target document class defined. Without that, there is no way to
  * infer the class of the referenced documents.
  *
  * @param PersistentCollection $persistentCollection
  * @param array                $groupedIds
  */
 private function addManyReferences(PersistentCollection $persistentCollection, array &$groupedIds)
 {
     $mapping = $persistentCollection->getMapping();
     if (!empty($mapping['simple'])) {
         $className = $mapping['targetDocument'];
         $class = $this->dm->getClassMetadata($className);
     }
     foreach ($persistentCollection->getMongoData() as $reference) {
         if (!empty($mapping['simple'])) {
             $id = $reference;
         } else {
             $id = $reference['$id'];
             $className = $this->uow->getClassNameForAssociation($mapping, $reference);
             $class = $this->dm->getClassMetadata($className);
         }
         $document = $this->uow->tryGetById($id, $class);
         if (!$document || $document instanceof Proxy && !$document->__isInitialized()) {
             $id = $class->getPHPIdentifierValue($id);
             $groupedIds[$className][serialize($id)] = $id;
         }
     }
 }
 /**
  * Finds a document by its identifier
  *
  * @param string|object $id The identifier
  * @param int $lockMode
  * @param int $lockVersion
  * @throws Mapping\MappingException
  * @throws LockException
  * @return object The document.
  */
 public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
 {
     if ($id === null) {
         return;
     }
     /* TODO: What if the ID object has a field with the same name as the
      * class' mapped identifier field name?
      */
     if (is_array($id)) {
         list($identifierFieldName) = $this->class->getIdentifierFieldNames();
         if (isset($id[$identifierFieldName])) {
             $id = $id[$identifierFieldName];
         }
     }
     // Check identity map first
     if ($document = $this->uow->tryGetById($id, $this->class)) {
         if ($lockMode != LockMode::NONE) {
             $this->dm->lock($document, $lockMode, $lockVersion);
         }
         return $document;
         // Hit!
     }
     $criteria = array('_id' => $id);
     if ($lockMode == LockMode::NONE) {
         return $this->getDocumentPersister()->load($criteria);
     }
     if ($lockMode == LockMode::OPTIMISTIC) {
         if (!$this->class->isVersioned) {
             throw LockException::notVersioned($this->documentName);
         }
         if ($document = $this->getDocumentPersister()->load($criteria)) {
             $this->uow->lock($document, $lockMode, $lockVersion);
         }
         return $document;
     }
     return $this->getDocumentPersister()->load($criteria, null, array(), $lockMode);
 }
Exemple #11
0
 /**
  * Hydrate array of Riak document data into the given document object.
  *
  * @param object $document  The document object to hydrate the data into.
  * @param array $data The array of document data.
  * @param array $hints Any hints to account for during reconstitution/lookup of the document.
  * @return array $values The array of hydrated values.
  */
 public function hydrate($document, $data, array $hints = array())
 {
     $metadata = $this->dm->getClassMetadata(get_class($document));
     // Invoke preLoad lifecycle events and listeners
     if (!empty($metadata->lifecycleCallbacks[Events::preLoad])) {
         $args = array(&$data);
         $metadata->invokeLifecycleCallbacks(Events::preLoad, $document, $args);
     }
     if ($this->evm->hasListeners(Events::preLoad)) {
         $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($document, $this->dm, $data));
     }
     // alsoLoadMethods may transform the document before hydration
     if (!empty($metadata->alsoLoadMethods)) {
         foreach ($metadata->alsoLoadMethods as $method => $fieldNames) {
             foreach ($fieldNames as $fieldName) {
                 // Invoke the method only once for the first field we find
                 if (array_key_exists($fieldName, $data)) {
                     $document->{$method}($data[$fieldName]);
                     continue 2;
                 }
             }
         }
     }
     $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints);
     if ($document instanceof Proxy) {
         $document->__isInitialized__ = true;
     }
     // Invoke the postLoad lifecycle callbacks and listeners
     if (!empty($metadata->lifecycleCallbacks[Events::postLoad])) {
         $metadata->invokeLifecycleCallbacks(Events::postLoad, $document);
     }
     if ($this->evm->hasListeners(Events::postLoad)) {
         $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($document, $this->dm));
     }
     return $data;
 }
Exemple #12
0
 /**
  * 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 mixed $value
  * @param ClassMetadata $class        Defaults to $this->class
  * @param boolean $prepareValue Whether or not to prepare the value
  * @return array        Prepared field name and value
  */
 private function prepareQueryElement($fieldName, $value = null, $class = null, $prepareValue = true)
 {
     $class = isset($class) ? $class : $this->class;
     // @todo Consider inlining calls to ClassMetadataInfo methods
     // Process all non-identifier fields by translating field names
     if ($class->hasField($fieldName) && !$class->isIdentifier($fieldName)) {
         $mapping = $class->fieldMappings[$fieldName];
         $fieldName = $mapping['name'];
         if (!$prepareValue) {
             return array($fieldName, $value);
         }
         // Prepare mapped, embedded objects
         if (!empty($mapping['embedded']) && is_object($value) && !$this->dm->getMetadataFactory()->isTransient(get_class($value))) {
             return array($fieldName, $this->pb->prepareEmbeddedDocumentValue($mapping, $value));
         }
         // No further preparation unless we're dealing with a simple reference
         if (empty($mapping['reference']) || empty($mapping['simple'])) {
             return array($fieldName, $value);
         }
         // Additional preparation for one or more simple reference values
         $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']);
         if (!is_array($value)) {
             return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
         }
         // Objects without operators or with DBRef fields can be converted immediately
         if (!$this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
             return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
         }
         return array($fieldName, $this->prepareQueryExpression($value, $targetClass));
     }
     // Process identifier fields
     if ($class->hasField($fieldName) && $class->isIdentifier($fieldName) || $fieldName === '_id') {
         $fieldName = '_id';
         if (!$prepareValue) {
             return array($fieldName, $value);
         }
         if (!is_array($value)) {
             return array($fieldName, $class->getDatabaseIdentifierValue($value));
         }
         // Objects without operators or with DBRef fields can be converted immediately
         if (!$this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
             return array($fieldName, $class->getDatabaseIdentifierValue($value));
         }
         return array($fieldName, $this->prepareQueryExpression($value, $class));
     }
     // No processing for unmapped, non-identifier, non-dotted field names
     if (strpos($fieldName, '.') === false) {
         return array($fieldName, $value);
     }
     /* Process "fieldName.objectProperty" queries (on arrays or objects).
      *
      * We can limit parsing here, since at most three segments are
      * significant: "fieldName.objectProperty" with an optional index or key
      * for collections stored as either BSON arrays or objects.
      */
     $e = explode('.', $fieldName, 4);
     // No further processing for unmapped fields
     if (!isset($class->fieldMappings[$e[0]])) {
         return array($fieldName, $value);
     }
     $mapping = $class->fieldMappings[$e[0]];
     $e[0] = $mapping['name'];
     // Hash and raw fields will not be prepared beyond the field name
     if ($mapping['type'] === Type::HASH || $mapping['type'] === Type::RAW) {
         $fieldName = implode('.', $e);
         return array($fieldName, $value);
     }
     if (isset($mapping['strategy']) && CollectionHelper::isHash($mapping['strategy']) && isset($e[2])) {
         $objectProperty = $e[2];
         $objectPropertyPrefix = $e[1] . '.';
         $nextObjectProperty = implode('.', array_slice($e, 3));
     } elseif ($e[1] != '$') {
         $fieldName = $e[0] . '.' . $e[1];
         $objectProperty = $e[1];
         $objectPropertyPrefix = '';
         $nextObjectProperty = implode('.', array_slice($e, 2));
     } elseif (isset($e[2])) {
         $fieldName = $e[0] . '.' . $e[1] . '.' . $e[2];
         $objectProperty = $e[2];
         $objectPropertyPrefix = $e[1] . '.';
         $nextObjectProperty = implode('.', array_slice($e, 3));
     } else {
         $fieldName = $e[0] . '.' . $e[1];
         return array($fieldName, $value);
     }
     // No further processing for fields without a targetDocument mapping
     if (!isset($mapping['targetDocument'])) {
         if ($nextObjectProperty) {
             $fieldName .= '.' . $nextObjectProperty;
         }
         return array($fieldName, $value);
     }
     $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']);
     // No further processing for unmapped targetDocument fields
     if (!$targetClass->hasField($objectProperty)) {
         if ($nextObjectProperty) {
             $fieldName .= '.' . $nextObjectProperty;
         }
         return array($fieldName, $value);
     }
     $targetMapping = $targetClass->getFieldMapping($objectProperty);
     $objectPropertyIsId = $targetClass->isIdentifier($objectProperty);
     // Prepare DBRef identifiers or the mapped field's property path
     $fieldName = $objectPropertyIsId && !empty($mapping['reference']) && empty($mapping['simple']) ? $e[0] . '.$id' : $e[0] . '.' . $objectPropertyPrefix . $targetMapping['name'];
     // Process targetDocument identifier fields
     if ($objectPropertyIsId) {
         if (!$prepareValue) {
             return array($fieldName, $value);
         }
         if (!is_array($value)) {
             return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
         }
         // Objects without operators or with DBRef fields can be converted immediately
         if (!$this->hasQueryOperators($value) || $this->hasDBRefFields($value)) {
             return array($fieldName, $targetClass->getDatabaseIdentifierValue($value));
         }
         return array($fieldName, $this->prepareQueryExpression($value, $targetClass));
     }
     /* The property path may include a third field segment, excluding the
      * collection item pointer. If present, this next object property must
      * be processed recursively.
      */
     if ($nextObjectProperty) {
         // Respect the targetDocument's class metadata when recursing
         $nextTargetClass = isset($targetMapping['targetDocument']) ? $this->dm->getClassMetadata($targetMapping['targetDocument']) : null;
         list($key, $value) = $this->prepareQueryElement($nextObjectProperty, $value, $nextTargetClass, $prepareValue);
         $fieldName .= '.' . $key;
     }
     return array($fieldName, $value);
 }
Exemple #13
0
 /**
  * Notifies this UnitOfWork of a property change in a document.
  *
  * @param object $document The document that owns the property.
  * @param string $propertyName The name of the property that changed.
  * @param mixed $oldValue The old value of the property.
  * @param mixed $newValue The new value of the property.
  */
 public function propertyChanged($document, $propertyName, $oldValue, $newValue)
 {
     $oid = spl_object_hash($document);
     $class = $this->dm->getClassMetadata(get_class($document));
     if (!isset($class->fieldMappings[$propertyName])) {
         return;
         // ignore non-persistent fields
     }
     // Update changeset and mark document for synchronization
     $this->documentChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
     if (!isset($this->scheduledForDirtyCheck[$class->name][$oid])) {
         $this->scheduleForDirtyCheck($document);
     }
 }
Exemple #14
0
 /**
  * Constructor.
  *
  * @param DocumentManager $dm
  * @param CriteriaMerger  $cm
  */
 public function __construct(DocumentManager $dm, CriteriaMerger $cm = null)
 {
     $this->dm = $dm;
     $this->cm = $cm ?: new CriteriaMerger();
     $this->config = $dm->getConfiguration();
 }
 /**
  * Returns the embedded document to be stored in Riak.
  *
  * The return value will usually be an associative array with string keys
  * corresponding to field names on the embedded document. An object may be
  * returned if the document is empty, to ensure that a BSON object will be
  * stored in lieu of an array.
  *
  * If $includeNestedCollections is true, nested collections will be included
  * in this prepared value and the option will cascade to all embedded
  * associations. If any nested PersistentCollections (embed or reference)
  * within this value were previously scheduled for deletion or update, they
  * will also be unscheduled.
  *
  * @param array $embeddedMapping
  * @param object $embeddedDocument
  * @param boolean $includeNestedCollections
  * @return array|object
  * @throws \UnexpectedValueException if an unsupported associating mapping is found
  */
 public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
 {
     $embeddedDocumentValue = array();
     $class = $this->dm->getClassMetadata(get_class($embeddedDocument));
     foreach ($class->fieldMappings as $mapping) {
         // Skip notSaved fields
         if (!empty($mapping['notSaved'])) {
             continue;
         }
         // Inline ClassMetadataInfo::getFieldValue()
         $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
         $value = null;
         if ($rawValue !== null) {
             switch (isset($mapping['association']) ? $mapping['association'] : null) {
                 // @Field, @String, @Date, etc.
                 case null:
                     $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
                     break;
                 case ClassMetadata::EMBED_ONE:
                 case ClassMetadata::REFERENCE_ONE:
                     // Nested collections should only be included for embedded relationships
                     $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
                     break;
                 case ClassMetadata::EMBED_MANY:
                 case ClassMetadata::REFERENCE_MANY:
                     // Skip PersistentCollections already scheduled for deletion
                     if (!$includeNestedCollections && $rawValue instanceof PersistentCollection && $this->uow->isCollectionScheduledForDeletion($rawValue)) {
                         break;
                     }
                     $value = $this->prepareAssociatedCollectionValue($rawValue, $includeNestedCollections);
                     break;
                 default:
                     throw new \UnexpectedValueException('Unsupported mapping association: ' . $mapping['association']);
             }
         }
         // Omit non-nullable fields that would have a null value
         if ($value === null && $mapping['nullable'] === false) {
             continue;
         }
         $embeddedDocumentValue[$mapping['name']] = $value;
     }
     /* Add a discriminator value if the embedded document is not mapped
      * explicitly to a targetDocument class.
      */
     if (!isset($embeddedMapping['targetDocument'])) {
         $discriminatorField = $embeddedMapping['discriminatorField'];
         $discriminatorValue = isset($embeddedMapping['discriminatorMap']) ? array_search($class->name, $embeddedMapping['discriminatorMap']) : $class->name;
         /* If the discriminator value was not found in the map, use the full
          * class name. In the future, it may be preferable to throw an
          * exception here (perhaps based on some strictness option).
          *
          * @see DocumentManager::createDBRef()
          */
         if ($discriminatorValue === false) {
             $discriminatorValue = $class->name;
         }
         $embeddedDocumentValue[$discriminatorField] = $discriminatorValue;
     }
     /* If the class has a discriminator (field and value), use it. A child
      * class that is not defined in the discriminator map may only have a
      * discriminator field and no value, so default to the full class name.
      */
     if (isset($class->discriminatorField)) {
         $embeddedDocumentValue[$class->discriminatorField] = isset($class->discriminatorValue) ? $class->discriminatorValue : $class->name;
     }
     // Ensure empty embedded documents are stored as BSON objects
     if (empty($embeddedDocumentValue)) {
         return (object) $embeddedDocumentValue;
     }
     /* @todo Consider always casting the return value to an object, or
      * building $embeddedDocumentValue as an object instead of an array, to
      * handle the edge case where all database field names are sequential,
      * numeric keys.
      */
     return $embeddedDocumentValue;
 }
Exemple #16
0
 public function getNewObj()
 {
     return $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name)->prepareQueryOrNewObj($this->newObj);
 }