<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ODM\MongoDB;
use Doctrine\ODM\MongoDB\Query,
Doctrine\ODM\MongoDB\Mapping\ClassMetadata,
Doctrine\ODM\MongoDB\Mapping\Types\Type,
Doctrine\ODM\MongoDB\PersistentCollection,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection,
Doctrine\ODM\MongoDB\Event\LifecycleEventArgs,
Doctrine\ODM\MongoDB\Event\PreLoadEventArgs;
/**
* The Hydrator class is responsible for converting a document from MongoDB
* which is an array to classes and collections based on the mapping of the document
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @author Jonathan H. Wage <hide@address.com>
*/
class Hydrator
{
/**
* The DocumentManager associated with this Hydrator
*
* @var Doctrine\ODM\MongoDB\DocumentManager
*/
private $dm;
/**
* The EventManager associated with this Hydrator
*
* @var Doctrine\Common\EventManager
*/
private $evm;
/**
* Mongo command prefix
* @var string
*/
private $cmd;
/**
* Create a new Hydrator instance
*
* @param Doctrine\ODM\MongoDB\DocumentManager $dm
*/
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
$this->evm = $this->dm->getEventManager();
$this->cmd = $dm->getConfiguration()->getMongoCmd();
}
/**
* Hydrate array of MongoDB 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.
* @return array $values The array of hydrated values.
*/
public function hydrate($document, &$data)
{
$metadata = $this->dm->getClassMetadata(get_class($document));
if (isset($metadata->lifecycleCallbacks[ODMEvents::preLoad])) {
$args = array(&$data);
$metadata->invokeLifecycleCallbacks(ODMEvents::preLoad, $document, $args);
}
if ($this->evm->hasListeners(ODMEvents::preLoad)) {
$this->evm->dispatchEvent(ODMEvents::preLoad, new PreLoadEventArgs($document, $this->dm, $data));
}
if (isset($metadata->alsoLoadMethods)) {
foreach ($metadata->alsoLoadMethods as $fieldName => $method) {
if (isset($data[$fieldName])) {
$document->$method($data[$fieldName]);
}
}
}
foreach ($metadata->fieldMappings as $mapping) {
if (isset($mapping['alsoLoadFields'])) {
$rawValue = null;
$names = isset($mapping['alsoLoadFields']) ? $mapping['alsoLoadFields'] : array();
array_unshift($names, $mapping['name']);
foreach ($names as $name) {
if (isset($data[$name])) {
$rawValue = $data[$name];
break;
}
}
} else {
$rawValue = isset($data[$mapping['name']]) ? $data[$mapping['name']] : null;
}
if ($rawValue === null) {
continue;
}
$value = null;
// Hydrate embedded
if (isset($mapping['embedded'])) {
if ($mapping['type'] === 'one') {
$embeddedDocument = $rawValue;
$className = $this->dm->getClassNameFromDiscriminatorValue($mapping, $embeddedDocument);
$embeddedMetadata = $this->dm->getClassMetadata($className);
$value = $embeddedMetadata->newInstance();
// unset a potential discriminator map field (unless it's a persisted property)
$discriminatorField = isset($mapping['discriminatorField']) ? $mapping['discriminatorField'] : '_doctrine_class_name';
if (!isset($embeddedMetadata->fieldMappings[$discriminatorField])) {
unset($embeddedDocument[$discriminatorField]);
}
$this->hydrate($value, $embeddedDocument);
$data[$mapping['name']] = $embeddedDocument;
$this->dm->getUnitOfWork()->registerManaged($value, null, $embeddedDocument);
} elseif ($mapping['type'] === 'many') {
$embeddedDocuments = $rawValue;
$coll = new PersistentCollection(new ArrayCollection(), $this->dm);
foreach ($embeddedDocuments as $key => $embeddedDocument) {
$className = $this->dm->getClassNameFromDiscriminatorValue($mapping, $embeddedDocument);
$embeddedMetadata = $this->dm->getClassMetadata($className);
$embeddedDocumentObject = $embeddedMetadata->newInstance();
// unset a potential discriminator map field (unless it's a persisted property)
$discriminatorField = isset($mapping['discriminatorField']) ? $mapping['discriminatorField'] : '_doctrine_class_name';
if (!isset($embeddedMetadata->fieldMappings[$discriminatorField])) {
unset($embeddedDocument[$discriminatorField]);
}
$this->hydrate($embeddedDocumentObject, $embeddedDocument);
$data[$mapping['name']][$key] = $embeddedDocument;
$this->dm->getUnitOfWork()->registerManaged($embeddedDocumentObject, null, $embeddedDocument);
$coll->add($embeddedDocumentObject);
}
$coll->setOwner($document, $mapping);
$coll->takeSnapshot();
$value = $coll;
}
// Hydrate reference
} elseif (isset($mapping['reference'])) {
$reference = $rawValue;
if ($mapping['type'] === 'one' && isset($reference[$this->cmd . 'id'])) {
$className = $this->dm->getClassNameFromDiscriminatorValue($mapping, $reference);
$targetMetadata = $this->dm->getClassMetadata($className);
$id = $targetMetadata->getPHPIdentifierValue($reference[$this->cmd . 'id']);
$value = $this->dm->getReference($className, $id);
} elseif ($mapping['type'] === 'many' && (is_array($reference) || $reference instanceof Collection)) {
$references = $reference;
$value = new PersistentCollection(new ArrayCollection(), $this->dm);
$value->setInitialized(false);
$value->setOwner($document, $mapping);
// Delay any hydration of reference objects until the collection is
// accessed and initialized for the first ime
$value->setReferences($references);
}
$data[$mapping['name']] = $value;
// Hydrate regular field
} else {
$value = Type::getType($mapping['type'])->convertToPHPValue($rawValue);
$data[$mapping['name']] = $value;
}
// Set hydrated field value to document
if ($value !== null) {
$metadata->setFieldValue($document, $mapping['fieldName'], $value);
}
}
// Set the document identifier
if (isset($data['_id'])) {
$metadata->setIdentifierValue($document, $data['_id']);
$data[$metadata->identifier] = $data['_id'];
unset($data['_id']);
}
if (isset($metadata->lifecycleCallbacks[ODMEvents::postLoad])) {
$metadata->invokeLifecycleCallbacks(ODMEvents::postLoad, $document);
}
if ($this->evm->hasListeners(ODMEvents::postLoad)) {
$this->evm->dispatchEvent(ODMEvents::postLoad, new LifecycleEventArgs($document, $this->dm));
}
return $document;
}
}