Compare commits

..

54 Commits
3.1.2 ... 3.2.0

Author SHA1 Message Date
Grégoire Paris
37946d3a21 Merge pull request #11472 from nicolas-grekas/no-readonly
Remove readonly modifier from EntityManager
2024-05-23 16:27:52 +02:00
Nicolas Grekas
baf96cdad4 Remove readonly modifier from EntityManager 2024-05-23 14:33:01 +02:00
Alexander M. Turek
ce09c96427 Deprecate the NotSupported exception (#11470) 2024-05-22 21:53:12 +02:00
Alexander M. Turek
ae659fe650 Deprecate SequenceGenerator implementing Serializable (#11468) 2024-05-22 10:48:46 +02:00
Alexander M. Turek
0a177d5074 Merge branch '3.1.x' into 3.2.x
* 3.1.x:
  Psalm 5.24.0 (#11467)
  PHPStan 1.11.1 (#11466)
2024-05-21 14:24:54 +02:00
Alexander M. Turek
dbfe47b07b Merge branch '2.19.x' into 3.1.x
* 2.19.x:
  Psalm 5.24.0 (#11467)
  PHPStan 1.11.1 (#11466)
2024-05-21 14:24:20 +02:00
Alexander M. Turek
d31aabb40c Psalm 5.24.0 (#11467) 2024-05-21 14:21:50 +02:00
Alexander M. Turek
22b1f52c1c Merge branch '3.1.x' into 3.2.x
* 3.1.x:
  Fix failed merge (#11464)
  Test with actual lock modes (#11465)
  Backport test for Query::setLockMode() (#11463)
  Fix return type of Query::getLockMode() (#11462)
2024-05-21 14:04:38 +02:00
Alexander M. Turek
d66884403f PHPStan 1.11.1 (#11466) 2024-05-21 13:32:25 +02:00
Alexander M. Turek
a90ee5c495 Merge branch '2.19.x' into 3.1.x
* 2.19.x:
  Test with actual lock modes (#11465)
  Backport test for Query::setLockMode() (#11463)
2024-05-21 12:52:03 +02:00
Alexander M. Turek
11270425e5 Fix failed merge (#11464) 2024-05-21 12:30:56 +02:00
Alexander M. Turek
552eae37a3 Test with actual lock modes (#11465) 2024-05-21 12:30:36 +02:00
Alexander M. Turek
ee4b03aa78 Backport test for Query::setLockMode() (#11463) 2024-05-21 12:30:16 +02:00
Alexander M. Turek
f1246d57c2 Fix return type of Query::getLockMode() (#11462)
… for DBAL 4
2024-05-21 12:30:01 +02:00
Alexander M. Turek
a14ef7c279 Merge branch '3.1.x' into 3.2.x
* 3.1.x:
  Using an integer as discriminator value with ORM v3
  Using an integer as discriminator value with ORM v3
  Bump ramsey/composer-install from 2 to 3 (#11442)
  Use ramsey/composer-install in PHPBench workflow (#11444)
  Bump doctrine/.github from 3.0.0 to 5.0.1
  Upgrade codecov/codecov-action
  Setup Dependabot
2024-05-21 08:42:44 +02:00
Alexander M. Turek
54c29140fa Merge branch '2.19.x' into 3.1.x
* 2.19.x:
  Bump ramsey/composer-install from 2 to 3 (#11442)
  Bump doctrine/.github from 3.0.0 to 5.0.1
  Upgrade codecov/codecov-action
2024-05-21 08:42:11 +02:00
Grégoire Paris
daa99f197b Merge pull request #11456 from prohalexey/IntegerDescriminatorInInstanceOf
Using an integer as discriminator value with ORM v3
2024-05-17 08:19:34 +02:00
Alexey Prohorov
2b04cc2e3f Using an integer as discriminator value with ORM v3
This fixes a bug that occurred when configuring integers as discriminator values and using DQL instanceOf function in the queries. Doctrine throws a type error whenever the application generates these queries.
2024-05-16 11:53:29 +03:00
Grégoire Paris
3d9af3187f Merge pull request #11425 from prohalexey/FixForIntegerDescriminatorValue
Discriminator value could be an integer
2024-05-15 11:31:09 +02:00
Alexey Prohorov
e83d8a80ba Using an integer as discriminator value with ORM v3
This fixes a bug that occurred when configuring integers as discriminator values. Doctrine throws a type error whenever the application generates queries.
2024-05-15 10:42:04 +03:00
dependabot[bot]
c5291b4de8 Bump ramsey/composer-install from 2 to 3 (#11442)
Bumps [ramsey/composer-install](https://github.com/ramsey/composer-install) from 2 to 3.
- [Release notes](https://github.com/ramsey/composer-install/releases)
- [Commits](https://github.com/ramsey/composer-install/compare/v2...v3)

---
updated-dependencies:
- dependency-name: ramsey/composer-install
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-05 23:47:43 +02:00
Grégoire Paris
029ca611f0 Use ramsey/composer-install in PHPBench workflow (#11444)
It will handle caching for us.
2024-05-05 23:38:41 +02:00
Grégoire Paris
831d86548c Merge pull request #11441 from doctrine/dependabot/github_actions/2.19.x/doctrine/dot-github-5.0.1
Bump doctrine/.github from 3.0.0 to 5.0.1
2024-05-05 23:23:39 +02:00
dependabot[bot]
f26b3b9cf9 Bump doctrine/.github from 3.0.0 to 5.0.1
Bumps [doctrine/.github](https://github.com/doctrine/.github) from 3.0.0 to 5.0.1.
- [Release notes](https://github.com/doctrine/.github/releases)
- [Commits](https://github.com/doctrine/.github/compare/3.0.0...5.0.1)

---
updated-dependencies:
- dependency-name: doctrine/.github
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-05 21:17:24 +00:00
Grégoire Paris
9e7715f678 Merge pull request #11439 from greg0ire/setup-dependabot
Setup Dependabot
2024-05-05 23:16:51 +02:00
Grégoire Paris
9ab84f7478 Merge pull request #11440 from greg0ire/update-codecov
Upgrade codecov/codecov-action
2024-05-05 22:56:55 +02:00
Grégoire Paris
e6bb4ef20e Upgrade codecov/codecov-action 2024-05-05 22:43:51 +02:00
Grégoire Paris
0e26e3ed50 Setup Dependabot
Targeting 2.19.x, since we want the updates to bubble up. Since
Dependabot has had no effect on doctrine/dbal yet, I suppose that means
that "dependabot.yml" must be present on the default branch.
2024-05-05 22:41:40 +02:00
Grégoire Paris
63315c8e4a Merge pull request #11434 from doctrine/3.1.x-merge-up-into-3.2.x_sjLAVzN7
Merge release 3.1.3 into 3.2.x
2024-04-30 09:57:38 +02:00
Grégoire Paris
8ca99fdfdc Merge pull request #11433 from greg0ire/3.1.x
Merge 3.0.x up into 3.1.x
2024-04-30 09:14:13 +02:00
Grégoire Paris
2d8e466636 Merge remote-tracking branch 'origin/2.19.x' into 3.1.x 2024-04-30 09:00:59 +02:00
Grégoire Paris
94986af284 Merge pull request #11430 from W0rma/fix-deprecation-layer-orm-exception
Fix deprecation layer of Doctrine\ORM\ORMException
2024-04-30 08:49:54 +02:00
W0rma
ad5c8e4bdc Make test compatible with PHP 7.1 2024-04-30 08:35:06 +02:00
W0rma
c363f55ad1 Fix deprecation layer 2024-04-29 14:48:36 +02:00
Grégoire Paris
c973a62272 Merge pull request #11429 from SenseException/unused-test-group
Remove unused test group
2024-04-27 11:42:05 +02:00
Grégoire Paris
8d3446015a Merge pull request #11428 from xificurk/keep-removed-entity-in-identity-map
Prevent creation of new MANAGED entity instance by reloading REMOVED entity from database
2024-04-27 11:40:56 +02:00
Claudio Zizza
4e335f4044 Remove unused test group 2024-04-27 10:46:19 +02:00
Petr Morávek
bb36d49b38 Keep entities in identity map until the scheduled deletions are executed.
If the entity gets reloaded from database before the deletions are
executed UnitOfWork needs to be able to return the original instance in
REMOVED state.
2024-04-26 21:54:02 +02:00
Grégoire Paris
2b81a8e260 Merge pull request #11426 from nasimic/patch-1
Update association-mapping.rst
2024-04-26 21:27:07 +02:00
Nasimi Mammadov
7d3b3f28e9 Update association-mapping.rst
Changed capitalized column names to lowercase for consistency. Other occurances of column names mentioned as lowercase several times at this same page.
2024-04-26 21:24:28 +02:00
Simon Podlipsky
cbec236e8b fix: always cleanup in AbstractHydrator::toIterable() (#11101)
Previously it didn't cleanup anything as long as the iteration hasn't reached the final row.

Co-authored-by: Oleg Andreyev <oleg.andreyev@lampa.lv>
2024-04-25 10:32:40 +02:00
Grégoire Paris
306963fe79 Merge pull request #11422 from tomasz-ryba/bugfix/fetch-eager-order-by
Bugfix: respect orderBy for fetch EAGER mode
2024-04-25 00:09:43 +02:00
Tomasz Ryba
fb4578406f Respect orderBy for EAGER fetch mode
EAGER fetch mode ignores orderBy as of changes introduced with #8391

Fixes #11163
Fixes #11381
2024-04-24 22:44:16 +02:00
Grégoire Paris
bdc41e2b5e Merge pull request #11420 from tyteen4a03/patch-1
fix(docs): typo
2024-04-22 15:40:39 +02:00
Timothy Choi
90376a6431 fix(docs): typo 2024-04-22 15:30:56 +02:00
Alexander M. Turek
97634ae6a1 Merge branch '3.1.x' into 3.2.x
* 3.1.x:
  Revert "Merge pull request #11399 from ThomasLandauer/issue-11377" (#11415)
  Fix BIGINT validation (#11414)
  docs: update PHP version in doc
  Fix fromMappingArray definition
  Fix templated phpdoc return type (#11407)
  [Documentation] Merging "Query Result Formats" with "Hydration Modes"
  SchemaValidator: Changing mapping of BIGINT to string|int
  Fix psalm errors: remove override of template type
  Update dql-doctrine-query-language.rst
  Adding `NonUniqueResultException`
  [Documentation] Query Result Formats
2024-04-15 16:31:08 +02:00
Alexander M. Turek
4672d284ff Merge branch '3.1.x' into 3.2.x
* 3.1.x:
  Adjust PHPBench mocks
  Set column length explicitly (#11393)
  Add missing import
  Remove unused variable (#11391)
  Fixed proxy initialization for EnumReflectionProperty
  Remove older versions from the docs (#11383)
  [Documentation] Removing "Doctrine Mapping Types" ... (#11384)
  [GH-11185] Bugfix: do not use collection batch loading for indexBy assocations. (#11380)
  Improve lazy ghost performance by avoiding self-referencing closure. (#11376)
  Remove outdated git metadata files (#11362)
  Switch join columns around, otherwise index doesnt match
  Key on fk
  Fix entities and mapping.
  Minor code style fix in AbstractRemoteControl
  Do not schedule batch loading for target classes with composite identifier.
  Cleanup tests not to use model sets.
  provides a test case for github issue 11154
2024-03-21 14:44:21 +01:00
Grégoire Paris
69f51cc794 Merge pull request #11371 from doctrine/3.1.x
Merge 3.1.x up into 3.2.x
2024-03-17 10:43:23 +01:00
Grégoire Paris
f9331ee2b9 Merge pull request #11369 from doctrine/3.1.x
Merge 3.1.x up into 3.2.x
2024-03-17 09:34:02 +01:00
Grégoire Paris
cb05f1aadf Merge pull request #11357 from DaDeather/11351-add-deprecation-for-obsolete-indexes-and-unique-constraint-properties-of-table-attribute
Deprecate obsolete and unnecessary properties from Table attribute (#11351)
2024-03-16 23:24:55 +01:00
Grégoire Paris
ab616f1a1d Merge pull request #11364 from doctrine/3.1.x
Merge 3.1.x up into 3.2.x
2024-03-16 23:24:34 +01:00
Ismail Özgün Turan
7d1444e5b6 Deprecate obsolete and unnecessary properties from Table attribute (#11351)
The properties `indexes` and `uniqueConstraints` were used by the
`AnnotationDriver` but were never implemented for the `AttributeDriver`.
Since the `AnnotationDriver` doesn't exist anymore these can become
deprecated and will then be removed afterwards.
2024-03-15 12:23:03 +01:00
Grégoire Paris
25d5936337 Merge pull request #11354 from greg0ire/depr--complete
Deprecate --complete option of orm:schema-tool:update
2024-03-15 07:42:35 +01:00
Grégoire Paris
68f9bf5dfa Deprecate --complete option of orm:schema-tool:update
It achieves nothing anymore.
2024-03-14 13:14:55 +01:00
31 changed files with 649 additions and 73 deletions

9
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "CI"
target-branch: "2.19.x"

View File

@@ -24,4 +24,4 @@ on:
jobs:
coding-standards:
uses: "doctrine/.github/.github/workflows/coding-standards.yml@3.0.0"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1"

View File

@@ -75,7 +75,7 @@ jobs:
if: "${{ matrix.dbal-version != 'default' }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
composer-options: "--ignore-platform-req=php+"
dependency-versions: "${{ matrix.deps }}"
@@ -156,7 +156,7 @@ jobs:
if: "${{ matrix.dbal-version != 'default' }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
composer-options: "--ignore-platform-req=php+"
@@ -222,7 +222,7 @@ jobs:
extensions: "${{ matrix.extension }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
composer-options: "--ignore-platform-req=php+"
@@ -296,7 +296,7 @@ jobs:
if: "${{ matrix.dbal-version != 'default' }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
composer-options: "--ignore-platform-req=php+"
@@ -337,6 +337,8 @@ jobs:
path: "reports"
- name: "Upload to Codecov"
uses: "codecov/codecov-action@v3"
uses: "codecov/codecov-action@v4"
with:
directory: reports
env:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"

View File

@@ -36,7 +36,7 @@ jobs:
run: "composer require --dev phpdocumentor/guides-cli --no-update"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"

View File

@@ -47,15 +47,8 @@ jobs:
coverage: "pcov"
ini-values: "zend.assertions=1, apc.enable_cli=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v3"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
- name: "Run PHPBench"
run: "vendor/bin/phpbench run --report=default"

View File

@@ -7,7 +7,7 @@ on:
jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@4.0.0"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}

View File

@@ -83,7 +83,7 @@ jobs:
if: "${{ matrix.dbal-version != 'default' }}"
- name: Install dependencies with Composer
uses: ramsey/composer-install@v2
uses: ramsey/composer-install@v3
- name: Run static analysis with Vimeo Psalm
run: vendor/bin/psalm --shepherd

View File

@@ -1,3 +1,29 @@
# Upgrade to 3.2
## Deprecate the `NotSupported` exception
The class `Doctrine\ORM\Exception\NotSupported` is deprecated without replacement.
## Deprecate remaining `Serializable` implementation
Relying on `SequenceGenerator` implementing the `Serializable` is deprecated
because that interface won't be implemented in ORM 4 anymore.
The following methods are deprecated:
* `SequenceGenerator::serialize()`
* `SequenceGenerator::unserialize()`
## `orm:schema-tool:update` option `--complete` is deprecated
That option behaves as a no-op, and is deprecated. It will be removed in 4.0.
## Deprecate properties `$indexes` and `$uniqueConstraints` of `Doctrine\ORM\Mapping\Table`
The properties `$indexes` and `$uniqueConstraints` have been deprecated since they had no effect at all.
The preferred way of defining indices and unique constraints is by
using the `\Doctrine\ORM\Mapping\UniqueConstraint` and `\Doctrine\ORM\Mapping\Index` attributes.
# Upgrade to 3.1
## Deprecate `Doctrine\ORM\Mapping\ReflectionEnumProperty`

View File

@@ -38,12 +38,12 @@
"require-dev": {
"doctrine/coding-standard": "^12.0",
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "1.10.59",
"phpstan/phpstan": "1.11.1",
"phpunit/phpunit": "^10.4.0",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^5.4 || ^6.2 || ^7.0",
"vimeo/psalm": "5.22.2"
"vimeo/psalm": "5.24.0"
},
"suggest": {
"ext-dom": "Provides support for XSD validation for XML mapping files",

View File

@@ -11,7 +11,7 @@ What we offer are hooks to execute any kind of validation.
.. note::
You don't need to validate your entities in the lifecycle
events. Its only one of many options. Of course you can also
events. It is only one of many options. Of course you can also
perform validations in value setters or any other method of your
entities that are used in your code.

View File

@@ -870,8 +870,8 @@ This is essentially the same as the following, more verbose, mapping:
* @var Collection<int, Group>
*/
#[JoinTable(name: 'User_Group')]
#[JoinColumn(name: 'User_id', referencedColumnName: 'id')]
#[InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')]
#[JoinColumn(name: 'user_id', referencedColumnName: 'id')]
#[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]
#[ManyToMany(targetEntity: Group::class)]
private Collection $groups;
// ...
@@ -884,10 +884,10 @@ This is essentially the same as the following, more verbose, mapping:
<many-to-many field="groups" target-entity="Group">
<join-table name="User_Group">
<join-columns>
<join-column id="User_id" referenced-column-name="id" />
<join-column id="user_id" referenced-column-name="id" />
</join-columns>
<inverse-join-columns>
<join-column id="Group_id" referenced-column-name="id" />
<join-column id="group_id" referenced-column-name="id" />
</inverse-join-columns>
</join-table>
</many-to-many>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.22.2@d768d914152dbbf3486c36398802f74e80cfde48">
<files psalm-version="5.24.0@462c80e31c34e58cc4f750c656be3927e80e550e">
<file src="src/AbstractQuery.php">
<FalsableReturnStatement>
<code><![CDATA[! $filteredParameters->isEmpty() ? $filteredParameters->first() : null]]></code>
@@ -206,6 +206,11 @@
<code><![CDATA[$entity]]></code>
</PossiblyNullArgument>
</file>
<file src="src/Id/SequenceGenerator.php">
<ParamNameMismatch>
<code><![CDATA[$serialized]]></code>
</ParamNameMismatch>
</file>
<file src="src/Internal/Hydration/AbstractHydrator.php">
<ReferenceConstraintViolation>
<code><![CDATA[return $rowData;]]></code>

View File

@@ -63,27 +63,27 @@ class EntityManager implements EntityManagerInterface
/**
* The metadata factory, used to retrieve the ORM metadata of entity classes.
*/
private readonly ClassMetadataFactory $metadataFactory;
private ClassMetadataFactory $metadataFactory;
/**
* The UnitOfWork used to coordinate object-level transactions.
*/
private readonly UnitOfWork $unitOfWork;
private UnitOfWork $unitOfWork;
/**
* The event manager that is the central point of the event system.
*/
private readonly EventManager $eventManager;
private EventManager $eventManager;
/**
* The proxy factory used to create dynamic proxies.
*/
private readonly ProxyFactory $proxyFactory;
private ProxyFactory $proxyFactory;
/**
* The repository factory used to create dynamic repositories.
*/
private readonly RepositoryFactory $repositoryFactory;
private RepositoryFactory $repositoryFactory;
/**
* The expression builder instance used to generate query expressions.
@@ -112,8 +112,8 @@ class EntityManager implements EntityManagerInterface
* @param Connection $conn The database connection used by the EntityManager.
*/
public function __construct(
private readonly Connection $conn,
private readonly Configuration $config,
private Connection $conn,
private Configuration $config,
EventManager|null $eventManager = null,
) {
if (! $config->getMetadataDriverImpl()) {

View File

@@ -8,6 +8,7 @@ use LogicException;
use function sprintf;
/** @deprecated */
final class NotSupported extends LogicException implements ORMException
{
public static function create(): self

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Id;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\EntityManagerInterface;
use Serializable;
@@ -65,8 +66,17 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
return $this->nextValue;
}
/** @deprecated without replacement. */
final public function serialize(): string
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11468',
'%s() is deprecated, use __serialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
__METHOD__,
self::class,
);
return serialize($this->__serialize());
}
@@ -79,8 +89,17 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
];
}
/** @deprecated without replacement. */
final public function unserialize(string $serialized): void
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11468',
'%s() is deprecated, use __unserialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
__METHOD__,
self::class,
);
$this->__unserialize(unserialize($serialized));
}

View File

@@ -104,29 +104,31 @@ abstract class AbstractHydrator
$this->prepare();
while (true) {
$row = $this->statement()->fetchAssociative();
try {
while (true) {
$row = $this->statement()->fetchAssociative();
if ($row === false) {
$this->cleanup();
break;
}
$result = [];
$this->hydrateRowData($row, $result);
$this->cleanupAfterRowIteration();
if (count($result) === 1) {
if (count($resultSetMapping->indexByMap) === 0) {
yield end($result);
} else {
yield from $result;
if ($row === false) {
break;
}
$result = [];
$this->hydrateRowData($row, $result);
$this->cleanupAfterRowIteration();
if (count($result) === 1) {
if (count($resultSetMapping->indexByMap) === 0) {
yield end($result);
} else {
yield from $result;
}
} else {
yield $result;
}
} else {
yield $result;
}
} finally {
$this->cleanup();
}
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use Attribute;
use Doctrine\Deprecations\Deprecation;
#[Attribute(Attribute::TARGET_CLASS)]
final class Table implements MappingAttribute
@@ -21,5 +22,24 @@ final class Table implements MappingAttribute
public readonly array|null $uniqueConstraints = null,
public readonly array $options = [],
) {
if ($this->indexes !== null) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11357',
'Providing the property $indexes on %s does not have any effect and will be removed in Doctrine ORM 4.0. Please use the %s attribute instead.',
self::class,
Index::class,
);
}
if ($this->uniqueConstraints !== null) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11357',
'Providing the property $uniqueConstraints on %s does not have any effect and will be removed in Doctrine ORM 4.0. Please use the %s attribute instead.',
self::class,
UniqueConstraint::class,
);
}
}
}

View File

@@ -635,9 +635,10 @@ class Query extends AbstractQuery
/**
* Get the current lock mode for this query.
*
* @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
* @return LockMode|int|null The current lock mode of this query or NULL if no specific lock mode is set.
* @psalm-return LockMode::*|null
*/
public function getLockMode(): int|null
public function getLockMode(): LockMode|int|null
{
$lockMode = $this->getHint(self::HINT_LOCK_MODE);

View File

@@ -30,6 +30,7 @@ use function count;
use function implode;
use function is_array;
use function is_float;
use function is_int;
use function is_numeric;
use function is_string;
use function preg_match;
@@ -384,7 +385,9 @@ class SqlWalker
$values = [];
if ($class->discriminatorValue !== null) { // discriminators can be 0
$values[] = $conn->quote($class->discriminatorValue);
$values[] = $class->getDiscriminatorColumn()->type === 'integer' && is_int($class->discriminatorValue)
? $class->discriminatorValue
: $conn->quote((string) $class->discriminatorValue);
}
foreach ($class->subClasses as $subclassName) {
@@ -396,7 +399,9 @@ class SqlWalker
continue;
}
$values[] = $conn->quote((string) $subclassMetadata->discriminatorValue);
$values[] = $subclassMetadata->getDiscriminatorColumn()->type === 'integer' && is_int($subclassMetadata->discriminatorValue)
? $subclassMetadata->discriminatorValue
: $conn->quote((string) $subclassMetadata->discriminatorValue);
}
if ($values !== []) {
@@ -2246,8 +2251,10 @@ class SqlWalker
$discriminators += HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($metadata, $this->em);
}
foreach (array_keys($discriminators) as $dis) {
$sqlParameterList[] = $this->conn->quote($dis);
foreach (array_keys($discriminators) as $discriminatorValue) {
$sqlParameterList[] = $rootClass->getDiscriminatorColumn()->type === 'integer' && is_int($discriminatorValue)
? $discriminatorValue
: $this->conn->quote((string) $discriminatorValue);
}
return '(' . implode(', ', $sqlParameterList) . ')';

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -28,7 +29,7 @@ class UpdateCommand extends AbstractCommand
$this->setName($this->name)
->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata')
->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')
->addOption('complete', null, InputOption::VALUE_NONE, 'This option is a no-op and will be removed in 4.0')
->addOption('complete', null, InputOption::VALUE_NONE, 'This option is a no-op, is deprecated and will be removed in 4.0')
->addOption('dump-sql', null, InputOption::VALUE_NONE, 'Dumps the generated SQL statements to the screen (does not execute them).')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Causes the generated SQL statements to be physically executed against your database.')
->setHelp(<<<'EOT'
@@ -75,6 +76,15 @@ EOT);
{
$notificationUi = $ui->getErrorStyle();
if ($input->getOption('complete') === true) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11354',
'The --complete option is a no-op, is deprecated and will be removed in Doctrine ORM 4.0.',
);
$notificationUi->warning('The --complete option is a no-op, is deprecated and will be removed in Doctrine ORM 4.0.');
}
$sqls = $schemaTool->getUpdateSchemaSql($metadatas);
if (empty($sqls)) {

View File

@@ -1143,6 +1143,8 @@ class UnitOfWork implements PropertyChangedListener
$eventsToDispatch = [];
foreach ($entities as $entity) {
$this->removeFromIdentityMap($entity);
$oid = spl_object_id($entity);
$class = $this->em->getClassMetadata($entity::class);
$persister = $this->getEntityPersister($class->name);
@@ -1484,8 +1486,6 @@ class UnitOfWork implements PropertyChangedListener
return;
}
$this->removeFromIdentityMap($entity);
unset($this->entityUpdates[$oid]);
if (! isset($this->entityDeletions[$oid])) {
@@ -2653,7 +2653,7 @@ class UnitOfWork implements PropertyChangedListener
$entities[] = $collection->getOwner();
}
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]);
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping->orderBy);
$targetClass = $this->em->getClassMetadata($targetEntity);
$targetProperty = $targetClass->getReflectionProperty($mappedBy);

View File

@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Cache\Persister\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Cache\Persister\Entity\AbstractEntityPersister;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
@@ -97,7 +98,7 @@ abstract class EntityPersisterTestCase extends OrmTestCase
->with(
self::identicalTo(['name' => 'Foo']),
self::identicalTo($associationMapping),
self::identicalTo(1),
self::identicalTo(LockMode::OPTIMISTIC),
self::identicalTo(2),
self::identicalTo(3),
self::identicalTo([4]),
@@ -107,7 +108,7 @@ abstract class EntityPersisterTestCase extends OrmTestCase
self::assertSame('SELECT * FROM foo WERE name = ?', $persister->getSelectSQL(
['name' => 'Foo'],
$associationMapping,
1,
LockMode::OPTIMISTIC,
2,
3,
[4],
@@ -233,13 +234,21 @@ abstract class EntityPersisterTestCase extends OrmTestCase
self::identicalTo($entity),
self::identicalTo($associationMapping),
self::identicalTo([1]),
self::identicalTo(2),
self::identicalTo(LockMode::PESSIMISTIC_READ),
self::identicalTo(3),
self::identicalTo([4]),
)
->willReturn($entity);
self::assertSame($entity, $persister->load(['id' => 1], $entity, $associationMapping, [1], 2, 3, [4]));
self::assertSame($entity, $persister->load(
['id' => 1],
$entity,
$associationMapping,
[1],
LockMode::PESSIMISTIC_READ,
3,
[4],
));
}
public function testInvokeLoadAll(): void
@@ -402,9 +411,9 @@ abstract class EntityPersisterTestCase extends OrmTestCase
$this->entityPersister->expects(self::once())
->method('lock')
->with(self::identicalTo($identifier), self::identicalTo(1));
->with(self::identicalTo($identifier), self::identicalTo(LockMode::OPTIMISTIC));
$persister->lock($identifier, 1);
$persister->lock($identifier, LockMode::OPTIMISTIC);
}
public function testInvokeExists(): void

View File

@@ -7,6 +7,7 @@ namespace Doctrine\Tests\ORM;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Exception\EntityManagerClosed;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
@@ -21,7 +22,9 @@ use Doctrine\Tests\OrmTestCase;
use Generator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use ReflectionProperty;
use stdClass;
use Symfony\Component\VarExporter\LazyGhostTrait;
use TypeError;
class EntityManagerTest extends OrmTestCase
@@ -172,4 +175,36 @@ class EntityManagerTest extends OrmTestCase
self::assertFalse($this->entityManager->isOpen());
}
}
/** Resetting the EntityManager relies on lazy objects until https://github.com/doctrine/orm/issues/5933 is resolved */
public function testLazyGhostEntityManager(): void
{
$em = new class () extends EntityManager {
use LazyGhostTrait;
public function __construct()
{
}
};
$em = $em::createLazyGhost(static function ($em): void {
$r = new ReflectionProperty(EntityManager::class, 'unitOfWork');
$r->setValue($em, new class () extends UnitOfWork {
public function __construct()
{
}
public function clear(): void
{
}
});
});
$this->assertTrue($em->isOpen());
$em->close();
$this->assertFalse($em->isOpen());
$em->resetLazyObject();
$this->assertTrue($em->isOpen());
}
}

View File

@@ -252,7 +252,6 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
self::assertEquals(1_600_000, $result[3]['op']);
}
#[Group('test')]
public function testOperatorDiv(): void
{
$result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Tests\OrmFunctionalTestCase;
class GH11163Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->setUpEntitySchema([
GH11163Bucket::class,
GH11163BucketItem::class,
]);
}
public function tearDown(): void
{
parent::tearDown();
$conn = static::$sharedConn;
$conn->executeStatement('DELETE FROM GH11163BucketItem');
$conn->executeStatement('DELETE FROM GH11163Bucket');
}
public function testFetchEagerModeWithOrderBy(): void
{
// Load entities into database
$this->_em->persist($bucket = new GH11163Bucket(11163));
$this->_em->persist(new GH11163BucketItem(1, $bucket, 2));
$this->_em->persist(new GH11163BucketItem(2, $bucket, 3));
$this->_em->persist(new GH11163BucketItem(3, $bucket, 1));
$this->_em->flush();
$this->_em->clear();
// Fetch entity from database
$dql = 'SELECT bucket FROM ' . GH11163Bucket::class . ' bucket WHERE bucket.id = :id';
$bucket = $this->_em->createQuery($dql)
->setParameter('id', 11163)
->getSingleResult();
// Assert associated entity is loaded eagerly
static::assertInstanceOf(GH11163Bucket::class, $bucket);
static::assertInstanceOf(PersistentCollection::class, $bucket->items);
static::assertTrue($bucket->items->isInitialized());
static::assertCount(3, $bucket->items);
// Assert order of entities
static::assertSame(1, $bucket->items[0]->position);
static::assertSame(3, $bucket->items[0]->id);
static::assertSame(2, $bucket->items[1]->position);
static::assertSame(1, $bucket->items[1]->id);
static::assertSame(3, $bucket->items[2]->position);
static::assertSame(2, $bucket->items[2]->id);
}
}
#[ORM\Entity]
class GH11163Bucket
{
#[ORM\Id]
#[ORM\Column(type: 'integer')]
private int $id;
/** @var Collection<int, GH11163BucketItem> */
#[ORM\OneToMany(
targetEntity: GH11163BucketItem::class,
mappedBy: 'bucket',
fetch: 'EAGER',
)]
#[ORM\OrderBy(['position' => 'ASC'])]
public Collection $items;
public function __construct(int $id)
{
$this->id = $id;
$this->items = new ArrayCollection();
}
}
#[ORM\Entity]
class GH11163BucketItem
{
#[ORM\ManyToOne(targetEntity: GH11163Bucket::class, inversedBy: 'items')]
#[ORM\JoinColumn(nullable: false)]
private GH11163Bucket $bucket;
#[ORM\Id]
#[ORM\Column(type: 'integer')]
public int $id;
#[ORM\Column(type: 'integer')]
public int $position;
public function __construct(int $id, GH11163Bucket $bucket, int $position)
{
$this->id = $id;
$this->bucket = $bucket;
$this->position = $position;
}
}

View File

@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Tests\OrmFunctionalTestCase;
use Generator;
use PHPUnit\Framework\Attributes\DataProvider;
class GH11341Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->setUpEntitySchema([
IntegerBaseClass::class,
IntegerFooEntity::class,
IntegerBarEntity::class,
StringAsIntBaseClass::class,
StringAsIntFooEntity::class,
StringAsIntBarEntity::class,
StringBaseClass::class,
StringFooEntity::class,
StringBarEntity::class,
]);
}
public static function dqlStatements(): Generator
{
yield ['SELECT e FROM ' . IntegerBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(1, 2\)$/'];
yield ['SELECT e FROM ' . IntegerFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(1\)$/'];
yield ['SELECT e FROM ' . IntegerBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(2\)$/'];
yield ['SELECT e FROM ' . StringAsIntBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\', \'2\'\)$/'];
yield ['SELECT e FROM ' . StringAsIntFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\'\)$/'];
yield ['SELECT e FROM ' . StringAsIntBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'2\'\)$/'];
yield ['SELECT e FROM ' . StringBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\', \'2\'\)$/'];
yield ['SELECT e FROM ' . StringFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\'\)$/'];
yield ['SELECT e FROM ' . StringBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'2\'\)$/'];
}
#[DataProvider('dqlStatements')]
public function testDiscriminatorValue(string $dql, string $expectedDiscriminatorValues): void
{
$query = $this->_em->createQuery($dql);
$sql = $query->getSQL();
self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql);
}
public static function dqlStatementsForInstanceOf(): Generator
{
yield [IntegerBaseClass::class, IntegerFooEntity::class];
yield [StringBaseClass::class, StringFooEntity::class];
yield [StringAsIntBaseClass::class, StringAsIntFooEntity::class];
}
/**
* @psalm-param class-string $baseClass
* @psalm-param class-string $inheritedClass
*/
#[DataProvider('dqlStatementsForInstanceOf')]
public function testInstanceOf(string $baseClass, string $inheritedClass): void
{
$this->_em->persist(new $inheritedClass());
$this->_em->flush();
$dql = 'SELECT p FROM ' . $baseClass . ' p WHERE p INSTANCE OF ' . $baseClass;
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
self::assertCount(1, $result);
self::assertContainsOnlyInstancesOf($baseClass, $result);
}
}
#[ORM\Entity]
#[ORM\Table(name: 'integer_discriminator')]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'integer')]
#[ORM\DiscriminatorMap([
1 => IntegerFooEntity::class,
2 => IntegerBarEntity::class,
])]
class IntegerBaseClass
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
private int|null $id = null;
}
#[ORM\Entity]
class IntegerFooEntity extends IntegerBaseClass
{
}
#[ORM\Entity]
class IntegerBarEntity extends IntegerBaseClass
{
}
#[ORM\Entity]
#[ORM\Table(name: 'string_as_int_discriminator')]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap([
1 => StringAsIntFooEntity::class,
2 => StringAsIntBarEntity::class,
])]
class StringAsIntBaseClass
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
private int|null $id = null;
}
#[ORM\Entity]
class StringAsIntFooEntity extends StringAsIntBaseClass
{
}
#[ORM\Entity]
class StringAsIntBarEntity extends StringAsIntBaseClass
{
}
#[ORM\Entity]
#[ORM\Table(name: 'string_discriminator')]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap([
'1' => StringFooEntity::class,
'2' => StringBarEntity::class,
])]
class StringBaseClass
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
private int|null $id = null;
}
#[ORM\Entity]
class StringFooEntity extends StringBaseClass
{
}
#[ORM\Entity]
class StringBarEntity extends StringBaseClass
{
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\OrmFunctionalTestCase;
class GH6123Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->createSchemaForModels(
GH6123Entity::class,
);
}
public function testLoadingRemovedEntityFromDatabaseDoesNotCreateNewManagedEntityInstance(): void
{
$entity = new GH6123Entity();
$this->_em->persist($entity);
$this->_em->flush();
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
$this->_em->remove($entity);
$freshEntity = $this->loadEntityFromDatabase($entity->id);
self::assertSame($entity, $freshEntity);
self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($freshEntity));
self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($freshEntity));
}
public function testRemovedEntityCanBePersistedAgain(): void
{
$entity = new GH6123Entity();
$this->_em->persist($entity);
$this->_em->flush();
$this->_em->remove($entity);
self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($entity));
self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
$this->loadEntityFromDatabase($entity->id);
$this->_em->persist($entity);
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
$this->_em->flush();
}
private function loadEntityFromDatabase(int $id): GH6123Entity|null
{
return $this->_em->createQueryBuilder()
->select('e')
->from(GH6123Entity::class, 'e')
->where('e.id = :id')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
}
#[ORM\Entity]
class GH6123Entity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
public int $id;
}

View File

@@ -13,6 +13,7 @@ use Doctrine\ORM\Events;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Tests\Models\Hydration\SimpleEntity;
use Doctrine\Tests\OrmFunctionalTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
@@ -149,4 +150,33 @@ class AbstractHydratorTest extends OrmFunctionalTestCase
$this->expectException(ORMException::class);
$this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping);
}
public function testToIterableIfYieldAndBreakBeforeFinishAlwaysCleansUp(): void
{
$this->setUpEntitySchema([SimpleEntity::class]);
$entity1 = new SimpleEntity();
$this->_em->persist($entity1);
$entity2 = new SimpleEntity();
$this->_em->persist($entity2);
$this->_em->flush();
$this->_em->clear();
$evm = $this->_em->getEventManager();
$q = $this->_em->createQuery('SELECT e.id FROM ' . SimpleEntity::class . ' e');
// select two entities, but do no iterate
$q->toIterable();
self::assertCount(0, $evm->getListeners(Events::onClear));
// select two entities, but abort after first record
foreach ($q->toIterable() as $result) {
self::assertCount(1, $evm->getListeners(Events::onClear));
break;
}
self::assertCount(0, $evm->getListeners(Events::onClear));
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use Doctrine\ORM\Mapping\Table;
use PHPUnit\Framework\TestCase;
final class TableMappingTest extends TestCase
{
use VerifyDeprecations;
public function testDeprecationOnIndexesPropertyIsTriggered(): void
{
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357');
new Table(indexes: []);
}
public function testDeprecationOnUniqueConstraintsPropertyIsTriggered(): void
{
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357');
new Table(uniqueConstraints: []);
}
}

View File

@@ -12,9 +12,12 @@ use Doctrine\DBAL\Cache\ArrayResult;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\UnitOfWork;
@@ -39,8 +42,7 @@ use function array_map;
class QueryTest extends OrmTestCase
{
/** @var EntityManagerMock */
protected $entityManager;
private EntityManagerMock $entityManager;
protected function setUp(): void
{
@@ -80,6 +82,30 @@ class QueryTest extends OrmTestCase
self::assertEquals($parameters, $query->getParameters());
}
/** @psalm-param LockMode::* $lockMode */
#[DataProvider('provideLockModes')]
public function testSetLockMode(LockMode|int $lockMode): void
{
$query = $this->entityManager->wrapInTransaction(static function (EntityManagerInterface $em) use ($lockMode): Query {
$query = $em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1');
$query->setLockMode($lockMode);
return $query;
});
self::assertSame($lockMode, $query->getLockMode());
self::assertSame($lockMode, $query->getHint(Query::HINT_LOCK_MODE));
}
/** @psalm-return list<array{LockMode::*}> */
public static function provideLockModes(): array
{
return [
[LockMode::PESSIMISTIC_READ],
[LockMode::PESSIMISTIC_WRITE],
];
}
public function testFree(): void
{
$query = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1');

View File

@@ -288,12 +288,18 @@ class UnitOfWorkTest extends OrmTestCase
$entity->id = 123;
$this->_unitOfWork->registerManaged($entity, ['id' => 123], []);
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity));
self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity));
self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));
$this->_unitOfWork->remove($entity);
self::assertFalse($this->_unitOfWork->isInIdentityMap($entity));
self::assertSame(UnitOfWork::STATE_REMOVED, $this->_unitOfWork->getEntityState($entity));
self::assertTrue($this->_unitOfWork->isScheduledForDelete($entity));
self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));
$this->_unitOfWork->persist($entity);
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity));
self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity));
self::assertTrue($this->_unitOfWork->isInIdentityMap($entity));
}