Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ public function getAggregation(array $options = []): IterableResult
{
$class = $this->hydrationClass ? $this->dm->getClassMetadata($this->hydrationClass) : null;

if ($this->hydrationClass) {
$options['typeMap'] = DocumentManager::HYDRATION_TYPEMAP;
}

return new Aggregation($this->dm, $class, $this->collection, $this->getPipeline(), $options, $this->rewindable);
}

Expand Down
8 changes: 8 additions & 0 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,16 @@
*/
class DocumentManager implements ObjectManager
{
/**
* TypeMap by default
*/
public const CLIENT_TYPEMAP = ['root' => 'array', 'document' => 'array'];

/**
* TypeMap when result is hydrated
*/
public const HYDRATION_TYPEMAP = ['root' => 'bson', 'document' => 'bson', 'array' => 'bson'];

/**
* The Doctrine MongoDB connection instance.
*/
Expand Down
33 changes: 30 additions & 3 deletions lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,57 @@
namespace Doctrine\ODM\MongoDB\Event;

use Doctrine\ODM\MongoDB\DocumentManager;
use MongoDB\BSON\Document;
use MongoDB\Driver\Session;

/**
* Class that holds event arguments for a preLoad event.
*/
final class PreLoadEventArgs extends LifecycleEventArgs
{
/** @param array<string, mixed> $data */
private array $deprecatedDataArray;

Check failure on line 16 in lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Property Doctrine\ODM\MongoDB\Event\PreLoadEventArgs::$deprecatedDataArray type has no value type specified in iterable type array.

Check failure on line 16 in lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Property Doctrine\ODM\MongoDB\Event\PreLoadEventArgs::$deprecatedDataArray type has no value type specified in iterable type array.

public function __construct(
object $document,
DocumentManager $dm,
private array &$data,
private Document &$data,
?Session $session = null,
) {
parent::__construct($document, $dm, $session);
}

public function getRawData(): Document
{
if (isset($this->deprecatedDataArray)) {
$this->data = Document::fromPHP($this->deprecatedDataArray);
unset($this->deprecatedDataArray);
}

return $this->data;
}

public function setRawData(Document $document): void
{
$this->data = $document;
unset($this->deprecatedDataArray);
}

/**
* Get the array of data to be loaded and hydrated.
*
* @deprecated Use {@see self::getBsonDocument()} and {@see self::setBsonDocument()}
*
* @return array<string, mixed>
*/
public function &getData(): array
{
return $this->data;
$this->deprecatedDataArray ??= $this->data->toPHP(['root' => 'array']);

return $this->deprecatedDataArray;
}

public function __destruct()
{
$this->getRawData();
}
}
55 changes: 31 additions & 24 deletions lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
use Doctrine\ODM\MongoDB\Proxy\InternalProxy;
use Doctrine\ODM\MongoDB\Types\Type;
use Doctrine\ODM\MongoDB\UnitOfWork;
use MongoDB\BSON\Document;
use ProxyManager\Proxy\GhostObjectInterface;

use function array_key_exists;
use function chmod;
use function class_exists;
use function dirname;
Expand Down Expand Up @@ -169,8 +169,11 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
<<<EOF

// AlsoLoad("$name")
if (! array_key_exists('%1\$s', \$data) && array_key_exists('$name', \$data)) {
if (! \$data->has('%1\$s') && \$data->has('$name')) {
// @todo extracting and repacking is not very efficient
\$data = \$data->toPHP(['root' => 'array', 'document' => 'bson', 'array' => 'bson']);
\$data['%1\$s'] = \$data['$name'];
\$data = Document::fromPHP(\$data);
}

EOF
Expand Down Expand Up @@ -203,8 +206,11 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
<<<EOF

// Field(type: "{$mapping['type']}")
if (isset(\$data['%1\$s']) || (! empty(\$this->class->fieldMappings['%2\$s']['nullable']) && array_key_exists('%1\$s', \$data))) {
\$value = \$data['%1\$s'];
if (\$data->has('%1\$s') || (! empty(\$this->class->fieldMappings['%2\$s']['nullable']) && \$data->has('%1\$s'))) {
\$value = \$data->get('%1\$s');
if (\$value instanceof PackedArray || \$value instanceof Document) {
\$value = \$value->toPHP(DocumentManager::CLIENT_TYPEMAP);
}
if (\$value !== null) {
\$typeIdentifier = \$this->class->fieldMappings['%2\$s']['type'];
%3\$s
Expand All @@ -226,11 +232,11 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
<<<'EOF'

// ReferenceOne
if (isset($data['%1$s']) || (! empty($this->class->fieldMappings['%2$s']['nullable']) && array_key_exists('%1$s', $data))) {
$return = $data['%1$s'];
if ($data->has('%1$s') || (! empty($this->class->fieldMappings['%2$s']['nullable']) && $data->has('%1$s'))) {
$return = $data->get('%1$s');
if ($return !== null) {
if ($this->class->fieldMappings['%2$s']['storeAs'] !== ClassMetadata::REFERENCE_STORE_AS_ID && ! is_array($return)) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', 'array', gettype($return));
if ($this->class->fieldMappings['%2$s']['storeAs'] !== ClassMetadata::REFERENCE_STORE_AS_ID && ! $return instanceof Document) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', Document::class, get_debug_type($return));
}

$className = $this->dm->getClassNameForAssociation($this->class->fieldMappings['%2$s'], $return);
Expand Down Expand Up @@ -295,10 +301,10 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
<<<'EOF'

// ReferenceMany & EmbedMany
$mongoData = $data['%1$s'] ?? null;
$mongoData = $data->has('%1$s') ? $data->get('%1$s') : null;

if ($mongoData !== null && ! is_array($mongoData)) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', 'array', gettype($mongoData));
if ($mongoData !== null && ! $mongoData instanceof PackedArray && ! $mongoData instanceof Document) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', Document::class . '|' . PackedArray::class, get_debug_type($mongoData));
}

$return = $this->dm->getConfiguration()->getPersistentCollectionFactory()->create($this->dm, $this->class->fieldMappings['%2$s']);
Expand All @@ -322,13 +328,13 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
<<<'EOF'

// EmbedOne
if (isset($data['%1$s']) || (! empty($this->class->fieldMappings['%2$s']['nullable']) && array_key_exists('%1$s', $data))) {
$return = $data['%1$s'];
if ($data->has('%1$s') || (! empty($this->class->fieldMappings['%2$s']['nullable']) && $data->has('%1$s'))) {
$return = $data->get('%1$s');
if ($return !== null) {
$embeddedDocument = $return;

if (! is_array($embeddedDocument)) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', 'array', gettype($embeddedDocument));
if (! $embeddedDocument instanceof Document) {
throw HydratorException::associationTypeMismatch('%3$s', '%1$s', Document::class, get_debug_type($embeddedDocument));
}

$className = $this->dm->getClassNameForAssociation($this->class->fieldMappings['%2$s'], $embeddedDocument);
Expand Down Expand Up @@ -370,9 +376,11 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla
use Doctrine\ODM\MongoDB\Hydrator\HydratorInterface;
use Doctrine\ODM\MongoDB\Query\Query;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use MongoDB\BSON\Document;
use MongoDB\BSON\PackedArray;

use function array_key_exists;
use function gettype;
use function get_debug_type;
use function is_array;

/**
Expand All @@ -382,10 +390,11 @@ class $hydratorClassName implements HydratorInterface
{
public function __construct(private DocumentManager \$dm, private ClassMetadata \$class) {}

public function hydrate(object \$document, array \$data, array \$hints = []): array
public function hydrate(object \$document, Document \$data, array \$hints = []): array
{
\$hydratedData = [];
%s return \$hydratedData;
%s
return \$hydratedData;
}
}
EOF
Expand Down Expand Up @@ -420,18 +429,16 @@ public function hydrate(object \$document, array \$data, array \$hints = []): ar
/**
* Hydrate array of MongoDB document data into the given document object.
*
* @param array<string, mixed> $data
* @phpstan-param Hints $hints Any hints to account for during reconstitution/lookup of the document.
*
* @return array<string, mixed>
*/
public function hydrate(object $document, array $data, array $hints = []): array
public function hydrate(object $document, Document $data, array $hints = []): array
{
$metadata = $this->dm->getClassMetadata($document::class);
// Invoke preLoad lifecycle events and listeners
if (! empty($metadata->lifecycleCallbacks[Events::preLoad])) {
$args = [new PreLoadEventArgs($document, $this->dm, $data)];
$metadata->invokeLifecycleCallbacks(Events::preLoad, $document, $args);
$metadata->invokeLifecycleCallbacks(Events::preLoad, $document, [new PreLoadEventArgs($document, $this->dm, $data)]);
}

$this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($document, $this->dm, $data));
Expand All @@ -441,8 +448,8 @@ public function hydrate(object $document, array $data, array $hints = []): array
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]);
if ($data->has($fieldName)) {
$document->$method($data->get($fieldName));
continue 2;
}
}
Expand Down
6 changes: 2 additions & 4 deletions lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Doctrine\ODM\MongoDB\Hydrator;

use Doctrine\ODM\MongoDB\UnitOfWork;
use MongoDB\BSON\Document;

/**
* The HydratorInterface defines methods all hydrator need to implement
Expand All @@ -16,10 +17,7 @@
/**
* Hydrate array of MongoDB document data into the given document object.
*
* @param array<string, mixed> $data
* @phpstan-param Hints $hints
*
* @return array<string, mixed>
*/
public function hydrate(object $document, array $data, array $hints = []): array;
public function hydrate(object $document, Document $data, array $hints = []): array;

Check failure on line 22 in lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Method Doctrine\ODM\MongoDB\Hydrator\HydratorInterface::hydrate() return type has no value type specified in iterable type array.

Check failure on line 22 in lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Method Doctrine\ODM\MongoDB\Hydrator\HydratorInterface::hydrate() return type has no value type specified in iterable type array.
}
8 changes: 2 additions & 6 deletions lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\ODM\MongoDB\UnitOfWork;
use Iterator;
use IteratorIterator;
use MongoDB\BSON\Document;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
Expand Down Expand Up @@ -47,7 +48,7 @@
#[ReturnTypeWillChange]
public function current()
{
return $this->hydrate($this->getIterator()->current());

Check failure on line 51 in lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Parameter #1 $document of method Doctrine\ODM\MongoDB\Iterator\HydratingIterator<TDocument of object>::hydrate() expects MongoDB\BSON\Document|null, array<string, mixed> given.

Check failure on line 51 in lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.2)

Parameter #1 $document of method Doctrine\ODM\MongoDB\Iterator\HydratingIterator<TDocument of object>::hydrate() expects MongoDB\BSON\Document|null, array<string, mixed> given.
}

/** @return mixed */
Expand Down Expand Up @@ -82,12 +83,7 @@
return $this->iterator;
}

/**
* @param array<string, mixed>|null $document
*
* @return TDocument|null
*/
private function hydrate(?array $document): ?object
private function hydrate(?Document $document): ?object
{
return $document !== null ? $this->unitOfWork->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints) : null;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Doctrine\Common\Collections\Collection;
use Doctrine\Instantiator\Instantiator;
use Doctrine\Instantiator\InstantiatorInterface;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Id\IdGenerator;
use Doctrine\ODM\MongoDB\LockException;
use Doctrine\ODM\MongoDB\Mapping\Annotations\TimeSeries;
Expand All @@ -25,6 +26,8 @@
use Doctrine\Persistence\Reflection\EnumReflectionProperty;
use InvalidArgumentException;
use LogicException;
use MongoDB\BSON\Document;
use MongoDB\BSON\PackedArray;
use ProxyManager\Proxy\GhostObjectInterface;
use ReflectionClass;
use ReflectionEnum;
Expand Down Expand Up @@ -1825,6 +1828,10 @@ public function getPHPIdentifierValue($id)
{
$idType = $this->fieldMappings[$this->identifier]['type'];

if ($id instanceof Document || $id instanceof PackedArray) {
$id = $id->toPHP(DocumentManager::CLIENT_TYPEMAP);
}

return Type::getType($idType)->convertToPHPValue($id);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use Doctrine\ODM\MongoDB\MongoDBException;
use Doctrine\ODM\MongoDB\UnitOfWork;
use Doctrine\Persistence\Mapping\ClassMetadata;
use MongoDB\BSON\Document;
use MongoDB\BSON\PackedArray;

/**
* Interface for persistent collection classes.
Expand All @@ -34,16 +36,14 @@ public function setDocumentManager(DocumentManager $dm);
/**
* Sets the array of raw mongo data that will be used to initialize this collection.
*
* @param mixed[] $mongoData
*
* @return void
*/
public function setMongoData(array $mongoData);
public function setMongoData(Document|PackedArray $mongoData);

/**
* Gets the array of raw mongo data that will be used to initialize this collection.
*
* @return mixed[] $mongoData
* @return Document|PackedArray|null $mongoData
*/
public function getMongoData();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use Doctrine\ODM\MongoDB\MongoDBException;
use Doctrine\ODM\MongoDB\UnitOfWork;
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
use MongoDB\BSON\Document;
use MongoDB\BSON\PackedArray;
use ReturnTypeWillChange;
use Traversable;

Expand Down Expand Up @@ -84,10 +86,8 @@ trait PersistentCollectionTrait

/**
* The raw mongo data that will be used to initialize this collection.
*
* @var mixed[]
*/
private array $mongoData = [];
private Document|PackedArray $mongoData;

/**
* Any hints to account for during reconstitution/lookup of the documents.
Expand All @@ -103,14 +103,14 @@ public function setDocumentManager(DocumentManager $dm)
$this->uow = $dm->getUnitOfWork();
}

public function setMongoData(array $mongoData)
public function setMongoData(Document|PackedArray $mongoData)
{
$this->mongoData = $mongoData;
}

public function getMongoData()
{
return $this->mongoData;
return $this->mongoData ?? null;
}

public function setHints(array $hints)
Expand Down Expand Up @@ -143,7 +143,7 @@ public function initialize()
$this->uow->loadCollection($this);
$this->takeSnapshot();

$this->mongoData = [];
unset($this->mongoData);

// Reattach any NEW objects added through add()
if (! $newObjects) {
Expand Down Expand Up @@ -480,7 +480,7 @@ public function clear()
}
}

$this->mongoData = [];
unset($this->mongoData);
$this->coll->clear();

// Nothing to do for inverse-side collections
Expand Down
Loading
Loading