protected function getQueryArray(ClassMetadata $metadata, $document, $path)
 {
     $class = $metadata->name;
     $field = $this->getFieldNameFromPropertyPath($path);
     if (!isset($metadata->fieldMappings[$field])) {
         throw new \LogicException('Mapping for \'' . $path . '\' doesn\'t exist for ' . $class);
     }
     $mapping = $metadata->fieldMappings[$field];
     if (isset($mapping['reference']) && $mapping['reference']) {
         throw new \LogicException('Cannot determine uniqueness of referenced document values');
     }
     switch ($mapping['type']) {
         case 'one':
             // TODO: implement support for embed one documents
         // TODO: implement support for embed one documents
         case 'many':
             // TODO: implement support for embed many documents
             throw new \RuntimeException('Not Implemented.');
         case 'hash':
             $value = $metadata->getFieldValue($document, $mapping['fieldName']);
             return array($path => $this->getFieldValueRecursively($path, $value));
         case 'collection':
             return array($mapping['fieldName'] => array('$in' => $metadata->getFieldValue($document, $mapping['fieldName'])));
         default:
             return array($mapping['fieldName'] => $metadata->getFieldValue($document, $mapping['fieldName']));
     }
 }
    /**
     * Updates the already persisted document if it has any new changesets.
     *
     * @param object $document
     * @param array $options Array of options to be used with update()
     */
    public function update($document, array $options = array())
    {
        $id = $this->uow->getDocumentIdentifier($document);
        $update = $this->dp->prepareUpdateData($document);

        if ( ! empty($update)) {

            $id = $this->class->getDatabaseIdentifierValue($id);
            $query = array('_id' => $id);

            // Include versioning updates
            if ($this->class->isVersioned) {
                $versionMapping = $this->class->fieldMappings[$this->class->versionField];
                $currentVersion = $this->class->getFieldValue($document, $this->class->versionField);
                if ($versionMapping['type'] === 'int') {
                    $nextVersion = $currentVersion + 1;
                    $update[$this->cmd . 'inc'][$versionMapping['name']] = 1;
                    $query[$versionMapping['name']] = $currentVersion;
                    $this->class->setFieldValue($document, $this->class->versionField, $nextVersion);
                } elseif ($versionMapping['type'] === 'date') {
                    $nextVersion = new \DateTime();
                    $update[$this->cmd . 'set'][$versionMapping['name']] = new \MongoDate($nextVersion->getTimestamp());
                    $query[$versionMapping['name']] = new \MongoDate($currentVersion->getTimestamp());
                    $this->class->setFieldValue($document, $this->class->versionField, $nextVersion);
                }
                $options['safe'] = true;
            }

            if ($this->class->isLockable) {
                $isLocked = $this->class->getFieldValue($document, $this->class->lockField);
                $lockMapping = $this->class->fieldMappings[$this->class->lockField];
                if ($isLocked) {
                    $update[$this->cmd . 'unset'] = array($lockMapping['name'] => true);
                } else {
                    $query[$lockMapping['name']] = array($this->cmd . 'exists' => false);
                }
            }

            if ($this->dm->getEventManager()->hasListeners(Events::onUpdatePrepared)) {
                $this->dm->getEventManager()->dispatchEvent(
                    Events::onUpdatePrepared, new OnUpdatePreparedArgs($this->dm, $document, $update)
                );
            }

            $result = $this->collection->update($query, $update, $options);

            if (($this->class->isVersioned || $this->class->isLockable) && ! $result['n']) {
                throw LockException::lockFailed($document);
            }
        }
    }
示例#3
0
 /**
  * @param $object
  * @param $fieldName
  * @return ArrayCollection|mixed
  * @throws \NForms\Exceptions\MetadataException
  */
 protected function getCollection($object, $fieldName)
 {
     if ($this->isSingleValuedAssociation($fieldName)) {
         throw new MetadataException("Can't get collection from association toOne.");
     }
     $collection = $this->classMetadata->getFieldValue($object, $fieldName);
     if ($collection === NULL) {
         $collection = new ArrayCollection();
         $this->classMetadata->setFieldValue($object, $fieldName, $collection);
     }
     if (!$collection instanceof Collection) {
         throw new MetadataException('Expected Doctrine\\Common\\Collections\\Collection, given ' . (is_object($collection) ? get_class($collection) : gettype($collection)));
     }
     return $collection;
 }
 /**
  * If you are priming references inside an embedded document you'll need to parse the dot syntax.
  * This method will traverse through embedded documents to find the reference to prime.
  * However this method will not traverse through multiple layers of references.
  * I.e. you can prime this: myDocument.embeddedDocument.embeddedDocuments.embeddedDocuments.referencedDocument(s)
  * ... but you cannot prime this: myDocument.embeddedDocument.referencedDocuments.referencedDocument(s)
  * This addresses Issue #624.
  *
  * @param string             $fieldName
  * @param ClassMetadata      $class
  * @param array|\Traversable $documents
  * @param array              $mapping
  * @return array
  */
 private function parseDotSyntaxForPrimer($fieldName, $class, $documents, $mapping = null)
 {
     // Recursion passthrough:
     if ($mapping != null) {
         return array('fieldName' => $fieldName, 'class' => $class, 'documents' => $documents, 'mapping' => $mapping);
     }
     // Gather mapping data:
     $e = explode('.', $fieldName);
     if (!isset($class->fieldMappings[$e[0]])) {
         throw new \InvalidArgumentException(sprintf('Field %s cannot be further parsed for priming because it is unmapped.', $fieldName));
     }
     $mapping = $class->fieldMappings[$e[0]];
     $e[0] = $mapping['name'];
     // Case of embedded document(s) to recurse through:
     if (!isset($mapping['reference'])) {
         if (empty($mapping['embedded'])) {
             throw new \InvalidArgumentException(sprintf('Field "%s" of fieldName "%s" is not an embedded document, therefore no children can be primed. Aborting. This feature does not support traversing nested referenced documents at this time.', $e[0], $fieldName));
         }
         if (!isset($mapping['targetDocument'])) {
             throw new \InvalidArgumentException(sprintf('No target document class has been specified for this embedded document. However, targetDocument mapping must be specified in order for prime to work on fieldName "%s" for mapping of field "%s".', $fieldName, $mapping['fieldName']));
         }
         $childDocuments = array();
         foreach ($documents as $document) {
             $fieldValue = $class->getFieldValue($document, $e[0]);
             if ($fieldValue instanceof PersistentCollection) {
                 foreach ($fieldValue as $elemDocument) {
                     array_push($childDocuments, $elemDocument);
                 }
             } else {
                 array_push($childDocuments, $fieldValue);
             }
         }
         array_shift($e);
         $childClass = $this->dm->getClassMetadata($mapping['targetDocument']);
         if (!$childClass->hasField($e[0])) {
             throw new \InvalidArgumentException(sprintf('Field to prime must exist in embedded target document. Reference fieldName "%s" for mapping of target document class "%s".', $fieldName, $mapping['targetDocument']));
         }
         $childFieldName = implode('.', $e);
         return $this->parseDotSyntaxForPrimer($childFieldName, $childClass, $childDocuments);
     }
     // Case of reference(s) to prime:
     if ($mapping['reference']) {
         if (count($e) > 1) {
             throw new \InvalidArgumentException(sprintf('Cannot prime more than one layer deep but field "%s" is a reference and has children in fieldName "%s".', $e[0], $fieldName));
         }
         return array('fieldName' => $fieldName, 'class' => $class, 'documents' => $documents, 'mapping' => $mapping);
     }
 }
 /**
  * Prepares array of values to be stored in mongo to represent embedded object.
  *
  * @param ClassMetadata $class
  * @param Document $doc
  * @return array
  */
 private function _prepareDocEmbeded(ClassMetadata $class, $doc)
 {
     if (!is_object($doc)) {
         return $doc;
     }
     $changeset = array();
     foreach ($class->fieldMappings as $mapping) {
         $rawValue = $class->getFieldValue($doc, $mapping['fieldName']);
         if (!isset($rawValue)) {
             continue;
         }
         if (isset($mapping['embedded']) || isset($mapping['reference'])) {
             $classMetadata = $this->_dm->getClassMetadata($mapping['targetDocument']);
             if (isset($mapping['embedded'])) {
                 if ($mapping['type'] == 'many') {
                     $value = array();
                     foreach ($rawValue as $doc) {
                         $value[] = $this->_prepareDocEmbeded($classMetadata, $doc);
                     }
                 } elseif ($mapping['type'] == 'one') {
                     $value = $this->_prepareDocEmbeded($classMetadata, $rawValue);
                 }
             } elseif (isset($mapping['reference'])) {
                 if ($mapping['type'] == 'many') {
                     $value = array();
                     foreach ($rawValue as $doc) {
                         $value[] = $this->_prepareDocReference($classMetadata, $doc);
                     }
                 } else {
                     $value = $this->_prepareDocReference($classMetadata, $rawValue);
                 }
             }
         } else {
             $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
         }
         $changeset[$mapping['fieldName']] = $value;
     }
     return $changeset;
 }
 /**
  * Cascades the preLoad event to embedded documents.
  *
  * @param ClassMetadata $class
  * @param object $document
  * @param array $data
  */
 private function cascadePreLoad(ClassMetadata $class, $document, $data)
 {
     foreach ($class->fieldMappings as $mapping) {
         if (isset($mapping['embedded'])) {
             $value = $class->getFieldValue($document, $mapping['fieldName']);
             if ($value === null) {
                 continue;
             }
             if ($mapping['type'] === 'one') {
                 $value = array($value);
             }
             foreach ($value as $entry) {
                 $entryClass = $this->dm->getClassMetadata(get_class($entry));
                 if (isset($entryClass->lifecycleCallbacks[Events::preLoad])) {
                     $args = array(&$data);
                     $entryClass->invokeLifecycleCallbacks(Events::preLoad, $entry, $args);
                 }
                 if ($this->evm->hasListeners(Events::preLoad)) {
                     $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($entry, $this->dm, $data[$mapping['name']]));
                 }
                 $this->cascadePreLoad($entryClass, $entry, $data[$mapping['name']]);
             }
         }
     }
 }
示例#7
0
 /**
  * Prime references within a mapped field of one or more documents.
  *
  * If a $primer callable is provided, it should have the same signature as
  * the default primer defined in the constructor. If $primer is not
  * callable, the default primer will be used.
  *
  * @param ClassMetadata      $class     Class metadata for the document
  * @param array|\Traversable $documents Documents containing references to prime
  * @param string             $fieldName Field name containing references to prime
  * @param array              $hints     UnitOfWork hints for priming queries
  * @param callable           $primer    Optional primer callable
  * @throws \InvalidArgumentException If the mapped field is not the owning
  *                                   side of a reference relationship.
  * @throws \InvalidArgumentException If $primer is not callable
  * @throws \LogicException If the mapped field is a simple reference and is
  *                         missing a target document class.
  */
 public function primeReferences(ClassMetadata $class, $documents, $fieldName, array $hints = array(), $primer = null)
 {
     $mapping = $class->getFieldMapping($fieldName);
     /* Inverse-side references would need to be populated before we can
      * collect references to be primed. This is not supported.
      */
     if (!isset($mapping['reference']) || !$mapping['isOwningSide']) {
         throw new \InvalidArgumentException(sprintf('Field "%s" is not the owning side of a reference relationship in class "%s"', $fieldName, $class->name));
     }
     /* Simple reference require a target document class so we can construct
      * the priming query.
      */
     if (!empty($mapping['simple']) && empty($mapping['targetDocument'])) {
         throw new \LogicException(sprintf('Field "%s" is a simple reference without a target document class in class "%s"', $fieldName, $class->name));
     }
     if ($primer !== null && !is_callable($primer)) {
         throw new \InvalidArgumentException('$primer is not callable');
     }
     $primer = $primer ?: $this->defaultPrimer;
     $groupedIds = array();
     foreach ($documents as $document) {
         $fieldValue = $class->getFieldValue($document, $fieldName);
         /* The field will need to be either a Proxy (reference-one) or
          * PersistentCollection (reference-many) in order to prime anything.
          */
         if (!is_object($fieldValue)) {
             continue;
         }
         if ($mapping['type'] === 'one' && $fieldValue instanceof Proxy && !$fieldValue->__isInitialized()) {
             $refClass = $this->dm->getClassMetadata(get_class($fieldValue));
             $id = $this->uow->getDocumentIdentifier($fieldValue);
             $groupedIds[$refClass->name][serialize($id)] = $id;
         } elseif ($mapping['type'] == 'many' && $fieldValue instanceof PersistentCollection) {
             $this->addManyReferences($fieldValue, $groupedIds);
         }
     }
     foreach ($groupedIds as $className => $ids) {
         $refClass = $this->dm->getClassMetadata($className);
         call_user_func($primer, $this->dm, $refClass, array_values($ids), $hints);
     }
 }