Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
257a0a2
Symfony 8.0 support (#2791)
GromNaN Oct 28, 2025
c11783a
Support update pipelines in query builder (#2881)
alcaeus Oct 30, 2025
2287b06
Enable symfony/var-exporter 8.0, without lazy ghost object support (#…
GromNaN Oct 30, 2025
ed1ea87
Bump doctrine/.github from 12.1.0 to 12.2.0 (#2887)
dependabot[bot] Nov 3, 2025
2d65c00
Fix missing PHP syntax highlighting on "Migrating Schemas" (#2899)
bcourtel Nov 5, 2025
134756f
Add attributes for every test classes with annotations (#2893)
GromNaN Nov 6, 2025
55d2cfd
Ensure proxy-manager is not used when native lazy are enabled
GromNaN Oct 31, 2025
5b7f434
Add a test without optional dependencies
GromNaN Oct 31, 2025
211488a
Don't change lazyGhostObject config when setting nativeLazyObject
GromNaN Oct 31, 2025
a311fe1
Don't use deprecated ClassMetadata::getPropertyAccessor()
GromNaN Oct 31, 2025
673e6dc
Fix initialisation of ProxyManager objects when hydrated from a colle…
GromNaN Oct 31, 2025
59c6f0c
Ensure proxy-manager is not used when native lazy objects are enabled…
GromNaN Nov 10, 2025
3529f28
Merge branch '2.15.x' into 2.14.x-merge-up-into-2.15.x_HtlvhBLI
GromNaN Nov 10, 2025
5acb54f
Merge release 2.14.1 into 2.15.x (#2900)
GromNaN Nov 10, 2025
4655b35
Deprecate `ClassMetadataFactoryInterface::getProxyClassNameResolver()…
GromNaN Nov 10, 2025
92a9f3b
Fix documentation sidebar (#2901)
GromNaN Nov 10, 2025
48a7de7
Upgrade to PHPUnit 11 for PHP >= 8.2 and check deprecation messages (…
GromNaN Nov 12, 2025
e08d5b5
Merge remote-tracking branch 'upstream/2.15.x' into 2.14.x-merge-up-i…
GromNaN Nov 12, 2025
a22ff89
Merge release 2.14.2 into 2.15.x (#2904)
GromNaN Nov 12, 2025
718826c
Deprecate ClassMetadata::isIdGeneratorUuid() (#2896)
GromNaN Nov 12, 2025
251a6e8
Merge branch '3.0.x' into 2.15.x-merge-up-into-3.0.x_l7Q7fH3Q
GromNaN Nov 12, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ on:
jobs:
coding-standards:
name: "Coding Standards"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@12.1.0"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@v12.2.0"
17 changes: 8 additions & 9 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,32 +95,31 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
tools: "pecl"
extensions: "mongodb-${{ matrix.driver-version }}, bcmath"
coverage: "none"
ini-values: "zend.assertions=1"

- name: "Show driver information"
run: "php --ri mongodb"

# Not used, skip transient dependencies
# Not used for tests, skip transient dependencies
- name: "Remove phpbench/phpbench"
run: composer remove --no-update --dev phpbench/phpbench

- name: "Configure Symfony ${{ matrix.symfony-version }}"
if: "${{ matrix.symfony-version != 'stable' }}"
- name: "Remove optional dependencies"
if: "${{ matrix.remove-optional-dependencies }}"
run: |
composer config minimum-stability dev
# update symfony deps
composer require --no-update symfony/console:^${{ matrix.symfony-version }}
composer require --no-update symfony/var-dumper:^${{ matrix.symfony-version }}
composer require --no-update --dev symfony/cache:^${{ matrix.symfony-version }}
composer remove --no-update friendsofphp/proxy-manager-lts symfony/var-exporter
composer remove --no-update --dev symfony/cache doctrine/orm doctrine/annotations
composer remove --no-update --dev doctrine/coding-standard phpstan/phpstan phpstan/phpstan-deprecation-rule phpstan/phpstan-phpunit

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "${{ matrix.dependencies }}"
composer-options: "--prefer-dist"
env:
SYMFONY_REQUIRE: ${{ matrix.symfony-version }}

- name: "Install latest Python version"
uses: actions/setup-python@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ on:
jobs:
documentation:
name: "Generate documentation"
uses: "doctrine/.github/.github/workflows/documentation.yml@12.1.0"
uses: "doctrine/.github/.github/workflows/documentation.yml@v12.2.0"
2 changes: 1 addition & 1 deletion .github/workflows/release-on-milestone-closed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
release:
name: "Git tag, release & create merge-up PR"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@12.1.0"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@v12.2.0"
with:
use-next-minor-as-default-branch: true
secrets:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/website-schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ on:
jobs:
json-validate:
name: "Validate JSON schema"
uses: "doctrine/.github/.github/workflows/website-schema.yml@12.1.0"
uses: "doctrine/.github/.github/workflows/website-schema.yml@v12.2.0"
14 changes: 8 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"mapping",
"object"
],
"minimum-stability": "dev",
"prefer-stable": true,
"homepage": "https://www.doctrine-project.org/projects/mongodb-odm.html",
"license": "MIT",
"authors": [
Expand All @@ -35,9 +37,9 @@
"friendsofphp/proxy-manager-lts": "^1.0",
"mongodb/mongodb": "^2.1.1",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"symfony/console": "^5.4 || ^6.4 || ^7.0 || ^8.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0",
"symfony/var-exporter": "^6.2 || ^7.0"
"symfony/var-exporter": "^6.4 || ^7.0 || ^8.0"
},
"require-dev": {
"ext-bcmath": "*",
Expand All @@ -49,11 +51,11 @@
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5.58",
"phpunit/phpunit": "^10.5.58|^11.5.43",
"squizlabs/php_codesniffer": "^4",
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
"symfony/uid": "^5.4 || ^6.0 || ^7.0",
"symfony/var-dumper": "^5.4 || ^6.0 || ^7.0"
"symfony/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0",
"symfony/uid": "^5.4 || ^6.0 || ^7.0 || ^8.0",
"symfony/var-dumper": "^5.4 || ^6.0 || ^7.0 || ^8.0"
},
"conflict": {
"doctrine/annotations": "<1.12 || >=3.0"
Expand Down
2 changes: 1 addition & 1 deletion docs/en/reference/console-commands.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Console Commands
================

Doctrine MongoDB ODM offers some console commands, which utilize Symfony2's
Doctrine MongoDB ODM offers some console commands, which utilize Symfony's
Console component, to ease your development process:

- ``odm:clear-cache:metadata`` - Clear all metadata cache of the various cache drivers.
Expand Down
4 changes: 4 additions & 0 deletions docs/en/reference/migrating-schemas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,17 @@ To create the collections for all the document classes, you can use the
For a specific document class, you can use the `createDocumentCollection()`
method with the class name as an argument:

.. code-block:: php

<?php

$schemaManager->createDocumentCollection(Person::class);

Once the collection is created, you can also set up indexes with ``ensureIndexes``,
and search indexes with ``createSearchIndexes``:

.. code-block:: php

<?php

$schemaManager->ensureIndexes();
Expand Down
46 changes: 44 additions & 2 deletions docs/en/reference/query-builder-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,7 @@ change document field values atomically. Additionally if you are modifying a fie
that is a reference you can pass managed document to the Builder and let ODM build
``DBRef`` object for you.

You have several modifier operations
available to you that make it easy to update documents in Mongo:
The following atomic update operators are available through the builder API:

* ``set($name, $value, $atomic = true)``
* ``setNewObj($newObj)``
Expand All @@ -655,6 +654,49 @@ available to you that make it easy to update documents in Mongo:
* ``pull($field, $value)``
* ``pullAll($field, array $valueArray)``

You can also run `updates with Aggregation Pipeline <https://www.mongodb.com/docs/manual/tutorial/update-documents-with-aggregation-pipeline/>`_
by using the ``pipeline()`` method. You can pass an aggregation builder instance, a ``Pipeline`` instance from the
MongoDB PHP library, or an array of pipeline stages:

.. code-block:: php

<?php

// The three following are equivalent ways to define the same update pipeline stage

$pipeline = $dm->createAggregationBuilder(User::class)
->set()
->field('totalScore')
->add('$score1', '$score2'),
);

$pipeline = new Pipeline(
Stage::set(
totalScore: Expression::add(
Expression::fieldPath('score1'),
Expression::fieldPath('score2'),
),
)
);

$pipeline = [
['$set' => [
'totalScore' => ['$add' => ['$score1', '$score2']],
]],
]

$dm->createQueryBuilder(User::class)
->updateOne()
->field('username')->equals('jwage')
->pipeline($pipeline)
->getQuery()
->execute();

.. note::

Pipeline updates are only available for ``updateOne``, ``updateMany``, and ``findAndUpdate`` operations.


Updating multiple documents
---------------------------

Expand Down
14 changes: 12 additions & 2 deletions docs/en/sidebar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:depth: 3
:glob:

tutorials/*
tutorials/getting-started

.. toctree::
:caption: Reference
Expand Down Expand Up @@ -53,4 +53,14 @@
:depth: 3
:glob:

cookbook/*
cookbook/blending-orm-and-mongodb-odm
cookbook/implementing-array-access-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/lookup-reference
cookbook/mapping-classes-to-orm-and-odm
cookbook/queryable-encryption
cookbook/resolve-target-document-listener
cookbook/simple-search-engine
cookbook/time-series-data
cookbook/validation-of-documents
cookbook/vector-search
1 change: 1 addition & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>src/Mapping/Driver/CompatibilityAnnotationDriver.php</exclude-pattern>
<exclude-pattern>src/Tools/Console/Command/CommandCompatibility.php</exclude-pattern>
<exclude-pattern>src/Tools/Console/Command/Schema/AbstractCommandCompatibility.php</exclude-pattern>
<exclude-pattern>src/Tools/Console/Helper/DocumentManagerHelper.php</exclude-pattern>
<exclude-pattern>tests/*</exclude-pattern>
</rule>
Expand Down
10 changes: 8 additions & 2 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ parameters:
path: src/Query/Query.php

-
message: '#^Strict comparison using \!\=\= between array\<string, mixed\>\|bool\|int\|MongoDB\\Driver\\ReadPreference\|string and null will always evaluate to true\.$#'
message: '#^Strict comparison using \!\=\= between array\<int\<0, max\>\|string, mixed\>\|bool\|int\|MongoDB\\Builder\\Pipeline\|MongoDB\\Driver\\ReadPreference\|string and null will always evaluate to true\.$#'
identifier: notIdentical.alwaysTrue
count: 1
path: src/Query/Query.php
Expand Down Expand Up @@ -1098,14 +1098,20 @@ parameters:
count: 1
path: tests/Tests/Query/BuilderTest.php

-
message: '#^Unreachable statement \- code above always terminates\.$#'
identifier: deadCode.unreachable
count: 1
path: tests/Tests/Query/PipelineUpdateTest.php

-
message: '#^Method Doctrine\\ODM\\MongoDB\\Tests\\QueryTest\:\:createCursorMock\(\) return type has no value type specified in iterable type Traversable\.$#'
identifier: missingType.iterableValue
count: 1
path: tests/Tests/QueryTest.php

-
message: '#^Parameter \#4 \$query of class Doctrine\\ODM\\MongoDB\\Query\\Query constructor expects array\{distinct\?\: string, hint\?\: array\<string, \-1\|1\>\|string, limit\?\: int, maxTimeMS\?\: int, multiple\?\: bool, new\?\: bool, newObj\?\: array\<string, mixed\>, query\?\: array\<string, mixed\>, \.\.\.\}, array\{type\: \-1\} given\.$#'
message: '#^Parameter \#4 \$query of class Doctrine\\ODM\\MongoDB\\Query\\Query constructor expects array\{distinct\?\: string, hint\?\: array\<string, \-1\|1\>\|string, limit\?\: int, maxTimeMS\?\: int, multiple\?\: bool, new\?\: bool, newObj\?\: array\<string, mixed\>, pipeline\?\: list\<array\<string, mixed\>\>\|MongoDB\\Builder\\Pipeline, \.\.\.\}, array\{type\: \-1\} given\.$#'
identifier: argument.type
count: 1
path: tests/Tests/QueryTest.php
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
bootstrap="tests/bootstrap.php"
cacheDirectory=".phpunit.cache"
displayDetailsOnAllIssues="true"
Expand Down
12 changes: 9 additions & 3 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;
use stdClass;
use Symfony\Component\VarExporter\LazyGhostTrait;
use Throwable;

use function array_diff_key;
Expand All @@ -44,6 +45,7 @@
use function class_exists;
use function interface_exists;
use function is_string;
use function trait_exists;
use function trigger_deprecation;
use function trim;

Expand Down Expand Up @@ -693,7 +695,11 @@ public function setUseLazyGhostObject(bool $flag): void
throw new LogicException('Cannot enable or disable LazyGhostObject when native lazy objects are enabled.');
}

if ($flag === false) {
if ($flag && ! trait_exists(LazyGhostTrait::class)) {
throw new LogicException('Package "symfony/var-exporter" >= 8.0 does not provide lazy ghost objects, use native lazy objects instead.');
}

if (! $flag) {
if (! class_exists(ProxyManagerConfiguration::class)) {
throw new LogicException('Package "friendsofphp/proxy-manager-lts" is required to disable LazyGhostObject.');
}
Expand All @@ -706,13 +712,13 @@ public function setUseLazyGhostObject(bool $flag): void

public function isLazyGhostObjectEnabled(): bool
{
return $this->lazyGhostObject;
// Always false if native lazy objects are enabled
return $this->lazyGhostObject && ! $this->nativeLazyObject;
}

public function setUseNativeLazyObject(bool $nativeLazyObject): void
{
$this->nativeLazyObject = $nativeLazyObject;
$this->lazyGhostObject = ! $nativeLazyObject || $this->lazyGhostObject;
}

public function isNativeLazyObjectEnabled(): bool
Expand Down
31 changes: 26 additions & 5 deletions src/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use function is_object;
use function ltrim;
use function sprintf;
use function trigger_deprecation;

/**
* The DocumentManager class is the central access point for managing the
Expand Down Expand Up @@ -153,15 +154,31 @@ protected function __construct(?Client $client = null, ?Configuration $config =
$this->config->getDriverOptions(),
);

$this->classNameResolver = $this->config->isLazyGhostObjectEnabled()
? new CachingClassNameResolver(new LazyGhostProxyClassNameResolver())
: new CachingClassNameResolver(new ProxyManagerClassNameResolver($this->config));
if ($this->config->isNativeLazyObjectEnabled()) {
$this->classNameResolver = new class implements ClassNameResolver, ProxyClassNameResolver {
public function getRealClass(string $class): string
{
return $class;
}

public function resolveClassName(string $className): string
{
return $className;
}
};
} elseif ($this->config->isLazyGhostObjectEnabled()) {
$this->classNameResolver = new CachingClassNameResolver(new LazyGhostProxyClassNameResolver());
} else {
$this->classNameResolver = new CachingClassNameResolver(new ProxyManagerClassNameResolver($this->config));
}

$metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
$this->metadataFactory = new $metadataFactoryClassName();
$this->metadataFactory->setDocumentManager($this);
$this->metadataFactory->setConfiguration($this->config);
$this->metadataFactory->setProxyClassNameResolver($this->classNameResolver);
if (! $this->config->isNativeLazyObjectEnabled()) {
$this->metadataFactory->setProxyClassNameResolver($this->classNameResolver);
}

$cacheDriver = $this->config->getMetadataCache();
if ($cacheDriver) {
Expand Down Expand Up @@ -295,10 +312,14 @@ public function getSchemaManager(): SchemaManager
/**
* Returns the class name resolver which is used to resolve real class names for proxy objects.
*
* @deprecated Fetch metadata for any class string (e.g. proxy object class) and read the class name from the metadata object
* @deprecated Since 2.15, the use of proxy classes is deprecated and will be removed in Doctrine ODM 3.0.
*/
public function getClassNameResolver(): ClassNameResolver
{
if ($this->getConfiguration()->isNativeLazyObjectEnabled()) {
trigger_deprecation('doctrine/mongodb-odm', '2.15', 'The %s() method is deprecated and will be removed in Doctrine ODM 3.0. There are no proxy classes when using native lazy objects', __METHOD__);
}

return $this->classNameResolver;
}

Expand Down
2 changes: 1 addition & 1 deletion src/DocumentNotFoundException.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use const JSON_THROW_ON_ERROR;

/**
* Class for exception when encountering proxy object that has
* Class for exception when encountering a lazy object that has
* an identifier that does not exist in the database.
*/
final class DocumentNotFoundException extends MongoDBException
Expand Down
2 changes: 1 addition & 1 deletion src/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private function __construct()
public const onClear = 'onClear';

/**
* The documentNotFound event occurs if a proxy object could not be found in
* The documentNotFound event occurs if a lazy object could not be found in
* the database.
*/
public const documentNotFound = 'documentNotFound';
Expand Down
Loading