diff --git a/.doctrine-project.json b/.doctrine-project.json index e761549298c..8dfa2ed2450 100644 --- a/.doctrine-project.json +++ b/.doctrine-project.json @@ -11,17 +11,29 @@ "slug": "latest", "upcoming": true }, + { + "name": "3.4", + "branchName": "3.4.x", + "slug": "3.4", + "upcoming": true + }, + { + "name": "3.3", + "branchName": "3.3.x", + "slug": "3.3", + "current": true + }, { "name": "3.2", "branchName": "3.2.x", "slug": "3.2", - "upcoming": true + "maintained": false }, { "name": "3.1", "branchName": "3.1.x", "slug": "3.1", - "current": true + "maintained": false }, { "name": "3.0", @@ -29,17 +41,23 @@ "slug": "3.0", "maintained": false }, + { + "name": "2.21", + "branchName": "2.21.x", + "slug": "2.21", + "upcoming": true + }, { "name": "2.20", "branchName": "2.20.x", "slug": "2.20", - "upcoming": true + "maintained": true }, { "name": "2.19", "branchName": "2.19.x", "slug": "2.19", - "maintained": true + "maintained": false }, { "name": "2.18", @@ -94,42 +112,6 @@ "branchName": "2.10.x", "slug": "2.10", "maintained": false - }, - { - "name": "2.9", - "branchName": "2.9.x", - "slug": "2.9", - "maintained": false - }, - { - "name": "2.8", - "branchName": "2.8.x", - "slug": "2.8", - "maintained": false - }, - { - "name": "2.7", - "branchName": "2.7", - "slug": "2.7", - "maintained": false - }, - { - "name": "2.6", - "branchName": "2.6", - "slug": "2.6", - "maintained": false - }, - { - "name": "2.5", - "branchName": "2.5", - "slug": "2.5", - "maintained": false - }, - { - "name": "2.4", - "branchName": "2.4", - "slug": "2.4", - "maintained": false } ] } diff --git a/.github/ISSUE_TEMPLATE/BC_Break.md b/.github/ISSUE_TEMPLATE/BC_Break.md deleted file mode 100644 index 9fdd4dd9229..00000000000 --- a/.github/ISSUE_TEMPLATE/BC_Break.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: πŸ’₯ BC Break -about: Have you encountered an issue during upgrade? πŸ’£ ---- - - - -### BC Break Report - - - -| Q | A -|------------ | ------ -| BC Break | yes -| Version | x.y.z - -#### Summary - - - -#### Previous behavior - - - -#### Current behavior - - - -#### How to reproduce - - diff --git a/.github/ISSUE_TEMPLATE/Bug.md b/.github/ISSUE_TEMPLATE/Bug.md deleted file mode 100644 index c65ac467ac7..00000000000 --- a/.github/ISSUE_TEMPLATE/Bug.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: 🐞 Bug Report -about: Something is broken? πŸ”¨ ---- - -### Bug Report - - - -| Q | A -|------------ | ------ -| BC Break | yes/no -| Version | x.y.z - -#### Summary - - - -#### Current behavior - - - -#### How to reproduce - - - -#### Expected behavior - - - diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md deleted file mode 100644 index 2620581d684..00000000000 --- a/.github/ISSUE_TEMPLATE/Feature_Request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: πŸŽ‰ Feature Request -about: You have a neat idea that should be implemented? 🎩 ---- - -### Feature Request - - - -| Q | A -|------------ | ------ -| New Feature | yes -| RFC | yes/no -| BC Break | yes/no - -#### Summary - - diff --git a/.github/ISSUE_TEMPLATE/Support_Question.md b/.github/ISSUE_TEMPLATE/Support_Question.md deleted file mode 100644 index e584c3994c2..00000000000 --- a/.github/ISSUE_TEMPLATE/Support_Question.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -name: ❓ Support Question -about: Have a problem that you can't figure out? πŸ€” ---- - -Please use https://github.com/doctrine/orm/discussions instead. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..54e72673914 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "CI" + target-branch: "2.20.x" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index df5dd9bb89c..252c9ff5ca0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -33,33 +33,28 @@ jobs: strategy: matrix: php-version: - - "7.2" - - "7.3" - - "7.4" - - "8.0" - "8.1" - "8.2" - "8.3" - "8.4" dbal-version: - "default" + - "3.7" extension: + - "sqlite3" - "pdo_sqlite" - proxy: - - "common" + deps: + - "highest" include: - - php-version: "8.0" - dbal-version: "2.13" - extension: "pdo_sqlite" - php-version: "8.2" - dbal-version: "3@dev" + dbal-version: "4@dev" extension: "pdo_sqlite" - php-version: "8.2" - dbal-version: "default" + dbal-version: "4@dev" extension: "sqlite3" - php-version: "8.1" dbal-version: "default" - proxy: "lazy-ghost" + deps: "lowest" extension: "pdo_sqlite" steps: @@ -84,23 +79,22 @@ jobs: uses: "ramsey/composer-install@v3" with: composer-options: "--ignore-platform-req=php+" + dependency-versions: "${{ matrix.deps }}" - name: "Run PHPUnit" run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml" env: ENABLE_SECOND_LEVEL_CACHE: 0 - ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}" - name: "Run PHPUnit with Second Level Cache" run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml" env: ENABLE_SECOND_LEVEL_CACHE: 1 - ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}" - name: "Upload coverage file" uses: "actions/upload-artifact@v4" with: - name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.proxy }}-coverage" + name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-coverage" path: "coverage*.xml" @@ -117,19 +111,19 @@ jobs: - "8.4" dbal-version: - "default" - - "3@dev" + - "3.7" postgres-version: - "17" extension: - pdo_pgsql - pgsql include: - - php-version: "8.0" - dbal-version: "2.13" + - php-version: "8.2" + dbal-version: "4@dev" postgres-version: "14" extension: pdo_pgsql - php-version: "8.2" - dbal-version: "default" + dbal-version: "3.7" postgres-version: "9.6" extension: pdo_pgsql @@ -191,17 +185,13 @@ jobs: - "8.4" dbal-version: - "default" - - "3@dev" + - "3.7" + - "4@dev" mariadb-version: - "11.4" extension: - "mysqli" - "pdo_mysql" - include: - - php-version: "8.0" - dbal-version: "2.13" - mariadb-version: "10.6" - extension: "pdo_mysql" services: mariadb: @@ -262,7 +252,7 @@ jobs: - "8.4" dbal-version: - "default" - - "3@dev" + - "3.7" mysql-version: - "5.7" - "8.0" @@ -270,8 +260,12 @@ jobs: - "mysqli" - "pdo_mysql" include: - - php-version: "8.0" - dbal-version: "2.13" + - php-version: "8.2" + dbal-version: "4@dev" + mysql-version: "8.0" + extension: "mysqli" + - php-version: "8.2" + dbal-version: "4@dev" mysql-version: "8.0" extension: "pdo_mysql" @@ -326,40 +320,6 @@ jobs: name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage" path: "coverage*.xml" - - phpunit-lower-php-versions: - name: "PHPUnit with SQLite" - runs-on: "ubuntu-22.04" - - strategy: - matrix: - php-version: - - "7.1" - deps: - - "highest" - - "lowest" - - steps: - - name: "Checkout" - uses: "actions/checkout@v4" - with: - fetch-depth: 2 - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php-version }}" - ini-values: "zend.assertions=1, apc.enable_cli=1" - - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v3" - with: - dependency-versions: "${{ matrix.deps }}" - - - name: "Run PHPUnit" - run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_sqlite.xml" - - upload_coverage: name: "Upload coverage to Codecov" runs-on: "ubuntu-22.04" diff --git a/.github/workflows/phpbench.yml b/.github/workflows/phpbench.yml index b223e635930..2a09ec3b18f 100644 --- a/.github/workflows/phpbench.yml +++ b/.github/workflows/phpbench.yml @@ -32,7 +32,7 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.1" steps: - name: "Checkout" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000000..08a88527759 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,24 @@ +name: 'Close stale pull requests' +on: + schedule: + - cron: '0 3 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + stale-pr-message: > + There hasn't been any activity on this pull request in the past 90 days, so + it has been marked as stale and it will be closed automatically if no + further activity occurs in the next 7 days. + + If you want to continue working on it, please leave a comment. + + close-pr-message: > + This pull request was closed due to inactivity. + + days-before-stale: -1 + days-before-pr-stale: 90 + days-before-pr-close: 7 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 23327aeda5f..ed36fd3675d 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -22,52 +22,35 @@ on: jobs: static-analysis-phpstan: - name: "Static Analysis with PHPStan" - runs-on: "ubuntu-22.04" + name: Static Analysis with PHPStan + runs-on: ubuntu-22.04 strategy: - fail-fast: false matrix: - dbal-version: - - "default" - persistence-version: - - "default" include: - - dbal-version: "2.13" - persistence-version: "default" - - dbal-version: "default" - persistence-version: "2.5" + - dbal-version: default + config: phpstan.neon + - dbal-version: 3.8.2 + config: phpstan-dbal3.neon steps: - name: "Checkout code" uses: "actions/checkout@v4" - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" + - name: Install PHP + uses: shivammathur/setup-php@v2 with: - coverage: "none" + coverage: none php-version: "8.4" + tools: cs2pr - - name: "Require specific DBAL version" + - name: Require specific DBAL version run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update" if: "${{ matrix.dbal-version != 'default' }}" - - name: "Require specific persistence version" - run: "composer require doctrine/persistence ^$([ ${{ matrix.persistence-version }} = default ] && echo '3.1' || echo ${{ matrix.persistence-version }}) --no-update" - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v3" - with: - dependency-versions: "highest" - - - name: "Run a static analysis with phpstan/phpstan" - run: "vendor/bin/phpstan analyse" - if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version == 'default'}}" - - - name: "Run a static analysis with phpstan/phpstan" - run: "vendor/bin/phpstan analyse -c phpstan-dbal2.neon" - if: "${{ matrix.dbal-version == '2.13' }}" + - name: Install dependencies with Composer + uses: ramsey/composer-install@v2 - - name: "Run a static analysis with phpstan/phpstan" - run: "vendor/bin/phpstan analyse -c phpstan-persistence2.neon" - if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version != 'default'}}" + - name: Run static analysis with phpstan/phpstan + run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr" diff --git a/.github/workflows/website-schema.yml b/.github/workflows/website-schema.yml new file mode 100644 index 00000000000..e251588e5de --- /dev/null +++ b/.github/workflows/website-schema.yml @@ -0,0 +1,21 @@ + +name: "Website config validation" + +on: + pull_request: + branches: + - "*.x" + paths: + - ".doctrine-project.json" + - ".github/workflows/website-schema.yml" + push: + branches: + - "*.x" + paths: + - ".doctrine-project.json" + - ".github/workflows/website-schema.yml" + +jobs: + json-validate: + name: "Validate JSON schema" + uses: "doctrine/.github/.github/workflows/website-schema.yml@7.1.0" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 10390f15c65..06364814b45 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,7 +57,7 @@ sqlite database. Tips for creating unit tests: 1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class. - See `https://github.com/doctrine/orm/tree/2.8.x/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an + See `https://github.com/doctrine/orm/tree/3.0.x/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an example. ## Getting merged diff --git a/README.md b/README.md index 752c2b2a0a2..47a36ddb27e 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ | [![Build status][4.0 image]][4.0] | [![Build status][3.4 image]][3.4] | [![Build status][3.3 image]][3.3] | [![Build status][2.21 image]][2.21] | [![Build status][2.20 image]][2.20] | | [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.4 coverage image]][3.4 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | -[

πŸ‡ΊπŸ‡¦ UKRAINE NEEDS YOUR HELP NOW!

](https://www.doctrine-project.org/stop-war.html) - -Doctrine ORM is an object-relational mapper for PHP 7.1+ that provides transparent persistence +Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility diff --git a/UPGRADE.md b/UPGRADE.md index 9de8af04d38..085f645c3f5 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,4 +1,13 @@ -# Upgrade to 2.20 +# Upgrade to 3.4 + +Using the same class several times in a discriminator map is deprecated. +In 4.0, this will be an error. + +# Upgrade to 3.3 + +## Deprecate `DatabaseDriver` + +The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is deprecated without replacement. ## Add `Doctrine\ORM\Query\OutputWalker` interface, deprecate `Doctrine\ORM\Query\SqlWalker::getExecutor()` @@ -9,6 +18,775 @@ The output walker must not base its workings on the query `firstResult`/`maxResu Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()` method. Details can be found at https://github.com/doctrine/orm/pull/11188. + +# 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` + +This class is deprecated and will be removed in 4.0. +Instead, use `Doctrine\Persistence\Reflection\EnumReflectionProperty` from +`doctrine/persistence`. + +## Deprecate passing null to `ClassMetadata::fullyQualifiedClassName()` + +Passing `null` to `Doctrine\ORM\ClassMetadata::fullyQualifiedClassName()` is +deprecated and will no longer be possible in 4.0. + +## Deprecate array access + +Using array access on instances of the following classes is deprecated: + +- `Doctrine\ORM\Mapping\DiscriminatorColumnMapping` +- `Doctrine\ORM\Mapping\EmbedClassMapping` +- `Doctrine\ORM\Mapping\FieldMapping` +- `Doctrine\ORM\Mapping\JoinColumnMapping` +- `Doctrine\ORM\Mapping\JoinTableMapping` + +# Upgrade to 3.0 + +## BC BREAK: Calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association now throws an exception + +Previously, calling +`Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField()` with +the owning side of an association returned `null`, which was undocumented, and +wrong according to the phpdoc of the parent method. + +If you do not know whether you are on the owning or inverse side of an association, +you can use `Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide()` +to find out. + +## BC BREAK: `Doctrine\ORM\Proxy\Autoloader` no longer extends `Doctrine\Common\Proxy\Autoloader` + +Make sure to use the former when writing a type declaration or an `instanceof` check. + +## Minor BC BREAK: Changed order of arguments passed to `OneToOne`, `ManyToOne` and `Index` mapping PHP attributes + +To keep PHP mapping attributes consistent, order of arguments passed to above attributes has been changed +so `$targetEntity` is a first argument now. This change affects only non-named arguments usage. + +## BC BREAK: AUTO keyword for identity generation defaults to IDENTITY for PostgreSQL when using `doctrine/dbal` 4 + +When using the `AUTO` strategy to let Doctrine determine the identity generation mechanism for +an entity, and when using `doctrine/dbal` 4, PostgreSQL now uses `IDENTITY` +instead of `SEQUENCE` or `SERIAL`. +* If you want to upgrade your existing tables to identity columns, you will need to follow [migration to identity columns on PostgreSQL](https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/how-to/postgresql-identity-migration.html) +* If you want to keep using SQL sequences, you need to configure the ORM this way: +```php +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\ORM\Configuration; +use Doctrine\ORM\Mapping\ClassMetadata; + +assert($configuration instanceof Configuration); +$configuration->setIdentityGenerationPreferences([ + PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE, +]); +``` + +## BC BREAK: Throw exceptions when using illegal attributes on Embeddable + +There are only a few attributes allowed on an embeddable such as `#[Column]` or +`#[Embedded]`. Previously all others that target entity classes where ignored, +now they throw an exception. + +## BC BREAK: Partial objects are removed + +WARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydration. + +- The `PARTIAL` keyword in DQL no longer exists (reintroduced in ORM 3.2) +- `Doctrine\ORM\Query\AST\PartialObjectExpression` is removed. (reintroduced in ORM 3.2) +- `Doctrine\ORM\Query\SqlWalker::HINT_PARTIAL` (reintroduced in ORM 3.2) and + `Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed. +- `Doctrine\ORM\EntityManager*::getPartialReference()` is removed. + +## BC BREAK: Enforce ArrayCollection Type on `\Doctrine\ORM\QueryBuilder::setParameters(ArrayCollection $parameters)` + +The argument $parameters can no longer be a key=>value array. Only ArrayCollection types are allowed. + +### Before + +```php +$qb = $em->createQueryBuilder() + ->select('u') + ->from('User', 'u') + ->where('u.id = :user_id1 OR u.id = :user_id2') + ->setParameters(array( + 'user_id1' => 1, + 'user_id2' => 2 + )); +``` + +### After + +```php +$qb = $em->createQueryBuilder() + ->select('u') + ->from('User', 'u') + ->where('u.id = :user_id1 OR u.id = :user_id2') + ->setParameters(new ArrayCollection(array( + new Parameter('user_id1', 1), + new Parameter('user_id2', 2) + ))); +``` + +## BC BREAK: `Doctrine\ORM\Persister\Entity\EntityPersister::executeInserts()` return type changed to `void` + +Implementors should adapt to the new signature, and should call +`UnitOfWork::assignPostInsertId()` for each entry in the previously returned +array. + +## BC BREAK: `Doctrine\ORM\Proxy\ProxyFactory` no longer extends abstract factory from `doctrine/common` + +It is no longer possible to call methods, constants or properties inherited +from that class on a `ProxyFactory` instance. + +`Doctrine\ORM\Proxy\ProxyFactory::createProxyDefinition()` and +`Doctrine\ORM\Proxy\ProxyFactory::resetUninitializedProxy()` are removed as well. + +## BC BREAK: lazy ghosts are enabled unconditionally + +`Doctrine\ORM\Configuration::setLazyGhostObjectEnabled()` and +`Doctrine\ORM\Configuration::isLazyGhostObjectEnabled()` are now no-ops and +will be deprecated in 3.1.0 + +## BC BREAK: collisions in identity map are unconditionally rejected + +`Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap()` and +`Doctrine\ORM\Configuration::isRejectIdCollisionInIdentityMapEnabled()` are now +no-ops and will be deprecated in 3.1.0. + +## BC BREAK: Lifecycle callback mapping on embedded classes is now explicitly forbidden + +Lifecycle callback mapping on embedded classes produced no effect, and is now +explicitly forbidden to point out mistakes. + +## BC BREAK: The `NOTIFY` change tracking policy is removed + +You should use `DEFERRED_EXPLICIT` instead. + +## BC BREAK: `Mapping\Driver\XmlDriver::__construct()` third argument is now enabled by default + +The third argument to +`Doctrine\ORM\Mapping\Driver\XmlDriver::__construct()` was introduced to +let users opt-in to XML validation, that is now always enabled by default. + +As a consequence, the same goes for +`Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver`, and for +`Doctrine\ORM\ORMSetup::createXMLMetadataConfiguration()`. + +## BC BREAK: `Mapping\Driver\AttributeDriver::__construct()` second argument is now a no-op + +The second argument to +`Doctrine\ORM\Mapping\Driver\AttributeDriver::__construct()` was introduced to +let users opt-in to a new behavior, that is now always enforced, regardless of +the value of that argument. + +## BC BREAK: `Query::setDQL()` and `Query::setFirstResult()` no longer accept `null` + +The `$dqlQuery` argument of `Doctrine\ORM\Query::setDQL()` must always be a +string. + +The `$firstResult` argument of `Doctrine\ORM\Query::setFirstResult()` must +always be an integer. + +## BC BREAK: `orm:schema-tool:update` option `--complete` is now a no-op + +`orm:schema-tool:update` now behaves as if `--complete` was provided, +regardless of whether it is provided or not. + +## BC BREAK: Removed `Doctrine\ORM\Proxy\Proxy` interface. + +Use `Doctrine\Persistence\Proxy` instead to check whether proxies are initialized. + +## BC BREAK: Overriding fields or associations declared in other than mapped superclasses + +As stated in the documentation, fields and associations may only be overridden when being inherited +from mapped superclasses. Overriding them for parent entity classes now throws a `MappingException`. + +## BC BREAK: Undeclared entity inheritance now throws a `MappingException` + +As soon as an entity class inherits from another entity class, inheritance has to +be declared by adding the appropriate configuration for the root entity. + +## Removed `getEntityManager()` in `Doctrine\ORM\Event\OnClearEventArgs` and `Doctrine\ORM\Event\*FlushEventArgs` + +Use `getObjectManager()` instead. + +## BC BREAK: Removed `Doctrine\ORM\Mapping\ClassMetadataInfo` class + +Use `Doctrine\ORM\Mapping\ClassMetadata` instead. + +## BC BREAK: Removed `Doctrine\ORM\Event\LifecycleEventArgs` class. + +Use one of the dedicated event classes instead: + +* `Doctrine\ORM\Event\PrePersistEventArgs` +* `Doctrine\ORM\Event\PreUpdateEventArgs` +* `Doctrine\ORM\Event\PreRemoveEventArgs` +* `Doctrine\ORM\Event\PostPersistEventArgs` +* `Doctrine\ORM\Event\PostUpdateEventArgs` +* `Doctrine\ORM\Event\PostRemoveEventArgs` +* `Doctrine\ORM\Event\PostLoadEventArgs` + +## BC BREAK: Removed `AttributeDriver::$entityAnnotationClasses` and `AttributeDriver::getReader()` + +* If you need to change the behavior of `AttributeDriver::isTransient()`, + override that method instead. +* The attribute reader is internal to the driver and should not be accessed from outside. + +## BC BREAK: Removed `Doctrine\ORM\Query\AST\InExpression` + +The AST parser will create a `InListExpression` or a `InSubselectExpression` when +encountering an `IN ()` DQL expression instead of a generic `InExpression`. + +As a consequence, `SqlWalker::walkInExpression()` has been replaced by +`SqlWalker::walkInListExpression()` and `SqlWalker::walkInSubselectExpression()`. + +## BC BREAK: Changed `EntityManagerInterface#refresh($entity)`, `EntityManagerDecorator#refresh($entity)` and `UnitOfWork#refresh($entity)` signatures + +The new signatures of these methods add an optional `LockMode|int|null $lockMode` +param with default `null` value (no lock). + +## BC Break: Removed AnnotationDriver + +The annotation driver and anything related to annotation has been removed. +Please migrate to another mapping driver. + +The `Doctrine\ORM\Mapping\Annotation` maker interface has been removed in favor of the new +`Doctrine\ORM\Mapping\MappingAttribute` interface. + +## BC BREAK: Removed `EntityManager::create()` + +The constructor of `EntityManager` is now public and must be used instead of the `create()` method. +However, the constructor expects a `Connection` while `create()` accepted an array with connection parameters. +You can pass that array to DBAL's `Doctrine\DBAL\DriverManager::getConnection()` method to bootstrap the +connection. + +## BC BREAK: Removed `QueryBuilder` methods and constants. + +The following `QueryBuilder` constants and methods have been removed: + +1. `SELECT`, +2. `DELETE`, +3. `UPDATE`, +4. `STATE_DIRTY`, +5. `STATE_CLEAN`, +6. `getState()`, +7. `getType()`. + +## BC BREAK: Omitting only the alias argument for `QueryBuilder::update` and `QueryBuilder::delete` is not supported anymore + +When building an UPDATE or DELETE query and when passing a class/type to the function, the alias argument must not be omitted. + +### Before + +```php +$qb = $em->createQueryBuilder() + ->delete('User u') + ->where('u.id = :user_id') + ->setParameter('user_id', 1); +``` + +### After + +```php +$qb = $em->createQueryBuilder() + ->delete('User', 'u') + ->where('u.id = :user_id') + ->setParameter('user_id', 1); +``` + +## BC BREAK: Split output walkers and tree walkers + +`SqlWalker` and its child classes don't implement the `TreeWalker` interface +anymore. + +The following methods have been removed from the `TreeWalker` interface and +from the `TreeWalkerAdapter` and `TreeWalkerChain` classes: + +* `setQueryComponent()` +* `walkSelectClause()` +* `walkFromClause()` +* `walkFunction()` +* `walkOrderByClause()` +* `walkOrderByItem()` +* `walkHavingClause()` +* `walkJoin()` +* `walkSelectExpression()` +* `walkQuantifiedExpression()` +* `walkSubselect()` +* `walkSubselectFromClause()` +* `walkSimpleSelectClause()` +* `walkSimpleSelectExpression()` +* `walkAggregateExpression()` +* `walkGroupByClause()` +* `walkGroupByItem()` +* `walkDeleteClause()` +* `walkUpdateClause()` +* `walkUpdateItem()` +* `walkWhereClause()` +* `walkConditionalExpression()` +* `walkConditionalTerm()` +* `walkConditionalFactor()` +* `walkConditionalPrimary()` +* `walkExistsExpression()` +* `walkCollectionMemberExpression()` +* `walkEmptyCollectionComparisonExpression()` +* `walkNullComparisonExpression()` +* `walkInExpression()` +* `walkInstanceOfExpression()` +* `walkLiteral()` +* `walkBetweenExpression()` +* `walkLikeExpression()` +* `walkStateFieldPathExpression()` +* `walkComparisonExpression()` +* `walkInputParameter()` +* `walkArithmeticExpression()` +* `walkArithmeticTerm()` +* `walkStringPrimary()` +* `walkArithmeticFactor()` +* `walkSimpleArithmeticExpression()` +* `walkPathExpression()` +* `walkResultVariable()` +* `getExecutor()` + +The following changes have been made to the abstract `TreeWalkerAdapter` class: + +* The method `setQueryComponent()` is now protected. +* The method `_getQueryComponents()` has been removed in favor of + `getQueryComponents()`. + +## BC BREAK: Removed identity columns emulation through sequences + +If the platform you are using does not support identity columns, you should +switch to the `SEQUENCE` strategy. + +## BC BREAK: Made setters parameters mandatory + +The following methods require an argument when being called. Pass `null` +instead of omitting the argument. + +* `Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs::setFoundMetadata()` +* `Doctrine\ORM\AbstractQuery::setHydrationCacheProfile()` +* `Doctrine\ORM\AbstractQuery::setResultCache()` +* `Doctrine\ORM\AbstractQuery::setResultCacheProfile()` + +## BC BREAK: New argument to `NamingStrategy::joinColumnName()` + +### Before + +```php + `Exception\MissingMappingDriverImplementation::create()` + * `unrecognizedField()` => `Persisters\Exception\UnrecognizedField::byName()` + * `unexpectedAssociationValue()` => `Exception\UnexpectedAssociationValue::create()` + * `invalidOrientation()` => `Persisters\Exception\InvalidOrientation::fromClassNameAndField()` + * `entityManagerClosed()` => `Exception\EntityManagerClosed::create()` + * `invalidHydrationMode()` => `Exception\InvalidHydrationMode::fromMode()` + * `mismatchedEventManager()` => `Exception\MismatchedEventManager::create()` + * `findByRequiresParameter()` => `Repository\Exception\InvalidMagicMethodCall::onMissingParameter()` + * `invalidMagicCall()` => `Repository\Exception\InvalidMagicMethodCall::becauseFieldNotFoundIn()` + * `invalidFindByInverseAssociation()` => `Repository\Exception\InvalidFindByCall::fromInverseSideUsage()` + * `invalidResultCacheDriver()` => `Cache\Exception\InvalidResultCacheDriver::create()` + * `notSupported()` => `Exception\NotSupported::create()` + * `queryCacheNotConfigured()` => `QueryCacheNotConfigured::create()` + * `metadataCacheNotConfigured()` => `Cache\Exception\MetadataCacheNotConfigured::create()` + * `queryCacheUsesNonPersistentCache()` => `Cache\Exception\QueryCacheUsesNonPersistentCache::fromDriver()` + * `metadataCacheUsesNonPersistentCache()` => `Cache\Exception\MetadataCacheUsesNonPersistentCache::fromDriver()` + * `proxyClassesAlwaysRegenerating()` => `Exception\ProxyClassesAlwaysRegenerating::create()` + * `invalidEntityRepository()` => `Exception\InvalidEntityRepository::fromClassName()` + * `missingIdentifierField()` => `Exception\MissingIdentifierField::fromFieldAndClass()` + * `unrecognizedIdentifierFields()` => `Exception\UnrecognizedIdentifierFields::fromClassAndFieldNames()` + * `cantUseInOperatorOnCompositeKeys()` => `Persisters\Exception\CantUseInOperatorOnCompositeKeys::create()` + +## BC Break: `CacheException` is no longer a class, but an interface + +All methods in `Doctrine\ORM\Cache\CacheException` have been extracted to dedicated exceptions. + + * `updateReadOnlyCollection()` => `Cache\Exception\CannotUpdateReadOnlyCollection::fromEntityAndField()` + * `updateReadOnlyEntity()` => `Cache\Exception\CannotUpdateReadOnlyEntity::fromEntity()` + * `nonCacheableEntity()` => `Cache\Exception\NonCacheableEntity::fromEntity()` + * `nonCacheableEntityAssociation()` => `Cache\Exception\NonCacheableEntityAssociation::fromEntityAndField()` + + +## BC Break: Missing type declaration added for identifier generators + +Although undocumented, it was possible to configure a custom repository +class that implements `ObjectRepository` but does not extend the +`EntityRepository` base class. Repository classes have to extend +`EntityRepository` now. + +## BC BREAK: Removed support for entity namespace alias + +- `EntityManager::getRepository()` no longer accepts the entity namespace alias + notation. +- `Configuration::addEntityNamespace()` and + `Configuration::getEntityNamespace()` have been removed. + +## BC BREAK: Remove helper methods from `AbstractCollectionPersister` + +The following protected methods of +`Doctrine\ORM\Cache\Persister\Collection\AbstractCollectionPersister` +have been removed. + +* `evictCollectionCache()` +* `evictElementCache()` + +## BC BREAK: `Doctrine\ORM\Query\TreeWalkerChainIterator` + +This class has been removed without replacement. + +## BC BREAK: Remove quoting methods from `ClassMetadata` + +The following methods have been removed from the class metadata because +quoting is handled by implementations of `Doctrine\ORM\Mapping\QuoteStrategy`: + +* `getQuotedIdentifierColumnNames()` +* `getQuotedColumnName()` +* `getQuotedTableName()` +* `getQuotedJoinTableName()` + +## BC BREAK: Remove ability to merge detached entities + +Merge semantics was a poor fit for the PHP "share-nothing" architecture. +In addition to that, merging caused multiple issues with data integrity +in the managed entity graph, which was constantly spawning more edge-case +bugs/scenarios. + +The method `UnitOfWork::merge()` has been removed. The method +`EntityManager::merge()` will throw an exception on each call. + +## BC BREAK: Removed ability to partially flush/commit entity manager and unit of work + +The following methods don't accept a single entity or an array of entities anymore: + +* `Doctrine\ORM\EntityManager::flush()` +* `Doctrine\ORM\Decorator\EntityManagerDecorator::flush()` +* `Doctrine\ORM\UnitOfWork::commit()` + +The semantics of `flush()` and `commit()` will remain the same, but the change +tracking will be performed on all entities managed by the unit of work, and not +just on the provided entities, as the parameter is now completely ignored. + +## BC BREAK: Removed ability to partially clear entity manager and unit of work + +* Passing an argument other than `null` to `EntityManager::clear()` will raise + an exception. +* The unit of work cannot be cleared partially anymore. Passing an argument to + `UnitOfWork::clear()` does not have any effect anymore; the unit of work is + cleared completely. +* The method `EntityRepository::clear()` has been removed. +* The methods `getEntityClass()` and `clearsAllEntities()` have been removed + from `OnClearEventArgs`. + +## BC BREAK: Remove support for Doctrine Cache + +The Doctrine Cache library is not supported anymore. The following methods +have been removed from `Doctrine\ORM\Configuration`: + +* `getQueryCacheImpl()` +* `setQueryCacheImpl()` +* `getHydrationCacheImpl()` +* `setHydrationCacheImpl()` +* `getMetadataCacheImpl()` +* `setMetadataCacheImpl()` + +The methods have been replaced by PSR-6 compatible counterparts +(just strip the `Impl` suffix from the old name to get the new one). + +## BC BREAK: Remove `Doctrine\ORM\Configuration::newDefaultAnnotationDriver` + +This functionality has been moved to the new `ORMSetup` class. Call +`Doctrine\ORM\ORMSetup::createDefaultAnnotationDriver()` to create +a new annotation driver. + +## BC BREAK: Remove `Doctrine\ORM\Tools\Setup` + +In our effort to migrate from Doctrine Cache to PSR-6, the `Setup` class which +accepted a Doctrine Cache instance in each method has been removed. + +The replacement is `Doctrine\ORM\ORMSetup` which accepts a PSR-6 +cache instead. + +## BC BREAK: Removed named queries + +All APIs related to named queries have been removed. + +## BC BREAK: Remove old cache accessors and mutators from query classes + +The following methods have been removed from `AbstractQuery`: + +* `setResultCacheDriver()` +* `getResultCacheDriver()` +* `useResultCache()` +* `getResultCacheLifetime()` +* `getResultCacheId()` + +The following methods have been removed from `Query`: + +* `setQueryCacheDriver()` +* `getQueryCacheDriver()` + +## BC BREAK: Remove `Doctrine\ORM\Cache\MultiGetRegion` + +The interface has been merged into `Doctrine\ORM\Cache\Region`. + +## BC BREAK: Rename `AbstractIdGenerator::generate()` to `generateId()` + +* Implementations of `AbstractIdGenerator` have to implement the method + `generateId()`. +* The method `generate()` has been removed from `AbstractIdGenerator`. + +## BC BREAK: Remove cache settings inspection + +Doctrine does not provide its own cache implementation anymore and relies on +the PSR-6 standard instead. As a consequence, we cannot determine anymore +whether a given cache adapter is suitable for a production environment. +Because of that, functionality that aims to do so has been removed: + +* `Configuration::ensureProductionSettings()` +* the `orm:ensure-production-settings` console command + +## BC BREAK: PSR-6-based second level cache + +The second level cache has been reworked to consume a PSR-6 cache. Using a +Doctrine Cache instance is not supported anymore. + +* `DefaultCacheFactory`: The constructor expects a PSR-6 cache item pool as + second argument now. +* `DefaultMultiGetRegion`: This class has been removed. +* `DefaultRegion`: + * The constructor expects a PSR-6 cache item pool as second argument now. + * The protected `$cache` property is removed. + * The properties `$name` and `$lifetime` as well as the constant + `REGION_KEY_SEPARATOR` and the method `getCacheEntryKey()` are + `private` now. + * The method `getCache()` has been removed. + + +## BC Break: Remove `Doctrine\ORM\Mapping\Driver\PHPDriver` + +Use `StaticPHPDriver` instead when you want to programmatically configure +entity metadata. + +## BC BREAK: Remove `Doctrine\ORM\EntityManagerInterface#transactional()` + +This method has been replaced by `Doctrine\ORM\EntityManagerInterface#wrapInTransaction()`. + +## BC BREAK: Removed support for schema emulation. + +The ORM no longer attempts to emulate schemas on SQLite. + +## BC BREAK: Remove `Setup::registerAutoloadDirectory()` + +Use Composer's autoloader instead. + +## BC BREAK: Remove YAML mapping drivers. + +If your code relies on `YamlDriver` or `SimpleYamlDriver`, you **MUST** migrate to +attribute, annotation or XML drivers instead. + +You can use the `orm:convert-mapping` command to convert your metadata mapping to XML +_before_ upgrading to 3.0: + +```sh +php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml +``` + +## BC BREAK: Remove code generators and related console commands + +These console commands have been removed: + +* `orm:convert-d1-schema` +* `orm:convert-mapping` +* `orm:generate:entities` +* `orm:generate-repositories` + +These classes have been deprecated: + +* `Doctrine\ORM\Tools\ConvertDoctrine1Schema` +* `Doctrine\ORM\Tools\EntityGenerator` +* `Doctrine\ORM\Tools\EntityRepositoryGenerator` + +The entire `Doctrine\ORM\Tools\Export` namespace has been removed as well. + +## BC BREAK: Removed `Doctrine\ORM\Version` + +Use Composer's runtime API if you _really_ need to check the version of the ORM package at runtime. + +## BC BREAK: EntityRepository::count() signature change + +The argument `$criteria` of `Doctrine\ORM\EntityRepository::count()` is now +optional. Overrides in child classes should be made compatible. + +## BC BREAK: changes in exception hierarchy + +- `Doctrine\ORM\ORMException` has been removed +- `Doctrine\ORM\Exception\ORMException` is now an interface + +## Variadic methods now use native variadics +The following methods were using `func_get_args()` to simulate a variadic argument: +- `Doctrine\ORM\Query\Expr#andX()` +- `Doctrine\ORM\Query\Expr#orX()` +- `Doctrine\ORM\QueryBuilder#select()` +- `Doctrine\ORM\QueryBuilder#addSelect()` +- `Doctrine\ORM\QueryBuilder#where()` +- `Doctrine\ORM\QueryBuilder#andWhere()` +- `Doctrine\ORM\QueryBuilder#orWhere()` +- `Doctrine\ORM\QueryBuilder#groupBy()` +- `Doctrine\ORM\QueryBuilder#andGroupBy()` +- `Doctrine\ORM\QueryBuilder#having()` +- `Doctrine\ORM\QueryBuilder#andHaving()` +- `Doctrine\ORM\QueryBuilder#orHaving()` +A variadic argument is now actually used in their signatures signature (`...$x`). +Signatures of overridden methods should be changed accordingly + +## Minor BC BREAK: removed `Doctrine\ORM\EntityManagerInterface#copy()` + +Method `Doctrine\ORM\EntityManagerInterface#copy()` never got its implementation and is removed in 3.0. + +## BC BREAK: Removed classes related to UUID and TABLE generator strategies + +The following classes have been removed: +- `Doctrine\ORM\Id\TableGenerator` +- `Doctrine\ORM\Id\UuidGenerator` + +Using the `UUID` strategy for generating identifiers is not supported anymore. + +## BC BREAK: Removed `Query::iterate()` + +The deprecated method `Query::iterate()` has been removed along with the +following classes and methods: + +- `AbstractHydrator::iterate()` +- `AbstractHydrator::hydrateRow()` +- `IterableResult` + +Use `toIterable()` instead. + +# Upgrade to 2.20 + +## Add `Doctrine\ORM\Query\OutputWalker` interface, deprecate `Doctrine\ORM\Query\SqlWalker::getExecutor()` + +Output walkers should implement the new `\Doctrine\ORM\Query\OutputWalker` interface and create +`Doctrine\ORM\Query\Exec\SqlFinalizer` instances instead of `Doctrine\ORM\Query\Exec\AbstractSqlExecutor`s. +The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the +`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values. +Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()` +method. Details can be found at https://github.com/doctrine/orm/pull/11188. + ## Explictly forbid property hooks Property hooks are not supported yet by Doctrine ORM. Until support is added, @@ -17,7 +795,7 @@ change in behavior. Progress on this is tracked at https://github.com/doctrine/orm/issues/11624 . -## PARTIAL DQL syntax is undeprecated +## PARTIAL DQL syntax is undeprecated Use of the PARTIAL keyword is not deprecated anymore in DQL, because we will be able to support PARTIAL objects with PHP 8.4 Lazy Objects and diff --git a/bin/doctrine b/bin/doctrine deleted file mode 100755 index 83d38042920..00000000000 --- a/bin/doctrine +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env php -register(); - -$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); -$classLoader->register(); - -$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; - -$helperSet = null; -if (file_exists($configFile)) { - if ( ! is_readable($configFile)) { - trigger_error( - 'Configuration file [' . $configFile . '] does not have read permission.', E_USER_ERROR - ); - } - - require $configFile; - - foreach ($GLOBALS as $helperSetCandidate) { - if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { - $helperSet = $helperSetCandidate; - break; - } - } -} - -$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); - -\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet); diff --git a/bin/doctrine.bat b/bin/doctrine.bat deleted file mode 100644 index a91645cc457..00000000000 --- a/bin/doctrine.bat +++ /dev/null @@ -1,9 +0,0 @@ -@echo off - -if "%PHPBIN%" == "" set PHPBIN=@php_bin@ -if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH -GOTO RUN -:USE_PEAR_PATH -set PHPBIN=%PHP_PEAR_PHP_BIN% -:RUN -"%PHPBIN%" "@bin_dir@\doctrine" %* diff --git a/bin/doctrine.php b/bin/doctrine.php deleted file mode 100644 index 2c11fd208da..00000000000 --- a/bin/doctrine.php +++ /dev/null @@ -1,62 +0,0 @@ - @@ -14,6 +13,9 @@ + + + @@ -25,11 +27,11 @@ - - + + ../../../src - - + + diff --git a/ci/github/phpunit/pdo_mysql.xml b/ci/github/phpunit/pdo_mysql.xml index 23cd676a9db..1db2cd4fb06 100644 --- a/ci/github/phpunit/pdo_mysql.xml +++ b/ci/github/phpunit/pdo_mysql.xml @@ -3,9 +3,8 @@ xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd" colors="true" beStrictAboutOutputDuringTests="true" - beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" - convertDeprecationsToExceptions="true" + cacheDirectory=".phpunit.cache" > @@ -14,6 +13,9 @@ + + + @@ -25,12 +27,11 @@ - - + + ../../../src - - - + + diff --git a/ci/github/phpunit/pdo_pgsql.xml b/ci/github/phpunit/pdo_pgsql.xml index 0360be046f5..53f270152b3 100644 --- a/ci/github/phpunit/pdo_pgsql.xml +++ b/ci/github/phpunit/pdo_pgsql.xml @@ -3,9 +3,8 @@ xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd" colors="true" beStrictAboutOutputDuringTests="true" - beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" - convertDeprecationsToExceptions="true" + cacheDirectory=".phpunit.cache" > @@ -25,11 +24,11 @@ - - + + ../../../src - - + + diff --git a/ci/github/phpunit/pdo_sqlite.xml b/ci/github/phpunit/pdo_sqlite.xml index 35de869baf2..385026dc732 100644 --- a/ci/github/phpunit/pdo_sqlite.xml +++ b/ci/github/phpunit/pdo_sqlite.xml @@ -3,9 +3,8 @@ xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd" colors="true" beStrictAboutOutputDuringTests="true" - beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" - convertDeprecationsToExceptions="true" + cacheDirectory=".phpunit.cache" > @@ -23,11 +22,11 @@ - - + + ../../../src - - + + diff --git a/ci/github/phpunit/pgsql.xml b/ci/github/phpunit/pgsql.xml index b547f47b488..58225decbe2 100644 --- a/ci/github/phpunit/pgsql.xml +++ b/ci/github/phpunit/pgsql.xml @@ -3,9 +3,8 @@ xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd" colors="true" beStrictAboutOutputDuringTests="true" - beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" - convertDeprecationsToExceptions="true" + cacheDirectory=".phpunit.cache" > @@ -25,11 +24,11 @@ - - + + ../../../src - - + + diff --git a/ci/github/phpunit/sqlite3.xml b/ci/github/phpunit/sqlite3.xml index 4b5ed70e6dd..a4f313b2e81 100644 --- a/ci/github/phpunit/sqlite3.xml +++ b/ci/github/phpunit/sqlite3.xml @@ -3,9 +3,8 @@ xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd" colors="true" beStrictAboutOutputDuringTests="true" - beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" - convertDeprecationsToExceptions="true" + cacheDirectory=".phpunit.cache" > @@ -23,11 +22,11 @@ - - + + ../../../src - - + + diff --git a/composer.json b/composer.json index c4de9690eaf..4543c9ba735 100644 --- a/composer.json +++ b/composer.json @@ -21,45 +21,36 @@ "sort-packages": true }, "require": { - "php": "^7.1 || ^8.0", + "php": "^8.1", "composer-runtime-api": "^2", "ext-ctype": "*", - "doctrine/cache": "^1.12.1 || ^2.1.1", - "doctrine/collections": "^1.5 || ^2.1", - "doctrine/common": "^3.0.3", - "doctrine/dbal": "^2.13.1 || ^3.2", + "doctrine/collections": "^2.2", + "doctrine/dbal": "^3.8.2 || ^4", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", "doctrine/instantiator": "^1.3 || ^2", - "doctrine/lexer": "^2 || ^3", - "doctrine/persistence": "^2.4 || ^3", + "doctrine/lexer": "^3", + "doctrine/persistence": "^3.3.1 || ^4", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/polyfill-php72": "^1.23", - "symfony/polyfill-php80": "^1.16" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.3.9 || ^7.0" }, "require-dev": { - "doctrine/annotations": "^1.13 || ^2", - "doctrine/coding-standard": "^9.0.2 || ^12.0", - "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/extension-installer": "~1.1.0 || ^1.4", - "phpstan/phpstan": "~1.4.10 || 2.0.3", - "phpstan/phpstan-deprecation-rules": "^1 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "doctrine/coding-standard": "^12.0", + "phpbench/phpbench": "^1.0", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "2.0.3", + "phpstan/phpstan-deprecation-rules": "^2", + "phpunit/phpunit": "^10.4.0", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", - "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "conflict": { - "doctrine/annotations": "<1.13 || >= 3.0" + "symfony/cache": "^5.4 || ^6.2 || ^7.0" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", - "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", - "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0" }, "autoload": { "psr-4": { "Doctrine\\ORM\\": "src" } @@ -71,7 +62,6 @@ "Doctrine\\Performance\\": "tests/Performance" } }, - "bin": ["bin/doctrine"], "archive": { "exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"] } diff --git a/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst index 67a14c8aa7f..8e7812e21a7 100644 --- a/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst +++ b/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -140,11 +140,6 @@ Now we're going to create the ``point`` type and implement all required methods. return $value; } - public function canRequireSQLConversion() - { - return true; - } - public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform) { return sprintf('AsText(%s)', $sqlExpr); diff --git a/docs/en/cookbook/aggregate-fields.rst b/docs/en/cookbook/aggregate-fields.rst index 001d70d34b4..20bfa32896f 100644 --- a/docs/en/cookbook/aggregate-fields.rst +++ b/docs/en/cookbook/aggregate-fields.rst @@ -352,7 +352,7 @@ the database using a FOR UPDATE. use Bank\Entities\Account; use Doctrine\DBAL\LockMode; - $account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ); + $account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_WRITE); Keeping Updates and Deletes in Sync ----------------------------------- diff --git a/docs/en/cookbook/dql-user-defined-functions.rst b/docs/en/cookbook/dql-user-defined-functions.rst index b189ed59fcd..e1782e05669 100644 --- a/docs/en/cookbook/dql-user-defined-functions.rst +++ b/docs/en/cookbook/dql-user-defined-functions.rst @@ -232,6 +232,33 @@ vendors SQL parser to show us further errors in the parsing process, for example if the Unit would not be one of the supported values by MySql. +Typed functions +--------------- +By default, result of custom functions is fetched as-is from the database driver. +If you want to be sure that the type is always the same, then your custom function needs to +implement ``Doctrine\ORM\Query\AST\TypedExpression``. Then, the result is wired +through ``Doctrine\DBAL\Types\Type::convertToPhpValue()`` of the ``Type`` returned in ``getReturnType()``. + +.. code-block:: php + + - -The NOTIFY change-tracking policy is the most effective -change-tracking policy provided by Doctrine but it requires some -boilerplate code. This recipe will show you how this boilerplate -code should look like. We will implement it on a -`Layer Supertype `_ -for all our domain objects. - -.. note:: - - The notify change tracking policy is deprecated and will be removed in ORM 3.0. - (\ `Details `_) - -Implementing NotifyPropertyChanged ----------------------------------- - -The NOTIFY policy is based on the assumption that the entities -notify interested listeners of changes to their properties. For -that purpose, a class that wants to use this policy needs to -implement the ``NotifyPropertyChanged`` interface from the -``Doctrine\Common`` namespace. - -.. code-block:: php - - listeners[] = $listener; - } - - /** Notifies listeners of a change. */ - protected function onPropertyChanged($propName, $oldValue, $newValue) { - if ($this->listeners) { - foreach ($this->listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } - } - } - -Then, in each property setter of concrete, derived domain classes, -you need to invoke onPropertyChanged as follows to notify -listeners: - -.. code-block:: php - - data) { // check: is it actually modified? - $this->onPropertyChanged('data', $this->data, $data); - $this->data = $data; - } - } - } - -The check whether the new value is different from the old one is -not mandatory but recommended. That way you can avoid unnecessary -updates and also have full control over when you consider a -property changed. diff --git a/docs/en/cookbook/mysql-enums.rst b/docs/en/cookbook/mysql-enums.rst index 5d5e4fd3372..9c4c3b5d822 100644 --- a/docs/en/cookbook/mysql-enums.rst +++ b/docs/en/cookbook/mysql-enums.rst @@ -43,13 +43,13 @@ entities: .. code-block:: php ` * **Drivers**: - :doc:`Docblock Annotations ` \| :doc:`Attributes ` \| :doc:`XML ` \| - :doc:`YAML ` \| :doc:`PHP ` Working with Objects @@ -75,6 +73,7 @@ Advanced Topics * :doc:`TypedFieldMapper ` * :doc:`Improving Performance ` * :doc:`Caching ` +* :doc:`Partial Hydration ` * :doc:`Partial Objects ` * :doc:`Change Tracking Policies ` * :doc:`Best Practices ` @@ -112,7 +111,6 @@ Cookbook * **Implementation**: :doc:`Array Access ` \| - :doc:`Notify ChangeTracking Example ` \| :doc:`Working with DateTime ` \| :doc:`Validation ` \| :doc:`Entities in the Session ` \| diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index 42a0833aca1..3282bbdb359 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -111,21 +111,16 @@ Gets or sets the metadata driver implementation that is used by Doctrine to acquire the object-relational metadata for your classes. -There are currently 5 available implementations: +There are currently 3 available implementations: - ``Doctrine\ORM\Mapping\Driver\AttributeDriver`` - ``Doctrine\ORM\Mapping\Driver\XmlDriver`` - ``Doctrine\ORM\Mapping\Driver\DriverChain`` -- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` (deprecated and will - be removed in ``doctrine/orm`` 3.0) -- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` (deprecated and will be - removed in ``doctrine/orm`` 3.0) Throughout the most part of this manual the AttributeDriver is used in the examples. For information on the usage of the -AnnotationDriver, XmlDriver or YamlDriver please refer to the dedicated -chapters ``Annotation Reference``, ``XML Mapping`` and ``YAML Mapping``. +XmlDriver please refer to the dedicated chapter ``XML Mapping``. The attribute driver can be injected in the ``Doctrine\ORM\Configuration``: @@ -155,9 +150,9 @@ Metadata Cache (***RECOMMENDED***) Gets or sets the cache adapter to use for caching metadata information, that is, all the information you supply via attributes, -annotations, xml or yaml, so that they do not need to be parsed and -loaded from scratch on every single request which is a waste of -resources. The cache implementation must implement the PSR-6 +xml, so that they do not need to be parsed and loaded from scratch on +every single request which is a waste of resources. The cache +implementation must implement the PSR-6 ``Psr\Cache\CacheItemPoolInterface`` interface. Usage of a metadata cache is highly recommended. @@ -425,7 +420,7 @@ Multiple Metadata Sources When using different components using Doctrine ORM you may end up with them using two different metadata drivers, for example XML and -YAML. You can use the MappingDriverChain Metadata implementations to +PHP. You can use the MappingDriverChain Metadata implementations to aggregate these drivers based on namespaces: .. code-block:: php @@ -435,7 +430,7 @@ aggregate these drivers based on namespaces: $chain = new MappingDriverChain(); $chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company'); - $chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping'); + $chain->addDriver($phpDriver, 'Doctrine\Tests\ORM\Mapping'); Based on the namespace of the entity the loading of entities is delegated to the appropriate driver. The chain semantics come from diff --git a/docs/en/reference/annotations-reference.rst b/docs/en/reference/annotations-reference.rst deleted file mode 100644 index f5cd6677bd1..00000000000 --- a/docs/en/reference/annotations-reference.rst +++ /dev/null @@ -1,1389 +0,0 @@ -Annotations Reference -===================== - -.. warning:: - The annotation driver is deprecated and will be removed in version - 3.0. It is strongly recommended to switch to one of the other - mapping drivers. - -.. note:: - - To be able to use annotations, you will have to install an extra - package called ``doctrine/annotations``. - -You've probably used docblock annotations in some form already, -most likely to provide documentation metadata for a tool like -``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a -tool to embed metadata inside the documentation section which can -then be processed by some tool. Doctrine ORM generalizes the concept -of docblock annotations so that they can be used for any kind of -metadata and so that it is easy to define new docblock annotations. -In order to allow more involved annotation values and to reduce the -chances of clashes with other docblock annotations, the Doctrine ORM -docblock annotations feature an alternative syntax that is heavily -inspired by the Annotation syntax introduced in Java 5. - -The implementation of these enhanced docblock annotations is located in -the ``doctrine/annotations`` package, but in the -``Doctrine\Common\Annotations`` namespace for backwards compatibility -reasons. Note that ``doctrine/annotations`` is not required by Doctrine -ORM, and you will need to require that package if you want to use -annotations. Doctrine ORM docblock annotations support namespaces and -nested annotations among other things. The Doctrine ORM defines its -own set of docblock annotations for supplying object-relational mapping -metadata. - -.. note:: - - If you're not comfortable with the concept of docblock - annotations, don't worry, as mentioned earlier Doctrine ORM provides - XML and YAML alternatives and you could easily implement your own - favourite mechanism for defining ORM metadata. - -In this chapter a reference of every Doctrine ORM Annotation is given -with short explanations on their context and usage. - -Index ------ - -- :ref:`@Column ` -- :ref:`@ColumnResult ` -- :ref:`@Cache ` -- :ref:`@ChangeTrackingPolicy ` -- :ref:`@CustomIdGenerator ` -- :ref:`@DiscriminatorColumn ` -- :ref:`@DiscriminatorMap ` -- :ref:`@Embeddable ` -- :ref:`@Embedded ` -- :ref:`@Entity ` -- :ref:`@EntityResult ` -- :ref:`@FieldResult ` -- :ref:`@GeneratedValue ` -- :ref:`@HasLifecycleCallbacks ` -- :ref:`@Index ` -- :ref:`@Id ` -- :ref:`@InheritanceType ` -- :ref:`@JoinColumn ` -- :ref:`@JoinColumns ` -- :ref:`@JoinTable ` -- :ref:`@ManyToOne ` -- :ref:`@ManyToMany ` -- :ref:`@MappedSuperclass ` -- :ref:`@NamedNativeQuery ` -- :ref:`@OneToOne ` -- :ref:`@OneToMany ` -- :ref:`@OrderBy ` -- :ref:`@PostLoad ` -- :ref:`@PostPersist ` -- :ref:`@PostRemove ` -- :ref:`@PostUpdate ` -- :ref:`@PrePersist ` -- :ref:`@PreRemove ` -- :ref:`@PreUpdate ` -- :ref:`@SequenceGenerator ` -- :ref:`@SqlResultSetMapping ` -- :ref:`@Table ` -- :ref:`@UniqueConstraint ` -- :ref:`@Version ` - -Reference ---------- - -.. _annref_column: - -@Column -~~~~~~~ - -Marks an annotated instance variable as "persistent". It has to be -inside the instance variables PHP DocBlock comment. Any value hold -inside this variable will be saved to and loaded from the database -as part of the lifecycle of the instance variables entity-class. - -Required attributes: - -- **type**: Name of the Doctrine Type which is converted between PHP - and Database representation. Default to ``string`` or :ref:`Type from PHP property type ` - -Optional attributes: - -- **name**: By default the property name is used for the database - column name also, however the 'name' attribute allows you to - determine the column name. - -- **length**: Used by the "string" type to determine its maximum - length in the database. Doctrine does not validate the length of a - string value for you. - -- **precision**: The precision for a decimal (exact numeric) column - (applies only for decimal column), which is the maximum number of - digits that are stored for the values. - -- **scale**: The scale for a decimal (exact numeric) column (applies - only for decimal column), which represents the number of digits - to the right of the decimal point and must not be greater than - *precision*. - -- **unique**: Boolean value to determine if the value of the column - should be unique across all rows of the underlying entities table. - -- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false. - -- **insertable**: Boolean value to determine if the column should be - included when inserting a new row into the underlying entities table. - If not specified, default value is true. - -- **updatable**: Boolean value to determine if the column should be - included when updating the row of the underlying entities table. - If not specified, default value is true. - -- **generated**: An enum with the possible values ALWAYS, INSERT, NEVER. Is - used after an INSERT or UPDATE statement to determine if the database - generated this value and it needs to be fetched using a SELECT statement. - -- **options**: Array of additional options: - - - ``default``: The default value to set for the column if no value - is supplied. - - - ``unsigned``: Boolean value to determine if the column should - be capable of representing only non-negative integers - (applies only for integer column and might not be supported by - all vendors). - - - ``fixed``: Boolean value to determine if the specified length of - a string column should be fixed or varying (applies only for - string/binary column and might not be supported by all vendors). - - - ``comment``: The comment of the column in the schema (might not - be supported by all vendors). - - - ``collation``: The collation of the column (only supported by Drizzle, Mysql, PostgreSQL>=9.1, Sqlite and SQLServer). - - - ``check``: Adds a check constraint type to the column (might not - be supported by all vendors). - -- **columnDefinition**: DDL SQL snippet that starts after the column - name and specifies the complete (non-portable!) column definition. - This attribute allows to make use of advanced RMDBS features. - However you should make careful use of this feature and the - consequences. SchemaTool will not detect changes on the column correctly - anymore if you use "columnDefinition". - - Additionally you should remember that the "type" - attribute still handles the conversion between PHP and Database - values. If you use this attribute on a column that is used for - joins between tables you should also take a look at - :ref:`@JoinColumn `. - -.. note:: - - For more detailed information on each attribute, please refer to - the DBAL ``Schema-Representation`` documentation. - -Examples: - -.. code-block:: php - - ` -can be found in the configuration section. - -Example: - -.. code-block:: php - - ` and :ref:`@GeneratedValue(strategy="CUSTOM") ` are specified. - -Required attributes: - -- **class**: name of the class which should extend Doctrine\ORM\Id\AbstractIdGenerator - -Example: - -.. code-block:: php - - ` -annotation to establish the relationship between the two classes. - -.. code-block:: php - - `. This -annotation is optional and only has meaning when used in -conjunction with @Id. - -If this annotation is not specified with @Id the NONE strategy is -used as default. - -Optional attributes: - - -- **strategy**: Set the name of the identifier generation strategy. - Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``UUID`` (deprecated), ``CUSTOM`` and ``NONE``, explained - in the :ref:`Identifier Generation Strategies ` section. - If not specified, default value is AUTO. - -Example: - -.. code-block:: php - - ` annotation on -the entity-class level. It provides a hint to the SchemaTool to -generate a database index on the specified table columns. It only -has meaning in the SchemaTool schema generation context. - -Required attributes: - - -- **fields**: Array of fields. Exactly one of **fields**, **columns** is required. -- **columns**: Array of columns. Exactly one of **fields**, **columns** is required. - -Optional attributes: - -- **name**: Name of the Index. If not provided, a generated name will be assigned. -- **options**: Array of platform specific options: - - - ``where``: SQL WHERE condition to be used for partial indexes. It will - only have effect on supported platforms. - -Basic example: - -.. code-block:: php - - ` and -:ref:`@DiscriminatorColumn ` annotations. - -Examples: - -.. code-block:: php - - `, :ref:`@OneToOne ` fields -and in the Context of :ref:`@JoinTable ` nested inside -a @ManyToMany. If this annotation or both *name* and *referencedColumnName* -are missing they will be computed considering the field's name and the current -:doc:`naming strategy `. - -Optional attributes: - -- **name**: Column name that holds the foreign key identifier for - this relation. In the context of @JoinTable it specifies the column - name in the join table. -- **referencedColumnName**: Name of the primary key identifier that - is used for joining of this relation. Defaults to *id*. -- **unique**: Determines whether this relation is exclusive between the - affected entities and should be enforced as such on the database - constraint level. Defaults to false. -- **nullable**: Determine whether the related entity is required, or if - null is an allowed state for the relation. Defaults to true. -- **onDelete**: Cascade Action (Database-level) -- **columnDefinition**: DDL SQL snippet that starts after the column - name and specifies the complete (non-portable!) column definition. - This attribute enables the use of advanced RMDBS features. Using - this attribute on @JoinColumn is necessary if you need slightly - different column definitions for joining columns, for example - regarding NULL/NOT NULL defaults. However by default a - "columnDefinition" attribute on :ref:`@Column ` also sets - the related @JoinColumn's columnDefinition. This is necessary to - make foreign keys work. - -Example: - -.. code-block:: php - - ` or :ref:`@OneToOne ` -relation with an entity that has multiple identifiers. - -.. _annref_jointable: - -@JoinTable -~~~~~~~~~~~~~~ - -Using :ref:`@OneToMany ` or -:ref:`@ManyToMany ` on the owning side of the relation -requires to specify the @JoinTable annotation which describes the -details of the database join table. If you do not specify -@JoinTable on these relations reasonable mapping defaults apply -using the affected table and the column names. - -Optional attributes: - - -- **name**: Database name of the join-table -- **joinColumns**: An array of @JoinColumn annotations describing the - join-relation between the owning entities table and the join table. -- **inverseJoinColumns**: An array of @JoinColumn annotations - describing the join-relation between the inverse entities table and - the join table. - -Example: - -.. code-block:: php - - ` is an -additional, optional annotation that has reasonable default -configuration values using the table and names of the two related -entities. - -Required attributes: - - -- **targetEntity**: FQCN of the referenced target entity. Can be the - unqualified class name if both classes are in the same namespace. - *IMPORTANT:* No leading backslash! - -Optional attributes: - - -- **mappedBy**: This option specifies the property name on the - targetEntity that is the owning side of this relation. It is a - required attribute for the inverse side of a relationship. -- **inversedBy**: The inversedBy attribute designates the field in the - entity that is the inverse side of the relationship. -- **cascade**: Cascade Option -- **fetch**: One of LAZY, EXTRA_LAZY or EAGER -- **indexBy**: Index the collection by a field on the target entity. - -.. note:: - - For ManyToMany bidirectional relationships either side may - be the owning side (the side that defines the @JoinTable and/or - does not make use of the mappedBy attribute, thus using a default - join table). - -Example: - -.. code-block:: php - - `. - -Optional attributes: - - -- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository. - That will be inherited for all subclasses of that Mapped Superclass. - -Example: - -.. code-block:: php - - ` with one additional option which can -be specified. The configuration defaults for -:ref:`@JoinColumn ` using the target entity table and -primary key column names apply here too. - -Required attributes: - - -- **targetEntity**: FQCN of the referenced target entity. Can be the - unqualified class name if both classes are in the same namespace. - When typed properties are used it is inherited from PHP type. - *IMPORTANT:* No leading backslash! - -Optional attributes: - - -- **cascade**: Cascade Option -- **fetch**: One of LAZY or EAGER -- **orphanRemoval**: Boolean that specifies if orphans, inverse - OneToOne entities that are not connected to any owning instance, - should be removed by Doctrine. Defaults to false. -- **inversedBy**: The inversedBy attribute designates the field in the - entity that is the inverse side of the relationship. - -Example: - -.. code-block:: php - - ` or :ref:`@OneToMany ` -annotation to specify by which criteria the collection should be -retrieved from the database by using an ORDER BY clause. - -This annotation requires a single non-attributed value with an DQL -snippet: - -Example: - -.. code-block:: php - - ` annotation on -the entity-class level. It allows to hint the SchemaTool to -generate a database unique constraint on the specified table -columns. It only has meaning in the SchemaTool schema generation -context. - -Required attributes: - - -- **fields**: Array of fields. Exactly one of **fields**, **columns** is required. -- **columns**: Array of columns. Exactly one of **fields**, **columns** is required. - -Optional attributes: - -- **name**: Name of the Index. If not provided, a generated name will be assigned. -- **options**: Array of platform specific options: - - - ``where``: SQL WHERE condition to be used for partial indexes. It will - only have effect on supported platforms. - -Basic example: - -.. code-block:: php - - ` -scenario. It only works on :ref:`@Column ` annotations that have -the type ``integer`` or ``datetime``. Combining ``@Version`` with -:ref:`@Id ` is not supported. - -Example: - -.. code-block:: php - - @@ -87,18 +66,6 @@ A many-to-one association is the most common association between objects. Exampl - .. code-block:: yaml - - User: - type: entity - manyToOne: - address: - targetEntity: Address - joinColumn: - name: address_id - referencedColumnName: id - - .. note:: The above ``#[JoinColumn]`` is optional as it would default @@ -154,30 +121,6 @@ references one ``Shipment`` entity. // ... } - .. code-block:: annotation - - @@ -188,17 +131,6 @@ references one ``Shipment`` entity. - .. code-block:: yaml - - Product: - type: entity - oneToOne: - shipment: - targetEntity: Shipment - joinColumn: - name: shipment_id - referencedColumnName: id - Note that the ``#[JoinColumn]`` is not really necessary in this example, as the defaults would be the same. @@ -259,38 +191,6 @@ object. // ... } - .. code-block:: annotation - - @@ -304,22 +204,6 @@ object. - .. code-block:: yaml - - Customer: - oneToOne: - cart: - targetEntity: Cart - mappedBy: customer - Cart: - oneToOne: - customer: - targetEntity: Customer - inversedBy: cart - joinColumn: - name: customer_id - referencedColumnName: id - Note that the @JoinColumn is not really necessary in this example, as the defaults would be the same. @@ -428,41 +312,6 @@ bidirectional many-to-one. // ... } - .. code-block:: annotation - - - * @OneToMany(targetEntity="Feature", mappedBy="product") - */ - private Collection $features; - // ... - - public function __construct() { - $this->features = new ArrayCollection(); - } - } - - /** @Entity */ - class Feature - { - // ... - /** - * Many features have one product. This is the owning side. - * @ManyToOne(targetEntity="Product", inversedBy="features") - * @JoinColumn(name="product_id", referencedColumnName="id") - */ - private Product|null $product = null; - // ... - } - .. code-block:: xml @@ -476,24 +325,6 @@ bidirectional many-to-one. - .. code-block:: yaml - - Product: - type: entity - oneToMany: - features: - targetEntity: Feature - mappedBy: product - Feature: - type: entity - manyToOne: - product: - targetEntity: Product - inversedBy: features - joinColumn: - name: product_id - referencedColumnName: id - Note that the @JoinColumn is not really necessary in this example, as the defaults would be the same. @@ -556,39 +387,6 @@ The following example sets up such a unidirectional one-to-many association: // ... } - .. code-block:: annotation - - - */ - private Collection $phonenumbers; - - public function __construct() - { - $this->phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); - } - - // ... - } - - /** @Entity */ - class Phonenumber - { - // ... - } - .. code-block:: xml @@ -606,24 +404,6 @@ The following example sets up such a unidirectional one-to-many association: - .. code-block:: yaml - - User: - type: entity - manyToMany: - phonenumbers: - targetEntity: Phonenumber - joinTable: - name: users_phonenumbers - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - phonenumber_id: - referencedColumnName: id - unique: true - - Generates the following MySQL Schema: .. code-block:: sql @@ -684,33 +464,6 @@ database perspective is known as an adjacency list approach. } } - .. code-block:: annotation - - - */ - private Collection $children; - - /** - * Many Categories have One Category. - * @ManyToOne(targetEntity="Category", inversedBy="children") - * @JoinColumn(name="parent_id", referencedColumnName="id") - */ - private Category|null $parent = null; - // ... - - public function __construct() { - $this->children = new \Doctrine\Common\Collections\ArrayCollection(); - } - } - .. code-block:: xml @@ -720,19 +473,6 @@ database perspective is known as an adjacency list approach. - .. code-block:: yaml - - Category: - type: entity - oneToMany: - children: - targetEntity: Category - mappedBy: parent - manyToOne: - parent: - targetEntity: Category - inversedBy: children - Note that the @JoinColumn is not really necessary in this example, as the defaults would be the same. @@ -787,38 +527,6 @@ entities: // ... } - .. code-block:: annotation - - - */ - private Collection $groups; - - // ... - - public function __construct() { - $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); - } - } - - /** @Entity */ - class Group - { - // ... - } - .. code-block:: xml @@ -836,22 +544,6 @@ entities: - .. code-block:: yaml - - User: - type: entity - manyToMany: - groups: - targetEntity: Group - joinTable: - name: users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - Generated MySQL Schema: .. code-block:: sql @@ -939,47 +631,6 @@ one is bidirectional. // ... } - .. code-block:: annotation - - - */ - private Collection $groups; - - public function __construct() { - $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); - } - - // ... - } - - /** @Entity */ - class Group - { - // ... - /** - * Many Groups have Many Users. - * @ManyToMany(targetEntity="User", mappedBy="groups") - * @var Collection - */ - private Collection $users; - - public function __construct() { - $this->users = new \Doctrine\Common\Collections\ArrayCollection(); - } - - // ... - } - .. code-block:: xml @@ -1001,30 +652,6 @@ one is bidirectional. - .. code-block:: yaml - - User: - type: entity - manyToMany: - groups: - targetEntity: Group - inversedBy: users - joinTable: - name: users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - - Group: - type: entity - manyToMany: - users: - targetEntity: User - mappedBy: groups - The MySQL schema is exactly the same as for the Many-To-Many uni-directional case above. @@ -1172,12 +799,6 @@ As an example, consider this mapping: #[OneToOne(targetEntity: Shipment::class)] private Shipment|null $shipment = null; - .. code-block:: annotation - - @@ -1186,14 +807,6 @@ As an example, consider this mapping: - .. code-block:: yaml - - Product: - type: entity - oneToOne: - shipment: - targetEntity: Shipment - This is essentially the same as the following, more verbose, mapping: @@ -1207,16 +820,6 @@ mapping: #[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')] private Shipment|null $shipment = null; - .. code-block:: annotation - - @@ -1227,17 +830,6 @@ mapping: - .. code-block:: yaml - - Product: - type: entity - oneToOne: - shipment: - targetEntity: Shipment - joinColumn: - name: shipment_id - referencedColumnName: id - The @JoinTable definition used for many-to-many mappings has similar defaults. As an example, consider this mapping: @@ -1255,20 +847,6 @@ similar defaults. As an example, consider this mapping: // ... } - .. code-block:: annotation - - - */ - private Collection $groups; - // ... - } - .. code-block:: xml @@ -1277,14 +855,6 @@ similar defaults. As an example, consider this mapping: - .. code-block:: yaml - - User: - type: entity - manyToMany: - groups: - targetEntity: Group - This is essentially the same as the following, more verbose, mapping: .. configuration-block:: @@ -1307,25 +877,6 @@ This is essentially the same as the following, more verbose, mapping: // ... } - .. code-block:: annotation - - - */ - private Collection $groups; - // ... - } - .. code-block:: xml @@ -1343,22 +894,6 @@ This is essentially the same as the following, more verbose, mapping: - .. code-block:: yaml - - User: - type: entity - manyToMany: - groups: - targetEntity: Group - joinTable: - name: User_Group - joinColumns: - User_id: - referencedColumnName: id - inverseJoinColumns: - Group_id: - referencedColumnName: id - In that case, the name of the join table defaults to a combination of the simple, unqualified class names of the participating classes, separated by an underscore character. The names of the @@ -1378,12 +913,6 @@ associations as they will be set based on type. So that: #[OneToOne] private Shipment $shipment; - .. code-block:: annotation - - @@ -1392,13 +921,6 @@ associations as they will be set based on type. So that: - .. code-block:: yaml - - Product: - type: entity - oneToOne: - shipment: ~ - Is essentially the same as following: .. configuration-block:: @@ -1431,17 +953,6 @@ Is essentially the same as following: - .. code-block:: yaml - - Product: - type: entity - oneToOne: - shipment: - targetEntity: Shipment - joinColumn: - name: shipment_id - referencedColumnName: id - If you accept these defaults, you can reduce the mapping code to a minimum. diff --git a/docs/en/reference/attributes-reference.rst b/docs/en/reference/attributes-reference.rst index 8709cda6279..d3a1eb04674 100644 --- a/docs/en/reference/attributes-reference.rst +++ b/docs/en/reference/attributes-reference.rst @@ -4,8 +4,9 @@ Attributes Reference PHP 8 adds native support for metadata with its "Attributes" feature. Doctrine ORM provides support for mapping metadata using PHP attributes as of version 2.9. -The attributes metadata support is closely modelled after the already existing -annotation metadata supported since the first version 2.0. +The attributes metadata support is closely modelled after the already +existing and now removed annotation metadata supported since the first +version 2.0. Index ----- @@ -310,7 +311,6 @@ Example: Entity, ChangeTrackingPolicy("DEFERRED_IMPLICIT"), ChangeTrackingPolicy("DEFERRED_EXPLICIT"), - ChangeTrackingPolicy("NOTIFY") ] class User {} @@ -485,9 +485,8 @@ used as default. Optional parameters: - **strategy**: Set the name of the identifier generation strategy. - Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``UUID`` - (deprecated), ``CUSTOM`` and ``NONE``. - If not specified, the default value is ``AUTO``. + Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``CUSTOM`` and + ``NONE``. If not specified, the default value is ``AUTO``. Example: @@ -712,10 +711,6 @@ details of the database join table. If you do not specify ``#[JoinTable]`` on these relations reasonable mapping defaults apply using the affected table and the column names. -A notable difference to the annotation metadata support, ``#[JoinColumn]`` -and ``#[InverseJoinColumn]`` can be specified at the property level and are not -nested within the ``#[JoinTable]`` attribute. - Required attribute: - **name**: Database name of the join-table @@ -931,7 +926,7 @@ Example: #[OneToMany( targetEntity: "Phonenumber", mappedBy: "user", - cascade: ["persist", "remove", "merge"], + cascade: ["persist", "remove"], orphanRemoval: true) ] public $phonenumbers; diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 0070c271762..55fe0389379 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -47,17 +47,14 @@ mapping metadata: - :doc:`Attributes ` - :doc:`XML ` - :doc:`PHP code ` -- :doc:`Docblock Annotations ` (deprecated and will be removed in ``doctrine/orm`` 3.0) -- :doc:`YAML ` (deprecated and will be removed in ``doctrine/orm`` 3.0.) This manual will usually show mapping metadata via attributes, though -many examples also show the equivalent configuration in annotations, -YAML and XML. +many examples also show the equivalent configuration in XML. .. note:: All metadata drivers perform equally. Once the metadata of a class has been - read from the source (attributes, annotations, XML, etc.) it is stored in an instance + read from the source (attributes, XML, etc.) it is stored in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class which are stored in the metadata cache. If you're not using a metadata cache (not recommended!) then the XML driver is the fastest. @@ -77,17 +74,6 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward: // ... } - .. code-block:: annotation - - @@ -96,12 +82,6 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward: - .. code-block:: yaml - - Message: - type: entity - # ... - With no additional information, Doctrine expects the entity to be saved into a table with the same name as the class in our case ``Message``. You can change this by configuring information about the table: @@ -121,21 +101,6 @@ You can change this by configuring information about the table: // ... } - .. code-block:: annotation - - @@ -144,13 +109,6 @@ You can change this by configuring information about the table: - .. code-block:: yaml - - Message: - type: entity - table: message - # ... - Now the class ``Message`` will be saved and fetched from the table ``message``. Property Mapping @@ -182,23 +140,6 @@ specified, ``string`` is used as the default. private $postedAt; } - .. code-block:: annotation - - @@ -209,19 +150,6 @@ specified, ``string`` is used as the default. - .. code-block:: yaml - - Message: - type: entity - fields: - id: - type: integer - text: - length: 140 - postedAt: - type: datetime - column: posted_at - When we don't explicitly specify a column name via the ``name`` option, Doctrine assumes the field name is also the column name. So in this example: @@ -347,20 +275,6 @@ the field that serves as the identifier with the ``#[Id]`` attribute. // ... } - .. code-block:: annotation - - @@ -372,24 +286,27 @@ the field that serves as the identifier with the ``#[Id]`` attribute. - .. code-block:: yaml - - Message: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - # fields here - In most cases using the automatic generator strategy (``#[GeneratedValue]``) is what you want, but for backwards-compatibility reasons it might not. It defaults to the identifier generation mechanism your current database vendor preferred at the time that strategy was introduced: ``AUTO_INCREMENT`` with MySQL, sequences with PostgreSQL and Oracle and so on. +If you are using `doctrine/dbal` 4, we now recommend using ``IDENTITY`` +for PostgreSQL, and ``AUTO`` resolves to it because of that. +You can stick with ``SEQUENCE`` while still using the ``AUTO`` +strategy, by configuring what it defaults to. + +.. code-block:: php + + setIdentityGenerationPreferences([ + PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE, + ]); .. _identifier-generation-strategies: @@ -418,14 +335,12 @@ Here is the list of possible generation strategies: generation. This strategy does currently not provide full portability. Sequences are supported by Oracle, PostgreSql and SQL Anywhere. -- ``UUID`` (deprecated): Tells Doctrine to use the built-in Universally - Unique Identifier generator. This strategy provides full portability. - ``NONE``: Tells Doctrine that the identifiers are assigned (and thus generated) by your code. The assignment must take place before a new entity is passed to ``EntityManager#persist``. NONE is the same as leaving off the ``#[GeneratedValue]`` entirely. - ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute. - It will allow you to pass a :ref:`class of your own to generate the identifiers. ` + It will allow you to pass a :ref:`class of your own to generate the identifiers. ` Sequence Generator ^^^^^^^^^^^^^^^^^^ @@ -448,20 +363,6 @@ besides specifying the sequence's name: // ... } - .. code-block:: annotation - - @@ -473,20 +374,6 @@ besides specifying the sequence's name: - .. code-block:: yaml - - Message: - type: entity - id: - id: - type: integer - generator: - strategy: SEQUENCE - sequenceGenerator: - sequenceName: message_seq - allocationSize: 100 - initialValue: 1 - The initial value specifies at which value the sequence should start. diff --git a/docs/en/reference/best-practices.rst b/docs/en/reference/best-practices.rst index 002c130d5ce..b6f63a61b7b 100644 --- a/docs/en/reference/best-practices.rst +++ b/docs/en/reference/best-practices.rst @@ -43,7 +43,7 @@ should use events judiciously. Use cascades judiciously ------------------------ -Automatic cascades of the persist/remove/merge/etc. operations are +Automatic cascades of the persist/remove/etc. operations are very handy but should be used wisely. Do NOT simply add all cascades to all associations. Think about which cascades actually do make sense for you for a particular association, given the diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index 19b30b3d837..1b84fbc93e9 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -109,7 +109,7 @@ Metadata Cache ~~~~~~~~~~~~~~ Your class metadata can be parsed from a few different sources like -YAML, XML, Attributes, Annotations etc. Instead of parsing this +XML, Attributes, etc. Instead of parsing this information on each request we should cache it using one of the cache drivers. diff --git a/docs/en/reference/change-tracking-policies.rst b/docs/en/reference/change-tracking-policies.rst index ea79e1ab050..3d2c2183065 100644 --- a/docs/en/reference/change-tracking-policies.rst +++ b/docs/en/reference/change-tracking-policies.rst @@ -5,7 +5,7 @@ Change tracking is the process of determining what has changed in managed entities since the last time they were synchronized with the database. -Doctrine provides 3 different change tracking policies, each having +Doctrine provides 2 different change tracking policies, each having its particular advantages and disadvantages. The change tracking policy can be defined on a per-class basis (or more precisely, per-hierarchy). @@ -56,122 +56,3 @@ This policy can be configured as follows: { // ... } - -Notify -~~~~~~ - -.. note:: - - The notify change tracking policy is deprecated and will be removed in ORM 3.0. - (\ `Details `_) - -This policy is based on the assumption that the entities notify -interested listeners of changes to their properties. For that -purpose, a class that wants to use this policy needs to implement -the ``NotifyPropertyChanged`` interface from the Doctrine -namespace. As a guideline, such an implementation can look as -follows: - -.. code-block:: php - - _listeners[] = $listener; - } - } - -Then, in each property setter of this class or derived classes, you -need to notify all the ``PropertyChangedListener`` instances. As an -example we add a convenience method on ``MyEntity`` that shows this -behaviour: - -.. code-block:: php - - _listeners) { - foreach ($this->_listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } - } - - public function setData($data): void - { - if ($data != $this->data) { - $this->_onPropertyChanged('data', $this->data, $data); - $this->data = $data; - } - } - } - -You have to invoke ``_onPropertyChanged`` inside every method that -changes the persistent state of ``MyEntity``. - -The check whether the new value is different from the old one is -not mandatory but recommended. That way you also have full control -over when you consider a property changed. - -If your entity contains an embeddable, you will need to notify -separately for each property in the embeddable when it changes -for example: - -.. code-block:: php - - equals($this->embeddable)) { - // notice the entityField.embeddableField notation for referencing the property - $this->_onPropertyChanged('embeddable.prop1', $this->embeddable->getProp1(), $embeddable->getProp1()); - $this->_onPropertyChanged('embeddable.prop2', $this->embeddable->getProp2(), $embeddable->getProp2()); - $this->embeddable = $embeddable; - } - } - } - -This would update all the fields of the embeddable, you may wish to -implement a diff method on your embedded object which returns only -the changed fields. - -The negative point of this policy is obvious: You need implement an -interface and write some plumbing code. But also note that we tried -hard to keep this notification functionality abstract. Strictly -speaking, it has nothing to do with the persistence layer and the -Doctrine ORM or DBAL. You may find that property notification -events come in handy in many other scenarios as well. As mentioned -earlier, the ``Doctrine\Common`` namespace is not that evil and -consists solely of very small classes and interfaces that have -almost no external dependencies (none to the DBAL and none to the -ORM) and that you can easily take with you should you want to swap -out the persistence layer. This change tracking policy does not -introduce a dependency on the Doctrine DBAL/ORM or the persistence -layer. - -The positive point and main advantage of this policy is its -effectiveness. It has the best performance characteristics of the 3 -policies with larger units of work and a flush() operation is very -cheap when nothing has changed. diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index a65ede69b28..ced4db14058 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -60,11 +60,6 @@ access point to ORM functionality provided by Doctrine. $connection = DriverManager::getConnection($dbParams, $config); $entityManager = new EntityManager($connection, $config); -.. note:: - - The ``ORMSetup`` class has been introduced with ORM 2.12. It's predecessor ``Setup`` is deprecated and will - be removed in version 3.0. - Or if you prefer XML: .. code-block:: php @@ -75,23 +70,6 @@ Or if you prefer XML: $connection = DriverManager::getConnection($dbParams, $config); $entityManager = new EntityManager($connection, $config); -Or if you prefer YAML: - -.. code-block:: php - - createQuery('SELECT partial u.{id, username} FROM CmsUser u'); $users = $query->getResult(); // array of partially loaded CmsUser objects -You use the partial syntax when joining as well: +You can use the partial syntax when joining as well: .. code-block:: php createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a'); + $usersArray = $query->getArrayResult(); // array of partially loaded CmsUser and CmsArticle fields $users = $query->getResult(); // array of partially loaded CmsUser objects "NEW" Operator Syntax @@ -587,7 +588,91 @@ And then use the ``NEW`` DQL keyword : $query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c'); $users = $query->getResult(); // array of CustomerDTO -Note that you can only pass scalar expressions to the constructor. +You can also nest several DTO : + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, NEW AddressDTO(a.street, a.city, a.zip)) FROM Customer c JOIN c.email e JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + +Note that you can only pass scalar expressions or other Data Transfer Objects to the constructor. + +If you use your data transfer objects for multiple queries, and you would rather not have to +specify arguments that precede the ones you are really interested in, you can use named arguments. + +Consider the following DTO, which uses optional arguments: + +.. code-block:: php + + createQuery('SELECT NEW NAMED CustomerDTO(a.city, c.name) FROM Customer c JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + + // CustomerDTO => {name : 'SMITH', email: null, city: 'London', value: null} + +ORM will also give precedence to column aliases over column names : + +.. code-block:: php + + createQuery('SELECT NEW NAMED CustomerDTO(c.name, CONCAT(a.city, ' ' , a.zip) AS value) FROM Customer c JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + + // CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'} + +To define a custom name for a DTO constructor argument, you can either alias the column with the ``AS`` keyword. + +The ``NAMED`` keyword must precede all DTO you want to instantiate : + +.. code-block:: php + + createQuery('SELECT NEW NAMED CustomerDTO(c.name, NEW NAMED AddressDTO(a.street, a.city, a.zip) AS address) FROM Customer c JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + + // CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'} + +If two arguments have the same name, a ``DuplicateFieldException`` is thrown. +If a field cannot be matched with a property name, a ``NoMatchingPropertyException`` is thrown. This typically happens when using functions without aliasing them. Using INDEX BY ~~~~~~~~~~~~~~ @@ -1516,8 +1601,8 @@ Identifiers /* Alias Identification declaration (the "u" of "FROM User u") */ AliasIdentificationVariable :: = identifier - /* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name or namespace-aliased */ - AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier + /* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name */ + AbstractSchemaName ::= fully_qualified_name | identifier /* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */ AliasResultVariable = identifier @@ -1617,7 +1702,7 @@ Select Expressions PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")" - NewObjectArg ::= ScalarExpression | "(" Subselect ")" + NewObjectArg ::= (ScalarExpression | "(" Subselect ")" | NewObjectExpression) ["AS" AliasResultVariable] Conditional Expressions ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index dbde6d19df2..4211a198577 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -260,40 +260,6 @@ specific to a particular entity class's lifecycle. $this->value = 'changed from preUpdate callback!'; } } - .. code-block:: annotation - - createdAt = date('Y-m-d H:i:s'); - } - - /** @PrePersist */ - public function doOtherStuffOnPrePersist() - { - $this->value = 'changed from prePersist callback!'; - } - - /** @PreUpdate */ - public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs) - { - $this->value = 'changed from preUpdate callback!'; - } - } .. code-block:: xml @@ -311,17 +277,6 @@ specific to a particular entity class's lifecycle. - .. code-block:: yaml - - User: - type: entity - fields: - # ... - value: - type: string(255) - lifecycleCallbacks: - prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] - preUpdate: [ doStuffOnPreUpdate ] Lifecycle Callbacks Event Argument ---------------------------------- @@ -794,16 +749,6 @@ An entity listener is a lifecycle listener class used for an entity. { // .... } - .. code-block:: annotation - - @@ -814,13 +759,6 @@ An entity listener is a lifecycle listener class used for an entity. - .. code-block:: yaml - - MyProject\Entity\User: - type: entity - entityListeners: - UserListener: - # .... .. _reference-entity-listeners: @@ -893,45 +831,6 @@ you need to map the listener method using the event type mapping: public function postLoadHandler(User $user, PostLoadEventArgs $event): void { // ... } } - .. code-block:: annotation - - @@ -954,24 +853,6 @@ you need to map the listener method using the event type mapping: - .. code-block:: yaml - - MyProject\Entity\User: - type: entity - entityListeners: - UserListener: - preFlush: [preFlushHandler] - postLoad: [postLoadHandler] - - postPersist: [postPersistHandler] - prePersist: [prePersistHandler] - - postUpdate: [postUpdateHandler] - preUpdate: [preUpdateHandler] - - postRemove: [postRemoveHandler] - preRemove: [preRemoveHandler] - # .... .. note:: @@ -994,7 +875,8 @@ Specifying an entity listener instance : // User.php - /** @Entity @EntityListeners({"UserListener"}) */ + #[Entity] + #[EntityListeners(["UserListener"]) class User { // .... @@ -1052,7 +934,7 @@ Load ClassMetadata Event ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the mapping metadata for a class has been loaded from a mapping source -(attributes/annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. +(attributes/xml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this process and manipulate the instance. This event is not a lifecycle callback. diff --git a/docs/en/reference/inheritance-mapping.rst b/docs/en/reference/inheritance-mapping.rst index 448c9dd401b..f06341810c0 100644 --- a/docs/en/reference/inheritance-mapping.rst +++ b/docs/en/reference/inheritance-mapping.rst @@ -16,7 +16,7 @@ is common to multiple entity classes. Mapped superclasses, just as regular, non-mapped classes, can appear in the middle of an otherwise mapped inheritance hierarchy (through Single Table Inheritance or Class Table Inheritance). They -are not query-able, and need not have an ``#[Id]`` property. +are not query-able, and do not require an ``#[Id]`` property. No database table will be created for a mapped superclass itself, only for entity classes inheriting from it. That implies that a @@ -208,44 +208,6 @@ Example: // ... } - .. code-block:: annotation - - - */ - protected Collection $groups; - - /** - * @ManyToOne(targetEntity="Address") - * @JoinColumn(name="address_id", referencedColumnName="id") - */ - protected Address|null $address = null; - } - - // admin mapping - namespace MyProject\Model; - /** - * @Entity - * @AssociationOverrides({ - * @AssociationOverride(name="groups", - * joinTable=@JoinTable( - * name="users_admingroups", - * joinColumns=@JoinColumn(name="adminuser_id"), - * inverseJoinColumns=@JoinColumn(name="admingroup_id") - * ) - * ), - * @AssociationOverride(name="address", - * joinColumns=@JoinColumn( - * name="adminaddress_id", referencedColumnName="id" - * ) - * ) - * }) - */ - class Admin extends User - { - } - .. code-block:: xml @@ -500,7 +410,6 @@ Example: - @@ -537,51 +446,6 @@ Example: - .. code-block:: yaml - - # user mapping - MyProject\Model\User: - type: mappedSuperclass - # other fields mapping - manyToOne: - address: - targetEntity: Address - joinColumn: - name: address_id - referencedColumnName: id - cascade: [ persist, merge ] - manyToMany: - groups: - targetEntity: Group - joinTable: - name: users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - cascade: [ persist, merge, detach ] - - # admin mapping - MyProject\Model\Admin: - type: entity - associationOverride: - address: - joinColumn: - adminaddress_id: - name: adminaddress_id - referencedColumnName: id - groups: - joinTable: - name: users_admingroups - joinColumns: - adminuser_id: - referencedColumnName: id - inverseJoinColumns: - admingroup_id: - referencedColumnName: id - Things to note: @@ -645,51 +509,6 @@ Could be used by an entity that extends a mapped superclass to override a field { } - .. code-block:: annotation - - @@ -702,7 +521,6 @@ Could be used by an entity that extends a mapped superclass to override a field - @@ -723,42 +541,6 @@ Could be used by an entity that extends a mapped superclass to override a field - .. code-block:: yaml - - # user mapping - MyProject\Model\User: - type: mappedSuperclass - id: - id: - type: integer - column: user_id - length: 150 - generator: - strategy: AUTO - fields: - name: - type: string - column: user_name - length: 250 - nullable: true - unique: false - #other fields mapping - - - # guest mapping - MyProject\Model\Guest: - type: entity - attributeOverride: - id: - column: guest_id - type: integer - length: 140 - name: - column: guest_name - type: string - length: 240 - nullable: false - unique: true Things to note: diff --git a/docs/en/reference/limitations-and-known-issues.rst b/docs/en/reference/limitations-and-known-issues.rst index 09b6e0ee578..d261355459e 100644 --- a/docs/en/reference/limitations-and-known-issues.rst +++ b/docs/en/reference/limitations-and-known-issues.rst @@ -65,15 +65,6 @@ Where the ``attribute_name`` column contains the key and The feature request for persistence of primitive value arrays `is described in the DDC-298 ticket `_. -Cascade Merge with Bi-directional Associations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are two bugs now that concern the use of cascade merge in combination with bi-directional associations. -Make sure to study the behavior of cascade merge if you are using it: - -- `DDC-875 `_ Merge can sometimes add the same entity twice into a collection -- `DDC-763 `_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability" - Custom Persisters ~~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/metadata-drivers.rst b/docs/en/reference/metadata-drivers.rst index a3718cbc838..6c3d62d8596 100644 --- a/docs/en/reference/metadata-drivers.rst +++ b/docs/en/reference/metadata-drivers.rst @@ -16,13 +16,6 @@ metadata: - **Attributes** (AttributeDriver) - **PHP Code in files or static functions** (PhpDriver) -There are also two deprecated ways to do this: - -- **Class DocBlock Annotations** (AnnotationDriver) -- **YAML files** (YamlDriver) - -They will be removed in 3.0, make sure to avoid them. - Something important to note about the above drivers is they are all an intermediate step to the same end result. The mapping information is populated to ``Doctrine\ORM\Mapping\ClassMetadata`` @@ -44,11 +37,7 @@ an entity. $em->getConfiguration()->setMetadataCacheImpl(new ApcuCache()); -If you want to use one of the included core metadata drivers you need to -configure it. If you pick the annotation driver despite it being -deprecated, you will additionally need to install -``doctrine/annotations``. All the drivers are in the -``Doctrine\ORM\Mapping\Driver`` namespace: +All the drivers are in the ``Doctrine\ORM\Mapping\Driver`` namespace: .. code-block:: php diff --git a/docs/en/reference/namingstrategy.rst b/docs/en/reference/namingstrategy.rst index 53d75465c2b..9ea3e772f33 100644 --- a/docs/en/reference/namingstrategy.rst +++ b/docs/en/reference/namingstrategy.rst @@ -110,28 +110,28 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrateg referenceColumnName(); } - public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + public function joinTableName(string $sourceEntity, string $targetEntity, string $propertyName): string { return strtolower($this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity)); } - public function joinKeyColumnName($entityName, $referencedColumnName = null) + public function joinKeyColumnName(string $entityName, ?string $referencedColumnName): string { return strtolower($this->classToTableName($entityName) . '_' . ($referencedColumnName ?: $this->referenceColumnName())); diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 97d92dd3861..62aaf11ab47 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -465,477 +465,3 @@ above would result in partial objects if any objects in the result are actually a subtype of User. When using DQL, Doctrine automatically includes the necessary joins for this mapping strategy but with native SQL it is your responsibility. - -Named Native Query ------------------- - -.. note:: - - Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0 - -You can also map a native query using a named native query mapping. - -To achieve that, you must describe the SQL resultset structure -using named native query (and sql resultset mappings if is a several resultset mappings). - -Like named query, a named native query can be defined at class level or in a XML or YAML file. - - -A resultSetMapping parameter is defined in @NamedNativeQuery, -it represents the name of a defined @SqlResultSetMapping. - -.. configuration-block:: - - .. code-block:: php - - - - - - SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username - - - - - - - - - - - - - - - - - - - - .. code-block:: yaml - - MyProject\Model\User: - type: entity - namedNativeQueries: - fetchMultipleJoinsEntityResults: - name: fetchMultipleJoinsEntityResults - resultSetMapping: mappingMultipleJoinsEntityResults - query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username - sqlResultSetMappings: - mappingMultipleJoinsEntityResults: - name: mappingMultipleJoinsEntityResults - columnResult: - 0: - name: numphones - entityResult: - 0: - entityClass: __CLASS__ - fieldResult: - 0: - name: id - column: u_id - 1: - name: name - column: u_name - 2: - name: status - column: u_status - 1: - entityClass: Address - fieldResult: - 0: - name: id - column: a_id - 1: - name: zip - column: a_zip - 2: - name: country - column: a_country - - -Things to note: - - The resultset mapping declares the entities retrieved by this native query. - - Each field of the entity is bound to a SQL alias (or column name). - - All fields of the entity including the ones of subclasses - and the foreign key columns of related entities have to be present in the SQL query. - - Field definitions are optional provided that they map to the same - column name as the one declared on the class property. - - ``__CLASS__`` is an alias for the mapped class - - -In the above example, -the ``fetchJoinedAddress`` named query use the joinMapping result set mapping. -This mapping returns 2 entities, User and Address, each property is declared and associated to a column name, -actually the column name retrieved by the query. - -Let's now see an implicit declaration of the property / column. - -.. configuration-block:: - - .. code-block:: php - - - - - - SELECT * FROM addresses - - - - - - - - - - .. code-block:: yaml - - MyProject\Model\Address: - type: entity - namedNativeQueries: - findAll: - resultSetMapping: mappingFindAll - query: SELECT * FROM addresses - sqlResultSetMappings: - mappingFindAll: - name: mappingFindAll - entityResult: - address: - entityClass: Address - - -In this example, we only describe the entity member of the result set mapping. -The property / column mappings is done using the entity mapping values. -In this case the model property is bound to the model_txt column. -If the association to a related entity involve a composite primary key, -a @FieldResult element should be used for each foreign key column. -The @FieldResult name is composed of the property name for the relationship, -followed by a dot ("."), followed by the name or the field or property of the primary key. - - -.. configuration-block:: - - .. code-block:: php - - - - - - SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? - - - - - - - - - - - - - - - - - - .. code-block:: yaml - - MyProject\Model\User: - type: entity - namedNativeQueries: - fetchJoinedAddress: - name: fetchJoinedAddress - resultSetMapping: mappingJoinedAddress - query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? - sqlResultSetMappings: - mappingJoinedAddress: - entityResult: - 0: - entityClass: __CLASS__ - fieldResult: - 0: - name: id - 1: - name: name - 2: - name: status - 3: - name: address.id - column: a_id - 4: - name: address.zip - column: a_zip - 5: - name: address.city - column: a_city - 6: - name: address.country - column: a_country - - - -If you retrieve a single entity and if you use the default mapping, -you can use the resultClass attribute instead of resultSetMapping: - -.. configuration-block:: - - .. code-block:: php - - - - - - SELECT * FROM addresses WHERE id = ? - - - - - .. code-block:: yaml - - MyProject\Model\Address: - type: entity - namedNativeQueries: - findAll: - name: findAll - resultClass: Address - query: SELECT * FROM addresses - - -In some of your native queries, you'll have to return scalar values, -for example when building report queries. -You can map them in the @SqlResultsetMapping through @ColumnResult. -You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though). - -.. configuration-block:: - - .. code-block:: php - - - - - SELECT COUNT(*) AS count FROM addresses - - - - - - - - - .. code-block:: yaml - - MyProject\Model\Address: - type: entity - namedNativeQueries: - count: - name: count - resultSetMapping: mappingCount - query: SELECT COUNT(*) AS count FROM addresses - sqlResultSetMappings: - mappingCount: - name: mappingCount - columnResult: - count: - name: count diff --git a/docs/en/reference/partial-hydration.rst b/docs/en/reference/partial-hydration.rst new file mode 100644 index 00000000000..fda14b98efa --- /dev/null +++ b/docs/en/reference/partial-hydration.rst @@ -0,0 +1,15 @@ +Partial Hydration +================= + +Partial hydration of entities is allowed in the array hydrator, when +only a subset of the fields of an entity are loaded from the database +and the nested results are still created based on the entity relationship structure. + +.. code-block:: php + + createQuery("SELECT PARTIAL u.{id,name}, partial a.{id,street} FROM MyApp\Domain\User u JOIN u.addresses a") + ->getArrayResult(); + +This is a useful optimization when you are not interested in all fields of an entity +for performance reasons, for example in use-cases for exporting or rendering lots of data. diff --git a/docs/en/reference/partial-objects.rst b/docs/en/reference/partial-objects.rst index 51f173adf6c..3123c083f3f 100644 --- a/docs/en/reference/partial-objects.rst +++ b/docs/en/reference/partial-objects.rst @@ -1,19 +1,11 @@ Partial Objects =============== - -.. note:: - - Creating Partial Objects through DQL is deprecated and - will be removed in the future, use data transfer object - support in DQL instead. (\ `Details - `_) - A partial object is an object whose state is not fully initialized after being reconstituted from the database and that is disconnected from the rest of its data. The following section will describe why partial objects are problematic and what the approach -of Doctrine2 to this problem is. +of Doctrine to this problem is. .. note:: @@ -94,5 +86,3 @@ When should I force partial objects? Mainly for optimization purposes, but be careful of premature optimization as partial objects lead to potentially more fragile code. - - diff --git a/docs/en/reference/php-mapping.rst b/docs/en/reference/php-mapping.rst index 73ebc332165..b9545238cb3 100644 --- a/docs/en/reference/php-mapping.rst +++ b/docs/en/reference/php-mapping.rst @@ -1,97 +1,20 @@ PHP Mapping =========== -Doctrine ORM also allows you to provide the ORM metadata in the form -of plain PHP code using the ``ClassMetadata`` API. You can write -the code in PHP files or inside of a static function named -``loadMetadata($class)`` on the entity class itself. - -PHP Files ---------- - -.. note:: - - PHPDriver is deprecated and will be removed in 3.0, use StaticPHPDriver - instead. - -If you wish to write your mapping information inside PHP files that -are named after the entity and included to populate the metadata -for an entity you can do so by using the ``PHPDriver``: - -.. code-block:: php - - getConfiguration()->setMetadataDriverImpl($driver); - -Now imagine we had an entity named ``Entities\User`` and we wanted -to write a mapping file for it using the above configured -``PHPDriver`` instance: - -.. code-block:: php - - mapField(array( - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer' - )); - - $metadata->mapField(array( - 'fieldName' => 'username', - 'type' => 'string', - 'options' => array( - 'fixed' => true, - 'comment' => "User's login name" - ) - )); - - $metadata->mapField(array( - 'fieldName' => 'login_count', - 'type' => 'integer', - 'nullable' => false, - 'options' => array( - 'unsigned' => true, - 'default' => 0 - ) - )); - -Now we can easily retrieve the populated ``ClassMetadata`` instance -where the ``PHPDriver`` includes the file and the -``ClassMetadataFactory`` caches it for later retrieval: - -.. code-block:: php - - getClassMetadata('Entities\User'); - // or - $class = $em->getMetadataFactory()->getMetadataFor('Entities\User'); +Doctrine ORM also allows you to provide the ORM metadata in the form of plain +PHP code using the ``ClassMetadata`` API. You can write the code in inside of a +static function named ``loadMetadata($class)`` on the entity class itself. Static Function --------------- -In addition to the PHP files you can also specify your mapping -information inside of a static function defined on the entity class -itself. This is useful for cases where you want to keep your entity -and mapping information together but don't want to use attributes or -annotations. For this you just need to use the ``StaticPHPDriver``: +In addition to other drivers using configuration languages you can also +programatically specify your mapping information inside of a static function +defined on the entity class itself. + +This is useful for cases where you want to keep your entity and mapping +information together but don't want to use attributes. For this you just +need to use the ``StaticPHPDriver``: .. code-block:: php @@ -150,7 +73,7 @@ To ease the use of the ClassMetadata API (which is very raw) there is a ``ClassM public static function loadMetadata(ClassMetadata $metadata) { $builder = new ClassMetadataBuilder($metadata); - $builder->createField('id', 'integer')->isPrimaryKey()->generatedValue()->build(); + $builder->createField('id', 'integer')->makePrimaryKey()->generatedValue()->build(); $builder->addField('username', 'string'); } } @@ -164,13 +87,11 @@ The API of the ClassMetadataBuilder has the following methods with a fluent inte - ``setTable($name)`` - ``addIndex(array $columns, $indexName)`` - ``addUniqueConstraint(array $columns, $constraintName)`` -- ``addNamedQuery($name, $dqlQuery)`` - ``setJoinedTableInheritance()`` - ``setSingleTableInheritance()`` - ``setDiscriminatorColumn($name, $type = 'string', $length = 255, $columnDefinition = null, $enumType = null, $options = [])`` - ``addDiscriminatorMapClass($name, $class)`` - ``setChangeTrackingPolicyDeferredExplicit()`` -- ``setChangeTrackingPolicyNotify()`` - ``addLifecycleEvent($methodName, $event)`` - ``addManyToOne($name, $targetEntity, $inversedBy = null)`` - ``addInverseOneToOne($name, $targetEntity, $mappedBy)`` @@ -272,7 +193,6 @@ Inheritance Getters - ``isInheritanceTypeNone()`` - ``isInheritanceTypeJoined()`` - ``isInheritanceTypeSingleTable()`` -- ``isInheritanceTypeTablePerClass()`` - ``isInheritedField($fieldName)`` - ``isInheritedAssociation($fieldName)`` @@ -282,7 +202,6 @@ Change Tracking Getters - ``isChangeTrackingDeferredExplicit()`` - ``isChangeTrackingDeferredImplicit()`` -- ``isChangeTrackingNotify()`` Field & Association Getters ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 3070cc234df..41ef31420ae 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -611,3 +611,21 @@ same query of example 6 written using ->add('from', new Expr\From('User', 'u')) ->add('where', new Expr\Comparison('u.id', '=', '?1')) ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Binding Parameters to Placeholders +---------------------------------- + +It is often not necessary to know about the exact placeholder names when +building a query. You can use a helper method to bind a value to a placeholder +and directly use that placeholder in your query as a return value: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.email = ' . $qb->createNamedParameter($userInputEmail)) + ; + // SELECT u FROM User u WHERE email = :dcValue1 diff --git a/docs/en/reference/second-level-cache.rst b/docs/en/reference/second-level-cache.rst index cf5cb37d4fa..829db16101f 100644 --- a/docs/en/reference/second-level-cache.rst +++ b/docs/en/reference/second-level-cache.rst @@ -295,30 +295,6 @@ level cache region. // other properties and methods } - .. code-block:: annotation - - @@ -335,24 +311,6 @@ level cache region. - .. code-block:: yaml - - Country: - type: entity - cache: - usage: READ_ONLY - region: my_entity_region - id: - id: - type: integer - id: true - generator: - strategy: IDENTITY - fields: - name: - type: string - - Association cache definition ---------------------------- The most common use case is to cache entities. But we can also cache relationships. @@ -389,44 +347,6 @@ It caches the primary keys of association and cache each element will be cached // other properties and methods } - .. code-block:: annotation - - - */ - protected Collection $cities; - - // other properties and methods - } - .. code-block:: xml @@ -458,38 +378,6 @@ It caches the primary keys of association and cache each element will be cached - .. code-block:: yaml - - State: - type: entity - cache: - usage: NONSTRICT_READ_WRITE - id: - id: - type: integer - id: true - generator: - strategy: IDENTITY - fields: - name: - type: string - - manyToOne: - state: - targetEntity: Country - joinColumns: - country_id: - referencedColumnName: id - cache: - usage: NONSTRICT_READ_WRITE - - oneToMany: - cities: - targetEntity:City - mappedBy: state - cache: - usage: NONSTRICT_READ_WRITE - .. note:: for this to work, the target entity must also be marked as cacheable. diff --git a/docs/en/reference/tools.rst b/docs/en/reference/tools.rst index 5c290ccebf9..a438fb2789b 100644 --- a/docs/en/reference/tools.rst +++ b/docs/en/reference/tools.rst @@ -83,18 +83,8 @@ The following Commands are currently available: cache drivers. - ``orm:clear-cache:result`` Clear result cache of the various cache drivers. -- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a - Doctrine 2.X schema. -- ``orm:convert-mapping`` Convert mapping information between - supported formats. -- ``orm:ensure-production-settings`` Verify that Doctrine is - properly configured for a production environment. -- ``orm:generate-entities`` Generate entity classes and method - stubs from your mapping information. - ``orm:generate-proxies`` Generates proxy classes for entity classes. -- ``orm:generate-repositories`` Generate repository classes from - your mapping information. - ``orm:run-dql`` Executes arbitrary DQL directly from the command line. - ``orm:schema-tool:create`` Processes the schema and either @@ -107,14 +97,10 @@ The following Commands are currently available: update the database schema of EntityManager Storage Connection or generate the SQL output. -For these commands are also available aliases: +The following alias is defined: -- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``. -- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``. -- ``orm:generate:entities`` is alias for ``orm:generate-entities``. - ``orm:generate:proxies`` is alias for ``orm:generate-proxies``. -- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``. .. note:: @@ -225,163 +211,6 @@ will output the SQL for the ran operation. $ php bin/doctrine orm:schema-tool:create --dump-sql -Entity Generation ------------------ - -Generate entity classes and method stubs from your mapping information. - -.. code-block:: php - - $ php bin/doctrine orm:generate-entities - $ php bin/doctrine orm:generate-entities --update-entities - $ php bin/doctrine orm:generate-entities --regenerate-entities - -This command is not suited for constant usage. It is a little helper and does -not support all the mapping edge cases very well. You still have to put work -in your entities after using this command. - -It is possible to use the EntityGenerator on code that you have already written. It will -not be lost. The EntityGenerator will only append new code to your -file and will not delete the old code. However this approach may still be prone -to error and we suggest you use code repositories such as GIT or SVN to make -backups of your code. - -It makes sense to generate the entity code if you are using entities as Data -Access Objects only and don't put much additional logic on them. If you are -however putting much more logic on the entities you should refrain from using -the entity-generator and code your entities manually. - -.. note:: - - Even if you specified Inheritance options in your - XML or YAML Mapping files the generator cannot generate the base and - child classes for you correctly, because it doesn't know which - class is supposed to extend which. You have to adjust the entity - code manually for inheritance to work! - - -Convert Mapping Information ---------------------------- - -Convert mapping information between supported formats. - -This is an **execute one-time** command. It should not be necessary for -you to call this method multiple times, especially when using the ``--from-database`` -flag. - -Converting an existing database schema into mapping files only solves about 70-80% -of the necessary mapping information. Additionally the detection from an existing -database cannot detect inverse associations, inheritance types, -entities with foreign keys as primary keys and many of the -semantical operations on associations such as cascade. - -.. note:: - - There is no need to convert YAML or XML mapping files to annotations - every time you make changes. All mapping drivers are first class citizens - in Doctrine 2 and can be used as runtime mapping for the ORM. See the - docs on XML and YAML Mapping for an example how to register this metadata - drivers as primary mapping source. - -To convert some mapping information between the various supported -formats you can use the ``ClassMetadataExporter`` to get exporter -instances for the different formats: - -.. code-block:: php - - getExporter('yml', '/path/to/export/yml'); - -Now you can export some ``ClassMetadata`` instances: - -.. code-block:: php - - getClassMetadata('Entities\User'), - $em->getClassMetadata('Entities\Profile') - ); - $exporter->setMetadata($classes); - $exporter->export(); - -This functionality is also available from the command line to -convert your loaded mapping information to another format. The -``orm:convert-mapping`` command accepts two arguments, the type to -convert to and the path to generate it: - -.. code-block:: php - - $ php bin/doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml - -Reverse Engineering -------------------- - -You can use the ``DatabaseDriver`` to reverse engineer a database to an -array of ``ClassMetadata`` instances and generate YAML, XML, etc. from -them. - -.. note:: - - Reverse Engineering is a **one-time** process that can get you started with a project. - Converting an existing database schema into mapping files only detects about 70-80% - of the necessary mapping information. Additionally the detection from an existing - database cannot detect inverse associations, inheritance types, - entities with foreign keys as primary keys and many of the - semantical operations on associations such as cascade. - -First you need to retrieve the metadata instances with the -``DatabaseDriver``: - -.. code-block:: php - - getConfiguration()->setMetadataDriverImpl( - new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( - $em->getConnection()->getSchemaManager() - ) - ); - - $cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory(); - $cmf->setEntityManager($em); - $metadata = $cmf->getAllMetadata(); - -Now you can get an exporter instance and export the loaded metadata -to yml: - -.. code-block:: php - - getExporter('yml', '/path/to/export/yml'); - $exporter->setMetadata($metadata); - $exporter->export(); - -You can also reverse engineer a database using the -``orm:convert-mapping`` command: - -.. code-block:: php - - $ php bin/doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml - -.. note:: - - Reverse Engineering is not always working perfectly - depending on special cases. It will only detect Many-To-One - relations (even if they are One-To-One) and will try to create - entities from Many-To-Many tables. It also has problems with naming - of foreign keys that have multiple column names. Any Reverse - Engineered Database-Schema needs considerable manual work to become - a useful domain model. - - Runtime vs Development Mapping Validation ----------------------------------------- diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index afcbb216bce..485b73e7251 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -202,17 +202,6 @@ example we'll use an integer. // ... } - .. code-block:: annotation - - @@ -221,15 +210,6 @@ example we'll use an integer. - .. code-block:: yaml - - User: - type: entity - fields: - version: - type: integer - version: true - Alternatively a datetime type can be used (which maps to a SQL timestamp or datetime): @@ -246,17 +226,6 @@ timestamp or datetime): // ... } - .. code-block:: annotation - - @@ -265,15 +234,6 @@ timestamp or datetime): - .. code-block:: yaml - - User: - type: entity - fields: - version: - type: datetime - version: true - Version numbers (not timestamps) should however be preferred as they can not potentially conflict in a highly concurrent environment, unlike timestamps where this is a possibility, diff --git a/docs/en/reference/typedfieldmapper.rst b/docs/en/reference/typedfieldmapper.rst index 982d0a50eda..47a703cebe6 100644 --- a/docs/en/reference/typedfieldmapper.rst +++ b/docs/en/reference/typedfieldmapper.rst @@ -47,21 +47,6 @@ Then, an entity using the ``CustomIdObject`` typed field will be correctly assig // ... } - .. code-block:: annotation - - @@ -176,4 +161,4 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\TypedFieldMap Note that this case checks whether the mapping is already assigned, and if yes, it skips it. This is up to your implementation. You can make a "greedy" mapper which will always override the mapping with its own type, or one - that behaves like the ``DefaultTypedFieldMapper`` and does not modify the type once its set prior in the chain. \ No newline at end of file + that behaves like the ``DefaultTypedFieldMapper`` and does not modify the type once its set prior in the chain. diff --git a/docs/en/reference/unitofwork.rst b/docs/en/reference/unitofwork.rst index 9c18fba0da7..94831ff4f36 100644 --- a/docs/en/reference/unitofwork.rst +++ b/docs/en/reference/unitofwork.rst @@ -129,16 +129,10 @@ optimize the performance of the Flush Operation: - Temporarily mark entities as read only. If you have a very large UnitOfWork but know that a large set of entities has not changed, just mark them as read only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``. -- Flush only a single entity with ``$entityManager->flush($entity)``. - Use :doc:`Change Tracking Policies ` to use more explicit strategies of notifying the UnitOfWork what objects/properties changed. -.. note:: - - Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0. - (\ `Details `_) - Query Internals --------------- diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 2c265679fa1..b5ca716ad07 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -415,7 +415,7 @@ Transitive persistence / Cascade Operations Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations. Each association to another entity or a collection of entities can be configured to automatically cascade the following operations to the associated entities: -``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``. +``persist``, ``remove``, ``detach``, ``refresh`` or ``all``. The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application. Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new diff --git a/docs/en/reference/working-with-objects.rst b/docs/en/reference/working-with-objects.rst index cc889ddde3f..d780b553941 100644 --- a/docs/en/reference/working-with-objects.rst +++ b/docs/en/reference/working-with-objects.rst @@ -166,7 +166,7 @@ your code. See the following code: Traversing the object graph for parts that are lazy-loaded will easily trigger lots of SQL queries and will perform badly if used - to heavily. Make sure to use DQL to fetch-join all the parts of the + too heavily. Make sure to use DQL to fetch-join all the parts of the object-graph that you need as efficiently as possible. @@ -414,77 +414,6 @@ automatically without invoking the ``detach`` method: The ``detach`` operation is usually not as frequently needed and used as ``persist`` and ``remove``. -Merging entities ----------------- - -Merging entities refers to the merging of (usually detached) -entities into the context of an EntityManager so that they become -managed again. To merge the state of an entity into an -EntityManager use the ``EntityManager#merge($entity)`` method. The -state of the passed entity will be merged into a managed copy of -this entity and this copy will subsequently be returned. - -Example: - -.. code-block:: php - - merge($detachedEntity); - // $entity now refers to the fully managed copy returned by the merge operation. - // The EntityManager $em now manages the persistence of $entity as usual. - - -The semantics of the merge operation, applied to an entity X, are -as follows: - - -- If X is a detached entity, the state of X is copied onto a - pre-existing managed entity instance X' of the same identity. -- If X is a new entity instance, a new managed copy X' will be - created and the state of X is copied onto this managed instance. -- If X is a removed entity instance, an InvalidArgumentException - will be thrown. -- If X is a managed entity, it is ignored by the merge operation, - however, the merge operation is cascaded to entities referenced by - relationships from X if these relationships have been mapped with - the cascade element value MERGE or ALL (see ":ref:`transitive-persistence`"). -- For all entities Y referenced by relationships from X having the - cascade element value MERGE or ALL, Y is merged recursively as Y'. - For all such Y referenced by X, X' is set to reference Y'. (Note - that if X is managed then X is the same object as X'.) -- If X is an entity merged to X', with a reference to another - entity Y, where cascade=MERGE or cascade=ALL is not specified, then - navigation of the same association from X' yields a reference to a - managed object Y' with the same persistent identity as Y. - -The ``merge`` operation will throw an ``OptimisticLockException`` -if the entity being merged uses optimistic locking through a -version field and the versions of the entity being merged and the -managed copy don't match. This usually means that the entity has -been modified while being detached. - -The ``merge`` operation is usually not as frequently needed and -used as ``persist`` and ``remove``. The most common scenario for -the ``merge`` operation is to reattach entities to an EntityManager -that come from some cache (and are therefore detached) and you want -to modify and persist such an entity. - -.. warning:: - - If you need to perform multiple merges of entities that share certain subparts - of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the - successive calls to ``EntityManager#merge()``. Otherwise you might end up with - multiple copies of the "same" object in the database, however with different ids. - -.. note:: - - If you load some detached entities from a cache and you do - not need to persist or delete them or otherwise make use of them - without the need for persistence services there is no need to use - ``merge``. I.e. you can simply pass detached objects from a cache - directly to the view. - Synchronization with the Database --------------------------------- @@ -595,7 +524,7 @@ during development. .. note:: Do not invoke ``flush`` after every change to an entity - or every single invocation of persist/remove/merge/... This is an + or every single invocation of persist/remove/... This is an anti-pattern and unnecessarily reduces the performance of your application. Instead, form units of work that operate on your objects and call ``flush`` when you are done. While serving a @@ -863,7 +792,7 @@ By default the EntityManager returns a default implementation of ``Doctrine\ORM\EntityRepository`` when you call ``EntityManager#getRepository($entityClass)``. You can overwrite this behaviour by specifying the class name of your own Entity -Repository in the Attribute, Annotation, XML or YAML metadata. In large +Repository in the Attribute or XML metadata. In large applications that require lots of specialized DQL queries using a custom repository is one recommended way of grouping these queries in a central location. diff --git a/docs/en/reference/xml-mapping.rst b/docs/en/reference/xml-mapping.rst index c8c1abe51d4..e010c93690b 100644 --- a/docs/en/reference/xml-mapping.rst +++ b/docs/en/reference/xml-mapping.rst @@ -2,7 +2,8 @@ XML Mapping =========== The XML mapping driver enables you to provide the ORM metadata in -form of XML documents. +form of XML documents. It requires the ``dom`` extension in order to be +able to validate your mapping documents against its XML Schema. The XML driver is backed by an XML Schema document that describes the structure of a mapping document. The most recent version of the @@ -691,7 +692,6 @@ specified by their respective tags: - ```` -- ```` - ```` - ```` - ```` diff --git a/docs/en/reference/yaml-mapping.rst b/docs/en/reference/yaml-mapping.rst deleted file mode 100644 index bb5feb9b870..00000000000 --- a/docs/en/reference/yaml-mapping.rst +++ /dev/null @@ -1,158 +0,0 @@ -YAML Mapping -============ - -.. warning:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - -The YAML mapping driver enables you to provide the ORM metadata in -form of YAML documents. - -The YAML mapping document of a class is loaded on-demand the first -time it is requested and subsequently stored in the metadata cache. -In order to work, this requires certain conventions: - - -- Each entity/mapped superclass must get its own dedicated YAML - mapping document. -- The name of the mapping document must consist of the fully - qualified name of the class, where namespace separators are - replaced by dots (.). -- All mapping documents should get the extension ".dcm.yml" to - identify it as a Doctrine mapping file. This is more of a - convention and you are not forced to do this. You can change the - file extension easily enough. - -.. code-block:: php - - setFileExtension('.yml'); - -It is recommended to put all YAML mapping documents in a single -folder but you can spread the documents over several folders if you -want to. In order to tell the YamlDriver where to look for your -mapping documents, supply an array of paths as the first argument -of the constructor, like this: - -.. code-block:: php - - setMetadataDriverImpl($driver); - -Simplified YAML Driver -~~~~~~~~~~~~~~~~~~~~~~ - -The Symfony project sponsored a driver that simplifies usage of the YAML Driver. -The changes between the original driver are: - -- File Extension is .orm.yml -- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml -- You can add a global file and add multiple entities in this file. - -Configuration of this client works a little bit different: - -.. code-block:: php - - 'MyProject\Entities', - '/path/to/files2' => 'OtherProject\Entities' - ); - $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces); - $driver->setGlobalBasename('global'); // global.orm.yml - -Example -------- - -As a quick start, here is a small example document that makes use -of several common elements: - -.. code-block:: yaml - - # Doctrine.Tests.ORM.Mapping.User.dcm.yml - Doctrine\Tests\ORM\Mapping\User: - type: entity - repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository - table: cms_users - schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5) - readOnly: true - indexes: - name_index: - columns: [ name ] - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - length: 50 - email: - type: string - length: 32 - column: user_email - unique: true - options: - fixed: true - comment: User's email address - loginCount: - type: integer - column: login_count - nullable: false - options: - unsigned: true - default: 0 - oneToOne: - address: - targetEntity: Address - joinColumn: - name: address_id - referencedColumnName: id - onDelete: CASCADE - oneToMany: - phonenumbers: - targetEntity: Phonenumber - mappedBy: user - cascade: ["persist", "merge"] - manyToMany: - groups: - targetEntity: Group - joinTable: - name: cms_users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - lifecycleCallbacks: - prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] - postPersist: [ doStuffOnPostPersist ] - -Be aware that class-names specified in the YAML files should be -fully qualified. - -Reference -~~~~~~~~~~~~~~~~~~~~~~ - -Unique Constraints ------------------- - -It is possible to define unique constraints by the following declaration: - -.. code-block:: yaml - - # ECommerceProduct.orm.yml - ECommerceProduct: - type: entity - fields: - # definition of some fields - uniqueConstraints: - search_idx: - columns: [ name, email ] - diff --git a/docs/en/sidebar.rst b/docs/en/sidebar.rst index b94c81fdd48..f52801c6b37 100644 --- a/docs/en/sidebar.rst +++ b/docs/en/sidebar.rst @@ -37,11 +37,10 @@ reference/query-builder reference/native-sql reference/change-tracking-policies + reference/partial-hydration reference/partial-objects - reference/annotations-reference reference/attributes-reference reference/xml-mapping - reference/yaml-mapping reference/php-mapping reference/caching reference/improving-performance @@ -66,7 +65,6 @@ cookbook/dql-custom-walkers cookbook/dql-user-defined-functions cookbook/implementing-arrayaccess-for-domain-objects - cookbook/implementing-the-notify-changetracking-policy cookbook/resolve-target-entity-listener cookbook/sql-table-prefixes cookbook/strategy-cookbook-introduction diff --git a/docs/en/tutorials/composite-primary-keys.rst b/docs/en/tutorials/composite-primary-keys.rst index 386f8f140c0..f8cae53d91e 100644 --- a/docs/en/tutorials/composite-primary-keys.rst +++ b/docs/en/tutorials/composite-primary-keys.rst @@ -50,38 +50,6 @@ and year of production as primary keys: } } - .. code-block:: annotation - - name = $name; - $this->year = $year; - } - - public function getModelName(): string - { - return $this->name; - } - - public function getYearOfProduction(): int - { - return $this->year; - } - } - .. code-block:: xml @@ -96,16 +64,6 @@ and year of production as primary keys: - .. code-block:: yaml - - VehicleCatalogue\Model\Car: - type: entity - id: - name: - type: string - year: - type: integer - Now you can use this entity: .. code-block:: php @@ -127,11 +85,12 @@ And for querying you can use arrays to both DQL and EntityRepositories: namespace VehicleCatalogue\Model; // $em is the EntityManager - $audi = $em->find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010)); + $audi = $em->find("VehicleCatalogue\Model\Car", ["name" => "Audi A8", "year" => 2010]); - $dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1"; + $dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.name = ?1 AND c.year = ?2"; $audi = $em->createQuery($dql) - ->setParameter(1, ["name" => "Audi A8", "year" => 2010]) + ->setParameter(1, "Audi A8") + ->setParameter(2, 2010) ->getSingleResult(); You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name`` @@ -160,7 +119,6 @@ The semantics of mapping identity through foreign entities are easy: - Only allowed on Many-To-One or One-To-One associations. - Plug an ``#[Id]`` attribute onto every association. - Set an attribute ``association-key`` with the field name of the association in XML. -- Set a key ``associationKey:`` with the field name of the association in YAML. Use-Case 1: Dynamic Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -214,57 +172,6 @@ We keep up the example of an Article with arbitrary attributes, the mapping look } } - .. code-block:: annotation - - - */ - private Collection $attributes; - - public function addAttribute($name, $value): void - { - $this->attributes[$name] = new ArticleAttribute($name, $value, $this); - } - } - - /** - * @Entity - */ - class ArticleAttribute - { - /** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */ - private Article|null $article; - - /** @Id @Column(type="string") */ - private string $attribute; - - /** @Column(type="string") */ - private string $value; - - public function __construct($name, $value, $article) - { - $this->attribute = $name; - $this->value = $value; - $this->article = $article; - } - } - .. code-block:: xml - .. code-block:: yaml - - Application\Model\ArticleAttribute: - type: entity - id: - article: - associationKey: true - attribute: - type: string - fields: - value: - type: string - manyToOne: - article: - targetEntity: Article - inversedBy: attributes - - Use-Case 2: Simple Derived Identity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -328,26 +217,6 @@ One good example for this is a user-address relationship: private User|null $user = null; } - .. code-block:: yaml - - User: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - - Address: - type: entity - id: - user: - associationKey: true - oneToOne: - user: - targetEntity: User - - Use-Case 3: Join-Table with Metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/tutorials/embeddables.rst b/docs/en/tutorials/embeddables.rst index b298cb6a558..d0a8d517657 100644 --- a/docs/en/tutorials/embeddables.rst +++ b/docs/en/tutorials/embeddables.rst @@ -44,33 +44,6 @@ instead of simply adding the respective columns to the ``User`` class. private string $country; } - .. code-block:: annotation - - @@ -86,22 +59,6 @@ instead of simply adding the respective columns to the ``User`` class. - .. code-block:: yaml - - User: - type: entity - embedded: - address: - class: Address - - Address: - type: embeddable - fields: - street: { type: string } - postalCode: { type: string } - city: { type: string } - country: { type: string } - In terms of your database schema, Doctrine will automatically inline all columns from the ``Address`` class into the table of the ``User`` class, just as if you had declared them directly there. @@ -147,32 +104,12 @@ The following example shows you how to set your prefix to ``myPrefix_``: private Address $address; } - .. code-block:: annotation - - - .. code-block:: yaml - - User: - type: entity - embedded: - address: - class: Address - columnPrefix: myPrefix_ - To have Doctrine drop the prefix and use the value object's property name directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML): @@ -189,32 +126,12 @@ directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML): private Address $address; } - .. code-block:: annotation - - - .. code-block:: yaml - - User: - type: entity - embedded: - address: - class: Address - columnPrefix: false - DQL --- diff --git a/docs/en/tutorials/extra-lazy-associations.rst b/docs/en/tutorials/extra-lazy-associations.rst index fbf1f00abd6..e2877a7649a 100644 --- a/docs/en/tutorials/extra-lazy-associations.rst +++ b/docs/en/tutorials/extra-lazy-associations.rst @@ -17,6 +17,7 @@ can be called without triggering a full load of the collection: - ``Collection#contains($entity)`` - ``Collection#containsKey($key)`` - ``Collection#count()`` +- ``Collection#first()`` - ``Collection#get($key)`` - ``Collection#isEmpty()`` - ``Collection#slice($offset, $length = null)`` @@ -66,23 +67,6 @@ switch to extra lazy as shown in these examples: public Collection $users; } - .. code-block:: annotation - - - */ - public Collection $users; - } - .. code-block:: xml @@ -96,14 +80,3 @@ switch to extra lazy as shown in these examples: - - .. code-block:: yaml - - Doctrine\Tests\Models\CMS\CmsGroup: - type: entity - # ... - manyToMany: - users: - targetEntity: CmsUser - mappedBy: groups - fetch: EXTRA_LAZY diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 1fa463dd797..b972c119c51 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -27,7 +27,7 @@ What is Doctrine? ----------------- Doctrine ORM is an `object-relational mapper (ORM) `_ -for PHP 7.1+ that provides transparent persistence for PHP objects. It uses the Data Mapper +for PHP that provides transparent persistence for PHP objects. It uses the Data Mapper pattern at the heart, aiming for a complete separation of your domain/business logic from the persistence in a relational database management system. @@ -82,10 +82,9 @@ that directory with the following contents: { "require": { - "doctrine/orm": "^2.11.0", - "doctrine/dbal": "^3.2", - "symfony/yaml": "^5.4", - "symfony/cache": "^5.4" + "doctrine/orm": "^3", + "doctrine/dbal": "^4", + "symfony/cache": "^7" }, "autoload": { "psr-0": {"": "src/"} @@ -107,12 +106,8 @@ Add the following directories:: doctrine2-tutorial |-- config | `-- xml - | `-- yaml `-- src -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. .. note:: It is strongly recommended that you require ``doctrine/dbal`` in your ``composer.json`` as well, because using the ORM means mapping objects @@ -147,19 +142,11 @@ step: paths: [__DIR__ . '/src'], isDevMode: true, ); - // or if you prefer annotation, YAML or XML - // $config = ORMSetup::createAnnotationMetadataConfiguration( - // paths: array(__DIR__."/src"), - // isDevMode: true, - // ); + // or if you prefer XML // $config = ORMSetup::createXMLMetadataConfiguration( // paths: [__DIR__ . '/config/xml'], // isDevMode: true, //); - // $config = ORMSetup::createYAMLMetadataConfiguration( - // paths: array(__DIR__."/config/yaml"), - // isDevMode: true, - // ); // configuring the database connection $connection = DriverManager::getConnection([ @@ -170,10 +157,6 @@ step: // obtaining the entity manager $entityManager = new EntityManager($connection, $config); -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - The ``require_once`` statement sets up the class autoloading for Doctrine and its dependencies using Composer's autoloader. @@ -500,8 +483,8 @@ language describes how entities, their properties and references should be persisted and what constraints should be applied to them. Metadata for an Entity can be configured using attributes directly in -the Entity class itself, or in an external XML or YAML file. This -Getting Started guide will demonstrate metadata mappings using all three +the Entity class itself, or in an external XML file. This +Getting Started guide will demonstrate metadata mappings using both methods, but you only need to choose one. .. configuration-block:: @@ -527,33 +510,6 @@ methods, but you only need to choose one. // .. (other code) } - .. code-block:: annotation - - @@ -571,25 +527,6 @@ methods, but you only need to choose one. -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - - .. code-block:: yaml - - # config/yaml/Product.dcm.yml - Product: - type: entity - table: products - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - The top-level ``entity`` definition specifies information about the class and table name. The primitive type ``Product#name`` is defined as a ``field`` attribute. The ``id`` property is defined with @@ -1082,59 +1019,6 @@ the ``Product`` before: // ... (other code) } - .. code-block:: annotation - - @@ -1159,40 +1043,6 @@ the ``Product`` before: -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - - .. code-block:: yaml - - # config/yaml/Bug.dcm.yml - Bug: - type: entity - table: bugs - id: - id: - type: integer - generator: - strategy: AUTO - fields: - description: - type: text - created: - type: datetime - status: - type: string - manyToOne: - reporter: - targetEntity: User - inversedBy: reportedBugs - engineer: - targetEntity: User - inversedBy: assignedBugs - manyToMany: - products: - targetEntity: Product - - Here we have the entity, id and primitive type definitions. For the "created" field we have used the ``datetime`` type, which translates the YYYY-mm-dd HH:mm:ss database format @@ -1249,47 +1099,6 @@ Finally, we'll add metadata mappings for the ``User`` entity. // .. (other code) } - .. code-block:: annotation - - An ArrayCollection of Bug objects. - */ - private Collection $reportedBugs; - - /** - * @ORM\OneToMany(targetEntity="Bug", mappedBy="engineer") - * @var Collection An ArrayCollection of Bug objects. - */ - private Collection $assignedBugs; - - // .. (other code) - } .. code-block:: xml @@ -1310,33 +1119,7 @@ Finally, we'll add metadata mappings for the ``User`` entity. -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - - .. code-block:: yaml - - # config/yaml/User.dcm.yml - User: - type: entity - table: users - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - oneToMany: - reportedBugs: - targetEntity: Bug - mappedBy: reporter - assignedBugs: - targetEntity: Bug - mappedBy: engineer - -Here are some new things to mention about the ``OneToMany`` attribute. +Here are some new things to mention about the ``one-to-many`` tags. Remember that we discussed about the inverse and owning side. Now both reportedBugs and assignedBugs are inverse relations, which means the join details have already been defined on the owning @@ -1800,21 +1583,6 @@ we have to adjust the metadata slightly. // ... } - .. code-block:: annotation - - -.. note:: - The YAML driver is deprecated and will be removed in version 3.0. - It is strongly recommended to switch to one of the other mappings. - - .. code-block:: yaml - - Bug: - type: entity - repositoryClass: BugRepository - Now we can remove our query logic in all the places and instead use them through the EntityRepository. As an example here is the code of the first use case "List of Bugs": diff --git a/docs/en/tutorials/ordered-associations.rst b/docs/en/tutorials/ordered-associations.rst index 9e414e0680e..e03eb45bf7a 100644 --- a/docs/en/tutorials/ordered-associations.rst +++ b/docs/en/tutorials/ordered-associations.rst @@ -27,22 +27,6 @@ can specify the ``#[OrderBy]`` in the following way: private Collection $groups; } - .. code-block:: annotation - - - */ - private Collection $groups; - } - .. code-block:: xml @@ -55,23 +39,6 @@ can specify the ``#[OrderBy]`` in the following way: - .. code-block:: yaml - - User: - type: entity - manyToMany: - groups: - orderBy: { 'name': 'ASC' } - targetEntity: Group - joinTable: - name: users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - The DQL Snippet in OrderBy is only allowed to consist of unqualified, unquoted field names and of an optional ASC/DESC positional statement. Multiple Fields are separated by a comma (,). diff --git a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst index b7f74adf387..f81faba7122 100644 --- a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst +++ b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -64,7 +64,7 @@ which has mapping metadata that is overridden by the attribute above: #[Column(name: 'trait_foo', type: 'integer', length: 100, nullable: true, unique: true)] protected int $foo; - #[OneToOne(targetEntity: Bar::class, cascade: ['persist', 'merge'])] + #[OneToOne(targetEntity: Bar::class, cascade: ['persist'])] #[JoinColumn(name: 'example_trait_bar_id', referencedColumnName: 'id')] protected Bar|null $bar = null; } @@ -79,4 +79,4 @@ The case for just extending a class would be just the same but: // ... } -Overriding is also supported via XML and YAML (:ref:`examples `). +Overriding is also supported via XML (:ref:`examples `). diff --git a/docs/en/tutorials/working-with-indexed-associations.rst b/docs/en/tutorials/working-with-indexed-associations.rst index e6822de3952..e15cae87aa4 100644 --- a/docs/en/tutorials/working-with-indexed-associations.rst +++ b/docs/en/tutorials/working-with-indexed-associations.rst @@ -24,9 +24,7 @@ Mapping Indexed Associations You can map indexed associations by adding: * ``indexBy`` argument to any ``#[OneToMany]`` or ``#[ManyToMany]`` attribute. - * ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation. * ``index-by`` attribute to any ```` or ```` xml element. - * ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files. The code and mappings for the Market entity looks like this: @@ -34,16 +32,9 @@ The code and mappings for the Market entity looks like this: .. literalinclude:: working-with-indexed-associations/Market.php :language: attribute - .. literalinclude:: working-with-indexed-associations/Market-annotations.php - :language: annotation - .. literalinclude:: working-with-indexed-associations/market.xml :language: xml - .. literalinclude:: working-with-indexed-associations/market.yaml - :language: yaml - - Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol, so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)`` we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown. @@ -83,47 +74,6 @@ here are the code and mappings for it: } } - .. code-block:: annotation - - symbol = $symbol; - $this->market = $market; - $market->addStock($this); - } - - public function getSymbol(): string - { - return $this->symbol; - } - } - .. code-block:: xml @@ -142,23 +92,6 @@ here are the code and mappings for it: - .. code-block:: yaml - - Doctrine\Tests\Models\StockExchange\Stock: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - symbol: - type: string - manyToOne: - market: - targetEntity: Market - inversedBy: stocks - Querying indexed associations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/tutorials/working-with-indexed-associations/Market-annotations.php b/docs/en/tutorials/working-with-indexed-associations/Market-annotations.php deleted file mode 100644 index 798b49d1d02..00000000000 --- a/docs/en/tutorials/working-with-indexed-associations/Market-annotations.php +++ /dev/null @@ -1,74 +0,0 @@ - - */ - private Collection $stocks; - - public function __construct($name) - { - $this->name = $name; - $this->stocks = new ArrayCollection(); - } - - public function getId(): int|null - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function addStock(Stock $stock): void - { - $this->stocks[$stock->getSymbol()] = $stock; - } - - public function getStock($symbol): Stock - { - if (!isset($this->stocks[$symbol])) { - throw new InvalidArgumentException("Symbol is not traded on this market."); - } - - return $this->stocks[$symbol]; - } - - /** @return array */ - public function getStocks(): array - { - return $this->stocks->toArray(); - } -} diff --git a/docs/en/tutorials/working-with-indexed-associations/market.yaml b/docs/en/tutorials/working-with-indexed-associations/market.yaml deleted file mode 100644 index b7c8132e090..00000000000 --- a/docs/en/tutorials/working-with-indexed-associations/market.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Doctrine\Tests\Models\StockExchange\Market: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type:string - oneToMany: - stocks: - targetEntity: Stock - mappedBy: market - indexBy: symbol diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 651c9d29c09..6131026f5f6 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -35,7 +35,6 @@ - @@ -82,36 +81,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -143,23 +112,6 @@ - - - - - - - - - - - - - - - - - @@ -175,9 +127,6 @@ - - - @@ -239,7 +188,6 @@ - @@ -247,7 +195,6 @@ - @@ -257,7 +204,6 @@ - diff --git a/phpcs.xml.dist b/phpcs.xml.dist index ec9ff9f2cc3..34ecdf46afc 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -11,7 +11,7 @@ - + src tests @@ -20,38 +20,38 @@ */tests/Tests/ORM/Tools/Export/export/* - - - - - - - - */tests/* + - */src/* tests/Tests/Mocks/HydratorMockStatement.php + tests/Tests/Models/Cache/ComplexAction.php + tests/Tests/Models/DDC117/DDC117ArticleDetails.php + tests/Tests/Models/DDC117/DDC117Translation.php + tests/Tests/ORM/Functional/Ticket/DDC2579Test.php + tests/Tests/ORM/Functional/ValueObjectsTest.php - - */src/* + + tests/* + + + + tests/* - src/Mapping/Driver/CompatibilityAnnotationDriver.php - src/Tools/Console/CommandCompatibility.php - src/Tools/Console/Helper/EntityManagerHelper.php + src/Mapping/Driver/LoadMappingFileImplementation.php + src/Mapping/GetReflectionClassImplementation.php tests/* @@ -70,14 +70,6 @@ src/Tools/ToolEvents.php - - - src/Mapping/Column.php - src/Mapping/Index.php - src/Mapping/Table.php - src/Mapping/UniqueConstraint.php - - src/Internal/Hydration/AbstractHydrator.php @@ -94,7 +86,6 @@ src/Mapping/Cache.php src/Mapping/ChangeTrackingPolicy.php src/Mapping/Column.php - src/Mapping/ColumnResult.php src/Mapping/CustomIdGenerator.php src/Mapping/DiscriminatorColumn.php src/Mapping/DiscriminatorMap.php @@ -102,8 +93,6 @@ src/Mapping/Embedded.php src/Mapping/Entity.php src/Mapping/EntityListeners.php - src/Mapping/EntityResult.php - src/Mapping/FieldResult.php src/Mapping/GeneratedValue.php src/Mapping/HasLifecycleCallbacks.php src/Mapping/Id.php @@ -115,10 +104,6 @@ src/Mapping/ManyToMany.php src/Mapping/ManyToOne.php src/Mapping/MappedSuperclass.php - src/Mapping/NamedNativeQueries.php - src/Mapping/NamedNativeQuery.php - src/Mapping/NamedQueries.php - src/Mapping/NamedQuery.php src/Mapping/OneToMany.php src/Mapping/OneToOne.php src/Mapping/OrderBy.php @@ -131,8 +116,6 @@ src/Mapping/PreRemove.php src/Mapping/PreUpdate.php src/Mapping/SequenceGenerator.php - src/Mapping/SqlResultSetMapping.php - src/Mapping/SqlResultSetMappings.php src/Mapping/Table.php src/Mapping/UniqueConstraint.php src/Mapping/Version.php @@ -142,6 +125,36 @@ src/Cache/DefaultQueryCache.php + + + + + + + + + + + + + + + + + + + + + + + + + + + src/EntityManagerInterface.php @@ -197,9 +210,6 @@ tests/Tests/Models/DDC1590/DDC1590User.php - - tests/Tests/ORM/Functional/Ticket/DDC832Test.php - @@ -224,34 +234,20 @@ src/AbstractQuery.php - src/Mapping/ClassMetadataInfo.php + src/Mapping/ClassMetadata.php src/NativeQuery.php src/Query.php src/Query/TreeWalkerAdapter.php src/Tools/Export/Driver/AbstractExporter.php - src/Tools/Export/Driver/AnnotationExporter.php src/Tools/Export/Driver/PhpExporter.php tests/Tests/Mocks/DatabasePlatformMock.php tests/Tests/Mocks/SchemaManagerMock.php + tests/Tests/ORM/AbstractQueryTest.php tests/Tests/ORM/Functional/Ticket/DDC3634Test.php - src/AbstractQuery.php - src/Configuration.php - src/EntityRepository.php - src/Internal/Hydration/AbstractHydrator.php - src/Query/Exec/AbstractSqlExecutor.php - src/Query/Exec/AbstractSqlExecutor.php - src/Query/Printer.php - src/Tools/EntityRepositoryGenerator.php - src/Tools/Console/Helper/EntityManagerHelper.php - src/Tools/Export/Driver/AbstractExporter.php - src/Tools/Export/Driver/AnnotationExporter.php - src/Tools/Export/Driver/PhpExporter.php - src/Tools/Export/Driver/XmlExporter.php - src/Tools/Export/Driver/YamlExporter.php tests/Tests/OrmFunctionalTestCase.php @@ -266,6 +262,7 @@ tests/Tests/ORM/Functional/Ticket/DDC1885Test.php tests/Tests/ORM/Functional/Ticket/DDC1843Test.php + tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -280,9 +277,4 @@ src/QueryBuilder.php - - - - src/Mapping/Driver/XmlDriver.php - diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 252cad57c2a..44cb4153e21 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,23 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Cache\\\\QueryCacheProfile'' and ''setResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 3 - path: src/AbstractQuery.php - - - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Cache\\QueryCacheProfile and ''getResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/AbstractQuery.php - - - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Cache\\QueryCacheProfile and ''setResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/AbstractQuery.php - - message: '#^Expression "\$setCacheEntry\(\$data\)" on a separate line does not do anything\.$#' identifier: expr.resultUnused @@ -37,35 +19,11 @@ parameters: path: src/AbstractQuery.php - - message: '#^Parameter \#1 \$parameters of method Doctrine\\ORM\\AbstractQuery\:\:setParameters\(\) expects array\\|Doctrine\\Common\\Collections\\ArrayCollection\, iterable\<\(int\|string\), mixed\> given\.$#' - identifier: argument.type - count: 1 - path: src/AbstractQuery.php - - - - message: '#^Parameter \#1 \$stmt of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:iterate\(\) expects Doctrine\\DBAL\\Driver\\ResultStatement\|Doctrine\\DBAL\\Result, Doctrine\\DBAL\\Result\|int given\.$#' - identifier: argument.type - count: 1 - path: src/AbstractQuery.php - - - - message: '#^Parameter \#1 \$stmt of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:toIterable\(\) expects Doctrine\\DBAL\\Driver\\ResultStatement\|Doctrine\\DBAL\\Result, Doctrine\\DBAL\\Result\|int given\.$#' - identifier: argument.type - count: 1 - path: src/AbstractQuery.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, iterable\<\(int\|string\), mixed\> given\.$#' + message: '#^Parameter \#1 \$stmt of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:toIterable\(\) expects Doctrine\\DBAL\\Result, Doctrine\\DBAL\\Result\|int given\.$#' identifier: argument.type count: 1 path: src/AbstractQuery.php - - - message: '#^Method Doctrine\\ORM\\Cache\\CacheFactory\:\:buildCachedCollectionPersister\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/CacheFactory.php - - message: '#^Method Doctrine\\ORM\\Cache\\CacheFactory\:\:buildCachedEntityPersister\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -84,6 +42,18 @@ parameters: count: 1 path: src/Cache/CollectionHydrator.php + - + message: '#^Method Doctrine\\ORM\\Cache\\CollectionHydrator\:\:buildCacheEntry\(\) has parameter \$collection with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/CollectionHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Cache\\CollectionHydrator\:\:buildCacheEntry\(\) has parameter \$collection with no value type specified in iterable type array\|\(Doctrine\\Common\\Collections\\Collection&iterable\)\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/CollectionHydrator.php + - message: '#^Method Doctrine\\ORM\\Cache\\CollectionHydrator\:\:buildCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -120,18 +90,6 @@ parameters: count: 1 path: src/Cache/DefaultCache.php - - - message: '#^Instanceof between Psr\\Cache\\CacheItemPoolInterface and Psr\\Cache\\CacheItemPoolInterface will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Cache/DefaultCacheFactory.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultCacheFactory\:\:buildCachedCollectionPersister\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/DefaultCacheFactory.php - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultCacheFactory\:\:buildCachedEntityPersister\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -150,6 +108,18 @@ parameters: count: 1 path: src/Cache/DefaultCollectionHydrator.php + - + message: '#^Method Doctrine\\ORM\\Cache\\DefaultCollectionHydrator\:\:buildCacheEntry\(\) has parameter \$collection with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/DefaultCollectionHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Cache\\DefaultCollectionHydrator\:\:buildCacheEntry\(\) has parameter \$collection with no value type specified in iterable type array\|\(Doctrine\\Common\\Collections\\Collection&iterable\)\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/DefaultCollectionHydrator.php + - message: '#^Method Doctrine\\ORM\\Cache\\DefaultCollectionHydrator\:\:buildCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -169,33 +139,39 @@ parameters: path: src/Cache/DefaultCollectionHydrator.php - - message: '#^Call to an undefined method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getCacheRegion\(\)\.$#' - identifier: method.notFound + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumnFieldNames\.$#' + identifier: property.notFound count: 1 path: src/Cache/DefaultEntityHydrator.php - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultEntityHydrator\:\:buildCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 path: src/Cache/DefaultEntityHydrator.php - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultEntityHydrator\:\:loadCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 path: src/Cache/DefaultEntityHydrator.php - - message: '#^Offset ''joinColumnFieldNames'' might not exist on array\{cache\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Call to an undefined method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getCacheRegion\(\)\.$#' + identifier: method.notFound count: 1 path: src/Cache/DefaultEntityHydrator.php - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 + message: '#^Method Doctrine\\ORM\\Cache\\DefaultEntityHydrator\:\:buildCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Cache/DefaultEntityHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Cache\\DefaultEntityHydrator\:\:loadCacheEntry\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 path: src/Cache/DefaultEntityHydrator.php - @@ -234,12 +210,6 @@ parameters: count: 2 path: src/Cache/DefaultQueryCache.php - - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultQueryCache\:\:storeAssociationCache\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/DefaultQueryCache.php - - message: '#^Method Doctrine\\ORM\\Cache\\DefaultQueryCache\:\:storeAssociationCache\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -300,18 +270,6 @@ parameters: count: 1 path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - - message: '#^Instanceof between Doctrine\\Common\\Collections\\Collection&iterable and Doctrine\\Common\\Collections\\Collection will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:__construct\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics @@ -330,12 +288,6 @@ parameters: count: 1 path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:evictCollectionCache\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics @@ -379,8 +331,14 @@ parameters: path: src/Cache/Persister/Collection/AbstractCollectionPersister.php - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list + message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:storeCollectionCache\(\) has parameter \$elements with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/Persister/Collection/AbstractCollectionPersister.php + + - + message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\AbstractCollectionPersister\:\:storeCollectionCache\(\) has parameter \$elements with no value type specified in iterable type array\|\(Doctrine\\Common\\Collections\\Collection&iterable\)\.$#' + identifier: missingType.iterableValue count: 1 path: src/Cache/Persister/Collection/AbstractCollectionPersister.php @@ -432,6 +390,18 @@ parameters: count: 1 path: src/Cache/Persister/Collection/CachedCollectionPersister.php + - + message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister\:\:storeCollectionCache\(\) has parameter \$elements with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/Persister/Collection/CachedCollectionPersister.php + + - + message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\CachedCollectionPersister\:\:storeCollectionCache\(\) has parameter \$elements with no value type specified in iterable type array\|\(Doctrine\\Common\\Collections\\Collection&iterable\)\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Cache/Persister/Collection/CachedCollectionPersister.php + - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics @@ -456,12 +426,6 @@ parameters: count: 2 path: src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister\:\:__construct\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics @@ -498,12 +462,6 @@ parameters: count: 1 path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\Common\\\\Collections\\\\Criteria'' and ''orderings'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:__construct\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -516,84 +474,24 @@ parameters: count: 1 path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:getManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:getOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:getSelectConditionStatementSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:getSelectSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:load\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadAll\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadOneToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadOneToOneEntity\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - message: '#^Parameter \#3 \$entry of method Doctrine\\ORM\\Cache\\EntityHydrator\:\:loadCacheEntry\(\) expects Doctrine\\ORM\\Cache\\EntityCacheEntry, Doctrine\\ORM\\Cache\\CacheEntry given\.$#' identifier: argument.type count: 1 path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - - message: '#^Property Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:\$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Cache/Persister/Entity/AbstractEntityPersister.php - - message: '#^Call to an undefined method Doctrine\\ORM\\Cache\\Region\:\:lock\(\)\.$#' identifier: method.notFound @@ -612,42 +510,12 @@ parameters: count: 1 path: src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php - - - message: '#^Instanceof between Psr\\Cache\\CacheItemPoolInterface and Psr\\Cache\\CacheItemPoolInterface will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Cache/Region/DefaultRegion.php - - - - message: '#^Method Doctrine\\ORM\\Cache\\Region\\DefaultRegion\:\:getCache\(\) should return Doctrine\\Common\\Cache\\CacheProvider but returns Doctrine\\Common\\Cache\\Cache\.$#' - identifier: return.type - count: 1 - path: src/Cache/Region/DefaultRegion.php - - message: '#^Access to an undefined property Doctrine\\ORM\\Cache\\CacheEntry\:\:\$time\.$#' identifier: property.notFound count: 1 path: src/Cache/TimestampQueryCacheValidator.php - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Configuration'' and ''getResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Configuration.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Configuration'' and ''setResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Configuration.php - - - - message: '#^Class Doctrine\\Common\\Cache\\ArrayCache not found\.$#' - identifier: class.notFound - count: 2 - path: src/Configuration.php - - message: '#^Method Doctrine\\ORM\\Configuration\:\:getDefaultRepositoryClassName\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' identifier: missingType.generics @@ -660,12 +528,6 @@ parameters: count: 1 path: src/Configuration.php - - - message: '#^Parameter \#1 \$reader of class Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver constructor expects Doctrine\\Common\\Annotations\\Reader, Doctrine\\Common\\Annotations\\AnnotationReader\|Doctrine\\Common\\Annotations\\CachedReader\|Doctrine\\Common\\Annotations\\SimpleAnnotationReader given\.$#' - identifier: argument.type - count: 1 - path: src/Configuration.php - - message: '#^Parameter \#2 \$className of method Doctrine\\ORM\\Configuration\:\:addCustomNumericFunction\(\) expects \(callable\(string\)\: Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode\)\|class\-string\, class\-string given\.$#' identifier: argument.type @@ -673,44 +535,26 @@ parameters: path: src/Configuration.php - - message: '#^Call to function method_exists\(\) with Doctrine\\ORM\\EntityManagerInterface and ''wrapInTransaction'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Decorator/EntityManagerDecorator.php - - - - message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:find\(\) has parameter \$lockMode with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Decorator/EntityManagerDecorator.php - - - - message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:find\(\) has parameter \$lockVersion with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Decorator/EntityManagerDecorator.php - - - - message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:flush\(\) has parameter \$entity with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Decorator/EntityManagerDecorator.php - - message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:wrapInTransaction\(\) has no return type specified\.$#' - identifier: missingType.return + message: '#^Method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:getRepository\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Decorator/EntityManagerDecorator.php - - message: '#^Method Doctrine\\Persistence\\ObjectManager\:\:find\(\) invoked with 4 parameters, 2 required\.$#' - identifier: arguments.count + message: '#^Return type \(Doctrine\\ORM\\Mapping\\ClassMetadataFactory\) of method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:getMetadataFactory\(\) should be compatible with return type \(Doctrine\\Persistence\\Mapping\\ClassMetadataFactory\\>\) of method Doctrine\\Persistence\\ObjectManager\:\:getMetadataFactory\(\)$#' + identifier: method.childReturnType count: 1 path: src/Decorator/EntityManagerDecorator.php - - message: '#^Method Doctrine\\Persistence\\ObjectManager\:\:flush\(\) invoked with 1 parameter, 0 required\.$#' - identifier: arguments.count + message: '#^Return type \(Doctrine\\ORM\\Mapping\\ClassMetadataFactory\) of method Doctrine\\ORM\\Decorator\\EntityManagerDecorator\:\:getMetadataFactory\(\) should be compatible with return type \(Doctrine\\Persistence\\Mapping\\ClassMetadataFactory\\>\) of method Doctrine\\Persistence\\ObjectManagerDecorator\\:\:getMetadataFactory\(\)$#' + identifier: method.childReturnType count: 1 path: src/Decorator/EntityManagerDecorator.php @@ -720,36 +564,6 @@ parameters: count: 1 path: src/EntityManager.php - - - message: '#^Call to function is_callable\(\) with callable\(\)\: mixed will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/EntityManager.php - - - - message: '#^Call to function is_object\(\) with \*NEVER\* will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/EntityManager.php - - - - message: '#^Call to function is_object\(\) with object will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 5 - path: src/EntityManager.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/EntityManager.php - - - - message: '#^Instanceof between Doctrine\\DBAL\\Connection and Doctrine\\DBAL\\Connection will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/EntityManager.php - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:checkLockRequirements\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -769,62 +583,26 @@ parameters: path: src/EntityManager.php - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:getPartialReference\(\) should return T\|null but returns object\.$#' + message: '#^Method Doctrine\\ORM\\EntityManager\:\:getReference\(\) should return \(T of object\)\|null but returns Doctrine\\ORM\\Proxy\\InternalProxy\.$#' identifier: return.type count: 1 path: src/EntityManager.php - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:getPartialReference\(\) should return T\|null but returns object\|null\.$#' + message: '#^Method Doctrine\\ORM\\EntityManager\:\:getReference\(\) should return \(T of object\)\|null but returns object\|null\.$#' identifier: return.type count: 1 path: src/EntityManager.php - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:getReference\(\) should return T\|null but returns Doctrine\\Common\\Proxy\\Proxy\.$#' - identifier: return.type - count: 1 - path: src/EntityManager.php - - - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:getReference\(\) should return T\|null but returns object\|null\.$#' - identifier: return.type - count: 2 - path: src/EntityManager.php - - - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:getRepository\(\) should return Doctrine\\ORM\\EntityRepository\ but returns Doctrine\\Persistence\\ObjectRepository\\.$#' - identifier: return.type - count: 1 - path: src/EntityManager.php - - - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:initializeObject\(\) has parameter \$obj with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/EntityManager.php - - - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:isUninitializedObject\(\) has parameter \$obj with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/EntityManager.php - - - - message: '#^Method Doctrine\\ORM\\EntityManager\:\:wrapInTransaction\(\) has no return type specified\.$#' - identifier: missingType.return + message: '#^Method Doctrine\\ORM\\EntityManager\:\:isUninitializedObject\(\) has parameter \$value with no type specified\.$#' + identifier: missingType.parameter count: 1 path: src/EntityManager.php - message: '#^Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\\:\:getMetadataFor\(\) expects class\-string, string given\.$#' identifier: argument.type - count: 2 - path: src/EntityManager.php - - - - message: '#^Parameter \#1 \$modeName of method Doctrine\\ORM\\Configuration\:\:getCustomHydrationMode\(\) expects string, int\|string given\.$#' - identifier: argument.type count: 1 path: src/EntityManager.php @@ -834,39 +612,33 @@ parameters: count: 1 path: src/EntityManager.php - - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse - count: 1 - path: src/EntityManager.php - - message: '#^Return type \(Doctrine\\ORM\\Mapping\\ClassMetadataFactory\) of method Doctrine\\ORM\\EntityManager\:\:getMetadataFactory\(\) should be compatible with return type \(Doctrine\\Persistence\\Mapping\\ClassMetadataFactory\\>\) of method Doctrine\\Persistence\\ObjectManager\:\:getMetadataFactory\(\)$#' identifier: method.childReturnType - count: 2 + count: 1 path: src/EntityManager.php - - message: '#^Unable to resolve the template type T in call to method Doctrine\\ORM\\EntityManager\:\:find\(\)$#' - identifier: argument.templateType + message: '#^Return type \(Doctrine\\ORM\\Mapping\\ClassMetadataFactory\) of method Doctrine\\ORM\\EntityManagerInterface\:\:getMetadataFactory\(\) should be compatible with return type \(Doctrine\\Persistence\\Mapping\\ClassMetadataFactory\\>\) of method Doctrine\\Persistence\\ObjectManager\:\:getMetadataFactory\(\)$#' + identifier: method.childReturnType count: 1 - path: src/EntityManager.php + path: src/EntityManagerInterface.php - - message: '#^Method Doctrine\\ORM\\EntityRepository\:\:findOneBy\(\) should return \(T of object\)\|null but returns object\|null\.$#' + message: '#^Method Doctrine\\ORM\\EntityRepository\:\:findBy\(\) should return list\ but returns array\\.$#' identifier: return.type count: 1 path: src/EntityRepository.php - - message: '#^Method Doctrine\\ORM\\EntityRepository\:\:matching\(\) should return Doctrine\\Common\\Collections\\AbstractLazyCollection\&Doctrine\\Common\\Collections\\Selectable\ but returns Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#' + message: '#^Method Doctrine\\ORM\\EntityRepository\:\:findOneBy\(\) should return \(T of object\)\|null but returns object\|null\.$#' identifier: return.type count: 1 path: src/EntityRepository.php - - message: '#^Method Doctrine\\Persistence\\ObjectManager\:\:find\(\) invoked with 4 parameters, 2 required\.$#' - identifier: arguments.count + message: '#^Method Doctrine\\ORM\\EntityRepository\:\:matching\(\) should return Doctrine\\Common\\Collections\\AbstractLazyCollection\&Doctrine\\Common\\Collections\\Selectable\ but returns Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#' + identifier: return.type count: 1 path: src/EntityRepository.php @@ -919,26 +691,14 @@ parameters: path: src/Event/PreUpdateEventArgs.php - - message: '#^Parameter \#1 \$className of method Doctrine\\ORM\\EntityManagerInterface\:\:getClassMetadata\(\) expects string, class\-string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Id\\AssignedGenerator\:\:generateId\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Id/AssignedGenerator.php - - message: '#^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getTableHiLoCurrentValSql\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Id/TableGenerator.php - - - - message: '#^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getTableHiLoUpdateNextValSql\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Id/TableGenerator.php - - - - message: '#^Instanceof between Doctrine\\DBAL\\Driver\\ResultStatement and Doctrine\\DBAL\\Driver\\ResultStatement will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 2 path: src/Internal/Hydration/AbstractHydrator.php @@ -949,27 +709,27 @@ parameters: path: src/Internal/Hydration/AbstractHydrator.php - - message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) return type with generic class ReflectionClass does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Internal/Hydration/AbstractHydrator.php - - message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:getDiscriminatorValues\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Internal/Hydration/AbstractHydrator.php - - message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:registerManaged\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:getDiscriminatorValues\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Internal/Hydration/AbstractHydrator.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:registerManaged\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 path: src/Internal/Hydration/AbstractHydrator.php - @@ -985,8 +745,8 @@ parameters: path: src/Internal/Hydration/AbstractHydrator.php - - message: '#^Offset ''args'' might not exist on array\{class\: mixed, args\?\: array\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\ArrayHydrator\:\:hydrateAllData\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: src/Internal/Hydration/ArrayHydrator.php @@ -997,22 +757,28 @@ parameters: path: src/Internal/Hydration/ArrayHydrator.php - - message: '#^Class Doctrine\\ORM\\Internal\\Hydration\\IterableResult implements generic interface Iterator but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics - count: 1 - path: src/Internal/Hydration/IterableResult.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$inversedBy\.$#' + identifier: property.notFound + count: 2 + path: src/Internal/Hydration/ObjectHydrator.php - - message: '#^Property Doctrine\\ORM\\Internal\\Hydration\\IterableResult\:\:\$current \(array\\|null\) does not accept array\\|false\.$#' - identifier: assign.propertyType + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 2 - path: src/Internal/Hydration/IterableResult.php + path: src/Internal/Hydration/ObjectHydrator.php - - message: '#^Strict comparison using \!\=\= between array\\|null and false will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound + count: 3 + path: src/Internal/Hydration/ObjectHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator\:\:hydrateAllData\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Internal/Hydration/IterableResult.php + path: src/Internal/Hydration/ObjectHydrator.php - message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator\:\:initRelatedCollection\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' @@ -1027,23 +793,35 @@ parameters: path: src/Internal/Hydration/ObjectHydrator.php - - message: '#^Offset ''args'' might not exist on array\{class\: mixed, args\?\: array\}\.$#' - identifier: offsetAccess.notFound + message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\PersistentCollection\<\(int\|string\),mixed\>\:\:setOwner\(\) expects Doctrine\\ORM\\Mapping\\AssociationMapping&Doctrine\\ORM\\Mapping\\ToManyAssociationMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' + identifier: argument.type count: 1 path: src/Internal/Hydration/ObjectHydrator.php - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Internal/Hydration/ObjectHydrator.php - - message: '#^Property Doctrine\\ORM\\Internal\\Hydration\\ObjectHydrator\:\:\$uninitializedCollections with generic class Doctrine\\ORM\\PersistentCollection does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 path: src/Internal/Hydration/ObjectHydrator.php + - + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\ScalarColumnHydrator\:\:hydrateAllData\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Internal/Hydration/ScalarColumnHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\ScalarHydrator\:\:hydrateAllData\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Internal/Hydration/ScalarHydrator.php + + - + message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\SimpleObjectHydrator\:\:hydrateAllData\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Internal/Hydration/SimpleObjectHydrator.php + - message: '#^Property Doctrine\\ORM\\Internal\\Hydration\\SimpleObjectHydrator\:\:\$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics @@ -1092,12 +870,6 @@ parameters: count: 1 path: src/Mapping/AnsiQuoteStrategy.php - - - message: '#^Method Doctrine\\ORM\\Mapping\\AnsiQuoteStrategy\:\:getJoinTableName\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/AnsiQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\AnsiQuoteStrategy\:\:getJoinTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -1123,16 +895,16 @@ parameters: path: src/Mapping/AnsiQuoteStrategy.php - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\JoinTableMapping\:\:fromMappingArray\(\) expects array\{name\: string, quoted\?\: bool\|null, joinColumns\?\: array\, inverseJoinColumns\?\: array\, schema\?\: string\|null, options\?\: array\\}, non\-empty\-array\ given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/AnsiQuoteStrategy.php + path: src/Mapping/AssociationMapping.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue + message: '#^Property Doctrine\\ORM\\Mapping\\AssociationMapping\:\:\$cache type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Mapping/AssociationOverrides.php + path: src/Mapping/AssociationMapping.php - message: '#^Instanceof between Doctrine\\ORM\\Mapping\\AssociationOverride and Doctrine\\ORM\\Mapping\\AssociationOverride will always evaluate to true\.$#' @@ -1140,12 +912,6 @@ parameters: count: 1 path: src/Mapping/AssociationOverrides.php - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 1 - path: src/Mapping/AttributeOverrides.php - - message: '#^Instanceof between Doctrine\\ORM\\Mapping\\AttributeOverride and Doctrine\\ORM\\Mapping\\AttributeOverride will always evaluate to true\.$#' identifier: instanceof.alwaysTrue @@ -1153,28 +919,28 @@ parameters: path: src/Mapping/AttributeOverrides.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder\:\:__construct\(\) has parameter \$cm with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder\:\:__construct\(\) has parameter \$cm with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/Builder/ClassMetadataBuilder.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/Builder/ClassMetadataBuilder.php - - message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string given\.$#' + message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string given\.$#' identifier: argument.type count: 1 path: src/Mapping/Builder/ClassMetadataBuilder.php - - message: '#^Property Doctrine\\ORM\\Mapping\\Builder\\ClassMetadataBuilder\:\:\$cm with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:mapEmbedded\(\) expects array\{fieldName\: string, class\?\: class\-string, declaredField\?\: string, columnPrefix\?\: string\|false\|null, originalField\?\: string\}, array\ given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/Builder/ClassMetadataBuilder.php + path: src/Mapping/Builder/EmbeddedBuilder.php - message: '#^Method Doctrine\\ORM\\Mapping\\Builder\\EntityListenerBuilder\:\:bindEntityListener\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' @@ -1183,3889 +949,2074 @@ parameters: path: src/Mapping/Builder/EntityListenerBuilder.php - - message: '#^Class Doctrine\\ORM\\Mapping\\ClassMetadata has type alias AssociationMapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 4 path: src/Mapping/ClassMetadata.php - message: '#^If condition is always true\.$#' identifier: if.alwaysTrue - count: 2 - path: src/Mapping/ClassMetadataFactory.php + count: 1 + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addDefaultDiscriminatorMap\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:inlineEmbeddable\(\) has parameter \$embeddable with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedEmbeddedClasses\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Mapping/ClassMetadataFactory.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:setAssociationOverride\(\) has parameter \$overrideMapping with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 2 + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedEmbeddedClasses\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:setCustomRepositoryClass\(\) has parameter \$repositoryClassName with generic class Doctrine\\ORM\\EntityRepository but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedFields\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' + identifier: arrayValues.list count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedFields\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\ReflectionService\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedIndexes\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:validateAndCompleteTypedAssociationMapping\(\) expects array\{type\: 1\|2\|4\|8, fieldName\: string, targetEntity\?\: class\-string\}, non\-empty\-array\ given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedIndexes\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\AssociationMapping\:\:fromMappingArray\(\) expects array\{fieldName\: string, sourceEntity\: class\-string, targetEntity\: class\-string, cascade\?\: list\<''all''\|''detach''\|''persist''\|''refresh''\|''remove''\>, fetch\?\: 2\|3\|4\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null, cache\?\: array\\|null, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedNamedNativeQueries\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\EmbeddedClassMapping\:\:fromMappingArray\(\) expects array\{class\: class\-string, columnPrefix\?\: string\|false\|null, declaredField\?\: string\|null, originalField\?\: string\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null\}, array\{class\: string, columnPrefix\: string\|false\|null, declaredField\: string\|null, originalField\: string\|null\} given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedNamedNativeQueries\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\:\:fromMappingArrayAndNamingStrategy\(\) expects array\{fieldName\: string, sourceEntity\: class\-string, targetEntity\: class\-string, cascade\?\: list\<''all''\|''detach''\|''persist''\|''refresh''\|''remove''\>, fetch\?\: 2\|3\|4\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null, cache\?\: array\\|null, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedNamedQueries\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\:\:fromMappingArrayAndName\(\) expects array\{fieldName\: string, sourceEntity\: class\-string, targetEntity\: class\-string, cascade\?\: list\<''all''\|''detach''\|''persist''\|''refresh''\|''remove''\>, fetch\?\: 2\|3\|4\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null, cache\?\: array\\|null, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedNamedQueries\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\ToOneInverseSideMapping\:\:fromMappingArrayAndName\(\) expects array\{fieldName\: string, sourceEntity\: class\-string, targetEntity\: class\-string, cascade\?\: list\<''all''\|''detach''\|''persist''\|''refresh''\|''remove''\>, fetch\?\: 2\|3\|4\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null, cache\?\: array\\|null, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedRelations\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Mapping/ClassMetadataFactory.php + message: '#^Parameter \#1 \$mappingArray of static method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArrayAndName\(\) expects array\{fieldName\: string, sourceEntity\: class\-string, targetEntity\: class\-string, cascade\?\: list\<''all''\|''detach''\|''persist''\|''refresh''\|''remove''\>, fetch\?\: 2\|3\|4\|null, inherited\?\: class\-string\|null, declared\?\: class\-string\|null, cache\?\: array\\|null, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type + count: 2 + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedRelations\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#2 \$class of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedSqlResultSetMappings\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#3 \$embeddedClass of class Doctrine\\ORM\\Mapping\\ReflectionEmbeddedProperty constructor expects class\-string, string given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedSqlResultSetMappings\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\:\:\$customRepositoryClassName with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addMappingInheritanceInformation\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\:\:\$table type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataFactory.php + count: 2 + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addMappingInheritanceInformation\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:\$customRepositoryClassName \(class\-string\\|null\) does not accept class\-string\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addNestedEmbeddedClasses\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:\$discriminatorMap \(array\\) does not accept array\\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addNestedEmbeddedClasses\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:\$entityListeners \(array\\>\) does not accept non\-empty\-array\\>\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:completeIdGeneratorMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:\$subClasses \(list\\) does not accept non\-empty\-list\\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:doLoadMetadata\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Strict comparison using \!\=\= between Doctrine\\ORM\\Mapping\\FieldMapping and false will always evaluate to true\.$#' + identifier: notIdentical.alwaysTrue + count: 2 + path: src/Mapping/ClassMetadata.php + + - + message: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\ORM\\Mapping\\ClassMetadata\:\:\$name\.$#' + identifier: generics.variance count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:doLoadMetadata\(\) has parameter \$parent with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\ORM\\Mapping\\ClassMetadata\:\:\$reflClass\.$#' + identifier: generics.variance count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:findAbstractEntityClassesNotListedInDiscriminatorMap\(\) has parameter \$rootEntityClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Unable to resolve the template type C in call to method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:fullyQualifiedClassName\(\)$#' + identifier: argument.templateType count: 1 - path: src/Mapping/ClassMetadataFactory.php + path: src/Mapping/ClassMetadata.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:getFqcnFromAlias\(\) has parameter \$namespaceAlias with no type specified\.$#' - identifier: missingType.parameter + message: '#^If condition is always true\.$#' + identifier: if.alwaysTrue count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:getFqcnFromAlias\(\) has parameter \$simpleClassName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addDefaultDiscriminatorMap\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:inheritIdGeneratorMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedEmbeddedClasses\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:inheritIdGeneratorMapping\(\) has parameter \$parent with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedEmbeddedClasses\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:initializeReflection\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedFields\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:isEntity\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedFields\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:newClassMetadataInstance\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedIndexes\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:onNotFoundMetadata\(\) has parameter \$className with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedIndexes\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:onNotFoundMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedRelations\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:validateRuntimeMetadata\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addInheritedRelations\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:validateRuntimeMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addMappingInheritanceInformation\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:wakeupReflection\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addNestedEmbeddedClasses\(\) has parameter \$parentClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Parameter \#1 \$fieldMapping of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:addInheritedFieldMapping\(\) expects array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}, array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\|array\{class\: class\-string, columnPrefix\: string\|null, declaredField\: string\|null, originalField\: string\|null, inherited\?\: class\-string, declared\?\: class\-string\}\|array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\} given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:addNestedEmbeddedClasses\(\) has parameter \$subClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Parameter \#1 \$generator of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setIdGenerator\(\) expects Doctrine\\ORM\\Id\\AbstractIdGenerator, object given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:completeIdGeneratorMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:addInheritedAssociationMapping\(\) expects array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}, array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\|array\{class\: class\-string, columnPrefix\: string\|null, declaredField\: string\|null, originalField\: string\|null, inherited\?\: class\-string, declared\?\: class\-string, sourceEntity\?\: class\-string\\}\|array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\} given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:doLoadMetadata\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Parameter \#2 \$class of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getSequenceName\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\ORM\\Mapping\\ClassMetadataInfo given\.$#' - identifier: argument.type - count: 2 + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:doLoadMetadata\(\) has parameter \$parent with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Parameter \#5 \.\.\.\$args of static method Doctrine\\Deprecations\\Deprecation\:\:trigger\(\) expects float\|int\|string, class\-string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:findAbstractEntityClassesNotListedInDiscriminatorMap\(\) has parameter \$rootEntityClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$embeddedClasses \(array\\) does not accept non\-empty\-array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\|array\{class\: class\-string, columnPrefix\: string\|null, declaredField\: string\|null, originalField\: string\|null, inherited\?\: class\-string, declared\?\: class\-string\}\|array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}\>\.$#' - identifier: assign.propertyType + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:inheritIdGeneratorMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_storeAssociationMapping\(\) has parameter \$assocMapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_validateAndCompleteAssociationMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:inheritIdGeneratorMapping\(\) has parameter \$parent with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_validateAndCompleteAssociationMapping\(\) should return array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\} but returns non\-empty\-array\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:initializeReflection\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_validateAndCompleteManyToManyMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:isEntity\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_validateAndCompleteOneToManyMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:newClassMetadataInstance\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:_validateAndCompleteOneToOneMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:onNotFoundMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:addInheritedAssociationMapping\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:validateRuntimeMetadata\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:assertMappingOrderBy\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:validateRuntimeMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getAssociationMappedByTargetField\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataFactory\:\:wakeupReflection\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getAssociationMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Parameter \#1 \$generator of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:setIdGenerator\(\) expects Doctrine\\ORM\\Id\\AbstractIdGenerator, object given\.$#' + identifier: argument.type + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getAssociationMappings\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Parameter \#1 \$rootEntityClass of static method Doctrine\\ORM\\Mapping\\MappingException\:\:missingInheritanceTypeDeclaration\(\) expects class\-string, class\-string\|false given\.$#' + identifier: argument.type + count: 1 + path: src/Mapping/ClassMetadataFactory.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getIdentifierColumnNames\(\) should return list\ but returns array\\.$#' - identifier: return.type + message: '#^Property Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver\:\:\$instances \(array\\) does not accept array\\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultEntityListenerResolver.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getQuotedIdentifierColumnNames\(\) should return list\ but returns array\\.$#' - identifier: return.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getSqlResultSetMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 2 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getColumnAlias\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getSqlResultSetMappings\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 2 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:hasAssociation\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getIdentifierColumnNames\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:hasField\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:inlineEmbeddable\(\) has parameter \$embeddable with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getJoinTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:isAssociationInverseSide\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getReferencedJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:isCollectionValuedAssociation\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getSequenceName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:isSingleValuedAssociation\(\) has parameter \$fieldName with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/DefaultQuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:setCustomRepositoryClass\(\) has parameter \$repositoryClassName with generic class Doctrine\\ORM\\EntityRepository but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver\:\:isRepeatedPropertyDeclaration\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/AttributeDriver.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:validateAndCompleteFieldMapping\(\) should return array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\} but returns array\{fieldName\: string, columnName\: string, id\?\: bool, generated\?\: int, enumType\?\: class\-string, type\: string, quoted\?\: true, requireSQLConversion\?\: true\}\.$#' - identifier: return.type + message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:mapEmbedded\(\) expects array\{fieldName\: string, class\?\: class\-string, declaredField\?\: string, columnPrefix\?\: string\|false\|null, originalField\?\: string\}, array\{fieldName\: string, cache\?\: array\{usage\: int, region\: string\|null\}, class\: string\|null, columnPrefix\: bool\|string\|null\} given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/AttributeDriver.php - - message: '#^Method Doctrine\\ORM\\Mapping\\NamingStrategy\:\:joinColumnName\(\) invoked with 2 parameters, 1 required\.$#' - identifier: arguments.count - count: 2 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Unable to resolve the template type C in call to method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:fullyQualifiedClassName\(\)$#' + identifier: argument.templateType + count: 1 + path: src/Mapping/Driver/AttributeDriver.php - - message: '#^Negated boolean expression is always false\.$#' - identifier: booleanNot.alwaysFalse - count: 2 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Cannot call method getName\(\) on Doctrine\\DBAL\\Schema\\Column\|false\.$#' + identifier: method.nonObject + count: 1 + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Instanceof between Doctrine\\ORM\\Mapping\\ClassMetadata\ and Doctrine\\ORM\\Mapping\\ClassMetadata will always evaluate to true\.$#' + identifier: instanceof.alwaysTrue + count: 1 + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Offset ''originalClass'' might not exist on array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:__construct\(\) has parameter \$sm with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Offset ''originalField'' might not exist on array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildFieldMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildIndexes\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\ReflectionService\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildToOneAssociationMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/DatabaseDriver.php + + - + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getClassNameForTable\(\) should return class\-string but returns string\.$#' + identifier: return.type + count: 2 + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:validateAndCompleteTypedAssociationMapping\(\) expects array\{type\: 1\|2\|4\|8, fieldName\: string, targetEntity\?\: class\-string\}, non\-empty\-array\ given\.$#' + message: '#^Parameter \#2 \$columnName of method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getFieldNameForColumn\(\) expects string, string\|false given\.$#' identifier: argument.type - count: 1 - path: src/Mapping/ClassMetadataInfo.php + count: 4 + path: src/Mapping/Driver/DatabaseDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:\$associationMappings type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/ClassMetadataInfo.php + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: src/Mapping/Driver/SimplifiedXmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:\$customRepositoryClassName with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$prefixes with no type specified\.$#' + identifier: missingType.parameter count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/SimplifiedXmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:\$reflClass with generic class ReflectionClass does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\XmlDriver\:\:cacheToArray\(\) should return array\{usage\: int\|null, region\?\: string\} but returns array\{usage\: ''''\|''0''\|int\|null, region\: string\|null\}\.$#' + identifier: return.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:\$table type has no value type specified in iterable type array\.$#' + message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\XmlDriver\:\:columnToArray\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue - count: 2 - path: src/Mapping/ClassMetadataInfo.php + count: 1 + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$associationMappings \(array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\>\) does not accept non\-empty\-array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\>\.$#' - identifier: assign.propertyType + message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string\|null, type\?\: string\|null, length\?\: int\|null, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\|null\}\|Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null, options\?\: array\\|bool\|string\>\} given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$customRepositoryClassName \(class\-string\\|null\) does not accept string\|null\.$#' - identifier: assign.propertyType + message: '#^Parameter \#1 \$data of function simplexml_load_string expects string, string\|false given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$discriminatorMap \(array\\) does not accept array\\.$#' - identifier: assign.propertyType + message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:mapEmbedded\(\) expects array\{fieldName\: string, class\?\: class\-string, declaredField\?\: string, columnPrefix\?\: string\|false\|null, originalField\?\: string\}, array\{fieldName\: string, class\: string\|null, columnPrefix\: string\|false\|null\} given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$embeddedClasses \(array\\) does not accept non\-empty\-array\\.$#' - identifier: assign.propertyType + message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$entityListeners \(array\\>\) does not accept non\-empty\-array\\>\.$#' - identifier: assign.propertyType + message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string\|null given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$namedNativeQueries \(array\\>\) does not accept array\\>\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\\:\:\$table \(array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\, quoted\?\: bool\}\) does not accept array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\: array\\|bool\|string\>, quoted\?\: bool\}\.$#' identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/Driver/XmlDriver.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$namedQueries \(array\\>\) does not accept array\\>\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\FieldMapping\:\:\$declared \(class\-string\|null\) does not accept string\|null\.$#' identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/FieldMapping.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$sqlResultSetMappings \(array\, columns\: array\\}\>\) does not accept non\-empty\-array\\>\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\FieldMapping\:\:\$enumType \(class\-string\\|null\) does not accept string\|null\.$#' identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/FieldMapping.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$subClasses \(list\\) does not accept non\-empty\-list\\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\FieldMapping\:\:\$inherited \(class\-string\|null\) does not accept string\|null\.$#' identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/FieldMapping.php - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse + message: '#^Property Doctrine\\ORM\\Mapping\\FieldMapping\:\:\$options type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Mapping/ClassMetadataInfo.php - - - - message: '#^Strict comparison using \!\=\= between array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\} and false will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue - count: 2 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/FieldMapping.php - - message: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:\$name\.$#' - identifier: generics.variance + message: '#^Property Doctrine\\ORM\\Mapping\\JoinTableMapping\:\:\$options \(array\\) does not accept array\\|bool\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/ClassMetadataInfo.php + path: src/Mapping/JoinTableMapping.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 8 - path: src/Mapping/Column.php - - - - message: '#^Call to function is_object\(\) with object will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType + message: '#^Property Doctrine\\ORM\\Mapping\\JoinTableMapping\:\:\$quoted \(bool\|null\) does not accept array\\|bool\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Mapping/DefaultEntityListenerResolver.php + path: src/Mapping/JoinTableMapping.php - - message: '#^Property Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver\:\:\$instances \(array\\) does not accept array\\.$#' + message: '#^Property Doctrine\\ORM\\Mapping\\JoinTableMapping\:\:\$schema \(string\|null\) does not accept array\\|bool\|string\.$#' identifier: assign.propertyType count: 1 - path: src/Mapping/DefaultEntityListenerResolver.php + path: src/Mapping/JoinTableMapping.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getColumnAlias\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\MappedSuperclass\:\:__construct\(\) has parameter \$repositoryClass with generic class Doctrine\\ORM\\EntityRepository but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/MappedSuperclass.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getColumnAlias\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getIdentifierColumnNames\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getIdentifierColumnNames\(\) should return list\ but returns array\\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getIdentifierColumnNames\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getJoinTableName\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getJoinTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getReferencedJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getReferencedJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getSequenceName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getSequenceName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Method Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy\:\:getTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/QuoteStrategy.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Mapping\\ReflectionEnumProperty\:\:getValue\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Mapping/DefaultQuoteStrategy.php + path: src/Mapping/ReflectionEnumProperty.php - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArray\(\) should return static\(Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\) but returns Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\.$#' + identifier: return.type count: 1 - path: src/Mapping/DefaultQuoteStrategy.php - - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 2 - path: src/Mapping/DiscriminatorColumn.php + path: src/Mapping/ToOneOwningSideMapping.php - - message: '#^Access to an undefined property object\:\:\$region\.$#' - identifier: property.notFound + message: '#^Method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArrayAndName\(\) has parameter \$table with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/Mapping/ToOneOwningSideMapping.php - - message: '#^Access to an undefined property object\:\:\$usage\.$#' - identifier: property.notFound + message: '#^Method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArrayAndName\(\) should return static\(Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\) but returns Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\.$#' + identifier: return.type count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/Mapping/ToOneOwningSideMapping.php - - message: '#^Access to an undefined property object\:\:\$value\.$#' - identifier: property.notFound - count: 3 - path: src/Mapping/Driver/AnnotationDriver.php + message: '#^Call to function is_int\(\) with string will always evaluate to false\.$#' + identifier: function.impossibleType + count: 1 + path: src/NativeQuery.php - - message: '#^Call to an undefined method Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:mapEmbedded\(\)\.$#' - identifier: method.notFound + message: '#^Method Doctrine\\ORM\\NativeQuery\:\:_doExecute\(\) never returns int so it can be removed from the return type\.$#' + identifier: return.unusedType count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/NativeQuery.php - - message: '#^Call to an undefined method Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:mapManyToMany\(\)\.$#' - identifier: method.notFound + message: '#^Result of && is always false\.$#' + identifier: booleanAnd.alwaysFalse count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/NativeQuery.php - - message: '#^Call to an undefined method Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:mapManyToOne\(\)\.$#' - identifier: method.notFound + message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:invalidAssociation\(\) has parameter \$targetClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/ORMInvalidArgumentException.php - - message: '#^Call to an undefined method Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:mapOneToMany\(\)\.$#' - identifier: method.notFound + message: '#^Call to method Doctrine\\ORM\\Mapping\\AssociationMapping\:\:isToMany\(\) will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Call to an undefined method Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:mapOneToOne\(\)\.$#' - identifier: method.notFound + message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:__construct\(\) has parameter \$typeClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Expression on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.expr + message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:get\(\) should return T\|null but returns object\|null\.$#' + identifier: return.type count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\:\:isRepeatedPropertyDeclaration\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:getTypeClass\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver\:\:loadRelationShipMapping\(\) has parameter \$metadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\ but returns Doctrine\\Common\\Collections\\ArrayCollection\<\(int\|string\), mixed\>\|Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#' + identifier: return.type count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Offset ''indexes'' might not exist on array\{name\: string\|null, schema\: string\|null, indexes\?\: non\-empty\-array\<0\|non\-falsy\-string, non\-empty\-array\{columns\?\: non\-empty\-array\, fields\?\: non\-empty\-array\, flags\?\: non\-empty\-array\, options\?\: non\-empty\-array\\}\>\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\ but returns Doctrine\\Common\\Collections\\ReadableCollection\&Doctrine\\Common\\Collections\\Selectable\\.$#' + identifier: return.type count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Offset ''uniqueConstraints'' might not exist on array\{name\: string\|null, schema\: string\|null, indexes\?\: non\-empty\-array\<0\|non\-falsy\-string, non\-empty\-array\{columns\?\: non\-empty\-array\, fields\?\: non\-empty\-array\, flags\?\: non\-empty\-array\, options\?\: non\-empty\-array\\}\>, uniqueConstraints\?\: non\-empty\-array\<0\|non\-falsy\-string, non\-empty\-array\{columns\?\: non\-empty\-array\, fields\?\: non\-empty\-array\, options\?\: non\-empty\-array\\}\>\}\.$#' - identifier: offsetAccess.notFound + message: '#^Parameter \#1 \$key of method Doctrine\\ORM\\PersistentCollection\\:\:set\(\) expects TKey of \(int\|string\), int\|string given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/Driver/AnnotationDriver.php + path: src/PersistentCollection.php - - message: '#^Expression on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.expr + message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\&Doctrine\\Common\\Collections\\Selectable\, ''add''\} given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/Driver/AttributeDriver.php + path: src/PersistentCollection.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver\:\:isRepeatedPropertyDeclaration\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/AttributeDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string, type\?\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null, options\?\: array\} given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/AttributeDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Cannot call method getName\(\) on Doctrine\\DBAL\\Schema\\Column\|false\.$#' - identifier: method.nonObject + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Instanceof between Doctrine\\ORM\\Mapping\\ClassMetadata\ and Doctrine\\ORM\\Mapping\\ClassMetadata will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:__construct\(\) has parameter \$schemaManager with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildFieldMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildIndexes\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildToOneAssociationMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Driver/DatabaseDriver.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getClassNameForTable\(\) should return class\-string but returns string\.$#' - identifier: return.type - count: 2 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/CollectionPersister.php - - message: '#^Parameter \#2 \$columnName of method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getFieldNameForColumn\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 4 - path: src/Mapping/Driver/DatabaseDriver.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 1 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#4 \.\.\.\$args of static method Doctrine\\Deprecations\\Deprecation\:\:trigger\(\) expects float\|int\|string, false given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTableColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Property Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:\$sm with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/Driver/DatabaseDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#' - identifier: missingType.parameter + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToTargetKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/Driver/SimplifiedXmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$prefixes with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/Driver/SimplifiedXmlDriver.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 2 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedYamlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/Driver/SimplifiedYamlDriver.php + message: '#^Call to function assert\(\) with true will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 2 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedYamlDriver\:\:__construct\(\) has parameter \$prefixes with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/Driver/SimplifiedYamlDriver.php + message: '#^Call to method Doctrine\\ORM\\Mapping\\AssociationMapping\:\:isIndexed\(\) will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 2 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Empty array passed to foreach\.$#' - identifier: foreach.emptyArray - count: 1 - path: src/Mapping/Driver/XmlDriver.php + message: '#^Instanceof between Doctrine\\ORM\\Mapping\\InverseSideMapping&Doctrine\\ORM\\Mapping\\ManyToManyAssociationMapping and Doctrine\\ORM\\Mapping\\InverseSideMapping will always evaluate to true\.$#' + identifier: instanceof.alwaysTrue + count: 2 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\XmlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:collectJoinTableColumnParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\XmlDriver\:\:cacheToArray\(\) should return array\{usage\: int\|null, region\?\: string\} but returns array\{usage\: ''''\|''0''\|int\|null, region\: string\|null\}\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\XmlDriver\:\:columnToArray\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Offset ''version'' on \*NEVER\* in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string, type\?\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null, options\?\: array\\|bool\|string\>\} given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$data of function simplexml_load_string expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$repositoryClassName of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setCustomRepositoryClass\(\) expects class\-string\\|null, string\|null given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteRowSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$table \(array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\, quoted\?\: bool\}\) does not accept array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\: array\\|bool\|string\>, quoted\?\: bool\}\.$#' - identifier: assign.propertyType + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteRowSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/XmlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\YamlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/YamlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\YamlDriver\:\:cacheToArray\(\) should return array\{usage\: int\|null, region\: string\|null\} but returns array\{usage\: ''''\|''0''\|int\|null, region\: string\|null\}\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/YamlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string, type\?\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null\} given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getInsertRowSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/YamlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Parameter \#1 \$input of static method Symfony\\Component\\Yaml\\Yaml\:\:parse\(\) expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getInsertRowSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Driver/YamlDriver.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Embedded\:\:__construct\(\) has parameter \$columnPrefix with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getJoinTableRestrictions\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Embedded.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getJoinTableRestrictionsWithKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/Entity.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getMapping\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/EntityListeners.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getOrderingSql\(\) has parameter \$targetClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/GeneratedValue.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 4 - path: src/Mapping/InverseJoinColumn.php + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 4 - path: src/Mapping/JoinColumn.php + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 3 - path: src/Mapping/JoinTable.php + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\JoinTable\:\:__construct\(\) has parameter \$inverseJoinColumns with no type specified\.$#' - identifier: missingType.parameter + message: '#^Parameter \#1 \$association of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) expects Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' + identifier: argument.type count: 1 - path: src/Mapping/JoinTable.php + path: src/Persisters/Collection/ManyToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\JoinTable\:\:__construct\(\) has parameter \$joinColumns with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Mapping/JoinTable.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 2 - path: src/Mapping/ManyToMany.php - - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 1 - path: src/Mapping/ManyToOne.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\MappedSuperclass\:\:__construct\(\) has parameter \$repositoryClass with generic class Doctrine\\ORM\\EntityRepository but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/MappedSuperclass.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Property Doctrine\\ORM\\Mapping\\MappedSuperclass\:\:\$repositoryClass with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/MappedSuperclass.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\MappingException\:\:invalidIndexConfiguration\(\) has parameter \$className with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/MappingException.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\MappingException\:\:invalidIndexConfiguration\(\) has parameter \$indexName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/MappingException.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\MappingException\:\:invalidUniqueConstraintConfiguration\(\) has parameter \$className with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/MappingException.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\MappingException\:\:invalidUniqueConstraintConfiguration\(\) has parameter \$indexName with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/Mapping/MappingException.php - - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 2 - path: src/Mapping/OneToMany.php - - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 2 - path: src/Mapping/OneToOne.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getColumnAlias\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:deleteEntityCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getIdentifierColumnNames\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:deleteJoinedEntityCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php - - - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:getMapping\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getReferencedJoinColumnName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getSequenceName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getTableName\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' identifier: missingType.generics count: 1 - path: src/Mapping/QuoteStrategy.php + path: src/Persisters/Collection/OneToManyPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\Reflection\\ReflectionPropertiesGetter\:\:getClassProperties\(\) has parameter \$reflectionClass with generic class ReflectionClass but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\AbstractEntityInheritancePersister\:\:getSelectColumnSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Mapping/Reflection/ReflectionPropertiesGetter.php + path: src/Persisters/Entity/AbstractEntityInheritancePersister.php - - message: '#^Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\ReflectionService\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#' - identifier: argument.type - count: 1 - path: src/Mapping/Reflection/ReflectionPropertiesGetter.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 5 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Mapping\\ReflectionEmbeddedProperty\:\:setValue\(\) has parameter \$object with no type specified\.$#' - identifier: missingType.parameter + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/ReflectionEmbeddedProperty.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Parameter \#1 \$className of method Doctrine\\Instantiator\\Instantiator\:\:instantiate\(\) expects class\-string\, string given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/ReflectionEmbeddedProperty.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Unable to resolve the template type T in call to method Doctrine\\Instantiator\\Instantiator\:\:instantiate\(\)$#' - identifier: argument.templateType - count: 1 - path: src/Mapping/ReflectionEmbeddedProperty.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 4 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 2 - path: src/Mapping/SequenceGenerator.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToTargetKeyColumns\.$#' + identifier: property.notFound + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$sourceToTargetKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Mapping/Table.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Call to function is_int\(\) with string will always evaluate to false\.$#' - identifier: function.impossibleType + message: '#^Call to an undefined method Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:indexBy\(\)\.$#' + identifier: method.notFound count: 1 - path: src/NativeQuery.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\NativeQuery\:\:_doExecute\(\) never returns int so it can be removed from the return type\.$#' - identifier: return.unusedType + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:__construct\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/NativeQuery.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandCriteriaParameters\(\) should return array\{list\, list\\} but returns array\{array\, list\\}\.$#' + identifier: return.type count: 1 - path: src/NativeQuery.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:detachedEntityFoundThroughRelationship\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/ORMInvalidArgumentException.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandParameters\(\) should return array\{list\, list\\} but returns array\{array\, list\\}\.$#' + identifier: return.type + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:invalidAssociation\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandToManyParameters\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue - count: 5 - path: src/ORMInvalidArgumentException.php + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:invalidAssociation\(\) has parameter \$targetClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:extractIdentifierTypes\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/ORMInvalidArgumentException.php - - - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:newEntitiesFoundThroughRelationships\(\) has parameter \$newEntitiesWithAssociations with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/ORMInvalidArgumentException.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:newEntityFoundThroughRelationship\(\) has parameter \$associationMapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/ORMInvalidArgumentException.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:fetchVersionAndNotUpsertableValues\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\ORMInvalidArgumentException\:\:newEntityFoundThroughRelationshipMessage\(\) has parameter \$associationMapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/ORMInvalidArgumentException.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\Common\\\\Collections\\\\Criteria'' and ''orderings'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getClassIdentifiersTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:__construct\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:get\(\) should return T\|null but returns object\|null\.$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getIndividualValue\(\) should return list\ but returns array\\.$#' identifier: return.type count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:getMapping\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/PersistentCollection.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnAssociationSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:getTypeClass\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\ but returns Doctrine\\Common\\Collections\\ArrayCollection\<\(int\|string\), mixed\>\|Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\ but returns Doctrine\\Common\\Collections\\ReadableCollection\&Doctrine\\Common\\Collections\\Selectable\\.$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) should return list\ but returns list\\.$#' identifier: return.type count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:setOwner\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/PersistentCollection.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadCollectionFromStatement\(\) has parameter \$coll with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Offset ''orphanRemoval'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/PersistentCollection.php + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics + count: 1 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Parameter \#1 \$key of method Doctrine\\ORM\\PersistentCollection\\:\:set\(\) expects TKey of \(int\|string\), int\|string given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadOneToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\&Doctrine\\Common\\Collections\\Selectable\, ''add''\} given\.$#' + message: '#^Parameter \#1 \$association of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) expects Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping, Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' identifier: argument.type count: 1 - path: src/PersistentCollection.php - - - - message: '#^Property Doctrine\\ORM\\PersistentCollection\:\:\$association type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/PersistentCollection.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Property Doctrine\\ORM\\PersistentCollection\:\:\$typeClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/PersistentCollection.php + message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#' + identifier: argument.type + count: 2 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics + message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 - path: src/Persisters/Collection/CollectionPersister.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/CollectionPersister.php + message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 4 + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:\$sqlTableAliases \(array\\) does not accept array\\.$#' + identifier: assign.propertyType count: 1 - path: src/Persisters/Collection/CollectionPersister.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics + message: '#^Strict comparison using \!\=\= between mixed and null will always evaluate to true\.$#' + identifier: notIdentical.alwaysTrue count: 1 - path: src/Persisters/Collection/CollectionPersister.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics + message: '#^Strict comparison using \=\=\= between string and null will always evaluate to false\.$#' + identifier: identical.alwaysFalse count: 1 - path: src/Persisters/Collection/CollectionPersister.php + path: src/Persisters/Entity/BasicEntityPersister.php - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:__construct\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Persisters/Collection/CollectionPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/CollectionPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\CollectionPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/CollectionPersister.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\Common\\\\Collections\\\\Criteria'' and ''orderings'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:collectJoinTableColumnParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteRowSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteRowSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getDeleteSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getFilterSql\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getInsertRowSQL\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getInsertRowSQLParameters\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getJoinTableRestrictions\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getJoinTableRestrictionsWithKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getOnConditionSQL\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:getOrderingSql\(\) has parameter \$targetClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''indexBy'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 10 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''joinTableColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 4 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''relationToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''relationToSourceKeyColumns''\|''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Offset ''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Parameter \#1 \$className of method Doctrine\\ORM\\EntityManagerInterface\:\:getClassMetadata\(\) expects string, class\-string\|false given\.$#' - identifier: argument.type - count: 3 - path: src/Persisters/Collection/ManyToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:contains\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:containsKey\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:count\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:delete\(\) should return int\|null but empty return statement found\.$#' - identifier: return.empty - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:deleteEntityCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:deleteJoinedEntityCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:get\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:loadCriteria\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:slice\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\OneToManyPersister\:\:update\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Offset ''orphanRemoval'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Collection/OneToManyPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\AbstractEntityInheritancePersister\:\:getSelectColumnSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/AbstractEntityInheritancePersister.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\Common\\\\Collections\\\\Criteria'' and ''orderings'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:__construct\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandCriteriaParameters\(\) should return array\{list\, list\\} but returns array\{array\, list\\}\.$#' - identifier: return.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandParameters\(\) should return array\{list\, list\\} but returns array\{array\, list\\}\.$#' - identifier: return.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandToManyParameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:extractIdentifierTypes\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:fetchVersionAndNotUpsertableValues\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getClassIdentifiersTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getIndividualValue\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getOneToManyStatement\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnAssociationSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnAssociationSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectConditionStatementColumnSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectConditionStatementSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectManyToManyJoinSQL\(\) has parameter \$manyToMany with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:load\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadAll\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadCollectionFromStatement\(\) has parameter \$coll with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadOneToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadOneToOneEntity\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 9 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 8 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Offset ''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Offset ''sourceToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Parameter \#1 \$association of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) expects array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}, array\ given\.$#' - identifier: argument.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectSQL\(\) expects array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\|null, non\-empty\-array\ given\.$#' - identifier: argument.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Parameter \#3 \$assoc of method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectConditionStatementSQL\(\) expects array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\|null, array\\|null given\.$#' - identifier: argument.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\, array\ given\.$#' - identifier: argument.type - count: 4 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Property Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:\$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Property Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:\$sqlTableAliases \(array\\) does not accept array\\.$#' - identifier: assign.propertyType - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Strict comparison using \!\=\= between mixed and null will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Strict comparison using \=\=\= between string and null will always evaluate to false\.$#' - identifier: identical.alwaysFalse - count: 1 - path: src/Persisters/Entity/BasicEntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:__construct\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/CachedPersisterContext.php - - - - message: '#^Property Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:\$class \(Doctrine\\ORM\\Mapping\\ClassMetadata\) does not accept Doctrine\\Persistence\\Mapping\\ClassMetadata\.$#' - identifier: assign.propertyType - count: 1 - path: src/Persisters/Entity/CachedPersisterContext.php - - - - message: '#^Property Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:\$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/CachedPersisterContext.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getSelectConditionStatementSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getSelectSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:load\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadAll\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadManyToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadOneToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadOneToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadOneToOneEntity\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/EntityPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister\:\:fetchVersionAndNotUpsertableValues\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/JoinedSubclassPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister\:\:getSelectSQL\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Persisters/Entity/JoinedSubclassPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister\:\:getVersionedClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/JoinedSubclassPersister.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Persisters/Entity/JoinedSubclassPersister.php - - - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Entity/JoinedSubclassPersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\SingleTablePersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/Entity/SingleTablePersister.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Persisters/Entity/SingleTablePersister.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\SqlExpressionVisitor\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/SqlExpressionVisitor.php - - - - message: '#^Property Doctrine\\ORM\\Persisters\\SqlExpressionVisitor\:\:\$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Persisters/SqlExpressionVisitor.php - - - - message: '#^Method Doctrine\\ORM\\Persisters\\SqlValueVisitor\:\:getParamsAndTypes\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Persisters/SqlValueVisitor.php - - - - message: ''' - #^Class Doctrine\\ORM\\Proxy\\Autoloader extends deprecated class Doctrine\\Common\\Proxy\\Autoloader\: - The Autoloader class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: class.extendsDeprecatedClass - count: 1 - path: src/Proxy/Autoloader.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\ but returns class\-string\\>\|class\-string\\.$#' - identifier: return.type - count: 1 - path: src/Proxy/DefaultProxyClassNameResolver.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\ but returns string\.$#' - identifier: return.type - count: 1 - path: src/Proxy/DefaultProxyClassNameResolver.php - - - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isEmbeddedClass\.$#' - identifier: property.notFound - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isMappedSuperclass\.$#' - identifier: property.notFound - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Call to an undefined method Doctrine\\Common\\Proxy\\Proxy\:\:__wakeup\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Call to an undefined static method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyGhost\(\)\.$#' - identifier: staticMethod.notFound - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Call to function is_bool\(\) with bool will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Call to method __construct\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\: - The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: staticMethod.deprecatedClass - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Call to method generateProxyClasses\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\: - The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: staticMethod.deprecatedClass - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Call to method getProxy\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\: - The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: staticMethod.deprecatedClass - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Class Doctrine\\ORM\\Proxy\\ProxyFactory extends deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\: - The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: class.extendsDeprecatedClass - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Comparison operation "\<" between 0\|1\|2\|3\|4 and 0 is always false\.$#' - identifier: smaller.alwaysFalse - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Comparison operation "\>" between 0\|1\|2\|3\|4 and 4 is always false\.$#' - identifier: greater.alwaysFalse - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Instantiation of deprecated class Doctrine\\Common\\Proxy\\ProxyGenerator\: - The ProxyGenerator class is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: new.deprecated - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createCloner\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#' - identifier: throws.unusedType - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createCloner\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createCloner\(\) return type with generic interface Doctrine\\Common\\Proxy\\Proxy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createInitializer\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createInitializer\(\) return type with generic interface Doctrine\\Common\\Proxy\\Proxy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#' - identifier: throws.unusedType - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type with generic interface Doctrine\\ORM\\Proxy\\InternalProxy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) has parameter \$classes with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateSerializeImpl\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateUseLazyGhostTrait\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:getProxy\(\) return type with generic interface Doctrine\\Common\\Proxy\\Proxy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:loadProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:resetUninitializedProxy\(\) has parameter \$proxy with generic interface Doctrine\\Common\\Proxy\\Proxy but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:resetUninitializedProxy\(\) return type with generic interface Doctrine\\Common\\Proxy\\Proxy does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:skipClass\(\) has parameter \$metadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Parameter \#1 \$class of method Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:flattenIdentifier\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\Persistence\\Mapping\\ClassMetadata given\.$#' - identifier: argument.type - count: 3 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Parameter \#1 \$filename of function filemtime expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Parameter \#3 \$newScope of static method Closure\:\:bind\(\) expects ''static''\|class\-string\|object\|null, string given\.$#' - identifier: argument.type - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Result of \|\| is always false\.$#' - identifier: booleanOr.alwaysFalse - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: ''' - #^Return type of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:getProxy\(\) has typehint with deprecated interface Doctrine\\Common\\Proxy\\Proxy\: - The Proxy interface is deprecated since doctrine/common 3\.5\.$# - ''' - identifier: return.deprecatedInterface - count: 1 - path: src/Proxy/ProxyFactory.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Cache\\\\QueryCacheProfile'' and ''getResultCache'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Query.php - - - - message: '#^Method Doctrine\\ORM\\Query\:\:processParameterMappings\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Query.php - - - - message: '#^Parameter \#2 \$sqlParams of method Doctrine\\ORM\\Query\:\:evictResultSetCache\(\) expects array\, list\ given\.$#' - identifier: argument.type - count: 1 - path: src/Query.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\AbsFunction\:\:\$simpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/AbsFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitAndFunction\:\:\$firstArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/BitAndFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitAndFunction\:\:\$secondArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/BitAndFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitOrFunction\:\:\$firstArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/BitOrFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitOrFunction\:\:\$secondArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/BitOrFunction.php - - - - message: '#^Access to an undefined property Doctrine\\ORM\\Query\\AST\\Node\:\:\$value\.$#' - identifier: property.notFound - count: 1 - path: src/Query/AST/Functions/DateAddFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateAddFunction\:\:\$firstDateExpression \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/DateAddFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateAddFunction\:\:\$intervalExpression \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/DateAddFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateDiffFunction\:\:\$date1 \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/DateDiffFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateDiffFunction\:\:\$date2 \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/DateDiffFunction.php - - - - message: '#^Access to an undefined property Doctrine\\ORM\\Query\\AST\\Node\:\:\$value\.$#' - identifier: property.notFound - count: 1 - path: src/Query/AST/Functions/DateSubFunction.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Query/AST/Functions/IdentityFunction.php - - - - message: '#^Parameter \#1 \$joinColumn of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinColumnName\(\) expects array\{name\: string, referencedColumnName\: string, unique\?\: bool, quoted\?\: bool, fieldName\?\: string, onDelete\?\: string, columnDefinition\?\: string, nullable\?\: bool\}, array\{name\: string, referencedColumnName\: string, unique\?\: bool, quoted\?\: bool, fieldName\?\: string, onDelete\?\: string, columnDefinition\?\: string, nullable\?\: bool\}\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Query/AST/Functions/IdentityFunction.php - - - - message: '#^Parameter \#1 \$simpleArithmeticExpr of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkSimpleArithmeticExpression\(\) expects Doctrine\\ORM\\Query\\AST\\Node\|string, Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\|true given\.$#' - identifier: argument.type - count: 1 - path: src/Query/AST/Functions/LocateFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\LocateFunction\:\:\$simpleArithmeticExpression \(bool\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/LocateFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\ModFunction\:\:\$firstSimpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/ModFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\ModFunction\:\:\$secondSimpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/ModFunction.php - - - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Query/AST/Functions/SizeFunction.php - - - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Query/AST/Functions/SizeFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\SqrtFunction\:\:\$simpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/SqrtFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\SubstringFunction\:\:\$firstSimpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/SubstringFunction.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\SubstringFunction\:\:\$secondSimpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\|null\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/AST/Functions/SubstringFunction.php - - - - message: '#^Method Doctrine\\ORM\\Query\\AST\\IndexBy\:\:dispatch\(\) should return string but returns null\.$#' - identifier: return.type - count: 1 - path: src/Query/AST/IndexBy.php - - - - message: '#^Result of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkIndexBy\(\) \(void\) is used\.$#' - identifier: method.void - count: 1 - path: src/Query/AST/IndexBy.php - - - - message: '#^Default value of the parameter \#2 \$value \(array\{\}\) of method Doctrine\\ORM\\Query\\AST\\InstanceOfExpression\:\:__construct\(\) is incompatible with type non\-empty\-list\\.$#' - identifier: parameter.defaultValue - count: 1 - path: src/Query/AST/InstanceOfExpression.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinPathExpression\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Query/AST/JoinClassPathExpression.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinVariableDeclaration\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Query/AST/JoinVariableDeclaration.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkWhenClauseExpression\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Query/AST/SimpleWhenClause.php - - - - message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkWhenClauseExpression\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Query/AST/WhenClause.php - - - - message: '#^Property Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor\:\:\$queryCacheProfile \(Doctrine\\DBAL\\Cache\\QueryCacheProfile\) does not accept null\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/Exec/AbstractSqlExecutor.php - - - - message: '#^Argument of an invalid type list\\|string supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/Query/Exec/MultiTableDeleteExecutor.php - - - - message: '#^Cannot assign new offset to list\\|string\.$#' - identifier: offsetAssign.dimType - count: 1 - path: src/Query/Exec/MultiTableDeleteExecutor.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Exec\\MultiTableDeleteExecutor\:\:execute\(\) should return int but returns int\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Exec/MultiTableDeleteExecutor.php - - - - message: '#^Argument of an invalid type list\\|string supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/Query/Exec/MultiTableUpdateExecutor.php - - - - message: '#^Cannot assign new offset to list\\|string\.$#' - identifier: offsetAssign.dimType - count: 1 - path: src/Query/Exec/MultiTableUpdateExecutor.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Exec\\MultiTableUpdateExecutor\:\:execute\(\) should return int but returns int\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Exec/MultiTableUpdateExecutor.php - - - - message: '#^Parameter \#1 \$sql of method Doctrine\\DBAL\\Connection\:\:executeQuery\(\) expects string, list\\|string given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Exec/SingleSelectExecutor.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Exec\\SingleTableDeleteUpdateExecutor\:\:execute\(\) should return int but returns int\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Exec/SingleTableDeleteUpdateExecutor.php - - - - message: '#^Parameter \#1 \$sql of method Doctrine\\DBAL\\Connection\:\:executeStatement\(\) expects string, list\\|string given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Exec/SingleTableDeleteUpdateExecutor.php - - - - message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Andx\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' - identifier: property.phpDocType - count: 1 - path: src/Query/Expr/Andx.php - - - - message: '#^Cannot cast object\|string to string\.$#' - identifier: cast.string - count: 1 - path: src/Query/Expr/Composite.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Expr\\Func\:\:getArguments\(\) should return list\ but returns array\\.$#' - identifier: return.type - count: 1 - path: src/Query/Expr/Func.php - - - - message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Orx\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' - identifier: property.phpDocType - count: 1 - path: src/Query/Expr/Orx.php - - - - message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Select\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' - identifier: property.phpDocType - count: 1 - path: src/Query/Expr/Select.php - - - - message: '#^Property Doctrine\\ORM\\Query\\Filter\\SQLFilter\:\:\$parameters \(array\\) does not accept non\-empty\-array\\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/Filter/SQLFilter.php - - - - message: '#^@readonly property cannot have a default value\.$#' - identifier: property.readOnlyByPhpDocDefaultValue - count: 3 - path: src/Query/Parser.php - - - - message: '#^Call to an undefined method object\:\:parse\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Query/Parser.php - - - - message: '#^Class Doctrine\\ORM\\Query\\Parser has type alias QueryComponent with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/Parser.php - - - - message: '#^Instanceof between Doctrine\\ORM\\Query\\AST\\SelectStatement and Doctrine\\ORM\\Query\\AST\\SelectStatement will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:ArithmeticFactor\(\) should return Doctrine\\ORM\\Query\\AST\\ArithmeticFactor but returns Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:ArithmeticTerm\(\) should return Doctrine\\ORM\\Query\\AST\\ArithmeticTerm but returns Doctrine\\ORM\\Query\\AST\\ArithmeticFactor\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:CustomFunctionsReturningStrings\(\) should return Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode but returns object\.$#' - identifier: return.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:SimpleArithmeticExpression\(\) should return Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression but returns Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|string\.$#' - identifier: return.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/Parser.php - - - - message: '#^Parameter \#1 \$AST of method Doctrine\\ORM\\Query\\Parser\:\:processDeferredNewObjectExpressions\(\) expects Doctrine\\ORM\\Query\\AST\\SelectStatement, Doctrine\\ORM\\Query\\AST\\DeleteStatement\|Doctrine\\ORM\\Query\\AST\\SelectStatement\|Doctrine\\ORM\\Query\\AST\\UpdateStatement given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Parameter \#1 \$expected of method Doctrine\\ORM\\Query\\Parser\:\:syntaxError\(\) expects string, int\|string given\.$#' - identifier: argument.type - count: 3 - path: src/Query/Parser.php - - - - message: '#^Parameter \#1 \$expression of class Doctrine\\ORM\\Query\\AST\\NullComparisonExpression constructor expects Doctrine\\ORM\\Query\\AST\\Node, Doctrine\\ORM\\Query\\AST\\CoalesceExpression\|Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode\|Doctrine\\ORM\\Query\\AST\\InputParameter\|Doctrine\\ORM\\Query\\AST\\NullIfExpression\|Doctrine\\ORM\\Query\\AST\\PathExpression\|string given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Parameter \#2 \$pathExpression of class Doctrine\\ORM\\Query\\AST\\AggregateExpression constructor expects Doctrine\\ORM\\Query\\AST\\PathExpression\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression, Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Parameter \#2 \$stringPattern of class Doctrine\\ORM\\Query\\AST\\LikeExpression constructor expects Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode\|Doctrine\\ORM\\Query\\AST\\InputParameter\|Doctrine\\ORM\\Query\\AST\\Literal\|Doctrine\\ORM\\Query\\AST\\PathExpression, Doctrine\\ORM\\Query\\AST\\Node given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Query/Parser.php - - - - message: '#^Property Doctrine\\ORM\\Query\\AST\\ArithmeticExpression\:\:\$simpleArithmeticExpression \(Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\|null\) does not accept Doctrine\\ORM\\Query\\AST\\ArithmeticTerm\|Doctrine\\ORM\\Query\\AST\\SimpleArithmeticExpression\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/Parser.php - - - - message: '#^Property Doctrine\\ORM\\Query\\Parser\:\:\$queryComponents type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/Parser.php - - - - message: '#^Strict comparison using \=\=\= between 102 and 102 will always evaluate to true\.$#' - identifier: identical.alwaysTrue - count: 1 - path: src/Query/Parser.php - - - - message: '#^Unreachable statement \- code above always terminates\.$#' - identifier: deadCode.unreachable - count: 3 - path: src/Query/Parser.php - - - - message: '#^Method Doctrine\\ORM\\Query\\QueryException\:\:iterateWithFetchJoinCollectionNotAllowed\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/QueryException.php - - - - message: '#^Method Doctrine\\ORM\\Query\\QueryException\:\:iterateWithFetchJoinNotAllowed\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/QueryException.php - - - - message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:addNamedNativeQueryEntityResultMapping\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:addNamedNativeQueryMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:addNamedNativeQueryResultClassMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:addNamedNativeQueryResultSetMapping\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:isInheritanceSupported\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Offset ''columns'' on array\{name\: string, entities\: array, columns\: array\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Offset ''entities'' on array\{name\: string, entities\: array, columns\: array\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Parameter \#2 \$class of static method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfColumn\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\ORM\\Mapping\\ClassMetadataInfo given\.$#' - identifier: argument.type - count: 1 - path: src/Query/ResultSetMappingBuilder.php - - - - message: '#^Instanceof between Doctrine\\ORM\\Query\\AST\\DeleteStatement and Doctrine\\ORM\\Query\\AST\\DeleteStatement will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Query/SqlOutputWalker.php - - - - message: '#^Call to function is_string\(\) with Doctrine\\ORM\\Query\\AST\\Node will always evaluate to false\.$#' - identifier: function.impossibleType - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Cannot assign new offset to list\\|string\.$#' - identifier: offsetAssign.dimType - count: 2 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:generateClassTableInheritanceJoins\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getChildDiscriminatorsFromClassMetadata\(\) has parameter \$rootClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getQueryComponent\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getQueryComponents\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:setQueryComponent\(\) has parameter \$queryComponent with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:walkConditionalPrimary\(\) should return string but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/Query/SqlWalker.php - - - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 6 - path: src/Query/SqlWalker.php - - - - message: '#^Offset ''sourceToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Query/SqlWalker.php - - - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/Query/SqlWalker.php - - - - message: '#^Parameter \#1 \$condTerm of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkConditionalTerm\(\) expects Doctrine\\ORM\\Query\\AST\\ConditionalFactor\|Doctrine\\ORM\\Query\\AST\\ConditionalPrimary\|Doctrine\\ORM\\Query\\AST\\ConditionalTerm, Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional given\.$#' - identifier: argument.type - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Parameter \#1 \$identVariable of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkEntityIdentificationVariable\(\) expects string, Doctrine\\ORM\\Query\\AST\\Node\|string given\.$#' - identifier: argument.type - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Parameter \#2 \$fieldName of method Doctrine\\ORM\\Query\\ResultSetMapping\:\:addIndexBy\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$queryComponents type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/SqlWalker.php - - - - message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses \(array\\) does not accept non\-empty\-array\\.$#' - identifier: assign.propertyType - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Result of && is always false\.$#' - identifier: booleanAnd.alwaysFalse - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Strict comparison using \!\=\= between Doctrine\\ORM\\Query\\AST\\WhereClause and null will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Strict comparison using \=\=\= between null and null will always evaluate to true\.$#' - identifier: identical.alwaysTrue - count: 1 - path: src/Query/SqlWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalker\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalker\:\:getQueryComponents\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalker\:\:setQueryComponent\(\) has parameter \$queryComponent with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalker.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:_getQueryComponents\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:getExecutor\(\) should return Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor but returns null\.$#' - identifier: return.type - count: 1 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:getQueryComponents\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:setQueryComponent\(\) has parameter \$queryComponent with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Property Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:\$queryComponents type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerAdapter.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerChain\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerChain.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerChain\:\:getExecutor\(\) should return Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor but returns null\.$#' - identifier: return.type - count: 1 - path: src/Query/TreeWalkerChain.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerChain\:\:getQueryComponents\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerChain.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerChain\:\:setQueryComponent\(\) has parameter \$queryComponent with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerChain.php - - - - message: '#^Property Doctrine\\ORM\\Query\\TreeWalkerChain\:\:\$queryComponents type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Query/TreeWalkerChain.php - - - - message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerChainIterator\:\:key\(\) should return int but returns int\|string\|null\.$#' - identifier: return.type - count: 1 - path: src/Query/TreeWalkerChainIterator.php - - - - message: '#^Parameter \#2 \$value \(string\) of method Doctrine\\ORM\\Query\\TreeWalkerChainIterator\:\:offsetSet\(\) should be compatible with parameter \$value \(Doctrine\\ORM\\Query\\TreeWalker\) of method ArrayAccess\\:\:offsetSet\(\)$#' - identifier: method.childParameterType - count: 1 - path: src/Query/TreeWalkerChainIterator.php - - - - message: '#^Property Doctrine\\ORM\\Query\\TreeWalkerChainIterator\:\:\$walkers \(array\\>\) does not accept array\\.$#' - identifier: assign.propertyType - count: 2 - path: src/Query/TreeWalkerChainIterator.php - - - - message: '#^Argument of an invalid type array\\|object\|string\>\|object\|string\|false supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/QueryBuilder.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\Common\\\\Collections\\\\Criteria'' and ''orderings'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/QueryBuilder.php - - - - message: '#^Method Doctrine\\ORM\\QueryBuilder\:\:getParameter\(\) should return Doctrine\\ORM\\Query\\Parameter\|null but returns Doctrine\\ORM\\Query\\Parameter\|false\|null\.$#' - identifier: return.type - count: 1 - path: src/QueryBuilder.php - - - - message: '#^PHPDoc tag @param references unknown parameter\: \$where$#' - identifier: parameter.notFound - count: 2 - path: src/QueryBuilder.php - - - - message: '#^Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder\:\:add\(\) expects array\<''join''\|int\<0, max\>, array\\|string\>\|object\|string, non\-empty\-array\ given\.$#' - identifier: argument.type - count: 2 - path: src/QueryBuilder.php - - - - message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' - identifier: argument.type - count: 2 - path: src/QueryBuilder.php - - - - message: '#^Instanceof between Doctrine\\ORM\\EntityRepository and Doctrine\\ORM\\EntityRepository will always evaluate to true\.$#' - identifier: instanceof.alwaysTrue - count: 1 - path: src/Repository/DefaultRepositoryFactory.php - - - - message: '#^Method Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:createRepository\(\) return type with generic interface Doctrine\\Persistence\\ObjectRepository does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Repository/DefaultRepositoryFactory.php - - - - message: '#^Property Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:\$repositoryList with generic interface Doctrine\\Persistence\\ObjectRepository does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Repository/DefaultRepositoryFactory.php - - - - message: '#^Class Doctrine\\Common\\Cache\\ApcCache not found\.$#' - identifier: class.notFound - count: 1 - path: src/Tools/Console/Command/ClearCache/QueryCommand.php - - - - message: '#^Class Doctrine\\Common\\Cache\\XcacheCache not found\.$#' - identifier: class.notFound - count: 1 - path: src/Tools/Console/Command/ClearCache/QueryCommand.php + path: src/Persisters/Entity/CachedPersisterContext.php - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\ORM\\\\Configuration'' and ''getResultCacheImpl'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php + path: src/Persisters/Entity/EntityPersister.php - - message: '#^Class Doctrine\\Common\\Cache\\ApcCache not found\.$#' - identifier: class.notFound + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadManyToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php + path: src/Persisters/Entity/EntityPersister.php - - message: '#^Class Doctrine\\Common\\Cache\\XcacheCache not found\.$#' - identifier: class.notFound + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadOneToManyCollection\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php + path: src/Persisters/Entity/EntityPersister.php - - message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 2 + path: src/Persisters/Entity/JoinedSubclassPersister.php - - message: '#^Parameter \#1 \$filename of function is_readable expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php + path: src/Persisters/Entity/JoinedSubclassPersister.php - - message: '#^Parameter \#1 \$from of class Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema constructor expects list\\|string, array\ given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister\:\:fetchVersionAndNotUpsertableValues\(\) has parameter \$versionedClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php + path: src/Persisters/Entity/JoinedSubclassPersister.php - - message: '#^Parameter \#1 \$metadata of method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:setMetadata\(\) expects list\, non\-empty\-list\ given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\JoinedSubclassPersister\:\:getVersionedClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php + path: src/Persisters/Entity/JoinedSubclassPersister.php - - message: '#^Parameter \#2 \$destPath of method Doctrine\\ORM\\Tools\\Console\\Command\\ConvertDoctrine1SchemaCommand\:\:convertDoctrine1Schema\(\) expects string, string\|false given\.$#' + message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php + path: src/Persisters/Entity/JoinedSubclassPersister.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' identifier: property.notFound count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Persisters/Entity/SingleTablePersister.php - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Connection'' and ''createSchemaManager'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType + message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\SingleTablePersister\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Persisters/Entity/SingleTablePersister.php - - message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\SqlExpressionVisitor\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Persisters/SqlExpressionVisitor.php - - message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Persisters\\SqlValueVisitor\:\:getParamsAndTypes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Persisters/SqlValueVisitor.php - - message: '#^Parameter \#1 \$metadata of method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:setMetadata\(\) expects list\, non\-empty\-array\ given\.$#' + message: '#^Parameter \#3 \$className of static method Doctrine\\ORM\\Proxy\\Autoloader\:\:resolveFile\(\) expects class\-string, string given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Proxy/Autoloader.php - - message: '#^Parameter \#2 \$destPath of method Doctrine\\ORM\\Tools\\Console\\Command\\ConvertMappingCommand\:\:getExporter\(\) expects string, string\|false given\.$#' + message: '#^Parameter \#3 of closure expects class\-string, string given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php + path: src/Proxy/Autoloader.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' - identifier: property.notFound + message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\ but returns class\-string\\>\|class\-string\\.$#' + identifier: return.type count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php + path: src/Proxy/DefaultProxyClassNameResolver.php - - message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\ but returns string\.$#' + identifier: return.type count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php + path: src/Proxy/DefaultProxyClassNameResolver.php - - message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isEmbeddedClass\.$#' + identifier: property.notFound count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$metadatas of method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generate\(\) expects list\, non\-empty\-array\ given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isMappedSuperclass\.$#' + identifier: property.notFound count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generate\(\) expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Call to an undefined static method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyGhost\(\)\.$#' + identifier: staticMethod.notFound count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' - identifier: property.notFound + message: '#^Call to function is_bool\(\) with bool will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType count: 1 - path: src/Tools/Console/Command/GenerateProxiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Comparison operation "\<" between 0\|1\|2\|3\|4 and 0 is always false\.$#' + identifier: smaller.alwaysFalse count: 1 - path: src/Tools/Console/Command/GenerateProxiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Comparison operation "\>" between 0\|1\|2\|3\|4 and 4 is always false\.$#' + identifier: greater.alwaysFalse count: 1 - path: src/Tools/Console/Command/GenerateProxiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#2 \$proxyDir of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) expects string\|null, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#' + identifier: throws.unusedType count: 1 - path: src/Tools/Console/Command/GenerateProxiesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$customRepositoryClassName\.$#' - identifier: property.notFound + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/GenerateRepositoriesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Tools/Console/Command/GenerateRepositoriesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type with generic interface Doctrine\\ORM\\Proxy\\InternalProxy does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/GenerateRepositoriesCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:writeEntityRepositoryClass\(\) expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/GenerateRepositoriesCommand.php - - - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatMappings\(\) has parameter \$propertyMappings with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Tools/Console/Command/MappingDescribeCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatValue\(\) should return string but returns string\|false\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) has parameter \$classes with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/MappingDescribeCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateSerializeImpl\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/MappingDescribeCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$entityListeners of method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatEntityListeners\(\) expects list\, array\\> given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateUseLazyGhostTrait\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/MappingDescribeCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(class\-string\)\: bool\)\|null, Closure\(mixed\)\: \(0\|1\|false\) given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:getProxy\(\) return type with generic interface Doctrine\\ORM\\Proxy\\InternalProxy does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/MappingDescribeCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:createSchema\(\) expects list\, array\ given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:loadProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/SchemaTool/CreateCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getCreateSchemaSql\(\) expects list\, array\ given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:skipClass\(\) has parameter \$metadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Console/Command/SchemaTool/CreateCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:dropSchema\(\) expects list\, array\ given\.$#' + message: '#^Parameter \#1 \$class of method Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:flattenIdentifier\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\Persistence\\Mapping\\ClassMetadata given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/Command/SchemaTool/DropCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getDropSchemaSQL\(\) expects list\, array\ given\.$#' + message: '#^Parameter \#1 \$filename of function filemtime expects string, string\|false given\.$#' identifier: argument.type - count: 2 - path: src/Tools/Console/Command/SchemaTool/DropCommand.php + count: 1 + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getUpdateSchemaSql\(\) expects list\, array\ given\.$#' + message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/Command/SchemaTool/UpdateCommand.php + path: src/Proxy/ProxyFactory.php - - message: '#^Parameter \#1 \$helpers of class Symfony\\Component\\Console\\Helper\\HelperSet constructor expects array\, array\ given\.$#' + message: '#^Parameter \#3 \$newScope of static method Closure\:\:bind\(\) expects ''static''\|class\-string\|object\|null, string given\.$#' identifier: argument.type count: 1 - path: src/Tools/Console/ConsoleRunner.php + path: src/Proxy/ProxyFactory.php - - message: '#^Class Doctrine\\ORM\\Tools\\Console\\MetadataFilter extends generic class FilterIterator but does not specify its types\: TKey, TValue, TIterator$#' - identifier: missingType.generics + message: '#^Result of \|\| is always false\.$#' + identifier: booleanOr.alwaysFalse count: 1 - path: src/Tools/Console/MetadataFilter.php + path: src/Proxy/ProxyFactory.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:__construct\(\) has parameter \$metadata with generic class ArrayIterator but does not specify its types\: TKey, TValue$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\:\:processParameterMappings\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 - path: src/Tools/Console/MetadataFilter.php + path: src/Query.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:filter\(\) has parameter \$metadatas with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$parameters of method Doctrine\\ORM\\AbstractQuery\:\:toIterable\(\) expects array\\|Doctrine\\Common\\Collections\\ArrayCollection\, iterable\<\(int\|string\), mixed\> given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Console/MetadataFilter.php + path: src/Query.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:filter\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#2 \$sqlParams of method Doctrine\\ORM\\Query\:\:evictResultSetCache\(\) expects array\, list\ given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Console/MetadataFilter.php + path: src/Query.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:getInnerIterator\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitAndFunction\:\:\$firstArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/Console/MetadataFilter.php + path: src/Query/AST/Functions/BitAndFunction.php - - message: '#^Argument of an invalid type list\\|false supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitAndFunction\:\:\$secondArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/BitAndFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertColumn\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitOrFunction\:\:\$firstArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/BitOrFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertColumns\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\BitOrFunction\:\:\$secondArithmetic \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/BitOrFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertIndexes\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Query\\AST\\Node\:\:\$value\.$#' + identifier: property.notFound count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateAddFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertRelations\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateAddFunction\:\:\$firstDateExpression \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateAddFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertTableName\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateAddFunction\:\:\$intervalExpression \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateAddFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertToClassMetadataInfo\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateDiffFunction\:\:\$date1 \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateDiffFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:getMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\AST\\Functions\\DateDiffFunction\:\:\$date2 \(Doctrine\\ORM\\Query\\AST\\Node\) does not accept Doctrine\\ORM\\Query\\AST\\Node\|string\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateDiffFunction.php - - message: '#^Parameter \#1 \$className of method Doctrine\\ORM\\Tools\\ConvertDoctrine1Schema\:\:convertToClassMetadataInfo\(\) expects class\-string, \(int\|string\) given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Query\\AST\\Node\:\:\$value\.$#' + identifier: property.notFound count: 1 - path: src/Tools/ConvertDoctrine1Schema.php + path: src/Query/AST/Functions/DateSubFunction.php - - message: '#^Parameter \#1 \$input of static method Symfony\\Component\\Yaml\\Yaml\:\:parse\(\) expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 2 - path: src/Tools/ConvertDoctrine1Schema.php - - - - message: '#^Parameter \#1 \$string of function html_entity_decode expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Debug.php + path: src/Query/AST/Functions/IdentityFunction.php - - message: '#^Parameter \#1 \$stream of function fclose expects resource, resource\|false given\.$#' + message: '#^Parameter \#1 \$joinColumn of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinColumnName\(\) expects Doctrine\\ORM\\Mapping\\JoinColumnMapping, Doctrine\\ORM\\Mapping\\JoinColumnMapping\|false given\.$#' identifier: argument.type count: 1 - path: src/Tools/DebugUnitOfWorkListener.php + path: src/Query/AST/Functions/IdentityFunction.php - - message: '#^Parameter \#1 \$stream of function fwrite expects resource, resource\|false given\.$#' + message: '#^Parameter \#1 \$simpleArithmeticExpr of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkSimpleArithmeticExpression\(\) expects Doctrine\\ORM\\Query\\AST\\Node\|string, Doctrine\\ORM\\Query\\AST\\Node\|string\|true given\.$#' identifier: argument.type - count: 14 - path: src/Tools/DebugUnitOfWorkListener.php - - - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generate\(\) has parameter \$metadatas with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/Functions/LocateFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateAssociationMappingPropertyDocBlock\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/Functions/SizeFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateDiscriminatorColumnAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/Functions/SizeFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateDiscriminatorMapAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/Functions/SizeFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEmbeddableConstructor\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$association of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) expects Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping, Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/Functions/SizeFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Tools/EntityGenerator.php + message: '#^Parameter \#2 \$mode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getTrimExpression\(\) expects Doctrine\\DBAL\\Platforms\\TrimMode, Doctrine\\DBAL\\Platforms\\TrimMode\:\:BOTH\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:LEADING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:TRAILING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:UNSPECIFIED\|int given\.$#' + identifier: argument.type + count: 2 + path: src/Query/AST/Functions/TrimFunction.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityAssociationMappingProperties\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinPathExpression\(\)\.$#' + identifier: method.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/JoinClassPathExpression.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityBody\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinVariableDeclaration\(\)\.$#' + identifier: method.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/JoinVariableDeclaration.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityClass\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkWhenClauseExpression\(\)\.$#' + identifier: method.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/SimpleWhenClause.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityClassName\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkWhenClauseExpression\(\)\.$#' + identifier: method.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/AST/WhenClause.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityConstructor\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Argument of an invalid type list\\|string supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableDeleteExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityDocBlock\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Cannot assign new offset to list\\|string\.$#' + identifier: offsetAssign.dimType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableDeleteExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityEmbeddedProperties\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\\Exec\\MultiTableDeleteExecutor\:\:execute\(\) should return int but returns int\|string\.$#' + identifier: return.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableDeleteExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityFieldMappingProperties\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Argument of an invalid type list\\|string supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableUpdateExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityLifecycleCallbackMethods\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\\Exec\\MultiTableUpdateExecutor\:\:execute\(\) should return int but returns int\|string\.$#' + identifier: return.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableUpdateExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityListenerAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#3 \$types of method Doctrine\\DBAL\\Connection\:\:executeStatement\(\) expects array\\|string, Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|Doctrine\\DBAL\\Types\\Type\|string\>, list\ given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/MultiTableUpdateExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityNamespace\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$sql of method Doctrine\\DBAL\\Connection\:\:executeQuery\(\) expects string, list\\|string given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/SingleSelectExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityStubMethod\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\\Exec\\SingleTableDeleteUpdateExecutor\:\:execute\(\) should return int but returns int\|string\.$#' + identifier: return.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/SingleTableDeleteUpdateExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateEntityStubMethods\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$sql of method Doctrine\\DBAL\\Connection\:\:executeStatement\(\) expects string, list\\|string given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Exec/SingleTableDeleteUpdateExecutor.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateFieldMappingPropertyDocBlock\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Andx\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' + identifier: property.phpDocType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Expr/Andx.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateInheritanceAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\\Expr\\Func\:\:getArguments\(\) should return list\ but returns array\\.$#' + identifier: return.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Expr/Func.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateLifecycleCallbackMethod\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Orx\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' + identifier: property.phpDocType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Expr/Orx.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateTableAnnotation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^PHPDoc type array\ of property Doctrine\\ORM\\Query\\Expr\\Select\:\:\$allowedClasses is not covariant with PHPDoc type list\ of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#' + identifier: property.phpDocType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Expr/Select.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generateUpdatedEntityClass\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\Filter\\SQLFilter\:\:\$parameters \(array\\) does not accept non\-empty\-array\\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Filter/SQLFilter.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:getClassName\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\Query\\ParameterTypeInferer\:\:inferType\(\) never returns int so it can be removed from the return type\.$#' + identifier: return.unusedType count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/ParameterTypeInferer.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:getNamespace\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Tools/EntityGenerator.php + message: '#^@readonly property cannot have a default value\.$#' + identifier: property.readOnlyByPhpDocDefaultValue + count: 3 + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:getTraits\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Call to an undefined method object\:\:parse\(\)\.$#' + identifier: method.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:getTraits\(\) should return array\\> but returns array\\.$#' + message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:CustomFunctionsReturningStrings\(\) should return Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode but returns object\.$#' identifier: return.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:hasMethod\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Query\\Parser\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:hasNamespace\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$AST of method Doctrine\\ORM\\Query\\Parser\:\:processDeferredNewObjectExpressions\(\) expects Doctrine\\ORM\\Query\\AST\\SelectStatement, Doctrine\\ORM\\Query\\AST\\DeleteStatement\|Doctrine\\ORM\\Query\\AST\\SelectStatement\|Doctrine\\ORM\\Query\\AST\\UpdateStatement given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:hasProperty\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/Tools/EntityGenerator.php + message: '#^Parameter \#1 \$expected of method Doctrine\\ORM\\Query\\Parser\:\:syntaxError\(\) expects string, int\|string given\.$#' + identifier: argument.type + count: 3 + path: src/Query/Parser.php - - message: '#^Method Doctrine\\ORM\\Tools\\EntityGenerator\:\:writeEntityClass\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$expression of class Doctrine\\ORM\\Query\\AST\\ParenthesisExpression constructor expects Doctrine\\ORM\\Query\\AST\\Node, Doctrine\\ORM\\Query\\AST\\Node\|string given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Offset ''allocationSize'' on array\{sequenceName\: string, allocationSize\: string, initialValue\: string, quoted\?\: mixed\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset + message: '#^Parameter \#2 \$stringPattern of class Doctrine\\ORM\\Query\\AST\\LikeExpression constructor expects Doctrine\\ORM\\Query\\AST\\Functions\\FunctionNode\|Doctrine\\ORM\\Query\\AST\\InputParameter\|Doctrine\\ORM\\Query\\AST\\Literal\|Doctrine\\ORM\\Query\\AST\\PathExpression, Doctrine\\ORM\\Query\\AST\\Node given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Offset ''initialValue'' on array\{sequenceName\: string, allocationSize\: string, initialValue\: string, quoted\?\: mixed\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset + message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' + identifier: argument.type count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Offset ''name'' on array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\, quoted\?\: bool\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset + message: '#^Strict comparison using \=\=\= between 102 and 102 will always evaluate to true\.$#' + identifier: identical.alwaysTrue count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Offset ''sequenceName'' on array\{sequenceName\: string, allocationSize\: string, initialValue\: string, quoted\?\: mixed\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset - count: 1 - path: src/Tools/EntityGenerator.php + message: '#^Unreachable statement \- code above always terminates\.$#' + identifier: deadCode.unreachable + count: 2 + path: src/Query/Parser.php - - message: '#^Parameter \#1 \$haystack of function strrpos expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Using nullsafe property access "\?\-\>position" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' + identifier: nullsafe.neverNull count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/Parser.php - - message: '#^Parameter \#1 \$objectOrClass of class ReflectionClass constructor expects class\-string\\|T of object, string given\.$#' - identifier: argument.type - count: 3 - path: src/Tools/EntityGenerator.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 2 + path: src/Query/ResultSetMappingBuilder.php - - message: '#^Parameter \#1 \$src of method Doctrine\\ORM\\Tools\\EntityGenerator\:\:parseTokensInEntityFile\(\) expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Query\\ResultSetMappingBuilder\:\:isInheritanceSupported\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/ResultSetMappingBuilder.php - - message: '#^Parameter \#1 \$string of function substr expects string, string\|false given\.$#' - identifier: argument.type + message: '#^Instanceof between Doctrine\\ORM\\Query\\AST\\DeleteStatement and Doctrine\\ORM\\Query\\AST\\DeleteStatement will always evaluate to true\.$#' + identifier: instanceof.alwaysTrue count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/SqlOutputWalker.php - - message: '#^Parameter \#1 \.\.\.\$arg1 of function max expects non\-empty\-array, list\\> given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 2 + path: src/Query/SqlWalker.php + + - + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound count: 1 - path: src/Tools/EntityGenerator.php + path: src/Query/SqlWalker.php - - message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$sourceToTargetKeyColumns\.$#' + identifier: property.notFound count: 2 - path: src/Tools/EntityGenerator.php + path: src/Query/SqlWalker.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$lifecycleCallbacks \(array\\>\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 1 - path: src/Tools/EntityGenerator.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound + count: 3 + path: src/Query/SqlWalker.php - - message: '#^Parameter \#1 \$fullClassName of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:generateClassName\(\) expects class\-string, string given\.$#' - identifier: argument.type + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 - path: src/Tools/EntityRepositoryGenerator.php + path: src/Query/SqlWalker.php - - message: '#^Parameter \#1 \$fullClassName of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:generateEntityRepositoryNamespace\(\) expects class\-string, string given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/EntityRepositoryGenerator.php + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 6 + path: src/Query/SqlWalker.php - - message: '#^Parameter \#1 \$fullClassName of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:getClassNamespace\(\) expects class\-string, string given\.$#' - identifier: argument.type + message: '#^Cannot assign new offset to list\\|string\.$#' + identifier: offsetAssign.dimType + count: 2 + path: src/Query/SqlWalker.php + + - + message: '#^Match arm comparison between 3 and 3 is always true\.$#' + identifier: match.alwaysTrue count: 1 - path: src/Tools/EntityRepositoryGenerator.php + path: src/Query/SqlWalker.php - - message: '#^Property Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:\$repositoryName \(class\-string\|null\) does not accept string\.$#' - identifier: assign.propertyType + message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:generateClassTableInheritanceJoins\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/EntityRepositoryGenerator.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:generateFilterConditionSQL\(\) has parameter \$targetEntity with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Event/GenerateSchemaTableEventArgs.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getChildDiscriminatorsFromClassMetadata\(\) has parameter \$rootClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Event/GenerateSchemaTableEventArgs.php + path: src/Query/SqlWalker.php - - message: '#^Property Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs\:\:\$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Query\\SqlWalker\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Event/GenerateSchemaTableEventArgs.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\ClassMetadataExporter\:\:getExporter\(\) should return Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter but returns object\.$#' - identifier: return.type + message: '#^Parameter \#1 \$association of method Doctrine\\ORM\\Mapping\\QuoteStrategy\:\:getJoinTableName\(\) expects Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping, Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' + identifier: argument.type + count: 2 + path: src/Query/SqlWalker.php + + - + message: '#^Parameter \#1 \$condTerm of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkConditionalTerm\(\) expects Doctrine\\ORM\\Query\\AST\\ConditionalFactor\|Doctrine\\ORM\\Query\\AST\\ConditionalPrimary\|Doctrine\\ORM\\Query\\AST\\ConditionalTerm, Doctrine\\ORM\\Query\\AST\\Phase2OptimizableConditional given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/ClassMetadataExporter.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:_generateOutputPath\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$identVariable of method Doctrine\\ORM\\Query\\SqlWalker\:\:walkEntityIdentificationVariable\(\) expects string, Doctrine\\ORM\\Query\\AST\\Node\|string given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/AbstractExporter.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:_getIdGeneratorTypeString\(\) should return string but return statement is missing\.$#' - identifier: return.missing + message: '#^Parameter \#2 \$fieldName of method Doctrine\\ORM\\Query\\ResultSetMapping\:\:addIndexBy\(\) expects string, string\|false given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/AbstractExporter.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:exportClassMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses \(array\\) does not accept non\-empty\-array\\.$#' + identifier: assign.propertyType count: 1 - path: src/Tools/Export/Driver/AbstractExporter.php + path: src/Query/SqlWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:setMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/AbstractExporter.php + path: src/Query/SqlWalker.php - - message: '#^Property Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:\$_metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Query\\TreeWalkerAdapter\:\:getMetadataForDqlAlias\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/AbstractExporter.php + path: src/Query/TreeWalkerAdapter.php + + - + message: '#^Argument of an invalid type array\\|object\|string\>\|object\|string\|false supplied for foreach, only iterables are supported\.$#' + identifier: foreach.nonIterable + count: 1 + path: src/QueryBuilder.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AnnotationExporter\:\:_generateOutputPath\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Method Doctrine\\ORM\\QueryBuilder\:\:getParameter\(\) should return Doctrine\\ORM\\Query\\Parameter\|null but returns Doctrine\\ORM\\Query\\Parameter\|false\|null\.$#' + identifier: return.type count: 1 - path: src/Tools/Export/Driver/AnnotationExporter.php + path: src/QueryBuilder.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\AnnotationExporter\:\:exportClassMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder\:\:add\(\) expects array\<''join''\|int\<0, max\>, array\\|string\>\|object\|string, array\{Doctrine\\ORM\\Query\\Expr\\Andx\|Doctrine\\ORM\\Query\\Expr\\Orx\}\|Doctrine\\ORM\\Query\\Expr\\Andx given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/AnnotationExporter.php + path: src/QueryBuilder.php - - message: '#^If condition is always true\.$#' - identifier: if.alwaysTrue + message: '#^Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder\:\:add\(\) expects array\<''join''\|int\<0, max\>, array\\|string\>\|object\|string, array\{Doctrine\\ORM\\Query\\Expr\\Composite\}\|Doctrine\\ORM\\Query\\Expr\\Andx given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/QueryBuilder.php + + - + message: '#^Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder\:\:add\(\) expects array\<''join''\|int\<0, max\>, array\\|string\>\|object\|string, non\-empty\-array\ given\.$#' + identifier: argument.type + count: 2 + path: src/QueryBuilder.php + + - + message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' + identifier: argument.type + count: 2 + path: src/QueryBuilder.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\PhpExporter\:\:exportClassMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:createRepository\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/Repository/DefaultRepositoryFactory.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\PhpExporter\:\:processEntityListeners\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Property Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:\$repositoryList with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/Repository/DefaultRepositoryFactory.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' + identifier: property.notFound count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/Tools/Console/Command/GenerateProxiesCommand.php - - message: '#^Offset ''orphanRemoval'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/Tools/Console/Command/GenerateProxiesCommand.php - - message: '#^Parameter \#1 \$policy of method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:_getChangeTrackingPolicyString\(\) expects 1\|2\|3, int\\|int\<1, max\> given\.$#' + message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#' identifier: argument.type count: 1 - path: src/Tools/Export/Driver/PhpExporter.php + path: src/Tools/Console/Command/GenerateProxiesCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\XmlExporter\:\:asXml\(\) should return string but returns string\|false\.$#' - identifier: return.type + message: '#^Parameter \#2 \$proxyDir of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) expects string\|null, string\|false given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/GenerateProxiesCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\XmlExporter\:\:exportClassMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/MappingDescribeCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\XmlExporter\:\:exportSequenceInformation\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$entityListeners of method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatEntityListeners\(\) expects list\, array\\> given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/MappingDescribeCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\XmlExporter\:\:generateEntityListenerXml\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(class\-string\)\: bool\)\|null, Closure\(mixed\)\: \(0\|1\|false\) given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/MappingDescribeCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\XmlExporter\:\:processEntityListeners\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:createSchema\(\) expects list\, array\ given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/SchemaTool/CreateCommand.php - - message: '#^Offset ''name'' on array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\, quoted\?\: bool\} in isset\(\) always exists and is not nullable\.$#' - identifier: isset.offset + message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getCreateSchemaSql\(\) expects list\, array\ given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/SchemaTool/CreateCommand.php - - message: '#^Parameter \#1 \$policy of method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:_getChangeTrackingPolicyString\(\) expects 1\|2\|3, int given\.$#' + message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:dropSchema\(\) expects list\, array\ given\.$#' identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/SchemaTool/DropCommand.php - - message: '#^Parameter \#1 \$source of method DOMDocument\:\:loadXML\(\) expects string, string\|false given\.$#' + message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getDropSchemaSQL\(\) expects list\, array\ given\.$#' identifier: argument.type - count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + count: 2 + path: src/Tools/Console/Command/SchemaTool/DropCommand.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$lifecycleCallbacks \(array\\>\) in isset\(\) is not nullable\.$#' - identifier: isset.property + message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getUpdateSchemaSql\(\) expects list\, array\ given\.$#' + identifier: argument.type count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/Command/SchemaTool/UpdateCommand.php - - message: '#^Right side of && is always true\.$#' - identifier: booleanAnd.rightAlwaysTrue + message: '#^Class Doctrine\\ORM\\Tools\\Console\\MetadataFilter extends generic class FilterIterator but does not specify its types\: TKey, TValue, TIterator$#' + identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/XmlExporter.php + path: src/Tools/Console/MetadataFilter.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\YamlExporter\:\:exportClassMetadata\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:__construct\(\) has parameter \$metadata with generic class ArrayIterator but does not specify its types\: TKey, TValue$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Console/MetadataFilter.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\YamlExporter\:\:processEntityListeners\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:filter\(\) has parameter \$metadatas with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Console/MetadataFilter.php - - message: '#^Method Doctrine\\ORM\\Tools\\Export\\Driver\\YamlExporter\:\:processEntityListeners\(\) should return array\{entityListeners\: array\\>\} but returns array\\.$#' - identifier: return.type + message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:filter\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Console/MetadataFilter.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Tools\\Console\\MetadataFilter\:\:getInnerIterator\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/YamlExporter.php - - - - message: '#^Offset ''orphanRemoval'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 2 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Console/MetadataFilter.php - - message: '#^Parameter \#1 \$array of method Doctrine\\ORM\\Tools\\Export\\Driver\\YamlExporter\:\:processEntityListenerConfig\(\) expects array\{entityListeners\: array\\>\}, non\-empty\-array\ given\.$#' + message: '#^Parameter \#1 \$string of function html_entity_decode expects string, string\|false given\.$#' identifier: argument.type count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Debug.php - - message: '#^Parameter \#1 \$policy of method Doctrine\\ORM\\Tools\\Export\\Driver\\AbstractExporter\:\:_getChangeTrackingPolicyString\(\) expects 1\|2\|3, int\\|int\<2, max\> given\.$#' + message: '#^Parameter \#1 \$stream of function fclose expects resource, resource\|false given\.$#' identifier: argument.type count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/DebugUnitOfWorkListener.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$lifecycleCallbacks \(array\\>\) in isset\(\) is not nullable\.$#' - identifier: isset.property - count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + message: '#^Parameter \#1 \$stream of function fwrite expects resource, resource\|false given\.$#' + identifier: argument.type + count: 14 + path: src/Tools/DebugUnitOfWorkListener.php - - message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:\$table \(array\\) on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.property + message: '#^Method Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Export/Driver/YamlExporter.php + path: src/Tools/Event/GenerateSchemaTableEventArgs.php - - message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\CountOutputWalker\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Tools/Pagination/CountOutputWalker.php + message: '#^Method Doctrine\\ORM\\Tools\\Event\\GenerateSchemaTableEventArgs\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#' + identifier: missingType.generics + count: 1 + path: src/Tools/Event/GenerateSchemaTableEventArgs.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 path: src/Tools/Pagination/CountOutputWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryOutputWalker\:\:__construct\(\) has parameter \$queryComponents with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Tools/Pagination/LimitSubqueryOutputWalker.php - - - - message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryOutputWalker\:\:walkSubSelect\(\) has no return type specified\.$#' - identifier: missingType.return + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 path: src/Tools/Pagination/LimitSubqueryOutputWalker.php - - message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryOutputWalker\:\:walkSubSelect\(\) has parameter \$subselect with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\LimitSubqueryOutputWalker\:\:walkSelectStatement\(\) should return string but returns list\\|string\.$#' + identifier: return.type count: 1 path: src/Tools/Pagination/LimitSubqueryOutputWalker.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' + identifier: argument.type count: 1 path: src/Tools/Pagination/LimitSubqueryOutputWalker.php - - message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\Paginator\:\:count\(\) should return int\<0, max\> but returns int\.$#' + identifier: return.type count: 1 - path: src/Tools/Pagination/LimitSubqueryOutputWalker.php + path: src/Tools/Pagination/Paginator.php - message: '#^PHPDoc tag @var for variable \$parameters contains generic interface Doctrine\\Common\\Collections\\Collection but does not specify its types\: TKey, T$#' @@ -5085,12 +3036,6 @@ parameters: count: 1 path: src/Tools/ResolveTargetEntityListener.php - - - message: '#^Method Doctrine\\ORM\\Tools\\ResolveTargetEntityListener\:\:remapAssociation\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Tools/ResolveTargetEntityListener.php - - message: '#^Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\\:\:setMetadataFor\(\) expects class\-string, \(int\|string\) given\.$#' identifier: argument.type @@ -5098,49 +3043,13 @@ parameters: path: src/Tools/ResolveTargetEntityListener.php - - message: '#^Call to function is_numeric\(\) with int\<0, max\> will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Connection'' and ''createSchemaManager'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform'' and ''getAlterSchemaSQL'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\AbstractSchemaManager'' and ''introspectSchema'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\AbstractSchemaManager and ''createComparator'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\AbstractSchemaManager and ''introspectSchema'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\Index and ''isFulfilledBy'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 3 path: src/Tools/SchemaTool.php - - message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\Table and ''modifyColumn'' will always evaluate to true\.$#' + message: '#^Call to function is_numeric\(\) with int\<0, max\> will always evaluate to true\.$#' identifier: function.alreadyNarrowedType count: 1 path: src/Tools/SchemaTool.php @@ -5181,12 +3090,6 @@ parameters: count: 1 path: src/Tools/SchemaTool.php - - - message: '#^Method Doctrine\\ORM\\Tools\\SchemaTool\:\:gatherRelationJoinColumns\(\) has parameter \$mapping with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/Tools/SchemaTool.php - - message: '#^Method Doctrine\\ORM\\Tools\\SchemaTool\:\:gatherRelationsSql\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -5247,54 +3150,12 @@ parameters: count: 1 path: src/Tools/SchemaTool.php - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 3 - path: src/Tools/SchemaTool.php - - - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Offset ''name'' might not exist on array\{name\: string, fieldName\: string, type\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|array\{type\: ''string'', length\: 255\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Offset ''precision'' might not exist on array\{type\: ''decimal'', fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Tools/SchemaTool.php - - - - message: '#^Offset ''scale'' might not exist on array\{type\: ''decimal'', fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/Tools/SchemaTool.php - - message: '#^Parameter \#1 \$classes of method Doctrine\\ORM\\Tools\\SchemaTool\:\:getUpdateSchemaSql\(\) expects list\, array\ given\.$#' identifier: argument.type count: 1 path: src/Tools/SchemaTool.php - - - message: '#^Parameter \#1 \$mapping of method Doctrine\\ORM\\Tools\\SchemaTool\:\:gatherColumnOptions\(\) expects array\{name\: string, fieldName\: string, type\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|array\{name\: string, referencedColumnName\: string, unique\?\: bool, quoted\?\: bool, fieldName\?\: string, onDelete\?\: string, columnDefinition\?\: string, nullable\?\: bool\}\|array\{type\: string, fieldName\: string, columnName\: string, length\?\: int, id\?\: bool, nullable\?\: bool, notInsertable\?\: bool, notUpdatable\?\: bool, \.\.\.\}, array\{name\: string, fieldName\: string, type\: string, length\?\: int, columnDefinition\?\: string\|null, enumType\?\: class\-string\\|null, options\?\: array\\}\|array\{type\: ''string'', length\: 255\} given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/SchemaTool.php - - message: '#^Property Doctrine\\ORM\\Tools\\SchemaTool\:\:\$schemaManager with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager does not specify its types\: T$#' identifier: missingType.generics @@ -5302,58 +3163,58 @@ parameters: path: src/Tools/SchemaTool.php - - message: '#^Method Doctrine\\ORM\\Tools\\SchemaValidator\:\:validateClass\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$inversedBy\.$#' + identifier: property.notFound + count: 2 path: src/Tools/SchemaValidator.php - - message: '#^Method Doctrine\\ORM\\Tools\\SchemaValidator\:\:validatePropertiesTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadataInfo but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 path: src/Tools/SchemaValidator.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 6 path: src/Tools/SchemaValidator.php - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound + count: 9 path: src/Tools/SchemaValidator.php - - message: '#^Offset ''relationToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToSourceKeyColumns\.$#' + identifier: property.notFound count: 1 path: src/Tools/SchemaValidator.php - - message: '#^Offset ''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToTargetKeyColumns\.$#' + identifier: property.notFound count: 1 path: src/Tools/SchemaValidator.php - - message: '#^Strict comparison using \!\=\= between array and null will always evaluate to true\.$#' - identifier: notIdentical.alwaysTrue + message: '#^Call to an undefined method Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:orderBy\(\)\.$#' + identifier: method.notFound count: 1 path: src/Tools/SchemaValidator.php - - message: '#^Parameter \#1 \$paths of method Doctrine\\ORM\\Configuration\:\:newDefaultAnnotationDriver\(\) expects list\\|string, array\ given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Tools\\SchemaValidator\:\:validateClass\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Setup.php + path: src/Tools/SchemaValidator.php - - message: '#^Parameter \#1 \$pool of static method Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider\:\:wrap\(\) expects Psr\\Cache\\CacheItemPoolInterface, Doctrine\\Common\\Cache\\ApcuCache\|Doctrine\\Common\\Cache\\ArrayCache\|Doctrine\\Common\\Cache\\MemcachedCache\|Doctrine\\Common\\Cache\\RedisCache\|Symfony\\Component\\Cache\\Adapter\\ApcuAdapter\|Symfony\\Component\\Cache\\Adapter\\ArrayAdapter\|Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter\|Symfony\\Component\\Cache\\Adapter\\RedisAdapter given\.$#' - identifier: argument.type + message: '#^Method Doctrine\\ORM\\Tools\\SchemaValidator\:\:validatePropertiesTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 - path: src/Tools/Setup.php + path: src/Tools/SchemaValidator.php - message: '#^ Parameter \#3 \$changeSet of class Doctrine\\ORM\\Event\\PreUpdateEventArgs constructor is passed by reference, so it expects variables only$#' @@ -5362,32 +3223,32 @@ parameters: path: src/UnitOfWork.php - - message: '#^Call to function is_object\(\) with object will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$inversedBy\.$#' + identifier: property.notFound + count: 1 path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:computeAssociationChanges\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 3 path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:convertSingleFieldIdentifierToPHPValue\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound count: 1 path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:doMerge\(\) has parameter \$assoc with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$targetToSourceKeyColumns\.$#' + identifier: property.notFound + count: 1 path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:doMerge\(\) has parameter \$prevManagedCopy with no type specified\.$#' - identifier: missingType.parameter + message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:convertSingleFieldIdentifierToPHPValue\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/UnitOfWork.php @@ -5397,12 +3258,6 @@ parameters: count: 1 path: src/UnitOfWork.php - - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:getCollectionPersister\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:getEntityChangeSet\(\) return type with generic class Doctrine\\ORM\\PersistentCollection does not specify its types\: TKey, T$#' identifier: missingType.generics @@ -5427,12 +3282,6 @@ parameters: count: 1 path: src/UnitOfWork.php - - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:newInstance\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 - path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:normalizeIdentifier\(\) has parameter \$targetClass with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics @@ -5458,45 +3307,21 @@ parameters: path: src/UnitOfWork.php - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:updateAssociationWithMergedEntity\(\) has parameter \$association with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/UnitOfWork.php - - - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:updateAssociationWithMergedEntity\(\) has parameter \$managedCopy with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/UnitOfWork.php - - - - message: '#^Method Doctrine\\ORM\\UnitOfWork\:\:updateAssociationWithMergedEntity\(\) has parameter \$previousManagedCopy with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/UnitOfWork.php - - - - message: '#^Offset ''orphanRemoval'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound - count: 1 - path: src/UnitOfWork.php - - - - message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^PHPDoc tag @phpstan\-assert\-if\-true for \$obj contains generic interface Doctrine\\ORM\\Proxy\\InternalProxy but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/UnitOfWork.php - - message: '#^PHPDoc tag @phpstan\-assert\-if\-true for \$obj contains generic interface Doctrine\\ORM\\Proxy\\InternalProxy but does not specify its types\: T$#' - identifier: missingType.generics - count: 1 + message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\PersistentCollection\<\(int\|string\),mixed\>\:\:setOwner\(\) expects Doctrine\\ORM\\Mapping\\AssociationMapping&Doctrine\\ORM\\Mapping\\ToManyAssociationMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' + identifier: argument.type + count: 4 path: src/UnitOfWork.php - - message: '#^Parameter \#1 \$className of method Doctrine\\ORM\\EntityManagerInterface\:\:getClassMetadata\(\) expects string, class\-string\|false given\.$#' + message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\PersistentCollection\<\*NEVER\*,\*NEVER\*\>\:\:setOwner\(\) expects Doctrine\\ORM\\Mapping\\AssociationMapping&Doctrine\\ORM\\Mapping\\ToManyAssociationMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#' identifier: argument.type - count: 2 + count: 1 path: src/UnitOfWork.php - @@ -5529,12 +3354,6 @@ parameters: count: 1 path: src/UnitOfWork.php - - - message: '#^Property Doctrine\\ORM\\UnitOfWork\:\:\$nonCascadedNewDetectedEntities type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 5 - path: src/UnitOfWork.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' identifier: property.notFound @@ -5553,6 +3372,12 @@ parameters: count: 1 path: src/Utility/HierarchyDiscriminatorResolver.php + - + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound + count: 1 + path: src/Utility/IdentifierFlattener.php + - message: '#^Method Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:__construct\(\) has parameter \$metadataFactory with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadataFactory but does not specify its types\: T$#' identifier: missingType.generics @@ -5566,31 +3391,37 @@ parameters: path: src/Utility/IdentifierFlattener.php - - message: '#^Offset ''joinColumns'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\JoinTableMapping\|Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 - path: src/Utility/IdentifierFlattener.php + path: src/Utility/PersisterHelper.php - - message: '#^Property Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:\$metadataFactory with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadataFactory does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#' + identifier: property.notFound count: 1 - path: src/Utility/IdentifierFlattener.php + path: src/Utility/PersisterHelper.php - - message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfColumn\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' - identifier: missingType.generics + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinTable\.$#' + identifier: property.notFound + count: 2 + path: src/Utility/PersisterHelper.php + + - + message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#' + identifier: property.notFound count: 1 path: src/Utility/PersisterHelper.php - - message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfField\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfColumn\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' identifier: missingType.generics count: 1 path: src/Utility/PersisterHelper.php - - message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#' - identifier: offsetAccess.notFound + message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfField\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#' + identifier: missingType.generics count: 1 path: src/Utility/PersisterHelper.php diff --git a/phpstan-dbal2.neon b/phpstan-dbal2.neon deleted file mode 100644 index dbefaf002c2..00000000000 --- a/phpstan-dbal2.neon +++ /dev/null @@ -1,111 +0,0 @@ -includes: - - phpstan-baseline.neon - - phpstan-params.neon - -parameters: - reportUnmatchedIgnoredErrors: false - - ignoreErrors: - # PHPStan doesn't understand our method_exists() safeguards. - - '/Call to function method_exists.*/' - - '/Call to an undefined method Doctrine\\DBAL\\Connection::createSchemaManager\(\)\./' - # Class name will change in DBAL 3. - - '/^Class Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform not found\.$/' - - '/^Class Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform not found\.$/' - - '/^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$/' - - - message: '/Doctrine\\DBAL\\Platforms\\MyS(ql|QL)Platform/' - path: src/Mapping/ClassMetadataFactory.php - - # Forward compatibility for DBAL 3.5 - - '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getAlterSchemaSQL\(\).$/' - - # Forward compatibility for DBAL 3.4 - - '/^Call to an undefined method Doctrine\\DBAL\\Cache\\QueryCacheProfile::[gs]etResultCache\(\)\.$/' - - - message: '/^Call to an undefined static method Doctrine\\DBAL\\Configuration::[gs]etResultCache\(\)\.$/' - path: src/Configuration.php - - - message: '/^Parameter #3 \$resultCache of class Doctrine\\DBAL\\Cache\\QueryCacheProfile constructor/' - path: src/AbstractQuery.php - - - message: '/^Parameter #2 \$\w+ of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getDateAdd\w+Expression\(\) expects int, string given\.$/' - path: src/Query/AST/Functions/DateAddFunction.php - - - message: '/^Parameter #2 \$\w+ of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getDateSub\w+Expression\(\) expects int, string given\.$/' - path: src/Query/AST/Functions/DateSubFunction.php - - # False positive - - - message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/' - count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php - # See https://github.com/doctrine/dbal/pull/5129 - - - message: '/^Parameter #3 \$startPos of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getLocateExpression\(\) expects int\|false, string given\.$/' - count: 1 - path: src/Query/AST/Functions/LocateFunction.php - - # Won't get fixed in DBAL 2 - - - message: '#.*deleteItem.*expects string.*#' - count: 1 - path: src/Query.php - - - - message: '#.*get(Drop|Create)SchemaS(ql|QL).*should return list.*but returns array.*#' - count: 2 - path: src/Tools/SchemaTool.php - - - - message: "#^Parameter \\#2 \\$start of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int, string given\\.$#" - count: 1 - path: src/Query/AST/Functions/SubstringFunction.php - - - - message: "#^Parameter \\#3 \\$length of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int\\|null, string\\|null given\\.$#" - count: 1 - path: src/Query/AST/Functions/SubstringFunction.php - - - - message: '#^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$#' - count: 2 - path: src/Mapping/ClassMetadataFactory.php - - - '~^Call to deprecated method getSQLResultCasing\(\) of class Doctrine\\DBAL\\Platforms\\AbstractPlatform\.$~' - - - message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ImportCommand\:~' - path: src/Tools/Console/ConsoleRunner.php - - - - message: '#^Method Doctrine\\ORM\\AbstractQuery\:\:getHydrationCacheId\(\) should return array\{string, string\} but returns array\\.$#' - path: src/AbstractQuery.php - - - - message: '#^Method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:\w+\(\) has parameter \$stmt with no value type specified in iterable type Doctrine\\DBAL\\Driver\\ResultStatement\.$#' - path: src/Internal/Hydration/AbstractHydrator.php - - - - message: '#^Parameter \#1 \$key of method Psr\\Cache\\CacheItemPoolInterface\:\:deleteItem\(\) expects string, string\|false given\.$#' - path: src/Query - - - # Symfony cache supports passing a key prefix to the clear method. - - '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/' - - # Persistence 2 support - - - message: '/clear.*invoked with 1 parameter/' - path: src/EntityRepository.php - - - message: '#^Class Doctrine\\Persistence\\ObjectManagerAware not found\.$#' - path: src/UnitOfWork.php - - - message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#' - path: src/UnitOfWork.php - - - - message: '#contains generic type.*but class.*is not generic#' - paths: - - src/Mapping/Driver/XmlDriver.php - - src/Mapping/Driver/YamlDriver.php diff --git a/phpstan-dbal3.neon b/phpstan-dbal3.neon new file mode 100644 index 00000000000..ea39e2c3a0b --- /dev/null +++ b/phpstan-dbal3.neon @@ -0,0 +1,77 @@ +includes: + - phpstan-baseline.neon + - phpstan-params.neon + +parameters: + reportUnmatchedIgnoredErrors: false # Some errors in the baseline only apply to DBAL 4 + ignoreErrors: + # Symfony cache supports passing a key prefix to the clear method. + - '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/' + + # We can be certain that those values are not matched. + - + message: '~^Match expression does not handle remaining values:~' + path: src/Persisters/Entity/BasicEntityPersister.php + + # DBAL 4 compatibility + - + message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~' + path: src/Query/AST/Functions/TrimFunction.php + + - + message: '~.*getTrimExpression.*expects int.*~' + path: src/Query/AST/Functions/TrimFunction.php + + - '~^Class Doctrine\\DBAL\\Platforms\\SQLitePlatform not found\.$~' + + # To be removed in 4.0 + - + message: '#Negated boolean expression is always false\.#' + paths: + - src/Mapping/Driver/AttributeDriver.php + + - + message: '~^Call to deprecated method getEventManager\(\) of class Doctrine\\DBAL\\Connection\.$~' + path: src/EntityManager.php + - + message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~' + path: src/Tools/Console/ConsoleRunner.php + + # Compatibility with Persistence 3 + - + message: '#Expression on left side of \?\? is not nullable.#' + path: src/Mapping/Driver/AttributeDriver.php + + - + message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~' + path: src/Persisters/Entity/BasicEntityPersister.php + + - + message: '~getTypes.*should return~' + path: src/Persisters/Entity/BasicEntityPersister.php + + - + message: '~.*appendLockHint.*expects.*LockMode given~' + paths: + - src/Persisters/Entity/BasicEntityPersister.php + - src/Persisters/Entity/JoinedSubclassPersister.php + + - + message: '~.*executeStatement.*expects~' + path: src/Query/Exec/MultiTableUpdateExecutor.php + + - + message: '~method_exists.*getEventManager~' + path: src/EntityManager.php + + - + message: '~method_exists.*getIdentitySequence~' + path: src/Mapping/ClassMetadataFactory.php + + - + message: '~expand(Criteria)?Parameters.*should return array~' + path: src/Persisters/Entity/BasicEntityPersister.php + + - + message: '~inferType.*never returns~' + path: src/Query/ParameterTypeInferer.php diff --git a/phpstan-persistence2.neon b/phpstan-persistence2.neon deleted file mode 100644 index b083dd2fa1d..00000000000 --- a/phpstan-persistence2.neon +++ /dev/null @@ -1,102 +0,0 @@ -includes: - - phpstan-baseline.neon - - phpstan-params.neon - -parameters: - reportUnmatchedIgnoredErrors: false - - ignoreErrors: - # deprecations from doctrine/dbal:3.x - - '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getGuidExpression\(\).$/' - - # Fallback logic for DBAL 2 - - - message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/' - path: src/Tools/Console/ConsoleRunner.php - - - '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/' - - '/^Call to method \w+\(\) on an unknown class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform\.$/' - - - - message: '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getSQLResultCasing\(\)\.$/' - path: src/Internal/SQLResultCasing.php - - - message: '/^Parameter \$stmt of method .* has invalid type Doctrine\\DBAL\\Driver\\ResultStatement\.$/' - path: src/Internal/Hydration/AbstractHydrator.php - - - message: '/^Class Doctrine\\DBAL\\Driver\\ResultStatement not found\.$/' - path: src/Internal/Hydration/AbstractHydrator.php - - - message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/' - path: src/Internal/Hydration/AbstractHydrator.php - - - message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/' - path: src/Utility/LockSqlHelper.php - - # Forward compatibility with Collections 3 - - - message: '#^Parameter \$order of anonymous function has invalid type Doctrine\\Common\\Collections\\Order\.$#' - path: src/Internal/CriteriaOrderings.php - - - - message: '#^Anonymous function has invalid return type Doctrine\\Common\\Collections\\Order\.$#' - path: src/Internal/CriteriaOrderings.php - - - - message: '#^Access to property \$value on an unknown class Doctrine\\Common\\Collections\\Order\.$#' - path: src/Internal/CriteriaOrderings.php - - - - message: '#^Call to static method from\(\) on an unknown class Doctrine\\Common\\Collections\\Order\.$#' - path: src/Internal/CriteriaOrderings.php - - - - message: '#^Call to an undefined method Doctrine\\Common\\Collections\\Criteria\:\:orderings\(\)\.$#' - path: src/Internal/CriteriaOrderings.php - - - - message: '#^Method .+\:\:mapToOrderEnumIfAvailable\(\) has invalid return type Doctrine\\Common\\Collections\\Order\.$#' - path: src/Internal/CriteriaOrderings.php - - # False positive - - - message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/' - count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php - - # Symfony cache supports passing a key prefix to the clear method. - - '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/' - - - - message: '#contains generic type.*but class.*is not generic#' - paths: - - src/Mapping/Driver/XmlDriver.php - - src/Mapping/Driver/YamlDriver.php - - # Extending a deprecated class conditionally to maintain BC - - - message: '~deprecated class Doctrine\\Persistence\\Mapping\\Driver\\AnnotationDriver\:~' - path: src/Mapping/Driver/CompatibilityAnnotationDriver.php - - # We're sniffing for this deprecated class in order to detect Persistence 2 - - - message: '~deprecated class Doctrine\\Common\\Persistence\\PersistentObject\:~' - path: src/EntityManager.php - - - - message: '#Cannot access offset \S+ on .*ClassMetadata.*#' - paths: - - src/Mapping/Driver/XmlDriver.php - - src/Mapping/Driver/YamlDriver.php - - - - message: '#^Parameter \#1 \$orderings of method Doctrine\\Common\\Collections\\Criteria\:\:orderBy\(\) expects array\, array\ given\.$#' - path: src/PersistentCollection.php - - - - message: '#^Parameter \#5 \.\.\.\$args of static method Doctrine\\Deprecations\\Deprecation\:\:trigger\(\) expects float\|int\|string, string\|false given\.$#' - path: src/Mapping/ClassMetadataFactory.php - - - - message: '#^Parameter \#1 \$classNames of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\\:\:setParentClasses\(\) expects list\, array\ given\.$#' - path: src/Mapping/ClassMetadataFactory.php diff --git a/phpstan.neon b/phpstan.neon index c47790fef18..f98eb8d00b8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,49 +4,49 @@ includes: parameters: ignoreErrors: - # deprecations from doctrine/dbal:3.x - - '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getGuidExpression\(\).$/' + # Symfony cache supports passing a key prefix to the clear method. + - '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/' - # Fallback logic for DBAL 2 + # We can be certain that those values are not matched. - - message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/' - path: src/Tools/Console/ConsoleRunner.php - - - '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/' - - '/^Call to method \w+\(\) on an unknown class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform\.$/' + message: '~^Match expression does not handle remaining values:~' + path: src/Persisters/Entity/BasicEntityPersister.php + # DBAL 4 compatibility - - message: '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getSQLResultCasing\(\)\.$/' - path: src/Internal/SQLResultCasing.php + message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~' + path: src/Query/AST/Functions/TrimFunction.php - - message: '/^Parameter \$stmt of method .* has invalid type Doctrine\\DBAL\\Driver\\ResultStatement\.$/' - path: src/Internal/Hydration/AbstractHydrator.php + message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~' + path: src/Persisters/Entity/BasicEntityPersister.php + + # Compatibility with DBAL 3 + # See https://github.com/doctrine/dbal/pull/3480 - - message: '/^Class Doctrine\\DBAL\\Driver\\ResultStatement not found\.$/' - path: src/Internal/Hydration/AbstractHydrator.php + message: '~^Result of method Doctrine\\DBAL\\Connection::commit\(\) \(void\) is used\.$~' + path: src/UnitOfWork.php - - message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/' - path: src/Internal/Hydration/AbstractHydrator.php + message: '~^Strict comparison using === between null and false will always evaluate to false\.$~' + path: src/UnitOfWork.php - - message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/' - path: src/Utility/LockSqlHelper.php + message: '~^Variable \$e on left side of \?\? always exists and is not nullable\.$~' + path: src/UnitOfWork.php - # False positive - - message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/' - count: 1 - path: src/Tools/Console/Command/ClearCache/ResultCommand.php - - # Symfony cache supports passing a key prefix to the clear method. - - '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/' + message: '~^Parameter #1 \$command of method Symfony\\Component\\Console\\Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command, Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand given\.$~' + path: src/Tools/Console/ConsoleRunner.php - # Persistence 2 support - - message: '/clear.*invoked with 1 parameter/' - path: src/EntityRepository.php + message: '~Strict comparison using \=\=\= between callable\(\)\: mixed and null will always evaluate to false\.~' + path: src/Tools/SchemaTool.php + + # To be removed in 4.0 - - message: '#^Class Doctrine\\Persistence\\ObjectManagerAware not found\.$#' - path: src/UnitOfWork.php + message: '#Negated boolean expression is always false\.#' + paths: + - src/Mapping/Driver/AttributeDriver.php + + # Compatibility with Persistence 3 - - message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#' - path: src/UnitOfWork.php + message: '#Expression on left side of \?\? is not nullable.#' + path: src/Mapping/Driver/AttributeDriver.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f596eb473c8..5e26bd5898a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,10 +14,14 @@ @@ -31,42 +35,45 @@ locking_functional + + + + + + + + - - - - + By default we assume that the `db_` config above has unrestricted access to the provided database + platform. - - - - + + + + + + + --> + + + + + + src + + diff --git a/src/AbstractQuery.php b/src/AbstractQuery.php index b931e69ee57..62862e9a915 100644 --- a/src/AbstractQuery.php +++ b/src/AbstractQuery.php @@ -5,19 +5,16 @@ namespace Doctrine\ORM; use BackedEnum; -use Countable; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Result; -use Doctrine\Deprecations\Deprecation; -use Doctrine\ORM\Cache\Exception\InvalidResultCacheDriver; use Doctrine\ORM\Cache\Logging\CacheLogger; use Doctrine\ORM\Cache\QueryCacheKey; use Doctrine\ORM\Cache\TimestampCacheKey; -use Doctrine\ORM\Internal\Hydration\IterableResult; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException as ORMMappingException; use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; use Doctrine\ORM\Query\Parameter; @@ -32,17 +29,13 @@ use function array_shift; use function assert; use function count; -use function func_num_args; -use function in_array; use function is_array; use function is_numeric; use function is_object; use function is_scalar; use function is_string; -use function iterator_count; use function iterator_to_array; use function ksort; -use function method_exists; use function reset; use function serialize; use function sha1; @@ -92,90 +85,71 @@ abstract class AbstractQuery * @var ArrayCollection|Parameter[] * @phpstan-var ArrayCollection */ - protected $parameters; + protected ArrayCollection $parameters; /** * The user-specified ResultSetMapping to use. - * - * @var ResultSetMapping|null - */ - protected $_resultSetMapping; - - /** - * The entity manager used by this query object. - * - * @var EntityManagerInterface */ - protected $_em; + protected ResultSetMapping|null $resultSetMapping = null; /** * The map of query hints. * * @phpstan-var array */ - protected $_hints = []; + protected array $hints = []; /** * The hydration mode. * - * @var string|int * @phpstan-var string|AbstractQuery::HYDRATE_* */ - protected $_hydrationMode = self::HYDRATE_OBJECT; + protected string|int $hydrationMode = self::HYDRATE_OBJECT; - /** @var QueryCacheProfile|null */ - protected $_queryCacheProfile; + protected QueryCacheProfile|null $queryCacheProfile = null; /** * Whether or not expire the result cache. - * - * @var bool */ - protected $_expireResultCache = false; + protected bool $expireResultCache = false; - /** @var QueryCacheProfile|null */ - protected $_hydrationCacheProfile; + protected QueryCacheProfile|null $hydrationCacheProfile = null; /** * Whether to use second level cache, if available. - * - * @var bool */ - protected $cacheable = false; + protected bool $cacheable = false; - /** @var bool */ - protected $hasCache = false; + protected bool $hasCache = false; /** * Second level cache region name. - * - * @var string|null */ - protected $cacheRegion; + protected string|null $cacheRegion = null; /** * Second level query cache mode. * - * @var int|null * @phpstan-var Cache::MODE_*|null */ - protected $cacheMode; + protected int|null $cacheMode = null; - /** @var CacheLogger|null */ - protected $cacheLogger; + protected CacheLogger|null $cacheLogger = null; - /** @var int */ - protected $lifetime = 0; + protected int $lifetime = 0; /** * Initializes a new instance of a class derived from AbstractQuery. */ - public function __construct(EntityManagerInterface $em) - { - $this->_em = $em; + public function __construct( + /** + * The entity manager used by this query object. + */ + protected EntityManagerInterface $em, + ) { $this->parameters = new ArrayCollection(); - $this->_hints = $em->getConfiguration()->getDefaultQueryHints(); - $this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled(); + $this->hints = $em->getConfiguration()->getDefaultQueryHints(); + $this->hasCache = $this->em->getConfiguration()->isSecondLevelCacheEnabled(); if ($this->hasCache) { $this->cacheLogger = $em->getConfiguration() @@ -187,31 +161,25 @@ public function __construct(EntityManagerInterface $em) /** * Enable/disable second level query (result) caching for this query. * - * @param bool $cacheable - * * @return $this */ - public function setCacheable($cacheable) + public function setCacheable(bool $cacheable): static { - $this->cacheable = (bool) $cacheable; + $this->cacheable = $cacheable; return $this; } /** @return bool TRUE if the query results are enabled for second level cache, FALSE otherwise. */ - public function isCacheable() + public function isCacheable(): bool { return $this->cacheable; } - /** - * @param string $cacheRegion - * - * @return $this - */ - public function setCacheRegion($cacheRegion) + /** @return $this */ + public function setCacheRegion(string $cacheRegion): static { - $this->cacheRegion = (string) $cacheRegion; + $this->cacheRegion = $cacheRegion; return $this; } @@ -221,19 +189,18 @@ public function setCacheRegion($cacheRegion) * * @return string|null The cache region name; NULL indicates the default region. */ - public function getCacheRegion() + public function getCacheRegion(): string|null { return $this->cacheRegion; } /** @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise. */ - protected function isCacheEnabled() + protected function isCacheEnabled(): bool { return $this->cacheable && $this->hasCache; } - /** @return int */ - public function getLifetime() + public function getLifetime(): int { return $this->lifetime; } @@ -241,35 +208,29 @@ public function getLifetime() /** * Sets the life-time for this query into second level cache. * - * @param int $lifetime - * * @return $this */ - public function setLifetime($lifetime) + public function setLifetime(int $lifetime): static { - $this->lifetime = (int) $lifetime; + $this->lifetime = $lifetime; return $this; } - /** - * @return int|null - * @phpstan-return Cache::MODE_*|null - */ - public function getCacheMode() + /** @phpstan-return Cache::MODE_*|null */ + public function getCacheMode(): int|null { return $this->cacheMode; } /** - * @param int $cacheMode * @phpstan-param Cache::MODE_* $cacheMode * * @return $this */ - public function setCacheMode($cacheMode) + public function setCacheMode(int $cacheMode): static { - $this->cacheMode = (int) $cacheMode; + $this->cacheMode = $cacheMode; return $this; } @@ -281,39 +242,34 @@ public function setCacheMode($cacheMode) * * @return list|string SQL query */ - abstract public function getSQL(); + abstract public function getSQL(): string|array; /** * Retrieves the associated EntityManager of this Query instance. - * - * @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { - return $this->_em; + return $this->em; } /** * Frees the resources used by the query object. * * Resets Parameters, Parameter Types and Query Hints. - * - * @return void */ - public function free() + public function free(): void { $this->parameters = new ArrayCollection(); - $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + $this->hints = $this->em->getConfiguration()->getDefaultQueryHints(); } /** * Get all defined parameters. * - * @return ArrayCollection The defined query parameters. * @phpstan-return ArrayCollection */ - public function getParameters() + public function getParameters(): ArrayCollection { return $this->parameters; } @@ -325,16 +281,12 @@ public function getParameters() * * @return Parameter|null The value of the bound parameter, or NULL if not available. */ - public function getParameter($key) + public function getParameter(int|string $key): Parameter|null { - $key = Query\Parameter::normalizeName($key); + $key = Parameter::normalizeName($key); $filteredParameters = $this->parameters->filter( - static function (Query\Parameter $parameter) use ($key): bool { - $parameterName = $parameter->getName(); - - return $key === $parameterName; - } + static fn (Parameter $parameter): bool => $parameter->getName() === $key ); return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null; @@ -348,7 +300,7 @@ static function (Query\Parameter $parameter) use ($key): bool { * * @return $this */ - public function setParameters($parameters) + public function setParameters(ArrayCollection|array $parameters): static { if (is_array($parameters)) { /** @phpstan-var ArrayCollection $parameterCollection */ @@ -369,15 +321,16 @@ public function setParameters($parameters) /** * Sets a query parameter. * - * @param string|int $key The parameter position or name. - * @param mixed $value The parameter value. - * @param string|int|null $type The parameter type. If specified, the given value will be run through - * the type conversion of this type. This is usually not needed for - * strings and numeric types. + * @param string|int $key The parameter position or name. + * @param mixed $value The parameter value. + * @param ParameterType|ArrayParameterType|string|int|null $type The parameter type. If specified, the given value + * will be run through the type conversion of this + * type. This is usually not needed for strings and + * numeric types. * * @return $this */ - public function setParameter($key, $value, $type = null) + public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static { $existingParameter = $this->getParameter($key); @@ -395,13 +348,9 @@ public function setParameter($key, $value, $type = null) /** * Processes an individual parameter value. * - * @param mixed $value - * - * @return mixed - * * @throws ORMInvalidArgumentException */ - public function processParameterValue($value) + public function processParameterValue(mixed $value): mixed { if (is_scalar($value)) { return $value; @@ -417,7 +366,7 @@ public function processParameterValue($value) return $value; } - if ($value instanceof Mapping\ClassMetadata) { + if ($value instanceof ClassMetadata) { return $value->name; } @@ -431,12 +380,12 @@ public function processParameterValue($value) try { $class = DefaultProxyClassNameResolver::getClass($value); - $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value); + $value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value); if ($value === null) { throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class); } - } catch (MappingException | ORMMappingException $e) { + } catch (MappingException | ORMMappingException) { /* Silence any mapping exceptions. These can occur if the object in question is not a mapped entity, in which case we just don't do any preparation on the value. @@ -451,12 +400,8 @@ public function processParameterValue($value) /** * If no mapping is detected, trying to resolve the value as a Traversable - * - * @param mixed $value - * - * @return mixed */ - private function potentiallyProcessIterable($value) + private function potentiallyProcessIterable(mixed $value): mixed { if ($value instanceof Traversable) { $value = iterator_to_array($value); @@ -488,32 +433,28 @@ private function processArrayParameterValue(array $value): array * * @return $this */ - public function setResultSetMapping(Query\ResultSetMapping $rsm) + public function setResultSetMapping(ResultSetMapping $rsm): static { $this->translateNamespaces($rsm); - $this->_resultSetMapping = $rsm; + $this->resultSetMapping = $rsm; return $this; } /** * Gets the ResultSetMapping used for hydration. - * - * @return ResultSetMapping|null */ - protected function getResultSetMapping() + protected function getResultSetMapping(): ResultSetMapping|null { - return $this->_resultSetMapping; + return $this->resultSetMapping; } /** * Allows to translate entity namespaces to full qualified names. */ - private function translateNamespaces(Query\ResultSetMapping $rsm): void + private function translateNamespaces(ResultSetMapping $rsm): void { - $translate = function ($alias): string { - return $this->_em->getClassMetadata($alias)->getName(); - }; + $translate = fn ($alias): string => $this->em->getClassMetadata($alias)->getName(); $rsm->aliasMap = array_map($translate, $rsm->aliasMap); $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses); @@ -539,49 +480,29 @@ private function translateNamespaces(Query\ResultSetMapping $rsm): void * $query->setHydrationCacheProfile(new QueryCacheProfile()); * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); */ - public function setHydrationCacheProfile(?QueryCacheProfile $profile = null) + public function setHydrationCacheProfile(QueryCacheProfile|null $profile): static { if ($profile === null) { - if (func_num_args() < 1) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9791', - 'Calling %s without arguments is deprecated, pass null instead.', - __METHOD__ - ); - } - - $this->_hydrationCacheProfile = null; + $this->hydrationCacheProfile = null; return $this; } - // DBAL 2 - if (! method_exists(QueryCacheProfile::class, 'setResultCache')) { - // @phpstan-ignore method.deprecated - if (! $profile->getResultCacheDriver()) { - $defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache(); - if ($defaultHydrationCacheImpl) { - // @phpstan-ignore method.deprecated - $profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultHydrationCacheImpl)); - } - } - } elseif (! $profile->getResultCache()) { - $defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache(); + if (! $profile->getResultCache()) { + $defaultHydrationCacheImpl = $this->em->getConfiguration()->getHydrationCache(); if ($defaultHydrationCacheImpl) { $profile = $profile->setResultCache($defaultHydrationCacheImpl); } } - $this->_hydrationCacheProfile = $profile; + $this->hydrationCacheProfile = $profile; return $this; } - /** @return QueryCacheProfile|null */ - public function getHydrationCacheProfile() + public function getHydrationCacheProfile(): QueryCacheProfile|null { - return $this->_hydrationCacheProfile; + return $this->hydrationCacheProfile; } /** @@ -592,144 +513,46 @@ public function getHydrationCacheProfile() * * @return $this */ - public function setResultCacheProfile(?QueryCacheProfile $profile = null) + public function setResultCacheProfile(QueryCacheProfile|null $profile): static { if ($profile === null) { - if (func_num_args() < 1) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9791', - 'Calling %s without arguments is deprecated, pass null instead.', - __METHOD__ - ); - } - - $this->_queryCacheProfile = null; + $this->queryCacheProfile = null; return $this; } - // DBAL 2 - if (! method_exists(QueryCacheProfile::class, 'setResultCache')) { - // @phpstan-ignore method.deprecated - if (! $profile->getResultCacheDriver()) { - $defaultResultCacheDriver = $this->_em->getConfiguration()->getResultCache(); - if ($defaultResultCacheDriver) { - // @phpstan-ignore method.deprecated - $profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultResultCacheDriver)); - } - } - } elseif (! $profile->getResultCache()) { - $defaultResultCache = $this->_em->getConfiguration()->getResultCache(); + if (! $profile->getResultCache()) { + $defaultResultCache = $this->em->getConfiguration()->getResultCache(); if ($defaultResultCache) { $profile = $profile->setResultCache($defaultResultCache); } } - $this->_queryCacheProfile = $profile; + $this->queryCacheProfile = $profile; return $this; } /** * Defines a cache driver to be used for caching result sets and implicitly enables caching. - * - * @deprecated Use {@see setResultCache()} instead. - * - * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver - * - * @return $this - * - * @throws InvalidResultCacheDriver */ - public function setResultCacheDriver($resultCacheDriver = null) - { - /** @phpstan-ignore-next-line */ - if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { - throw InvalidResultCacheDriver::create(); - } - - return $this->setResultCache($resultCacheDriver ? CacheAdapter::wrap($resultCacheDriver) : null); - } - - /** - * Defines a cache driver to be used for caching result sets and implicitly enables caching. - * - * @return $this - */ - public function setResultCache(?CacheItemPoolInterface $resultCache = null) + public function setResultCache(CacheItemPoolInterface|null $resultCache): static { if ($resultCache === null) { - if (func_num_args() < 1) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9791', - 'Calling %s without arguments is deprecated, pass null instead.', - __METHOD__ - ); - } - - if ($this->_queryCacheProfile) { - $this->_queryCacheProfile = new QueryCacheProfile($this->_queryCacheProfile->getLifetime(), $this->_queryCacheProfile->getCacheKey()); + if ($this->queryCacheProfile) { + $this->queryCacheProfile = new QueryCacheProfile($this->queryCacheProfile->getLifetime(), $this->queryCacheProfile->getCacheKey()); } return $this; } - // DBAL 2 - if (! method_exists(QueryCacheProfile::class, 'setResultCache')) { - $resultCacheDriver = DoctrineProvider::wrap($resultCache); - - $this->_queryCacheProfile = $this->_queryCacheProfile - // @phpstan-ignore method.deprecated - ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) - : new QueryCacheProfile(0, null, $resultCacheDriver); - - return $this; - } - - $this->_queryCacheProfile = $this->_queryCacheProfile - ? $this->_queryCacheProfile->setResultCache($resultCache) + $this->queryCacheProfile = $this->queryCacheProfile + ? $this->queryCacheProfile->setResultCache($resultCache) : new QueryCacheProfile(0, null, $resultCache); return $this; } - /** - * Returns the cache driver used for caching result sets. - * - * @deprecated - * - * @return \Doctrine\Common\Cache\Cache Cache driver - */ - public function getResultCacheDriver() - { - if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { - return $this->_queryCacheProfile->getResultCacheDriver(); - } - - return $this->_em->getConfiguration()->getResultCacheImpl(); - } - - /** - * Set whether or not to cache the results of this query and if so, for - * how long and which ID to use for the cache entry. - * - * @deprecated 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead. - * - * @param bool $useCache Whether or not to cache the results of this query. - * @param int $lifetime How long the cache entry is valid, in seconds. - * @param string $resultCacheId ID to use for the cache entry. - * - * @return $this - */ - public function useResultCache($useCache, $lifetime = null, $resultCacheId = null) - { - return $useCache - ? $this->enableResultCache($lifetime, $resultCacheId) - : $this->disableResultCache(); - } - /** * Enables caching of the results of this query, for given or default amount of seconds * and optionally specifies which ID to use for the cache entry. @@ -739,7 +562,7 @@ public function useResultCache($useCache, $lifetime = null, $resultCacheId = nul * * @return $this */ - public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null): self + public function enableResultCache(int|null $lifetime = null, string|null $resultCacheId = null): static { $this->setResultCacheLifetime($lifetime); $this->setResultCacheId($resultCacheId); @@ -752,9 +575,9 @@ public function enableResultCache(?int $lifetime = null, ?string $resultCacheId * * @return $this */ - public function disableResultCache(): self + public function disableResultCache(): static { - $this->_queryCacheProfile = null; + $this->queryCacheProfile = null; return $this; } @@ -766,48 +589,28 @@ public function disableResultCache(): self * * @return $this */ - public function setResultCacheLifetime($lifetime) + public function setResultCacheLifetime(int|null $lifetime): static { $lifetime = (int) $lifetime; - if ($this->_queryCacheProfile) { - $this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime); + if ($this->queryCacheProfile) { + $this->queryCacheProfile = $this->queryCacheProfile->setLifetime($lifetime); return $this; } - $this->_queryCacheProfile = new QueryCacheProfile($lifetime); + $this->queryCacheProfile = new QueryCacheProfile($lifetime); - $cache = $this->_em->getConfiguration()->getResultCache(); + $cache = $this->em->getConfiguration()->getResultCache(); if (! $cache) { return $this; } - // Compatibility for DBAL 2 - if (! method_exists($this->_queryCacheProfile, 'setResultCache')) { - // @phpstan-ignore method.deprecated - $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver(DoctrineProvider::wrap($cache)); - - return $this; - } - - $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCache($cache); + $this->queryCacheProfile = $this->queryCacheProfile->setResultCache($cache); return $this; } - /** - * Retrieves the lifetime of resultset cache. - * - * @deprecated - * - * @return int - */ - public function getResultCacheLifetime() - { - return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0; - } - /** * Defines if the result cache is active or not. * @@ -815,52 +618,35 @@ public function getResultCacheLifetime() * * @return $this */ - public function expireResultCache($expire = true) + public function expireResultCache(bool $expire = true): static { - $this->_expireResultCache = $expire; + $this->expireResultCache = $expire; return $this; } /** * Retrieves if the resultset cache is active or not. - * - * @return bool */ - public function getExpireResultCache() + public function getExpireResultCache(): bool { - return $this->_expireResultCache; + return $this->expireResultCache; } - /** @return QueryCacheProfile|null */ - public function getQueryCacheProfile() + public function getQueryCacheProfile(): QueryCacheProfile|null { - return $this->_queryCacheProfile; + return $this->queryCacheProfile; } /** * Change the default fetch mode of an association for this query. * * @param class-string $class - * @param string $assocName - * @param int $fetchMode * @phpstan-param Mapping\ClassMetadata::FETCH_EAGER|Mapping\ClassMetadata::FETCH_LAZY $fetchMode - * - * @return $this */ - public function setFetchMode($class, $assocName, $fetchMode) + public function setFetchMode(string $class, string $assocName, int $fetchMode): static { - if (! in_array($fetchMode, [Mapping\ClassMetadata::FETCH_EAGER, Mapping\ClassMetadata::FETCH_LAZY], true)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9777', - 'Calling %s() with something else than ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY is deprecated.', - __METHOD__ - ); - $fetchMode = Mapping\ClassMetadata::FETCH_LAZY; - } - - $this->_hints['fetchMode'][$class][$assocName] = $fetchMode; + $this->hints['fetchMode'][$class][$assocName] = $fetchMode; return $this; } @@ -874,9 +660,9 @@ public function setFetchMode($class, $assocName, $fetchMode) * * @return $this */ - public function setHydrationMode($hydrationMode) + public function setHydrationMode(string|int $hydrationMode): static { - $this->_hydrationMode = $hydrationMode; + $this->hydrationMode = $hydrationMode; return $this; } @@ -884,12 +670,11 @@ public function setHydrationMode($hydrationMode) /** * Gets the hydration mode currently used by the query. * - * @return string|int * @phpstan-return string|AbstractQuery::HYDRATE_* */ - public function getHydrationMode() + public function getHydrationMode(): string|int { - return $this->_hydrationMode; + return $this->hydrationMode; } /** @@ -897,12 +682,9 @@ public function getHydrationMode() * * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT). * - * @param string|int $hydrationMode * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode - * - * @return mixed */ - public function getResult($hydrationMode = self::HYDRATE_OBJECT) + public function getResult(string|int $hydrationMode = self::HYDRATE_OBJECT): mixed { return $this->execute(null, $hydrationMode); } @@ -914,7 +696,7 @@ public function getResult($hydrationMode = self::HYDRATE_OBJECT) * * @return mixed[] */ - public function getArrayResult() + public function getArrayResult(): array { return $this->execute(null, self::HYDRATE_ARRAY); } @@ -926,7 +708,7 @@ public function getArrayResult() * * @return mixed[] */ - public function getSingleColumnResult() + public function getSingleColumnResult(): array { return $this->execute(null, self::HYDRATE_SCALAR_COLUMN); } @@ -938,7 +720,7 @@ public function getSingleColumnResult() * * @return mixed[] */ - public function getScalarResult() + public function getScalarResult(): array { return $this->execute(null, self::HYDRATE_SCALAR); } @@ -946,22 +728,19 @@ public function getScalarResult() /** * Get exactly one result or null. * - * @param string|int|null $hydrationMode * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode * - * @return mixed - * * @throws NonUniqueResultException */ - public function getOneOrNullResult($hydrationMode = null) + public function getOneOrNullResult(string|int|null $hydrationMode = null): mixed { try { $result = $this->execute(null, $hydrationMode); - } catch (NoResultException $e) { + } catch (NoResultException) { return null; } - if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { return null; } @@ -984,19 +763,16 @@ public function getOneOrNullResult($hydrationMode = null) * If the result is not unique, a NonUniqueResultException is thrown. * If there is no result, a NoResultException is thrown. * - * @param string|int|null $hydrationMode * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode * - * @return mixed - * * @throws NonUniqueResultException If the query result is not unique. * @throws NoResultException If the query returned no result. */ - public function getSingleResult($hydrationMode = null) + public function getSingleResult(string|int|null $hydrationMode = null): mixed { $result = $this->execute(null, $hydrationMode); - if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException(); } @@ -1021,7 +797,7 @@ public function getSingleResult($hydrationMode = null) * @throws NoResultException If the query returned no result. * @throws NonUniqueResultException If the query result is not unique. */ - public function getSingleScalarResult() + public function getSingleScalarResult(): mixed { return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); } @@ -1029,14 +805,11 @@ public function getSingleScalarResult() /** * Sets a query hint. If the hint name is not recognized, it is silently ignored. * - * @param string $name The name of the hint. - * @param mixed $value The value of the hint. - * * @return $this */ - public function setHint($name, $value) + public function setHint(string $name, mixed $value): static { - $this->_hints[$name] = $value; + $this->hints[$name] = $value; return $this; } @@ -1044,25 +817,16 @@ public function setHint($name, $value) /** * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. * - * @param string $name The name of the hint. - * * @return mixed The value of the hint or FALSE, if the hint name is not recognized. */ - public function getHint($name) + public function getHint(string $name): mixed { - return $this->_hints[$name] ?? false; + return $this->hints[$name] ?? false; } - /** - * Check if the query has a hint - * - * @param string $name The name of the hint - * - * @return bool False if the query does not have any hint - */ - public function hasHint($name) + public function hasHint(string $name): bool { - return isset($this->_hints[$name]); + return isset($this->hints[$name]); } /** @@ -1070,72 +834,29 @@ public function hasHint($name) * * @return array */ - public function getHints() - { - return $this->_hints; - } - - /** - * Executes the query and returns an IterableResult that can be used to incrementally - * iterate over the result. - * - * @deprecated 2.8 Use {@see toIterable} instead. See https://github.com/doctrine/orm/issues/8463 - * - * @param ArrayCollection|mixed[]|null $parameters The query parameters. - * @param string|int|null $hydrationMode The hydration mode to use. - * @phpstan-param ArrayCollection|array|null $parameters - * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode The hydration mode to use. - * - * @return IterableResult - */ - public function iterate($parameters = null, $hydrationMode = null) + public function getHints(): array { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8463', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.', - __METHOD__ - ); - - if ($hydrationMode !== null) { - $this->setHydrationMode($hydrationMode); - } - - if (! empty($parameters)) { - $this->setParameters($parameters); - } - - $rsm = $this->getResultSetMapping(); - if ($rsm === null) { - throw new LogicException('Uninitialized result set mapping.'); - } - - $stmt = $this->_doExecute(); - - return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints); + return $this->hints; } /** * Executes the query and returns an iterable that can be used to incrementally * iterate over the result. * - * @param ArrayCollection|array|mixed[] $parameters The query parameters. - * @param string|int|null $hydrationMode The hydration mode to use. * @phpstan-param ArrayCollection|mixed[] $parameters * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode * * @return iterable */ - public function toIterable(iterable $parameters = [], $hydrationMode = null): iterable - { + public function toIterable( + ArrayCollection|array $parameters = [], + string|int|null $hydrationMode = null, + ): iterable { if ($hydrationMode !== null) { $this->setHydrationMode($hydrationMode); } - if ( - ($this->isCountable($parameters) && count($parameters) !== 0) - || ($parameters instanceof Traversable && iterator_count($parameters) !== 0) - ) { + if (count($parameters) !== 0) { $this->setParameters($parameters); } @@ -1150,21 +871,19 @@ public function toIterable(iterable $parameters = [], $hydrationMode = null): it $stmt = $this->_doExecute(); - return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints); + return $this->em->newHydrator($this->hydrationMode)->toIterable($stmt, $rsm, $this->hints); } /** * Executes the query. * - * @param ArrayCollection|mixed[]|null $parameters Query parameters. - * @param string|int|null $hydrationMode Processing mode to be used during the hydration process. * @phpstan-param ArrayCollection|mixed[]|null $parameters * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode - * - * @return mixed */ - public function execute($parameters = null, $hydrationMode = null) - { + public function execute( + ArrayCollection|array|null $parameters = null, + string|int|null $hydrationMode = null, + ): mixed { if ($this->cacheable && $this->isCacheEnabled()) { return $this->executeUsingQueryCache($parameters, $hydrationMode); } @@ -1175,15 +894,13 @@ public function execute($parameters = null, $hydrationMode = null) /** * Execute query ignoring second level cache. * - * @param ArrayCollection|mixed[]|null $parameters - * @param string|int|null $hydrationMode * @phpstan-param ArrayCollection|mixed[]|null $parameters * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode - * - * @return mixed */ - private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null) - { + private function executeIgnoreQueryCache( + ArrayCollection|array|null $parameters = null, + string|int|null $hydrationMode = null, + ): mixed { if ($hydrationMode !== null) { $this->setHydrationMode($hydrationMode); } @@ -1195,7 +912,7 @@ private function executeIgnoreQueryCache($parameters = null, $hydrationMode = nu $setCacheEntry = static function ($data): void { }; - if ($this->_hydrationCacheProfile !== null) { + if ($this->hydrationCacheProfile !== null) { [$cacheKey, $realCacheKey] = $this->getHydrationCacheId(); $cache = $this->getHydrationCache(); @@ -1228,7 +945,7 @@ private function executeIgnoreQueryCache($parameters = null, $hydrationMode = nu throw new LogicException('Uninitialized result set mapping.'); } - $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints); + $data = $this->em->newHydrator($this->hydrationMode)->hydrateAll($stmt, $rsm, $this->hints); $setCacheEntry($data); @@ -1237,18 +954,9 @@ private function executeIgnoreQueryCache($parameters = null, $hydrationMode = nu private function getHydrationCache(): CacheItemPoolInterface { - assert($this->_hydrationCacheProfile !== null); + assert($this->hydrationCacheProfile !== null); - // Support for DBAL 2 - if (! method_exists($this->_hydrationCacheProfile, 'getResultCache')) { - // @phpstan-ignore method.deprecated - $cacheDriver = $this->_hydrationCacheProfile->getResultCacheDriver(); - assert($cacheDriver !== null); - - return CacheAdapter::wrap($cacheDriver); - } - - $cache = $this->_hydrationCacheProfile->getResultCache(); + $cache = $this->hydrationCacheProfile->getResultCache(); assert($cache !== null); return $cache; @@ -1257,29 +965,27 @@ private function getHydrationCache(): CacheItemPoolInterface /** * Load from second level cache or executes the query and put into cache. * - * @param ArrayCollection|mixed[]|null $parameters - * @param string|int|null $hydrationMode * @phpstan-param ArrayCollection|mixed[]|null $parameters * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode - * - * @return mixed */ - private function executeUsingQueryCache($parameters = null, $hydrationMode = null) - { + private function executeUsingQueryCache( + ArrayCollection|array|null $parameters = null, + string|int|null $hydrationMode = null, + ): mixed { $rsm = $this->getResultSetMapping(); if ($rsm === null) { throw new LogicException('Uninitialized result set mapping.'); } - $queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion); + $queryCache = $this->em->getCache()->getQueryCache($this->cacheRegion); $queryKey = new QueryCacheKey( $this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL, - $this->getTimestampKey() + $this->getTimestampKey(), ); - $result = $queryCache->get($queryKey, $rsm, $this->_hints); + $result = $queryCache->get($queryKey, $rsm, $this->hints); if ($result !== null) { if ($this->cacheLogger) { @@ -1290,7 +996,7 @@ private function executeUsingQueryCache($parameters = null, $hydrationMode = nul } $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode); - $cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints); + $cached = $queryCache->put($queryKey, $rsm, $result, $this->hints); if ($this->cacheLogger) { $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey); @@ -1303,18 +1009,18 @@ private function executeUsingQueryCache($parameters = null, $hydrationMode = nul return $result; } - private function getTimestampKey(): ?TimestampCacheKey + private function getTimestampKey(): TimestampCacheKey|null { - assert($this->_resultSetMapping !== null); - $entityName = reset($this->_resultSetMapping->aliasMap); + assert($this->resultSetMapping !== null); + $entityName = reset($this->resultSetMapping->aliasMap); if (empty($entityName)) { return null; } - $metadata = $this->_em->getClassMetadata($entityName); + $metadata = $this->em->getClassMetadata($entityName); - return new Cache\TimestampCacheKey($metadata->rootEntityName); + return new TimestampCacheKey($metadata->rootEntityName); } /** @@ -1325,7 +1031,7 @@ private function getTimestampKey(): ?TimestampCacheKey * @return string[] ($key, $hash) * @phpstan-return array{string, string} ($key, $hash) */ - protected function getHydrationCacheId() + protected function getHydrationCacheId(): array { $parameters = []; $types = []; @@ -1351,34 +1057,18 @@ protected function getHydrationCacheId() * Set the result cache id to use to store the result set cache entry. * If this is not explicitly set by the developer then a hash is automatically * generated for you. - * - * @param string|null $id - * - * @return $this */ - public function setResultCacheId($id) + public function setResultCacheId(string|null $id): static { - if (! $this->_queryCacheProfile) { + if (! $this->queryCacheProfile) { return $this->setResultCacheProfile(new QueryCacheProfile(0, $id)); } - $this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id); + $this->queryCacheProfile = $this->queryCacheProfile->setCacheKey($id); return $this; } - /** - * Get the result cache id to use to store the result set cache entry if set. - * - * @deprecated - * - * @return string|null - */ - public function getResultCacheId() - { - return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null; - } - /** * Executes the query and returns a the resulting Statement object. * @@ -1386,27 +1076,23 @@ public function getResultCacheId() * the results, or an integer indicating how * many rows were affected. */ - abstract protected function _doExecute(); + abstract protected function _doExecute(): Result|int; /** * Cleanup Query resource when clone is called. - * - * @return void */ public function __clone() { $this->parameters = new ArrayCollection(); - $this->_hints = []; - $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + $this->hints = []; + $this->hints = $this->em->getConfiguration()->getDefaultQueryHints(); } /** * Generates a string of currently query to use for the cache second level cache. - * - * @return string */ - protected function getHash() + protected function getHash(): string { $query = $this->getSQL(); assert(is_string($query)); @@ -1427,10 +1113,4 @@ protected function getHash() return sha1($query . '-' . serialize($params) . '-' . serialize($hints)); } - - /** @param iterable $subject */ - private function isCountable(iterable $subject): bool - { - return $subject instanceof Countable || is_array($subject); - } } diff --git a/src/Cache.php b/src/Cache.php index 52c3903a0bb..8020b27546a 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -38,127 +38,69 @@ interface Cache */ public const MODE_REFRESH = 4; - /** - * @param string $className The entity class. - * - * @return Region|null - */ - public function getEntityCacheRegion($className); + public function getEntityCacheRegion(string $className): Region|null; - /** - * @param string $className The entity class. - * @param string $association The field name that represents the association. - * - * @return Region|null - */ - public function getCollectionCacheRegion($className, $association); + public function getCollectionCacheRegion(string $className, string $association): Region|null; /** * Determine whether the cache contains data for the given entity "instance". - * - * @param string $className The entity class. - * @param mixed $identifier The entity identifier - * - * @return bool true if the underlying cache contains corresponding data; false otherwise. */ - public function containsEntity($className, $identifier); + public function containsEntity(string $className, mixed $identifier): bool; /** * Evicts the entity data for a particular entity "instance". - * - * @param string $className The entity class. - * @param mixed $identifier The entity identifier. - * - * @return void */ - public function evictEntity($className, $identifier); + public function evictEntity(string $className, mixed $identifier): void; /** * Evicts all entity data from the given region. - * - * @param string $className The entity metadata. - * - * @return void */ - public function evictEntityRegion($className); + public function evictEntityRegion(string $className): void; /** * Evict data from all entity regions. - * - * @return void */ - public function evictEntityRegions(); + public function evictEntityRegions(): void; /** * Determine whether the cache contains data for the given collection. - * - * @param string $className The entity class. - * @param string $association The field name that represents the association. - * @param mixed $ownerIdentifier The identifier of the owning entity. - * - * @return bool true if the underlying cache contains corresponding data; false otherwise. */ - public function containsCollection($className, $association, $ownerIdentifier); + public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool; /** * Evicts the cache data for the given identified collection instance. - * - * @param string $className The entity class. - * @param string $association The field name that represents the association. - * @param mixed $ownerIdentifier The identifier of the owning entity. - * - * @return void */ - public function evictCollection($className, $association, $ownerIdentifier); + public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void; /** * Evicts all entity data from the given region. - * - * @param string $className The entity class. - * @param string $association The field name that represents the association. - * - * @return void */ - public function evictCollectionRegion($className, $association); + public function evictCollectionRegion(string $className, string $association): void; /** * Evict data from all collection regions. - * - * @return void */ - public function evictCollectionRegions(); + public function evictCollectionRegions(): void; /** * Determine whether the cache contains data for the given query. - * - * @param string $regionName The cache name given to the query. - * - * @return bool true if the underlying cache contains corresponding data; false otherwise. */ - public function containsQuery($regionName); + public function containsQuery(string $regionName): bool; /** * Evicts all cached query results under the given name, or default query cache if the region name is NULL. - * - * @param string|null $regionName The cache name associated to the queries being cached. - * - * @return void */ - public function evictQueryRegion($regionName = null); + public function evictQueryRegion(string|null $regionName = null): void; /** * Evict data from all query regions. - * - * @return void */ - public function evictQueryRegions(); + public function evictQueryRegions(): void; /** * Get query cache by region name or create a new one if none exist. * * @param string|null $regionName Query cache region name, or default query cache if the region name is NULL. - * - * @return QueryCache The Query Cache associated with the region name. */ - public function getQueryCache($regionName = null); + public function getQueryCache(string|null $regionName = null): QueryCache; } diff --git a/src/Cache/AssociationCacheEntry.php b/src/Cache/AssociationCacheEntry.php index 2e4fc61491b..7dc1fbeefc8 100644 --- a/src/Cache/AssociationCacheEntry.php +++ b/src/Cache/AssociationCacheEntry.php @@ -4,35 +4,16 @@ namespace Doctrine\ORM\Cache; -/** - * Association cache entry - */ class AssociationCacheEntry implements CacheEntry { /** - * The entity identifier - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var array - */ - public $identifier; - - /** - * The entity class name - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var class-string - */ - public $class; - - /** - * @param class-string $class The entity class. * @param array $identifier The entity identifier. + * @param class-string $class The entity class name */ - public function __construct($class, array $identifier) - { - $this->class = $class; - $this->identifier = $identifier; + public function __construct( + public readonly string $class, + public readonly array $identifier, + ) { } /** @@ -41,10 +22,8 @@ public function __construct($class, array $identifier) * This method allow Doctrine\Common\Cache\PhpFileCache compatibility * * @param array $values array containing property values - * - * @return AssociationCacheEntry */ - public static function __set_state(array $values) + public static function __set_state(array $values): self { return new self($values['class'], $values['identifier']); } diff --git a/src/Cache/CacheConfiguration.php b/src/Cache/CacheConfiguration.php index e69aa686cc4..0f8dea7682a 100644 --- a/src/Cache/CacheConfiguration.php +++ b/src/Cache/CacheConfiguration.php @@ -11,72 +11,49 @@ */ class CacheConfiguration { - /** @var CacheFactory|null */ - private $cacheFactory; + private CacheFactory|null $cacheFactory = null; + private RegionsConfiguration|null $regionsConfig = null; + private CacheLogger|null $cacheLogger = null; + private QueryCacheValidator|null $queryValidator = null; - /** @var RegionsConfiguration|null */ - private $regionsConfig; - - /** @var CacheLogger|null */ - private $cacheLogger; - - /** @var QueryCacheValidator|null */ - private $queryValidator; - - /** @return CacheFactory|null */ - public function getCacheFactory() + public function getCacheFactory(): CacheFactory|null { return $this->cacheFactory; } - /** @return void */ - public function setCacheFactory(CacheFactory $factory) + public function setCacheFactory(CacheFactory $factory): void { $this->cacheFactory = $factory; } - /** @return CacheLogger|null */ - public function getCacheLogger() + public function getCacheLogger(): CacheLogger|null { return $this->cacheLogger; } - /** @return void */ - public function setCacheLogger(CacheLogger $logger) + public function setCacheLogger(CacheLogger $logger): void { $this->cacheLogger = $logger; } - /** @return RegionsConfiguration */ - public function getRegionsConfiguration() + public function getRegionsConfiguration(): RegionsConfiguration { - if ($this->regionsConfig === null) { - $this->regionsConfig = new RegionsConfiguration(); - } - - return $this->regionsConfig; + return $this->regionsConfig ??= new RegionsConfiguration(); } - /** @return void */ - public function setRegionsConfiguration(RegionsConfiguration $regionsConfig) + public function setRegionsConfiguration(RegionsConfiguration $regionsConfig): void { $this->regionsConfig = $regionsConfig; } - /** @return QueryCacheValidator */ - public function getQueryValidator() + public function getQueryValidator(): QueryCacheValidator { - if ($this->queryValidator === null) { - $this->queryValidator = new TimestampQueryCacheValidator( - $this->cacheFactory->getTimestampRegion() - ); - } - - return $this->queryValidator; + return $this->queryValidator ??= new TimestampQueryCacheValidator( + $this->cacheFactory->getTimestampRegion(), + ); } - /** @return void */ - public function setQueryValidator(QueryCacheValidator $validator) + public function setQueryValidator(QueryCacheValidator $validator): void { $this->queryValidator = $validator; } diff --git a/src/Cache/CacheException.php b/src/Cache/CacheException.php index 0c3403609a8..b42209580dc 100644 --- a/src/Cache/CacheException.php +++ b/src/Cache/CacheException.php @@ -5,57 +5,22 @@ namespace Doctrine\ORM\Cache; use Doctrine\ORM\Exception\ORMException; +use LogicException; use function sprintf; /** * Exception for cache. */ -class CacheException extends ORMException +class CacheException extends LogicException implements ORMException { - /** - * @param string $sourceEntity - * @param string $fieldName - * - * @return CacheException - */ - public static function updateReadOnlyCollection($sourceEntity, $fieldName) + public static function updateReadOnlyCollection(string $sourceEntity, string $fieldName): self { return new self(sprintf('Cannot update a readonly collection "%s#%s"', $sourceEntity, $fieldName)); } - /** - * @deprecated This method is not used anymore. - * - * @param string $entityName - * - * @return CacheException - */ - public static function updateReadOnlyEntity($entityName) - { - return new self(sprintf('Cannot update a readonly entity "%s"', $entityName)); - } - - /** - * @param string $entityName - * - * @return CacheException - */ - public static function nonCacheableEntity($entityName) + public static function nonCacheableEntity(string $entityName): self { return new self(sprintf('Entity "%s" not configured as part of the second-level cache.', $entityName)); } - - /** - * @deprecated This method is not used anymore. - * - * @param string $entityName - * @param string $field - * - * @return CacheException - */ - public static function nonCacheableEntityAssociation($entityName, $field) - { - return new self(sprintf('Entity association field "%s#%s" not configured as part of the second-level cache.', $entityName, $field)); - } } diff --git a/src/Cache/CacheFactory.php b/src/Cache/CacheFactory.php index 6d4171e18e9..b15c23e8df4 100644 --- a/src/Cache/CacheFactory.php +++ b/src/Cache/CacheFactory.php @@ -8,82 +8,57 @@ use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister; use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\ORM\Persisters\Entity\EntityPersister; /** * Contract for building second level cache regions components. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ interface CacheFactory { /** * Build an entity persister for the given entity metadata. - * - * @param EntityManagerInterface $em The entity manager. - * @param EntityPersister $persister The entity persister that will be cached. - * @param ClassMetadata $metadata The entity metadata. - * - * @return CachedEntityPersister */ - public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata); + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister; - /** - * Build a collection persister for the given relation mapping. - * - * @param AssociationMapping $mapping The association mapping. - * - * @return CachedCollectionPersister - */ - public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping); + /** Build a collection persister for the given relation mapping. */ + public function buildCachedCollectionPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + AssociationMapping $mapping, + ): CachedCollectionPersister; /** * Build a query cache based on the given region name - * - * @param string|null $regionName The region name. - * - * @return QueryCache The built query cache. */ - public function buildQueryCache(EntityManagerInterface $em, $regionName = null); + public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache; /** * Build an entity hydrator - * - * @return EntityHydrator The built entity hydrator. */ - public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata); + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator; /** * Build a collection hydrator - * - * @param mixed[] $mapping The association mapping. - * - * @return CollectionHydrator The built collection hydrator. */ - public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping); + public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator; /** * Build a cache region * * @param array $cache The cache configuration. - * - * @return Region The cache region. */ - public function getRegion(array $cache); + public function getRegion(array $cache): Region; /** * Build timestamp cache region - * - * @return TimestampRegion The timestamp region. */ - public function getTimestampRegion(); + public function getTimestampRegion(): TimestampRegion; /** * Build \Doctrine\ORM\Cache - * - * @return Cache */ - public function createCache(EntityManagerInterface $entityManager); + public function createCache(EntityManagerInterface $entityManager): Cache; } diff --git a/src/Cache/CacheKey.php b/src/Cache/CacheKey.php index 59b4b297c11..970702cbcd1 100644 --- a/src/Cache/CacheKey.php +++ b/src/Cache/CacheKey.php @@ -4,33 +4,13 @@ namespace Doctrine\ORM\Cache; -use Doctrine\Deprecations\Deprecation; - /** * Defines entity / collection / query key to be stored in the cache region. * Allows multiple roles to be stored in the same cache region. */ abstract class CacheKey { - /** - * Unique identifier - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var string - */ - public $hash; - - public function __construct(?string $hash = null) + public function __construct(public readonly string $hash) { - if ($hash === null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10212', - 'Calling %s() without providing a value for the $hash parameter is deprecated.', - __METHOD__ - ); - } else { - $this->hash = $hash; - } } } diff --git a/src/Cache/CollectionCacheEntry.php b/src/Cache/CollectionCacheEntry.php index d9ee6ee4d7c..fde45759312 100644 --- a/src/Cache/CollectionCacheEntry.php +++ b/src/Cache/CollectionCacheEntry.php @@ -4,23 +4,11 @@ namespace Doctrine\ORM\Cache; -/** - * Collection cache entry - */ class CollectionCacheEntry implements CacheEntry { - /** - * The list of entity identifiers hold by the collection - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var CacheKey[] - */ - public $identifiers; - /** @param CacheKey[] $identifiers List of entity identifiers hold by the collection */ - public function __construct(array $identifiers) + public function __construct(public readonly array $identifiers) { - $this->identifiers = $identifiers; } /** @@ -29,10 +17,8 @@ public function __construct(array $identifiers) * This method allows for Doctrine\Common\Cache\PhpFileCache compatibility * * @param array $values array containing property values - * - * @return CollectionCacheEntry */ - public static function __set_state(array $values) + public static function __set_state(array $values): CollectionCacheEntry { return new self($values['identifiers']); } diff --git a/src/Cache/CollectionCacheKey.php b/src/Cache/CollectionCacheKey.php index b6c1ae803f8..c7f96dfc8f1 100644 --- a/src/Cache/CollectionCacheKey.php +++ b/src/Cache/CollectionCacheKey.php @@ -17,39 +17,23 @@ class CollectionCacheKey extends CacheKey /** * The owner entity identifier * - * @readonly Public only for performance reasons, it should be considered immutable. * @var array */ - public $ownerIdentifier; + public readonly array $ownerIdentifier; /** - * The owner entity class - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var class-string - */ - public $entityClass; - - /** - * The association name - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var string - */ - public $association; - - /** - * @param class-string $entityClass The entity class. - * @param string $association The field name that represents the association. + * @param class-string $entityClass The owner entity class. * @param array $ownerIdentifier The identifier of the owning entity. */ - public function __construct($entityClass, $association, array $ownerIdentifier, string $filterHash = '') - { + public function __construct( + public readonly string $entityClass, + public readonly string $association, + array $ownerIdentifier, + string $filterHash = '', + ) { ksort($ownerIdentifier); $this->ownerIdentifier = $ownerIdentifier; - $this->entityClass = (string) $entityClass; - $this->association = (string) $association; $filterHash = $filterHash === '' ? '' : '_' . $filterHash; diff --git a/src/Cache/CollectionHydrator.php b/src/Cache/CollectionHydrator.php index 27d71b82c3a..16a6572aa42 100644 --- a/src/Cache/CollectionHydrator.php +++ b/src/Cache/CollectionHydrator.php @@ -13,13 +13,9 @@ */ interface CollectionHydrator { - /** - * @param array|mixed[]|Collection $collection The collection. - * - * @return CollectionCacheEntry - */ - public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection); + /** @param mixed[]|Collection $collection The collection. */ + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry; /** @return mixed[]|null */ - public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection); + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null; } diff --git a/src/Cache/ConcurrentRegion.php b/src/Cache/ConcurrentRegion.php index 10935795296..e9ca8707422 100644 --- a/src/Cache/ConcurrentRegion.php +++ b/src/Cache/ConcurrentRegion.php @@ -22,7 +22,7 @@ interface ConcurrentRegion extends Region * * @throws LockException Indicates a problem accessing the region. */ - public function lock(CacheKey $key); + public function lock(CacheKey $key): Lock|null; /** * Attempts to read unlock the mapping for the given key. @@ -30,9 +30,7 @@ public function lock(CacheKey $key); * @param CacheKey $key The key of the item to unlock. * @param Lock $lock The lock previously obtained from {@link readLock} * - * @return bool - * * @throws LockException Indicates a problem accessing the region. */ - public function unlock(CacheKey $key, Lock $lock); + public function unlock(CacheKey $key, Lock $lock): bool; } diff --git a/src/Cache/DefaultCache.php b/src/Cache/DefaultCache.php index d318dd007d8..3f947cf773b 100644 --- a/src/Cache/DefaultCache.php +++ b/src/Cache/DefaultCache.php @@ -20,37 +20,27 @@ */ class DefaultCache implements Cache { - /** @var EntityManagerInterface */ - private $em; - - /** @var UnitOfWork */ - private $uow; - - /** @var CacheFactory */ - private $cacheFactory; + private readonly UnitOfWork $uow; + private readonly CacheFactory $cacheFactory; /** * @var QueryCache[] * @phpstan-var array */ - private $queryCaches = []; + private array $queryCaches = []; - /** @var QueryCache|null */ - private $defaultQueryCache; + private QueryCache|null $defaultQueryCache = null; - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + private readonly EntityManagerInterface $em, + ) { $this->uow = $em->getUnitOfWork(); $this->cacheFactory = $em->getConfiguration() ->getSecondLevelCacheConfiguration() ->getCacheFactory(); } - /** - * {@inheritDoc} - */ - public function getEntityCacheRegion($className) + public function getEntityCacheRegion(string $className): Region|null { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getEntityPersister($metadata->rootEntityName); @@ -62,10 +52,7 @@ public function getEntityCacheRegion($className) return $persister->getCacheRegion(); } - /** - * {@inheritDoc} - */ - public function getCollectionCacheRegion($className, $association) + public function getCollectionCacheRegion(string $className, string $association): Region|null { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); @@ -77,10 +64,7 @@ public function getCollectionCacheRegion($className, $association) return $persister->getCacheRegion(); } - /** - * {@inheritDoc} - */ - public function containsEntity($className, $identifier) + public function containsEntity(string $className, mixed $identifier): bool { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getEntityPersister($metadata->rootEntityName); @@ -92,10 +76,7 @@ public function containsEntity($className, $identifier) return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier)); } - /** - * {@inheritDoc} - */ - public function evictEntity($className, $identifier) + public function evictEntity(string $className, mixed $identifier): void { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getEntityPersister($metadata->rootEntityName); @@ -107,10 +88,7 @@ public function evictEntity($className, $identifier) $persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier)); } - /** - * {@inheritDoc} - */ - public function evictEntityRegion($className) + public function evictEntityRegion(string $className): void { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getEntityPersister($metadata->rootEntityName); @@ -122,10 +100,7 @@ public function evictEntityRegion($className) $persister->getCacheRegion()->evictAll(); } - /** - * {@inheritDoc} - */ - public function evictEntityRegions() + public function evictEntityRegions(): void { $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); @@ -140,10 +115,7 @@ public function evictEntityRegions() } } - /** - * {@inheritDoc} - */ - public function containsCollection($className, $association, $ownerIdentifier) + public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); @@ -155,10 +127,7 @@ public function containsCollection($className, $association, $ownerIdentifier) return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); } - /** - * {@inheritDoc} - */ - public function evictCollection($className, $association, $ownerIdentifier) + public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); @@ -170,10 +139,7 @@ public function evictCollection($className, $association, $ownerIdentifier) $persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); } - /** - * {@inheritDoc} - */ - public function evictCollectionRegion($className, $association) + public function evictCollectionRegion(string $className, string $association): void { $metadata = $this->em->getClassMetadata($className); $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); @@ -185,16 +151,13 @@ public function evictCollectionRegion($className, $association) $persister->getCacheRegion()->evictAll(); } - /** - * {@inheritDoc} - */ - public function evictCollectionRegions() + public function evictCollectionRegions(): void { $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); foreach ($metadatas as $metadata) { foreach ($metadata->associationMappings as $association) { - if (! $association['type'] & ClassMetadata::TO_MANY) { + if (! $association->isToMany()) { continue; } @@ -209,18 +172,12 @@ public function evictCollectionRegions() } } - /** - * {@inheritDoc} - */ - public function containsQuery($regionName) + public function containsQuery(string $regionName): bool { return isset($this->queryCaches[$regionName]); } - /** - * {@inheritDoc} - */ - public function evictQueryRegion($regionName = null) + public function evictQueryRegion(string|null $regionName = null): void { if ($regionName === null && $this->defaultQueryCache !== null) { $this->defaultQueryCache->clear(); @@ -233,10 +190,7 @@ public function evictQueryRegion($regionName = null) } } - /** - * {@inheritDoc} - */ - public function evictQueryRegions() + public function evictQueryRegions(): void { $this->getQueryCache()->clear(); @@ -245,25 +199,16 @@ public function evictQueryRegions() } } - /** - * {@inheritDoc} - */ - public function getQueryCache($regionName = null) + public function getQueryCache(string|null $regionName = null): QueryCache { if ($regionName === null) { - return $this->defaultQueryCache ?: - $this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em); - } - - if (! isset($this->queryCaches[$regionName])) { - $this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName); + return $this->defaultQueryCache ??= $this->cacheFactory->buildQueryCache($this->em); } - return $this->queryCaches[$regionName]; + return $this->queryCaches[$regionName] ??= $this->cacheFactory->buildQueryCache($this->em, $regionName); } - /** @param mixed $identifier The entity identifier. */ - private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): EntityCacheKey + private function buildEntityCacheKey(ClassMetadata $metadata, mixed $identifier): EntityCacheKey { if (! is_array($identifier)) { $identifier = $this->toIdentifierArray($metadata, $identifier); @@ -272,11 +217,10 @@ private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): Enti return new EntityCacheKey($metadata->rootEntityName, $identifier); } - /** @param mixed $ownerIdentifier The identifier of the owning entity. */ private function buildCollectionCacheKey( ClassMetadata $metadata, string $association, - $ownerIdentifier + mixed $ownerIdentifier, ): CollectionCacheKey { if (! is_array($ownerIdentifier)) { $ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier); @@ -285,21 +229,14 @@ private function buildCollectionCacheKey( return new CollectionCacheKey($metadata->rootEntityName, $association, $ownerIdentifier); } - /** - * @param mixed $identifier The entity identifier. - * - * @return array - */ - private function toIdentifierArray(ClassMetadata $metadata, $identifier): array + /** @return array */ + private function toIdentifierArray(ClassMetadata $metadata, mixed $identifier): array { if (is_object($identifier)) { $class = DefaultProxyClassNameResolver::getClass($identifier); if ($this->em->getMetadataFactory()->hasMetadataFor($class)) { - $identifier = $this->uow->getSingleIdentifierValue($identifier); - - if ($identifier === null) { - throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class); - } + $identifier = $this->uow->getSingleIdentifierValue($identifier) + ?? throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class); } } diff --git a/src/Cache/DefaultCacheFactory.php b/src/Cache/DefaultCacheFactory.php index 091f236330f..84ea490f95f 100644 --- a/src/Cache/DefaultCacheFactory.php +++ b/src/Cache/DefaultCacheFactory.php @@ -4,13 +4,12 @@ namespace Doctrine\ORM\Cache; -use Doctrine\Common\Cache\Cache as LegacyCache; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister; use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister; use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister; use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister; use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister; use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister; use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister; @@ -18,97 +17,53 @@ use Doctrine\ORM\Cache\Region\FileLockRegion; use Doctrine\ORM\Cache\Region\UpdateTimestampCache; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\ORM\Persisters\Entity\EntityPersister; use InvalidArgumentException; use LogicException; use Psr\Cache\CacheItemPoolInterface; -use TypeError; use function assert; -use function get_debug_type; use function sprintf; use const DIRECTORY_SEPARATOR; class DefaultCacheFactory implements CacheFactory { - /** @var CacheItemPoolInterface */ - private $cacheItemPool; - - /** @var RegionsConfiguration */ - private $regionsConfig; - - /** @var TimestampRegion|null */ - private $timestampRegion; + private TimestampRegion|null $timestampRegion = null; /** @var Region[] */ - private $regions = []; + private array $regions = []; - /** @var string|null */ - private $fileLockRegionDirectory; + private string|null $fileLockRegionDirectory = null; - /** @param CacheItemPoolInterface $cacheItemPool */ - public function __construct(RegionsConfiguration $cacheConfig, $cacheItemPool) + public function __construct(private readonly RegionsConfiguration $regionsConfig, private readonly CacheItemPoolInterface $cacheItemPool) { - if ($cacheItemPool instanceof LegacyCache) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9322', - 'Passing an instance of %s to %s is deprecated, pass a %s instead.', - get_debug_type($cacheItemPool), - __METHOD__, - CacheItemPoolInterface::class - ); - - $this->cacheItemPool = CacheAdapter::wrap($cacheItemPool); - } elseif (! $cacheItemPool instanceof CacheItemPoolInterface) { - throw new TypeError(sprintf( - '%s: Parameter #2 is expected to be an instance of %s, got %s.', - __METHOD__, - CacheItemPoolInterface::class, - get_debug_type($cacheItemPool) - )); - } else { - $this->cacheItemPool = $cacheItemPool; - } - - $this->regionsConfig = $cacheConfig; } - /** - * @param string $fileLockRegionDirectory - * - * @return void - */ - public function setFileLockRegionDirectory($fileLockRegionDirectory) + public function setFileLockRegionDirectory(string $fileLockRegionDirectory): void { - $this->fileLockRegionDirectory = (string) $fileLockRegionDirectory; + $this->fileLockRegionDirectory = $fileLockRegionDirectory; } - /** @return string */ - public function getFileLockRegionDirectory() + public function getFileLockRegionDirectory(): string|null { return $this->fileLockRegionDirectory; } - /** @return void */ - public function setRegion(Region $region) + public function setRegion(Region $region): void { $this->regions[$region->getName()] = $region; } - /** @return void */ - public function setTimestampRegion(TimestampRegion $region) + public function setTimestampRegion(TimestampRegion $region): void { $this->timestampRegion = $region; } - /** - * {@inheritDoc} - */ - public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata) + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister { assert($metadata->cache !== null); $region = $this->getRegion($metadata->cache); @@ -133,14 +88,14 @@ public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPer throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage)); } - /** - * {@inheritDoc} - */ - public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping) - { - assert(isset($mapping['cache'])); - $usage = $mapping['cache']['usage']; - $region = $this->getRegion($mapping['cache']); + public function buildCachedCollectionPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + AssociationMapping $mapping, + ): CachedCollectionPersister { + assert(isset($mapping->cache)); + $usage = $mapping->cache['usage']; + $region = $this->getRegion($mapping->cache); if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) { return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping); @@ -161,10 +116,7 @@ public function buildCachedCollectionPersister(EntityManagerInterface $em, Colle throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage)); } - /** - * {@inheritDoc} - */ - public function buildQueryCache(EntityManagerInterface $em, $regionName = null) + public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache { return new DefaultQueryCache( $em, @@ -172,23 +124,17 @@ public function buildQueryCache(EntityManagerInterface $em, $regionName = null) [ 'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME, 'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE, - ] - ) + ], + ), ); } - /** - * {@inheritDoc} - */ - public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping) + public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator { return new DefaultCollectionHydrator($em); } - /** - * {@inheritDoc} - */ - public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata) + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator { return new DefaultEntityHydrator($em); } @@ -196,7 +142,7 @@ public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $m /** * {@inheritDoc} */ - public function getRegion(array $cache) + public function getRegion(array $cache): Region { if (isset($this->regions[$cache['region']])) { return $this->regions[$cache['region']]; @@ -213,7 +159,7 @@ public function getRegion(array $cache) ) { throw new LogicException( 'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' . - 'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ' + 'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ', ); } @@ -224,10 +170,7 @@ public function getRegion(array $cache) return $this->regions[$cache['region']] = $region; } - /** - * {@inheritDoc} - */ - public function getTimestampRegion() + public function getTimestampRegion(): TimestampRegion { if ($this->timestampRegion === null) { $name = Cache::DEFAULT_TIMESTAMP_REGION_NAME; @@ -239,10 +182,7 @@ public function getTimestampRegion() return $this->timestampRegion; } - /** - * {@inheritDoc} - */ - public function createCache(EntityManagerInterface $entityManager) + public function createCache(EntityManagerInterface $entityManager): Cache { return new DefaultCache($entityManager); } diff --git a/src/Cache/DefaultCollectionHydrator.php b/src/Cache/DefaultCollectionHydrator.php index 69f34a99658..249d48f7d4f 100644 --- a/src/Cache/DefaultCollectionHydrator.php +++ b/src/Cache/DefaultCollectionHydrator.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Cache; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Cache\Persister\CachedPersister; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; @@ -18,26 +19,18 @@ */ class DefaultCollectionHydrator implements CollectionHydrator { - /** @var EntityManagerInterface */ - private $em; - - /** @var UnitOfWork */ - private $uow; + private readonly UnitOfWork $uow; /** @var array */ - private static $hints = [Query::HINT_CACHE_ENABLED => true]; + private static array $hints = [Query::HINT_CACHE_ENABLED => true]; - /** @param EntityManagerInterface $em The entity manager. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + private readonly EntityManagerInterface $em, + ) { $this->uow = $em->getUnitOfWork(); } - /** - * {@inheritDoc} - */ - public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection) + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry { $data = []; @@ -48,13 +41,10 @@ public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key return new CollectionCacheEntry($data); } - /** - * {@inheritDoc} - */ - public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection) + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null { $assoc = $metadata->associationMappings[$key->association]; - $targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $targetPersister = $this->uow->getEntityPersister($assoc->targetEntity); assert($targetPersister instanceof CachedPersister); $targetRegion = $targetPersister->getCacheRegion(); $list = []; @@ -70,7 +60,7 @@ public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $entity = $this->uow->createEntity( $entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), - self::$hints + self::$hints, ); $collection->hydrateSet($index, $entity); diff --git a/src/Cache/DefaultEntityHydrator.php b/src/Cache/DefaultEntityHydrator.php index d97d805a393..6bd15246abb 100644 --- a/src/Cache/DefaultEntityHydrator.php +++ b/src/Cache/DefaultEntityHydrator.php @@ -11,7 +11,6 @@ use Doctrine\ORM\UnitOfWork; use Doctrine\ORM\Utility\IdentifierFlattener; -use function array_merge; use function assert; use function is_array; use function is_object; @@ -22,37 +21,23 @@ */ class DefaultEntityHydrator implements EntityHydrator { - /** @var EntityManagerInterface */ - private $em; - - /** @var UnitOfWork */ - private $uow; - - /** - * The IdentifierFlattener used for manipulating identifiers - * - * @var IdentifierFlattener - */ - private $identifierFlattener; + private readonly UnitOfWork $uow; + private readonly IdentifierFlattener $identifierFlattener; /** @var array */ - private static $hints = [Query::HINT_CACHE_ENABLED => true]; + private static array $hints = [Query::HINT_CACHE_ENABLED => true]; - /** @param EntityManagerInterface $em The entity manager. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + private readonly EntityManagerInterface $em, + ) { $this->uow = $em->getUnitOfWork(); $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); } - /** - * {@inheritDoc} - */ - public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity) + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry { $data = $this->uow->getOriginalEntityData($entity); - $data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ? + $data = [...$data, ...$metadata->getIdentifierValues($entity)]; // why update has no identifier values ? if ($metadata->requiresFetchAfterChange) { if ($metadata->isVersioned) { @@ -61,7 +46,7 @@ public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $e } foreach ($metadata->fieldMappings as $name => $fieldMapping) { - if (isset($fieldMapping['generated'])) { + if (isset($fieldMapping->generated)) { $data[$name] = $metadata->getFieldValue($entity, $name); } } @@ -72,37 +57,37 @@ public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $e continue; } - if (! ($assoc['type'] & ClassMetadata::TO_ONE)) { + if (! $assoc->isToOne()) { unset($data[$name]); continue; } - if (! isset($assoc['cache'])) { - $targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']); - $owningAssociation = ! $assoc['isOwningSide'] - ? $targetClassMetadata->associationMappings[$assoc['mappedBy']] - : $assoc; + if (! isset($assoc->cache)) { + $targetClassMetadata = $this->em->getClassMetadata($assoc->targetEntity); + $owningAssociation = $this->em->getMetadataFactory()->getOwningSide($assoc); $associationIds = $this->identifierFlattener->flattenIdentifier( $targetClassMetadata, - $targetClassMetadata->getIdentifierValues($data[$name]) + $targetClassMetadata->getIdentifierValues($data[$name]), ); unset($data[$name]); foreach ($associationIds as $fieldName => $fieldValue) { if (isset($targetClassMetadata->fieldMappings[$fieldName])) { + assert($owningAssociation->isToOneOwningSide()); $fieldMapping = $targetClassMetadata->fieldMappings[$fieldName]; - $data[$owningAssociation['targetToSourceKeyColumns'][$fieldMapping['columnName']]] = $fieldValue; + $data[$owningAssociation->targetToSourceKeyColumns[$fieldMapping->columnName]] = $fieldValue; continue; } $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; - foreach ($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { - if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { + assert($assoc->isToOneOwningSide()); + foreach ($assoc->targetToSourceKeyColumns as $referencedColumn => $localColumn) { + if (isset($targetAssoc->sourceToTargetKeyColumns[$referencedColumn])) { $data[$localColumn] = $fieldValue; } } @@ -111,7 +96,7 @@ public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $e continue; } - if (! isset($assoc['id'])) { + if (! isset($assoc->id)) { $targetClass = DefaultProxyClassNameResolver::getClass($data[$name]); $targetId = $this->uow->getEntityIdentifier($data[$name]); $data[$name] = new AssociationCacheEntry($targetClass, $targetId); @@ -127,22 +112,20 @@ public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $e // @TODO - fix it ! // handle UnitOfWork#createEntity hash generation if (! is_array($targetId)) { - $data[reset($assoc['joinColumnFieldNames'])] = $targetId; + assert($assoc->isToOneOwningSide()); + $data[reset($assoc->joinColumnFieldNames)] = $targetId; - $targetEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $targetEntity = $this->em->getClassMetadata($assoc->targetEntity); $targetId = [$targetEntity->identifier[0] => $targetId]; } - $data[$name] = new AssociationCacheEntry($assoc['targetEntity'], $targetId); + $data[$name] = new AssociationCacheEntry($assoc->targetEntity, $targetId); } return new EntityCacheEntry($metadata->name, $data); } - /** - * {@inheritDoc} - */ - public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null) + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null { $data = $entry->data; $hints = self::$hints; @@ -153,13 +136,13 @@ public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, Ent } foreach ($metadata->associationMappings as $name => $assoc) { - if (! isset($assoc['cache']) || ! isset($data[$name])) { + if (! isset($assoc->cache) || ! isset($data[$name])) { continue; } $assocClass = $data[$name]->class; $assocId = $data[$name]->identifier; - $isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide'])); + $isEagerLoad = ($assoc->fetch === ClassMetadata::FETCH_EAGER || ($assoc->isOneToOne() && ! $assoc->isOwningSide())); if (! $isEagerLoad) { $data[$name] = $this->em->getReference($assocClass, $assocId); @@ -167,9 +150,9 @@ public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, Ent continue; } - $assocMetadata = $this->em->getClassMetadata($assoc['targetEntity']); + $assocMetadata = $this->em->getClassMetadata($assoc->targetEntity); $assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocId); - $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity); $assocRegion = $assocPersister->getCacheRegion(); $assocEntry = $assocRegion->get($assocKey); diff --git a/src/Cache/DefaultQueryCache.php b/src/Cache/DefaultQueryCache.php index 6be68da4a0d..5f280755ecb 100644 --- a/src/Cache/DefaultQueryCache.php +++ b/src/Cache/DefaultQueryCache.php @@ -11,10 +11,12 @@ use Doctrine\ORM\Cache\Logging\CacheLogger; use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Query; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\UnitOfWork; use function array_map; @@ -28,39 +30,22 @@ /** * Default query cache implementation. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ class DefaultQueryCache implements QueryCache { - /** @var EntityManagerInterface */ - private $em; - - /** @var UnitOfWork */ - private $uow; - - /** @var Region */ - private $region; - - /** @var QueryCacheValidator */ - private $validator; - - /** @var CacheLogger|null */ - protected $cacheLogger; + private readonly UnitOfWork $uow; + private readonly QueryCacheValidator $validator; + protected CacheLogger|null $cacheLogger = null; /** @var array */ - private static $hints = [Query::HINT_CACHE_ENABLED => true]; + private static array $hints = [Query::HINT_CACHE_ENABLED => true]; - /** - * @param EntityManagerInterface $em The entity manager. - * @param Region $region The query region. - */ - public function __construct(EntityManagerInterface $em, Region $region) - { + public function __construct( + private readonly EntityManagerInterface $em, + private readonly Region $region, + ) { $cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration(); - $this->em = $em; - $this->region = $region; $this->uow = $em->getUnitOfWork(); $this->cacheLogger = $cacheConfig->getCacheLogger(); $this->validator = $cacheConfig->getQueryValidator(); @@ -69,7 +54,7 @@ public function __construct(EntityManagerInterface $em, Region $region) /** * {@inheritDoc} */ - public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []) + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null { if (! ($key->cacheMode & Cache::MODE_GET)) { return null; @@ -98,9 +83,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $cm = $this->em->getClassMetadata($entityName); - $generateKeys = static function (array $entry) use ($cm): EntityCacheKey { - return new EntityCacheKey($cm->rootEntityName, $entry['identifier']); - }; + $generateKeys = static fn (array $entry): EntityCacheKey => new EntityCacheKey($cm->rootEntityName, $entry['identifier']); $cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $cacheEntry->result)); $entries = $region->getMultiple($cacheKeys) ?? []; @@ -110,16 +93,12 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $entityEntry = $entries[$index] ?? null; if (! $entityEntry instanceof EntityCacheEntry) { - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheMiss($regionName, $cacheKeys->identifiers[$index]); - } + $this->cacheLogger?->entityCacheMiss($regionName, $cacheKeys->identifiers[$index]); return null; } - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheHit($regionName, $cacheKeys->identifiers[$index]); - } + $this->cacheLogger?->entityCacheHit($regionName, $cacheKeys->identifiers[$index]); if (! $hasRelation) { $result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); @@ -141,9 +120,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $assocEntry = $assocRegion->get($assocKey); if ($assocEntry === null) { - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey); - } + $this->cacheLogger?->entityCacheMiss($assocRegion->getName(), $assocKey); $this->uow->hydrationComplete(); @@ -152,9 +129,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); - } + $this->cacheLogger?->entityCacheHit($assocRegion->getName(), $assocKey); continue; } @@ -163,9 +138,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] continue; } - $generateKeys = static function ($id) use ($assocMetadata): EntityCacheKey { - return new EntityCacheKey($assocMetadata->rootEntityName, $id); - }; + $generateKeys = static fn (array $id): EntityCacheKey => new EntityCacheKey($assocMetadata->rootEntityName, $id); $collection = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection()); $assocKeys = new CollectionCacheEntry(array_map($generateKeys, $assoc['list'])); @@ -175,9 +148,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $assocEntry = is_array($assocEntries) ? ($assocEntries[$assocIndex] ?? null) : null; if ($assocEntry === null) { - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]); - } + $this->cacheLogger?->entityCacheMiss($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]); $this->uow->hydrationComplete(); @@ -188,9 +159,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] $collection->hydrateSet($assocIndex, $element); - if ($this->cacheLogger !== null) { - $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]); - } + $this->cacheLogger?->entityCacheHit($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]); } $data[$name] = $collection; @@ -212,7 +181,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] if ($unCachedAssociationData instanceof AssociationCacheEntry) { $data[$fieldName] = $this->em->getReference( $unCachedAssociationData->class, - $unCachedAssociationData->identifier + $unCachedAssociationData->identifier, ); } } @@ -228,7 +197,7 @@ public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [] /** * {@inheritDoc} */ - public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = []) + public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool { if ($rsm->scalarMappings) { throw FeatureNotImplemented::scalarResults(); @@ -242,7 +211,7 @@ public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $h throw FeatureNotImplemented::nonSelectStatements(); } - if (($hints[Query\SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) { + if (($hints[SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) { throw FeatureNotImplemented::partialEntities(); } @@ -326,20 +295,17 @@ public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $h } /** - * @param AssociationMapping $assoc - * @param mixed $assocValue - * * @return mixed[]|null * @phpstan-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null */ - private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue): ?array + private function storeAssociationCache(QueryCacheKey $key, AssociationMapping $assoc, mixed $assocValue): array|null { - $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity); $assocMetadata = $assocPersister->getClassMetadata(); $assocRegion = $assocPersister->getCacheRegion(); // Handle *-to-one associations - if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc->isToOne()) { $assocIdentifier = $this->uow->getEntityIdentifier($assocValue); $entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier); @@ -353,7 +319,7 @@ private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocV return [ 'targetEntity' => $assocMetadata->rootEntityName, 'identifier' => $assocIdentifier, - 'type' => $assoc['type'], + 'type' => $assoc->type(), ]; } @@ -376,22 +342,17 @@ private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocV return [ 'targetEntity' => $assocMetadata->rootEntityName, - 'type' => $assoc['type'], + 'type' => $assoc->type(), 'list' => $list, ]; } - /** - * @param object $entity - * - * @return mixed[]|object|null - * @phpstan-return list|object|null - */ + /** @phpstan-return list|object|null */ private function getAssociationValue( ResultSetMapping $rsm, string $assocAlias, - $entity - ) { + object $entity, + ): array|object|null { $path = []; $alias = $assocAlias; @@ -412,13 +373,11 @@ private function getAssociationValue( } /** - * @param mixed $value * @phpstan-param array $path * - * @return mixed[]|object|null * @phpstan-return list|object|null */ - private function getAssociationPathValue($value, array $path) + private function getAssociationPathValue(mixed $value, array $path): array|object|null { $mapping = array_shift($path); $metadata = $this->em->getClassMetadata($mapping['class']); @@ -434,7 +393,7 @@ private function getAssociationPathValue($value, array $path) } // Handle *-to-one associations - if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc->isToOne()) { return $this->getAssociationPathValue($value, $path); } @@ -447,18 +406,12 @@ private function getAssociationPathValue($value, array $path) return $values; } - /** - * {@inheritDoc} - */ - public function clear() + public function clear(): bool { return $this->region->evictAll(); } - /** - * {@inheritDoc} - */ - public function getRegion() + public function getRegion(): Region { return $this->region; } diff --git a/src/Cache/EntityCacheEntry.php b/src/Cache/EntityCacheEntry.php index 40382270559..c66a1834bec 100644 --- a/src/Cache/EntityCacheEntry.php +++ b/src/Cache/EntityCacheEntry.php @@ -8,47 +8,26 @@ use function array_map; -/** - * Entity cache entry - */ class EntityCacheEntry implements CacheEntry { /** - * The entity map data - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var array - */ - public $data; - - /** - * The entity class name - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var class-string + * @param class-string $class The entity class name + * @param array $data The entity map data */ - public $class; - - /** - * @param class-string $class The entity class. - * @param array $data The entity data. - */ - public function __construct($class, array $data) - { - $this->class = $class; - $this->data = $data; + public function __construct( + public readonly string $class, + public readonly array $data, + ) { } /** * Creates a new EntityCacheEntry * - * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * This method allows Doctrine\Common\Cache\PhpFileCache compatibility * * @param array $values array containing property values - * - * @return EntityCacheEntry */ - public static function __set_state(array $values) + public static function __set_state(array $values): self { return new self($values['class'], $values['data']); } @@ -58,7 +37,7 @@ public static function __set_state(array $values) * * @return array */ - public function resolveAssociationEntries(EntityManagerInterface $em) + public function resolveAssociationEntries(EntityManagerInterface $em): array { return array_map(static function ($value) use ($em) { if (! ($value instanceof AssociationCacheEntry)) { diff --git a/src/Cache/EntityCacheKey.php b/src/Cache/EntityCacheKey.php index 30872864010..095ddaaf198 100644 --- a/src/Cache/EntityCacheKey.php +++ b/src/Cache/EntityCacheKey.php @@ -17,29 +17,21 @@ class EntityCacheKey extends CacheKey /** * The entity identifier * - * @readonly Public only for performance reasons, it should be considered immutable. * @var array */ - public $identifier; - - /** - * The entity class name - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var class-string - */ - public $entityClass; + public readonly array $identifier; /** * @param class-string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class. * @param array $identifier The entity identifier */ - public function __construct($entityClass, array $identifier) - { + public function __construct( + public readonly string $entityClass, + array $identifier, + ) { ksort($identifier); - $this->identifier = $identifier; - $this->entityClass = $entityClass; + $this->identifier = $identifier; parent::__construct(str_replace('\\', '.', strtolower($entityClass) . '_' . implode(' ', $identifier))); } diff --git a/src/Cache/EntityHydrator.php b/src/Cache/EntityHydrator.php index 9b3286264d7..13cd21f3b37 100644 --- a/src/Cache/EntityHydrator.php +++ b/src/Cache/EntityHydrator.php @@ -15,18 +15,14 @@ interface EntityHydrator * @param ClassMetadata $metadata The entity metadata. * @param EntityCacheKey $key The entity cache key. * @param object $entity The entity. - * - * @return EntityCacheEntry */ - public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity); + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry; /** * @param ClassMetadata $metadata The entity metadata. * @param EntityCacheKey $key The entity cache key. * @param EntityCacheEntry $entry The entity cache entry. * @param object|null $entity The entity to load the cache into. If not specified, a new entity is created. - * - * @return object|null */ - public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null); + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null; } diff --git a/src/Cache/Exception/CannotUpdateReadOnlyCollection.php b/src/Cache/Exception/CannotUpdateReadOnlyCollection.php index ad6a21daa7c..7ecb4fe7b76 100644 --- a/src/Cache/Exception/CannotUpdateReadOnlyCollection.php +++ b/src/Cache/Exception/CannotUpdateReadOnlyCollection.php @@ -13,7 +13,7 @@ public static function fromEntityAndField(string $sourceEntity, string $fieldNam return new self(sprintf( 'Cannot update a readonly collection "%s#%s"', $sourceEntity, - $fieldName + $fieldName, )); } } diff --git a/src/Cache/Exception/InvalidResultCacheDriver.php b/src/Cache/Exception/InvalidResultCacheDriver.php deleted file mode 100644 index 4d23f46f1f8..00000000000 --- a/src/Cache/Exception/InvalidResultCacheDriver.php +++ /dev/null @@ -1,16 +0,0 @@ -value = $value; - $this->time = $time ?: time(); + public function __construct( + public string $value, + int|null $time = null, + ) { + $this->time = $time ?? time(); } - /** @return Lock */ - public static function createLockRead() + public static function createLockRead(): Lock { return new self(uniqid((string) time(), true)); } diff --git a/src/Cache/Logging/CacheLogger.php b/src/Cache/Logging/CacheLogger.php index 60db34a998e..64c97c1b0d1 100644 --- a/src/Cache/Logging/CacheLogger.php +++ b/src/Cache/Logging/CacheLogger.php @@ -15,91 +15,46 @@ interface CacheLogger { /** * Log an entity put into second level cache. - * - * @param string $regionName The name of the cache region. - * @param EntityCacheKey $key The cache key of the entity. - * - * @return void */ - public function entityCachePut($regionName, EntityCacheKey $key); + public function entityCachePut(string $regionName, EntityCacheKey $key): void; /** * Log an entity get from second level cache resulted in a hit. - * - * @param string $regionName The name of the cache region. - * @param EntityCacheKey $key The cache key of the entity. - * - * @return void */ - public function entityCacheHit($regionName, EntityCacheKey $key); + public function entityCacheHit(string $regionName, EntityCacheKey $key): void; /** * Log an entity get from second level cache resulted in a miss. - * - * @param string $regionName The name of the cache region. - * @param EntityCacheKey $key The cache key of the entity. - * - * @return void */ - public function entityCacheMiss($regionName, EntityCacheKey $key); + public function entityCacheMiss(string $regionName, EntityCacheKey $key): void; /** * Log an entity put into second level cache. - * - * @param string $regionName The name of the cache region. - * @param CollectionCacheKey $key The cache key of the collection. - * - * @return void */ - public function collectionCachePut($regionName, CollectionCacheKey $key); + public function collectionCachePut(string $regionName, CollectionCacheKey $key): void; /** * Log an entity get from second level cache resulted in a hit. - * - * @param string $regionName The name of the cache region. - * @param CollectionCacheKey $key The cache key of the collection. - * - * @return void */ - public function collectionCacheHit($regionName, CollectionCacheKey $key); + public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void; /** * Log an entity get from second level cache resulted in a miss. - * - * @param string $regionName The name of the cache region. - * @param CollectionCacheKey $key The cache key of the collection. - * - * @return void */ - public function collectionCacheMiss($regionName, CollectionCacheKey $key); + public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void; /** * Log a query put into the query cache. - * - * @param string $regionName The name of the cache region. - * @param QueryCacheKey $key The cache key of the query. - * - * @return void */ - public function queryCachePut($regionName, QueryCacheKey $key); + public function queryCachePut(string $regionName, QueryCacheKey $key): void; /** * Log a query get from the query cache resulted in a hit. - * - * @param string $regionName The name of the cache region. - * @param QueryCacheKey $key The cache key of the query. - * - * @return void */ - public function queryCacheHit($regionName, QueryCacheKey $key); + public function queryCacheHit(string $regionName, QueryCacheKey $key): void; /** * Log a query get from the query cache resulted in a miss. - * - * @param string $regionName The name of the cache region. - * @param QueryCacheKey $key The cache key of the query. - * - * @return void */ - public function queryCacheMiss($regionName, QueryCacheKey $key); + public function queryCacheMiss(string $regionName, QueryCacheKey $key): void; } diff --git a/src/Cache/Logging/CacheLoggerChain.php b/src/Cache/Logging/CacheLoggerChain.php index 32df641ff2d..8eef3b54572 100644 --- a/src/Cache/Logging/CacheLoggerChain.php +++ b/src/Cache/Logging/CacheLoggerChain.php @@ -11,118 +11,81 @@ class CacheLoggerChain implements CacheLogger { /** @var array */ - private $loggers = []; - - /** - * @param string $name - * - * @return void - */ - public function setLogger($name, CacheLogger $logger) + private array $loggers = []; + + public function setLogger(string $name, CacheLogger $logger): void { $this->loggers[$name] = $logger; } - /** - * @param string $name - * - * @return CacheLogger|null - */ - public function getLogger($name) + public function getLogger(string $name): CacheLogger|null { return $this->loggers[$name] ?? null; } /** @return array */ - public function getLoggers() + public function getLoggers(): array { return $this->loggers; } - /** - * {@inheritDoc} - */ - public function collectionCacheHit($regionName, CollectionCacheKey $key) + public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->collectionCacheHit($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function collectionCacheMiss($regionName, CollectionCacheKey $key) + public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->collectionCacheMiss($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function collectionCachePut($regionName, CollectionCacheKey $key) + public function collectionCachePut(string $regionName, CollectionCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->collectionCachePut($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function entityCacheHit($regionName, EntityCacheKey $key) + public function entityCacheHit(string $regionName, EntityCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->entityCacheHit($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function entityCacheMiss($regionName, EntityCacheKey $key) + public function entityCacheMiss(string $regionName, EntityCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->entityCacheMiss($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function entityCachePut($regionName, EntityCacheKey $key) + public function entityCachePut(string $regionName, EntityCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->entityCachePut($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function queryCacheHit($regionName, QueryCacheKey $key) + public function queryCacheHit(string $regionName, QueryCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->queryCacheHit($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function queryCacheMiss($regionName, QueryCacheKey $key) + public function queryCacheMiss(string $regionName, QueryCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->queryCacheMiss($regionName, $key); } } - /** - * {@inheritDoc} - */ - public function queryCachePut($regionName, QueryCacheKey $key) + public function queryCachePut(string $regionName, QueryCacheKey $key): void { foreach ($this->loggers as $logger) { $logger->queryCachePut($regionName, $key); diff --git a/src/Cache/Logging/StatisticsCacheLogger.php b/src/Cache/Logging/StatisticsCacheLogger.php index 56d8c3436ab..092104eaa36 100644 --- a/src/Cache/Logging/StatisticsCacheLogger.php +++ b/src/Cache/Logging/StatisticsCacheLogger.php @@ -16,90 +16,63 @@ class StatisticsCacheLogger implements CacheLogger { /** @var array */ - private $cacheMissCountMap = []; + private array $cacheMissCountMap = []; /** @var array */ - private $cacheHitCountMap = []; + private array $cacheHitCountMap = []; /** @var array */ - private $cachePutCountMap = []; + private array $cachePutCountMap = []; - /** - * {@inheritDoc} - */ - public function collectionCacheMiss($regionName, CollectionCacheKey $key) + public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void { $this->cacheMissCountMap[$regionName] = ($this->cacheMissCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function collectionCacheHit($regionName, CollectionCacheKey $key) + public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void { $this->cacheHitCountMap[$regionName] = ($this->cacheHitCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function collectionCachePut($regionName, CollectionCacheKey $key) + public function collectionCachePut(string $regionName, CollectionCacheKey $key): void { $this->cachePutCountMap[$regionName] = ($this->cachePutCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function entityCacheMiss($regionName, EntityCacheKey $key) + public function entityCacheMiss(string $regionName, EntityCacheKey $key): void { $this->cacheMissCountMap[$regionName] = ($this->cacheMissCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function entityCacheHit($regionName, EntityCacheKey $key) + public function entityCacheHit(string $regionName, EntityCacheKey $key): void { $this->cacheHitCountMap[$regionName] = ($this->cacheHitCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function entityCachePut($regionName, EntityCacheKey $key) + public function entityCachePut(string $regionName, EntityCacheKey $key): void { $this->cachePutCountMap[$regionName] = ($this->cachePutCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function queryCacheHit($regionName, QueryCacheKey $key) + public function queryCacheHit(string $regionName, QueryCacheKey $key): void { $this->cacheHitCountMap[$regionName] = ($this->cacheHitCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function queryCacheMiss($regionName, QueryCacheKey $key) + public function queryCacheMiss(string $regionName, QueryCacheKey $key): void { $this->cacheMissCountMap[$regionName] = ($this->cacheMissCountMap[$regionName] ?? 0) + 1; } - /** - * {@inheritDoc} - */ - public function queryCachePut($regionName, QueryCacheKey $key) + public function queryCachePut(string $regionName, QueryCacheKey $key): void { $this->cachePutCountMap[$regionName] = ($this->cachePutCountMap[$regionName] ?? 0) + 1; @@ -109,10 +82,8 @@ public function queryCachePut($regionName, QueryCacheKey $key) * Get the number of entries successfully retrieved from cache. * * @param string $regionName The name of the cache region. - * - * @return int */ - public function getRegionHitCount($regionName) + public function getRegionHitCount(string $regionName): int { return $this->cacheHitCountMap[$regionName] ?? 0; } @@ -121,10 +92,8 @@ public function getRegionHitCount($regionName) * Get the number of cached entries *not* found in cache. * * @param string $regionName The name of the cache region. - * - * @return int */ - public function getRegionMissCount($regionName) + public function getRegionMissCount(string $regionName): int { return $this->cacheMissCountMap[$regionName] ?? 0; } @@ -133,28 +102,26 @@ public function getRegionMissCount($regionName) * Get the number of cacheable entries put in cache. * * @param string $regionName The name of the cache region. - * - * @return int */ - public function getRegionPutCount($regionName) + public function getRegionPutCount(string $regionName): int { return $this->cachePutCountMap[$regionName] ?? 0; } /** @return array */ - public function getRegionsMiss() + public function getRegionsMiss(): array { return $this->cacheMissCountMap; } /** @return array */ - public function getRegionsHit() + public function getRegionsHit(): array { return $this->cacheHitCountMap; } /** @return array */ - public function getRegionsPut() + public function getRegionsPut(): array { return $this->cachePutCountMap; } @@ -163,10 +130,8 @@ public function getRegionsPut() * Clear region statistics * * @param string $regionName The name of the cache region. - * - * @return void */ - public function clearRegionStats($regionName) + public function clearRegionStats(string $regionName): void { $this->cachePutCountMap[$regionName] = 0; $this->cacheHitCountMap[$regionName] = 0; @@ -175,10 +140,8 @@ public function clearRegionStats($regionName) /** * Clear all statistics - * - * @return void */ - public function clearStats() + public function clearStats(): void { $this->cachePutCountMap = []; $this->cacheHitCountMap = []; @@ -187,30 +150,24 @@ public function clearStats() /** * Get the total number of put in cache. - * - * @return int */ - public function getPutCount() + public function getPutCount(): int { return array_sum($this->cachePutCountMap); } /** * Get the total number of entries successfully retrieved from cache. - * - * @return int */ - public function getHitCount() + public function getHitCount(): int { return array_sum($this->cacheHitCountMap); } /** * Get the total number of cached entries *not* found in cache. - * - * @return int */ - public function getMissCount() + public function getMissCount(): int { return array_sum($this->cacheMissCountMap); } diff --git a/src/Cache/MultiGetRegion.php b/src/Cache/MultiGetRegion.php deleted file mode 100644 index ded16f7adeb..00000000000 --- a/src/Cache/MultiGetRegion.php +++ /dev/null @@ -1,25 +0,0 @@ -getConfiguration(); $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); $cacheFactory = $cacheConfig->getCacheFactory(); @@ -86,38 +59,26 @@ public function __construct(CollectionPersister $persister, Region $region, Enti $this->metadataFactory = $em->getMetadataFactory(); $this->cacheLogger = $cacheConfig->getCacheLogger(); $this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association); - $this->sourceEntity = $em->getClassMetadata($association['sourceEntity']); - $this->targetEntity = $em->getClassMetadata($association['targetEntity']); + $this->sourceEntity = $em->getClassMetadata($association->sourceEntity); + $this->targetEntity = $em->getClassMetadata($association->targetEntity); } - /** - * {@inheritDoc} - */ - public function getCacheRegion() + public function getCacheRegion(): Region { return $this->region; } - /** - * {@inheritDoc} - */ - public function getSourceEntityMetadata() + public function getSourceEntityMetadata(): ClassMetadata { return $this->sourceEntity; } - /** - * {@inheritDoc} - */ - public function getTargetEntityMetadata() + public function getTargetEntityMetadata(): ClassMetadata { return $this->targetEntity; } - /** - * {@inheritDoc} - */ - public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key) + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null { $cache = $this->region->get($key); @@ -128,10 +89,7 @@ public function loadCollectionCache(PersistentCollection $collection, Collection return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection); } - /** - * {@inheritDoc} - */ - public function storeCollectionCache(CollectionCacheKey $key, $elements) + public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void { $associationMapping = $this->sourceEntity->associationMappings[$key->association]; $targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName); @@ -140,7 +98,7 @@ public function storeCollectionCache(CollectionCacheKey $key, $elements) $targetHydrator = $targetPersister->getEntityHydrator(); // Only preserve ordering if association configured it - if (! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) { + if (! $associationMapping->isIndexed()) { // Elements may be an array or a Collection $elements = array_values($elements instanceof Collection ? $elements->getValues() : $elements); } @@ -165,36 +123,25 @@ public function storeCollectionCache(CollectionCacheKey $key, $elements) $targetRegion->put($entityKey, $entityEntry); } - $cached = $this->region->put($key, $entry); - - if ($this->cacheLogger && $cached) { - $this->cacheLogger->collectionCachePut($this->regionName, $key); + if ($this->region->put($key, $entry)) { + $this->cacheLogger?->collectionCachePut($this->regionName, $key); } } - /** - * {@inheritDoc} - */ - public function contains(PersistentCollection $collection, $element) + public function contains(PersistentCollection $collection, object $element): bool { return $this->persister->contains($collection, $element); } - /** - * {@inheritDoc} - */ - public function containsKey(PersistentCollection $collection, $key) + public function containsKey(PersistentCollection $collection, mixed $key): bool { return $this->persister->containsKey($collection, $key); } - /** - * {@inheritDoc} - */ - public function count(PersistentCollection $collection) + public function count(PersistentCollection $collection): int { $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); - $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId, $this->filters->getHash()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash()); $entry = $this->region->get($key); if ($entry !== null) { @@ -204,10 +151,7 @@ public function count(PersistentCollection $collection) return $this->persister->count($collection); } - /** - * {@inheritDoc} - */ - public function get(PersistentCollection $collection, $index) + public function get(PersistentCollection $collection, mixed $index): mixed { return $this->persister->get($collection, $index); } @@ -215,7 +159,7 @@ public function get(PersistentCollection $collection, $index) /** * {@inheritDoc} */ - public function slice(PersistentCollection $collection, $offset, $length = null) + public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array { return $this->persister->slice($collection, $offset, $length); } @@ -223,65 +167,8 @@ public function slice(PersistentCollection $collection, $offset, $length = null) /** * {@inheritDoc} */ - public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array { return $this->persister->loadCriteria($collection, $criteria); } - - /** - * Clears cache entries related to the current collection - * - * @deprecated This method is not used anymore. - * - * @return void - */ - protected function evictCollectionCache(PersistentCollection $collection) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9512', - 'The method %s() is deprecated and will be removed without replacement.' - ); - - $key = new CollectionCacheKey( - $this->sourceEntity->rootEntityName, - $this->association['fieldName'], - $this->uow->getEntityIdentifier($collection->getOwner()), - $this->filters->getHash() - ); - - $this->region->evict($key); - - if ($this->cacheLogger) { - $this->cacheLogger->collectionCachePut($this->regionName, $key); - } - } - - /** - * @deprecated This method is not used anymore. - * - * @param class-string $targetEntity - * @param object $element - * - * @return void - */ - protected function evictElementCache($targetEntity, $element) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9512', - 'The method %s() is deprecated and will be removed without replacement.' - ); - - $targetPersister = $this->uow->getEntityPersister($targetEntity); - assert($targetPersister instanceof CachedEntityPersister); - $targetRegion = $targetPersister->getCacheRegion(); - $key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element)); - - $targetRegion->evict($key); - - if ($this->cacheLogger) { - $this->cacheLogger->entityCachePut($targetRegion->getName(), $key); - } - } } diff --git a/src/Cache/Persister/Collection/CachedCollectionPersister.php b/src/Cache/Persister/Collection/CachedCollectionPersister.php index 7899f01ed19..6b10c808b01 100644 --- a/src/Cache/Persister/Collection/CachedCollectionPersister.php +++ b/src/Cache/Persister/Collection/CachedCollectionPersister.php @@ -16,25 +16,21 @@ */ interface CachedCollectionPersister extends CachedPersister, CollectionPersister { - /** @return ClassMetadata */ - public function getSourceEntityMetadata(); + public function getSourceEntityMetadata(): ClassMetadata; - /** @return ClassMetadata */ - public function getTargetEntityMetadata(); + public function getTargetEntityMetadata(): ClassMetadata; /** * Loads a collection from cache * * @return mixed[]|null */ - public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key); + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null; /** * Stores a collection into cache * * @param mixed[]|Collection $elements - * - * @return void */ - public function storeCollectionCache(CollectionCacheKey $key, $elements); + public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void; } diff --git a/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php b/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php index cfe62a2d143..41b3648a366 100644 --- a/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php +++ b/src/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php @@ -11,10 +11,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister { - /** - * {@inheritDoc} - */ - public function afterTransactionComplete() + public function afterTransactionComplete(): void { if (isset($this->queuedCache['update'])) { foreach ($this->queuedCache['update'] as $item) { @@ -31,31 +28,22 @@ public function afterTransactionComplete() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function afterTransactionRolledBack() + public function afterTransactionRolledBack(): void { $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function delete(PersistentCollection $collection) + public function delete(PersistentCollection $collection): void { $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); - $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId, $this->filters->getHash()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash()); $this->persister->delete($collection); $this->queuedCache['delete'][spl_object_id($collection)] = $key; } - /** - * {@inheritDoc} - */ - public function update(PersistentCollection $collection) + public function update(PersistentCollection $collection): void { $isInitialized = $collection->isInitialized(); $isDirty = $collection->isDirty(); @@ -65,10 +53,10 @@ public function update(PersistentCollection $collection) } $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); - $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId, $this->filters->getHash()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash()); // Invalidate non initialized collections OR ordered collection - if ($isDirty && ! $isInitialized || isset($this->association['orderBy'])) { + if ($isDirty && ! $isInitialized || $this->association->isOrdered()) { $this->persister->update($collection); $this->queuedCache['delete'][spl_object_id($collection)] = $key; diff --git a/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php b/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php index 5722172d0ab..96e0a4bd73a 100644 --- a/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php +++ b/src/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php @@ -10,15 +10,12 @@ class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister { - /** - * {@inheritDoc} - */ - public function update(PersistentCollection $collection) + public function update(PersistentCollection $collection): void { if ($collection->isDirty() && $collection->getSnapshot()) { throw CannotUpdateReadOnlyCollection::fromEntityAndField( DefaultProxyClassNameResolver::getClass($collection->getOwner()), - $this->association['fieldName'] + $this->association->fieldName, ); } diff --git a/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php b/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php index 35e7797b39b..6ecf34b3eed 100644 --- a/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php +++ b/src/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php @@ -7,25 +7,24 @@ use Doctrine\ORM\Cache\CollectionCacheKey; use Doctrine\ORM\Cache\ConcurrentRegion; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use function spl_object_id; -/** @phpstan-import-type AssociationMapping from ClassMetadata */ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister { - /** @param AssociationMapping $association The association mapping. */ - public function __construct(CollectionPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, array $association) - { + public function __construct( + CollectionPersister $persister, + ConcurrentRegion $region, + EntityManagerInterface $em, + AssociationMapping $association, + ) { parent::__construct($persister, $region, $em, $association); } - /** - * {@inheritDoc} - */ - public function afterTransactionComplete() + public function afterTransactionComplete(): void { if (isset($this->queuedCache['update'])) { foreach ($this->queuedCache['update'] as $item) { @@ -42,10 +41,7 @@ public function afterTransactionComplete() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function afterTransactionRolledBack() + public function afterTransactionRolledBack(): void { if (isset($this->queuedCache['update'])) { foreach ($this->queuedCache['update'] as $item) { @@ -62,13 +58,10 @@ public function afterTransactionRolledBack() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function delete(PersistentCollection $collection) + public function delete(PersistentCollection $collection): void { $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); - $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId, $this->filters->getHash()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash()); $lock = $this->region->lock($key); $this->persister->delete($collection); @@ -83,10 +76,7 @@ public function delete(PersistentCollection $collection) ]; } - /** - * {@inheritDoc} - */ - public function update(PersistentCollection $collection) + public function update(PersistentCollection $collection): void { $isInitialized = $collection->isInitialized(); $isDirty = $collection->isDirty(); @@ -98,7 +88,7 @@ public function update(PersistentCollection $collection) $this->persister->update($collection); $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); - $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId, $this->filters->getHash()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash()); $lock = $this->region->lock($key); if ($lock === null) { diff --git a/src/Cache/Persister/Entity/AbstractEntityPersister.php b/src/Cache/Persister/Entity/AbstractEntityPersister.php index a9d447b96bf..945ad5b348b 100644 --- a/src/Cache/Persister/Entity/AbstractEntityPersister.php +++ b/src/Cache/Persister/Entity/AbstractEntityPersister.php @@ -5,6 +5,8 @@ namespace Doctrine\ORM\Cache\Persister\Entity; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Order; +use Doctrine\DBAL\LockMode; use Doctrine\ORM\Cache; use Doctrine\ORM\Cache\CollectionCacheKey; use Doctrine\ORM\Cache\EntityCacheKey; @@ -16,13 +18,14 @@ use Doctrine\ORM\Cache\TimestampCacheKey; use Doctrine\ORM\Cache\TimestampRegion; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Internal\CriteriaOrderings; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Persisters\Entity\EntityPersister; use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; use Doctrine\ORM\Query\FilterCollection; +use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\UnitOfWork; use function array_merge; @@ -32,69 +35,37 @@ abstract class AbstractEntityPersister implements CachedEntityPersister { - use CriteriaOrderings; - - /** @var UnitOfWork */ - protected $uow; - - /** @var ClassMetadataFactory */ - protected $metadataFactory; - - /** @var EntityPersister */ - protected $persister; - - /** @var ClassMetadata */ - protected $class; + protected UnitOfWork $uow; + protected ClassMetadataFactory $metadataFactory; /** @var mixed[] */ - protected $queuedCache = []; - - /** @var Region */ - protected $region; - - /** @var TimestampRegion */ - protected $timestampRegion; - - /** @var TimestampCacheKey */ - protected $timestampKey; - - /** @var EntityHydrator */ - protected $hydrator; - - /** @var Cache */ - protected $cache; - - /** @var FilterCollection */ - protected $filters; - - /** @var CacheLogger|null */ - protected $cacheLogger; + protected array $queuedCache = []; - /** @var string */ - protected $regionName; + protected TimestampRegion $timestampRegion; + protected TimestampCacheKey $timestampKey; + protected EntityHydrator $hydrator; + protected Cache $cache; + protected FilterCollection $filters; + protected CacheLogger|null $cacheLogger = null; + protected string $regionName; /** * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. * * @var array|null */ - protected $joinedAssociations; - - /** - * @param EntityPersister $persister The entity persister to cache. - * @param Region $region The entity cache region. - * @param EntityManagerInterface $em The entity manager. - * @param ClassMetadata $class The entity metadata. - */ - public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class) - { + protected array|null $joinedAssociations = null; + + public function __construct( + protected EntityPersister $persister, + protected Region $region, + EntityManagerInterface $em, + protected ClassMetadata $class, + ) { $configuration = $em->getConfiguration(); $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); $cacheFactory = $cacheConfig->getCacheFactory(); - $this->class = $class; - $this->region = $region; - $this->persister = $persister; $this->cache = $em->getCache(); $this->filters = $em->getFilters(); $this->regionName = $region->getName(); @@ -106,10 +77,7 @@ public function __construct(EntityPersister $persister, Region $region, EntityMa $this->timestampKey = new TimestampCacheKey($this->class->rootEntityName); } - /** - * {@inheritDoc} - */ - public function addInsert($entity) + public function addInsert(object $entity): void { $this->persister->addInsert($entity); } @@ -117,55 +85,47 @@ public function addInsert($entity) /** * {@inheritDoc} */ - public function getInserts() + public function getInserts(): array { return $this->persister->getInserts(); } - /** - * {@inheritDoc} - */ - public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null) - { + public function getSelectSQL( + array|Criteria $criteria, + AssociationMapping|null $assoc = null, + LockMode|int|null $lockMode = null, + int|null $limit = null, + int|null $offset = null, + array|null $orderBy = null, + ): string { return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy); } - /** - * {@inheritDoc} - */ - public function getCountSQL($criteria = []) + public function getCountSQL(array|Criteria $criteria = []): string { return $this->persister->getCountSQL($criteria); } - /** - * {@inheritDoc} - */ - public function getInsertSQL() + public function getInsertSQL(): string { return $this->persister->getInsertSQL(); } - /** - * {@inheritDoc} - */ - public function getResultSetMapping() + public function getResultSetMapping(): ResultSetMapping { return $this->persister->getResultSetMapping(); } - /** - * {@inheritDoc} - */ - public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) - { + public function getSelectConditionStatementSQL( + string $field, + mixed $value, + AssociationMapping|null $assoc = null, + string|null $comparison = null, + ): string { return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison); } - /** - * {@inheritDoc} - */ - public function exists($entity, ?Criteria $extraConditions = null) + public function exists(object $entity, Criteria|null $extraConditions = null): bool { if ($extraConditions === null) { $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity)); @@ -178,24 +138,17 @@ public function exists($entity, ?Criteria $extraConditions = null) return $this->persister->exists($entity, $extraConditions); } - /** - * {@inheritDoc} - */ - public function getCacheRegion() + public function getCacheRegion(): Region { return $this->region; } - /** @return EntityHydrator */ - public function getEntityHydrator() + public function getEntityHydrator(): EntityHydrator { return $this->hydrator; } - /** - * {@inheritDoc} - */ - public function storeEntityCache($entity, EntityCacheKey $key) + public function storeEntityCache(object $entity, EntityCacheKey $key): bool { $class = $this->class; $className = DefaultProxyClassNameResolver::getClass($entity); @@ -207,24 +160,23 @@ public function storeEntityCache($entity, EntityCacheKey $key) $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); $cached = $this->region->put($key, $entry); - if ($this->cacheLogger && $cached) { - $this->cacheLogger->entityCachePut($this->regionName, $key); + if ($cached) { + $this->cacheLogger?->entityCachePut($this->regionName, $key); } return $cached; } - /** @param object $entity */ - private function storeJoinedAssociations($entity): void + private function storeJoinedAssociations(object $entity): void { if ($this->joinedAssociations === null) { $associations = []; foreach ($this->class->associationMappings as $name => $assoc) { if ( - isset($assoc['cache']) && - ($assoc['type'] & ClassMetadata::TO_ONE) && - ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide']) + isset($assoc->cache) && + ($assoc->isToOne()) && + ($assoc->fetch === ClassMetadata::FETCH_EAGER || ! $assoc->isOwningSide()) ) { $associations[] = $name; } @@ -242,9 +194,9 @@ private function storeJoinedAssociations($entity): void } $assocId = $this->uow->getEntityIdentifier($assocEntity); - $assocMetadata = $this->metadataFactory->getMetadataFor($assoc['targetEntity']); + $assocMetadata = $this->metadataFactory->getMetadataFor($assoc->targetEntity); $assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocId); - $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocPersister = $this->uow->getEntityPersister($assoc->targetEntity); $assocPersister->storeEntityCache($assocEntity, $assocKey); } @@ -253,16 +205,16 @@ private function storeJoinedAssociations($entity): void /** * Generates a string of currently query * - * @param string $query - * @param string[]|Criteria $criteria - * @param string[]|null $orderBy - * @param int|null $limit - * @param int|null $offset - * - * @return string + * @param string[]|Criteria $criteria + * @param array|null $orderBy */ - protected function getHash($query, $criteria, ?array $orderBy = null, $limit = null, $offset = null) - { + protected function getHash( + string $query, + array|Criteria $criteria, + array|null $orderBy = null, + int|null $limit = null, + int|null $offset = null, + ): string { [$params] = $criteria instanceof Criteria ? $this->persister->expandCriteriaParameters($criteria) : $this->persister->expandParameters($criteria); @@ -273,7 +225,7 @@ protected function getHash($query, $criteria, ?array $orderBy = null, $limit = n /** * {@inheritDoc} */ - public function expandParameters($criteria) + public function expandParameters(array $criteria): array { return $this->persister->expandParameters($criteria); } @@ -281,15 +233,12 @@ public function expandParameters($criteria) /** * {@inheritDoc} */ - public function expandCriteriaParameters(Criteria $criteria) + public function expandCriteriaParameters(Criteria $criteria): array { return $this->persister->expandCriteriaParameters($criteria); } - /** - * {@inheritDoc} - */ - public function getClassMetadata() + public function getClassMetadata(): ClassMetadata { return $this->persister->getClassMetadata(); } @@ -297,31 +246,33 @@ public function getClassMetadata() /** * {@inheritDoc} */ - public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) - { + public function getManyToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array { return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit); } /** * {@inheritDoc} */ - public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) - { + public function getOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array { return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit); } - /** - * {@inheritDoc} - */ - public function getOwningTable($fieldName) + public function getOwningTable(string $fieldName): string { return $this->persister->getOwningTable($fieldName); } - /** - * {@inheritDoc} - */ - public function executeInserts() + public function executeInserts(): void { // The commit order/foreign key relationships may make it necessary that multiple calls to executeInsert() // are performed, so collect all the new entities. @@ -331,30 +282,35 @@ public function executeInserts() $this->queuedCache['insert'] = array_merge($this->queuedCache['insert'] ?? [], $newInserts); } - return $this->persister->executeInserts(); + $this->persister->executeInserts(); } /** * {@inheritDoc} */ - public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, ?array $orderBy = null) - { + public function load( + array $criteria, + object|null $entity = null, + AssociationMapping|null $assoc = null, + array $hints = [], + LockMode|int|null $lockMode = null, + int|null $limit = null, + array|null $orderBy = null, + ): object|null { if ($entity !== null || $assoc !== null || $hints !== [] || $lockMode !== null) { return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy); } //handle only EntityRepository#findOneBy $query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy); - $hash = $this->getHash($query, $criteria, null, null, null); + $hash = $this->getHash($query, $criteria); $rsm = $this->getResultSetMapping(); $queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey); $queryCache = $this->cache->getQueryCache($this->regionName); $result = $queryCache->get($queryKey, $rsm); if ($result !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->queryCacheHit($this->regionName, $queryKey); - } + $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey); return $result[0]; } @@ -367,12 +323,10 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint $cached = $queryCache->put($queryKey, $rsm, [$result]); - if ($this->cacheLogger) { - $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey); + $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey); - if ($cached) { - $this->cacheLogger->queryCachePut($this->regionName, $queryKey); - } + if ($cached) { + $this->cacheLogger?->queryCachePut($this->regionName, $queryKey); } return $result; @@ -381,19 +335,21 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint /** * {@inheritDoc} */ - public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null) - { + public function loadAll( + array $criteria = [], + array|null $orderBy = null, + int|null $limit = null, + int|null $offset = null, + ): array { $query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); - $hash = $this->getHash($query, $criteria, null, null, null); + $hash = $this->getHash($query, $criteria); $rsm = $this->getResultSetMapping(); $queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey); $queryCache = $this->cache->getQueryCache($this->regionName); $result = $queryCache->get($queryKey, $rsm); if ($result !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->queryCacheHit($this->regionName, $queryKey); - } + $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey); return $result; } @@ -401,14 +357,12 @@ public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = n $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset); $cached = $queryCache->put($queryKey, $rsm, $result); - if ($this->cacheLogger) { - if ($result) { - $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey); - } + if ($result) { + $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey); + } - if ($cached) { - $this->cacheLogger->queryCachePut($this->regionName, $queryKey); - } + if ($cached) { + $this->cacheLogger?->queryCachePut($this->regionName, $queryKey); } return $result; @@ -417,7 +371,7 @@ public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = n /** * {@inheritDoc} */ - public function loadById(array $identifier, $entity = null) + public function loadById(array $identifier, object|null $entity = null): object|null { $cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier); $cacheEntry = $this->region->get($cacheKey); @@ -431,9 +385,7 @@ public function loadById(array $identifier, $entity = null) $cachedEntity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity); if ($cachedEntity !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey); - } + $this->cacheLogger?->entityCacheHit($this->regionName, $cacheKey); return $cachedEntity; } @@ -459,21 +411,16 @@ public function loadById(array $identifier, $entity = null) $this->storeJoinedAssociations($entity); } - if ($this->cacheLogger) { - if ($cached) { - $this->cacheLogger->entityCachePut($this->regionName, $cacheKey); - } - - $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey); + if ($cached) { + $this->cacheLogger?->entityCachePut($this->regionName, $cacheKey); } + $this->cacheLogger?->entityCacheMiss($this->regionName, $cacheKey); + return $entity; } - /** - * {@inheritDoc} - */ - public function count($criteria = []) + public function count(array|Criteria $criteria = []): int { return $this->persister->count($criteria); } @@ -481,9 +428,9 @@ public function count($criteria = []) /** * {@inheritDoc} */ - public function loadCriteria(Criteria $criteria) + public function loadCriteria(Criteria $criteria): array { - $orderBy = self::getCriteriaOrderings($criteria); + $orderBy = $criteria->orderings(); $limit = $criteria->getMaxResults(); $offset = $criteria->getFirstResult(); $query = $this->persister->getSelectSQL($criteria); @@ -494,9 +441,7 @@ public function loadCriteria(Criteria $criteria) $cacheResult = $queryCache->get($queryKey, $rsm); if ($cacheResult !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->queryCacheHit($this->regionName, $queryKey); - } + $this->cacheLogger?->queryCacheHit($this->regionName, $queryKey); return $cacheResult; } @@ -504,14 +449,12 @@ public function loadCriteria(Criteria $criteria) $result = $this->persister->loadCriteria($criteria); $cached = $queryCache->put($queryKey, $rsm, $result); - if ($this->cacheLogger) { - if ($result) { - $this->cacheLogger->queryCacheMiss($this->regionName, $queryKey); - } + if ($result) { + $this->cacheLogger?->queryCacheMiss($this->regionName, $queryKey); + } - if ($cached) { - $this->cacheLogger->queryCachePut($this->regionName, $queryKey); - } + if ($cached) { + $this->cacheLogger?->queryCachePut($this->regionName, $queryKey); } return $result; @@ -520,8 +463,11 @@ public function loadCriteria(Criteria $criteria) /** * {@inheritDoc} */ - public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection) - { + public function loadManyToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + PersistentCollection $collection, + ): array { $persister = $this->uow->getCollectionPersister($assoc); $hasCache = ($persister instanceof CachedPersister); @@ -534,9 +480,7 @@ public function loadManyToManyCollection(array $assoc, $sourceEntity, Persistent $list = $persister->loadCollectionCache($collection, $key); if ($list !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); - } + $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key); return $list; } @@ -545,18 +489,16 @@ public function loadManyToManyCollection(array $assoc, $sourceEntity, Persistent $persister->storeCollectionCache($key, $list); - if ($this->cacheLogger) { - $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); - } + $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); return $list; } - /** - * {@inheritDoc} - */ - public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection) - { + public function loadOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + PersistentCollection $collection, + ): mixed { $persister = $this->uow->getCollectionPersister($assoc); $hasCache = ($persister instanceof CachedPersister); @@ -569,9 +511,7 @@ public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentC $list = $persister->loadCollectionCache($collection, $key); if ($list !== null) { - if ($this->cacheLogger) { - $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); - } + $this->cacheLogger?->collectionCacheHit($persister->getCacheRegion()->getName(), $key); return $list; } @@ -580,9 +520,7 @@ public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentC $persister->storeCollectionCache($key, $list); - if ($this->cacheLogger) { - $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); - } + $this->cacheLogger?->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); return $list; } @@ -590,7 +528,7 @@ public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentC /** * {@inheritDoc} */ - public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = []) + public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null { return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier); } @@ -598,7 +536,7 @@ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifie /** * {@inheritDoc} */ - public function lock(array $criteria, $lockMode) + public function lock(array $criteria, LockMode|int $lockMode): void { $this->persister->lock($criteria, $lockMode); } @@ -606,26 +544,21 @@ public function lock(array $criteria, $lockMode) /** * {@inheritDoc} */ - public function refresh(array $id, $entity, $lockMode = null) + public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void { $this->persister->refresh($id, $entity, $lockMode); } - /** - * @param array $association - * @param array $ownerId - * - * @return CollectionCacheKey - */ - protected function buildCollectionCacheKey(array $association, $ownerId/*, string $filterHash */) + /** @param array $ownerId */ + protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey { $filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release return new CollectionCacheKey( - $this->metadataFactory->getMetadataFor($association['sourceEntity'])->rootEntityName, - $association['fieldName'], + $this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName, + $association->fieldName, $ownerId, - $filterHash + $filterHash, ); } } diff --git a/src/Cache/Persister/Entity/CachedEntityPersister.php b/src/Cache/Persister/Entity/CachedEntityPersister.php index 0dbf82bc656..5fba56f2914 100644 --- a/src/Cache/Persister/Entity/CachedEntityPersister.php +++ b/src/Cache/Persister/Entity/CachedEntityPersister.php @@ -14,13 +14,7 @@ */ interface CachedEntityPersister extends CachedPersister, EntityPersister { - /** @return EntityHydrator */ - public function getEntityHydrator(); + public function getEntityHydrator(): EntityHydrator; - /** - * @param object $entity - * - * @return bool - */ - public function storeEntityCache($entity, EntityCacheKey $key); + public function storeEntityCache(object $entity, EntityCacheKey $key): bool; } diff --git a/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php b/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php index 3c97061b404..43c76ab2cac 100644 --- a/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php +++ b/src/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php @@ -6,17 +6,12 @@ use Doctrine\ORM\Cache\EntityCacheKey; -use function get_class; - /** * Specific non-strict read/write cached entity persister */ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister { - /** - * {@inheritDoc} - */ - public function afterTransactionComplete() + public function afterTransactionComplete(): void { $isChanged = false; @@ -47,18 +42,12 @@ public function afterTransactionComplete() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function afterTransactionRolledBack() + public function afterTransactionRolledBack(): void { $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function delete($entity) + public function delete(object $entity): bool { $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); $deleted = $this->persister->delete($entity); @@ -72,27 +61,23 @@ public function delete($entity) return $deleted; } - /** - * {@inheritDoc} - */ - public function update($entity) + public function update(object $entity): void { $this->persister->update($entity); $this->queuedCache['update'][] = $entity; } - /** @param object $entity */ - private function updateCache($entity, bool $isChanged): bool + private function updateCache(object $entity, bool $isChanged): bool { - $class = $this->metadataFactory->getMetadataFor(get_class($entity)); + $class = $this->metadataFactory->getMetadataFor($entity::class); $key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity)); $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); $cached = $this->region->put($key, $entry); $isChanged = $isChanged || $cached; - if ($this->cacheLogger && $cached) { - $this->cacheLogger->entityCachePut($this->regionName, $key); + if ($cached) { + $this->cacheLogger?->entityCachePut($this->regionName, $key); } return $isChanged; diff --git a/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php b/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php index 497815950e5..4cd1784d62b 100644 --- a/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php +++ b/src/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php @@ -12,10 +12,7 @@ */ class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister { - /** - * {@inheritDoc} - */ - public function update($entity) + public function update(object $entity): void { throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity)); } diff --git a/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php b/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php index ce0497698a8..a1ea0dcf07e 100644 --- a/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php +++ b/src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php @@ -20,10 +20,7 @@ public function __construct(EntityPersister $persister, ConcurrentRegion $region parent::__construct($persister, $region, $em, $class); } - /** - * {@inheritDoc} - */ - public function afterTransactionComplete() + public function afterTransactionComplete(): void { $isChanged = true; @@ -50,10 +47,7 @@ public function afterTransactionComplete() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function afterTransactionRolledBack() + public function afterTransactionRolledBack(): void { if (isset($this->queuedCache['update'])) { foreach ($this->queuedCache['update'] as $item) { @@ -70,10 +64,7 @@ public function afterTransactionRolledBack() $this->queuedCache = []; } - /** - * {@inheritDoc} - */ - public function delete($entity) + public function delete(object $entity): bool { $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); $lock = $this->region->lock($key); @@ -95,10 +86,7 @@ public function delete($entity) return $deleted; } - /** - * {@inheritDoc} - */ - public function update($entity) + public function update(object $entity): void { $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); $lock = $this->region->lock($key); diff --git a/src/Cache/QueryCache.php b/src/Cache/QueryCache.php index da505db252d..e6976802400 100644 --- a/src/Cache/QueryCache.php +++ b/src/Cache/QueryCache.php @@ -12,24 +12,17 @@ */ interface QueryCache { - /** @return bool */ - public function clear(); + public function clear(): bool; - /** - * @param mixed $result - * @param mixed[] $hints - * - * @return bool - */ - public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = []); + /** @param mixed[] $hints */ + public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool; /** * @param mixed[] $hints * * @return mixed[]|null */ - public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []); + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null; - /** @return Region */ - public function getRegion(); + public function getRegion(): Region; } diff --git a/src/Cache/QueryCacheEntry.php b/src/Cache/QueryCacheEntry.php index 0bd18afc7b6..1e39262b92d 100644 --- a/src/Cache/QueryCacheEntry.php +++ b/src/Cache/QueryCacheEntry.php @@ -6,43 +6,23 @@ use function microtime; -/** - * Query cache entry - */ class QueryCacheEntry implements CacheEntry { - /** - * List of entity identifiers - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var array - */ - public $result; - /** * Time creation of this cache entry - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var float */ - public $time; + public readonly float $time; - /** - * @param array $result - * @param float|null $time - */ - public function __construct($result, $time = null) - { - $this->result = $result; - $this->time = $time ?: microtime(true); + /** @param array $result List of entity identifiers */ + public function __construct( + public readonly array $result, + float|null $time = null, + ) { + $this->time = $time ?: microtime(true); } - /** - * @param array $values - * - * @return QueryCacheEntry - */ - public static function __set_state(array $values) + /** @param array $values */ + public static function __set_state(array $values): self { return new self($values['result'], $values['time']); } diff --git a/src/Cache/QueryCacheKey.php b/src/Cache/QueryCacheKey.php index 2051e572161..2372e5a253e 100644 --- a/src/Cache/QueryCacheKey.php +++ b/src/Cache/QueryCacheKey.php @@ -11,40 +11,13 @@ */ class QueryCacheKey extends CacheKey { - /** - * Cache key lifetime - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var int - */ - public $lifetime; - - /** - * Cache mode - * - * @readonly Public only for performance reasons, it should be considered immutable. - * @var int - * @phpstan-var Cache::MODE_* - */ - public $cacheMode; - - /** - * @readonly Public only for performance reasons, it should be considered immutable. - * @var TimestampCacheKey|null - */ - public $timestampKey; - - /** @phpstan-param Cache::MODE_* $cacheMode */ + /** @param Cache::MODE_* $cacheMode */ public function __construct( string $cacheId, - int $lifetime = 0, - int $cacheMode = Cache::MODE_NORMAL, - ?TimestampCacheKey $timestampKey = null + public readonly int $lifetime = 0, + public readonly int $cacheMode = Cache::MODE_NORMAL, + public readonly TimestampCacheKey|null $timestampKey = null, ) { - $this->lifetime = $lifetime; - $this->cacheMode = $cacheMode; - $this->timestampKey = $timestampKey; - parent::__construct($cacheId); } } diff --git a/src/Cache/QueryCacheValidator.php b/src/Cache/QueryCacheValidator.php index 8ef61299a5b..8a0d39f6469 100644 --- a/src/Cache/QueryCacheValidator.php +++ b/src/Cache/QueryCacheValidator.php @@ -11,8 +11,6 @@ interface QueryCacheValidator { /** * Checks if the query entry is valid - * - * @return bool */ - public function isValid(QueryCacheKey $key, QueryCacheEntry $entry); + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool; } diff --git a/src/Cache/Region.php b/src/Cache/Region.php index a87afb9b951..f7a1b26a936 100644 --- a/src/Cache/Region.php +++ b/src/Cache/Region.php @@ -8,26 +8,20 @@ /** * Defines a contract for accessing a particular named region. - * - * @phpstan-ignore interface.extendsDeprecatedInterface */ -interface Region extends MultiGetRegion +interface Region { /** * Retrieve the name of this region. - * - * @return string The region name */ - public function getName(); + public function getName(): string; /** * Determine whether this region contains data for the given key. * * @param CacheKey $key The cache key - * - * @return bool TRUE if the underlying cache contains corresponding data; FALSE otherwise. */ - public function contains(CacheKey $key); + public function contains(CacheKey $key): bool; /** * Get an item from the cache. @@ -38,7 +32,17 @@ public function contains(CacheKey $key); * * @throws CacheException Indicates a problem accessing the item or region. */ - public function get(CacheKey $key); + public function get(CacheKey $key): CacheEntry|null; + + /** + * Get all items from the cache identified by $keys. + * It returns NULL if some elements can not be found. + * + * @param CollectionCacheEntry $collection The collection of the items to be retrieved. + * + * @return CacheEntry[]|null The cached entries or NULL if one or more entries can not be found + */ + public function getMultiple(CollectionCacheEntry $collection): array|null; /** * Put an item into the cache. @@ -47,29 +51,23 @@ public function get(CacheKey $key); * @param CacheEntry $entry The entry to cache. * @param Lock|null $lock The lock previously obtained. * - * @return bool - * * @throws CacheException Indicates a problem accessing the region. */ - public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null); + public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool; /** * Remove an item from the cache. * * @param CacheKey $key The key under which to cache the item. * - * @return bool - * * @throws CacheException Indicates a problem accessing the region. */ - public function evict(CacheKey $key); + public function evict(CacheKey $key): bool; /** * Remove all contents of this particular cache region. * - * @return bool - * * @throws CacheException Indicates problem accessing the region. */ - public function evictAll(); + public function evictAll(): bool; } diff --git a/src/Cache/Region/DefaultMultiGetRegion.php b/src/Cache/Region/DefaultMultiGetRegion.php deleted file mode 100644 index 8fe5c7a9130..00000000000 --- a/src/Cache/Region/DefaultMultiGetRegion.php +++ /dev/null @@ -1,14 +0,0 @@ -cache = $cacheItemPool; - $this->cacheItemPool = CacheAdapter::wrap($cacheItemPool); - } elseif (! $cacheItemPool instanceof CacheItemPoolInterface) { - throw new TypeError(sprintf( - '%s: Parameter #2 is expected to be an instance of %s, got %s.', - __METHOD__, - CacheItemPoolInterface::class, - get_debug_type($cacheItemPool) - )); - } else { - // @phpstan-ignore property.deprecated - $this->cache = DoctrineProvider::wrap($cacheItemPool); - $this->cacheItemPool = $cacheItemPool; - } - - $this->name = $name; - $this->lifetime = $lifetime; + private const REGION_KEY_SEPARATOR = '_'; + private const REGION_PREFIX = 'DC2_REGION_'; + + public function __construct( + private readonly string $name, + private readonly CacheItemPoolInterface $cacheItemPool, + private readonly int $lifetime = 0, + ) { } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return $this->name; } - /** - * @deprecated - * - * @return CacheProvider - */ - public function getCache() - { - return $this->cache; - } - - /** - * {@inheritDoc} - */ - public function contains(CacheKey $key) + public function contains(CacheKey $key): bool { return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key)); } - /** - * {@inheritDoc} - */ - public function get(CacheKey $key) + public function get(CacheKey $key): CacheEntry|null { $item = $this->cacheItemPool->getItem($this->getCacheEntryKey($key)); $entry = $item->isHit() ? $item->get() : null; @@ -133,14 +54,11 @@ public function get(CacheKey $key) return $entry; } - /** - * {@inheritDoc} - */ - public function getMultiple(CollectionCacheEntry $collection) + public function getMultiple(CollectionCacheEntry $collection): array|null { $keys = array_map( - Closure::fromCallable([$this, 'getCacheEntryKey']), - $collection->identifiers + $this->getCacheEntryKey(...), + $collection->identifiers, ); /** @var iterable $items */ $items = $this->cacheItemPool->getItems($keys); @@ -165,12 +83,7 @@ public function getMultiple(CollectionCacheEntry $collection) return $result; } - /** - * {@inheritDoc} - * - * @return bool - */ - public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null) + public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool { $item = $this->cacheItemPool ->getItem($this->getCacheEntryKey($key)) @@ -183,32 +96,17 @@ public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null) return $this->cacheItemPool->save($item); } - /** - * {@inheritDoc} - * - * @return bool - */ - public function evict(CacheKey $key) + public function evict(CacheKey $key): bool { return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key)); } - /** - * {@inheritDoc} - * - * @return bool - */ - public function evictAll() + public function evictAll(): bool { return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name); } - /** - * @internal since 2.11, this method will be private in 3.0. - * - * @return string - */ - protected function getCacheEntryKey(CacheKey $key) + private function getCacheEntryKey(CacheKey $key): string { return self::REGION_PREFIX . $this->name . self::REGION_KEY_SEPARATOR . strtr($key->hash, '{}()/\@:', '________'); } diff --git a/src/Cache/Region/FileLockRegion.php b/src/Cache/Region/FileLockRegion.php index 8da30325c2b..bedd6a68e1a 100644 --- a/src/Cache/Region/FileLockRegion.php +++ b/src/Cache/Region/FileLockRegion.php @@ -35,25 +35,18 @@ */ class FileLockRegion implements ConcurrentRegion { - public const LOCK_EXTENSION = 'lock'; - - /** @var Region */ - private $region; - - /** @var string */ - private $directory; - - /** @phpstan-var numeric-string */ - private $lockLifetime; + final public const LOCK_EXTENSION = 'lock'; /** - * @param string $directory - * @param numeric-string $lockLifetime + * @param numeric-string|int $lockLifetime * * @throws InvalidArgumentException */ - public function __construct(Region $region, $directory, $lockLifetime) - { + public function __construct( + private readonly Region $region, + private readonly string $directory, + private readonly string|int $lockLifetime, + ) { if (! is_dir($directory) && ! @mkdir($directory, 0775, true)) { throw new InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $directory)); } @@ -61,13 +54,9 @@ public function __construct(Region $region, $directory, $lockLifetime) if (! is_writable($directory)) { throw new InvalidArgumentException(sprintf('The directory "%s" is not writable.', $directory)); } - - $this->region = $region; - $this->directory = $directory; - $this->lockLifetime = $lockLifetime; } - private function isLocked(CacheKey $key, ?Lock $lock = null): bool + private function isLocked(CacheKey $key, Lock|null $lock = null): bool { $filename = $this->getLockFileName($key); @@ -78,7 +67,7 @@ private function isLocked(CacheKey $key, ?Lock $lock = null): bool $time = $this->getLockTime($filename); $content = $this->getLockContent($filename); - if (! $content || ! $time) { + if ($content === false || $time === false) { @unlink($filename); return false; @@ -103,30 +92,22 @@ private function getLockFileName(CacheKey $key): string return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION; } - /** @return string|false */ - private function getLockContent(string $filename) + private function getLockContent(string $filename): string|false { return @file_get_contents($filename); } - /** @return int|false */ - private function getLockTime(string $filename) + private function getLockTime(string $filename): int|false { return @fileatime($filename); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return $this->region->getName(); } - /** - * {@inheritDoc} - */ - public function contains(CacheKey $key) + public function contains(CacheKey $key): bool { if ($this->isLocked($key)) { return false; @@ -135,10 +116,7 @@ public function contains(CacheKey $key) return $this->region->contains($key); } - /** - * {@inheritDoc} - */ - public function get(CacheKey $key) + public function get(CacheKey $key): CacheEntry|null { if ($this->isLocked($key)) { return null; @@ -147,22 +125,16 @@ public function get(CacheKey $key) return $this->region->get($key); } - /** - * {@inheritDoc} - */ - public function getMultiple(CollectionCacheEntry $collection) + public function getMultiple(CollectionCacheEntry $collection): array|null { - if (array_filter(array_map([$this, 'isLocked'], $collection->identifiers))) { + if (array_filter(array_map($this->isLocked(...), $collection->identifiers))) { return null; } return $this->region->getMultiple($collection); } - /** - * {@inheritDoc} - */ - public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null) + public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool { if ($this->isLocked($key, $lock)) { return false; @@ -171,10 +143,7 @@ public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null) return $this->region->put($key, $entry); } - /** - * {@inheritDoc} - */ - public function evict(CacheKey $key) + public function evict(CacheKey $key): bool { if ($this->isLocked($key)) { @unlink($this->getLockFileName($key)); @@ -183,28 +152,20 @@ public function evict(CacheKey $key) return $this->region->evict($key); } - /** - * {@inheritDoc} - */ - public function evictAll() + public function evictAll(): bool { // The check below is necessary because on some platforms glob returns false // when nothing matched (even though no errors occurred) - $filenames = glob(sprintf('%s/*.%s', $this->directory, self::LOCK_EXTENSION)); + $filenames = glob(sprintf('%s/*.%s', $this->directory, self::LOCK_EXTENSION)) ?: []; - if ($filenames) { - foreach ($filenames as $filename) { - @unlink($filename); - } + foreach ($filenames as $filename) { + @unlink($filename); } return $this->region->evictAll(); } - /** - * {@inheritDoc} - */ - public function lock(CacheKey $key) + public function lock(CacheKey $key): Lock|null { if ($this->isLocked($key)) { return null; @@ -213,7 +174,7 @@ public function lock(CacheKey $key) $lock = Lock::createLockRead(); $filename = $this->getLockFileName($key); - if (! @file_put_contents($filename, $lock->value, LOCK_EX)) { + if (@file_put_contents($filename, $lock->value, LOCK_EX) === false) { return null; } @@ -222,10 +183,7 @@ public function lock(CacheKey $key) return $lock; } - /** - * {@inheritDoc} - */ - public function unlock(CacheKey $key, Lock $lock) + public function unlock(CacheKey $key, Lock $lock): bool { if ($this->isLocked($key, $lock)) { return false; diff --git a/src/Cache/Region/UpdateTimestampCache.php b/src/Cache/Region/UpdateTimestampCache.php index 7817bbeb8fd..aa75a90360d 100644 --- a/src/Cache/Region/UpdateTimestampCache.php +++ b/src/Cache/Region/UpdateTimestampCache.php @@ -13,10 +13,7 @@ */ class UpdateTimestampCache extends DefaultRegion implements TimestampRegion { - /** - * {@inheritDoc} - */ - public function update(CacheKey $key) + public function update(CacheKey $key): void { $this->put($key, new TimestampCacheEntry()); } diff --git a/src/Cache/RegionsConfiguration.php b/src/Cache/RegionsConfiguration.php index 231caffbbed..a8528312b6a 100644 --- a/src/Cache/RegionsConfiguration.php +++ b/src/Cache/RegionsConfiguration.php @@ -10,98 +10,54 @@ class RegionsConfiguration { /** @var array */ - private $lifetimes = []; + private array $lifetimes = []; /** @var array */ - private $lockLifetimes = []; + private array $lockLifetimes = []; - /** @var int */ - private $defaultLifetime; - - /** @var int */ - private $defaultLockLifetime; - - /** - * @param int $defaultLifetime - * @param int $defaultLockLifetime - */ - public function __construct($defaultLifetime = 3600, $defaultLockLifetime = 60) - { - $this->defaultLifetime = (int) $defaultLifetime; - $this->defaultLockLifetime = (int) $defaultLockLifetime; + public function __construct( + private int $defaultLifetime = 3600, + private int $defaultLockLifetime = 60, + ) { } - /** @return int */ - public function getDefaultLifetime() + public function getDefaultLifetime(): int { return $this->defaultLifetime; } - /** - * @param int $defaultLifetime - * - * @return void - */ - public function setDefaultLifetime($defaultLifetime) + public function setDefaultLifetime(int $defaultLifetime): void { - $this->defaultLifetime = (int) $defaultLifetime; + $this->defaultLifetime = $defaultLifetime; } - /** @return int */ - public function getDefaultLockLifetime() + public function getDefaultLockLifetime(): int { return $this->defaultLockLifetime; } - /** - * @param int $defaultLockLifetime - * - * @return void - */ - public function setDefaultLockLifetime($defaultLockLifetime) + public function setDefaultLockLifetime(int $defaultLockLifetime): void { - $this->defaultLockLifetime = (int) $defaultLockLifetime; + $this->defaultLockLifetime = $defaultLockLifetime; } - /** - * @param string $regionName - * - * @return int - */ - public function getLifetime($regionName) + public function getLifetime(string $regionName): int { return $this->lifetimes[$regionName] ?? $this->defaultLifetime; } - /** - * @param string $name - * @param int $lifetime - * - * @return void - */ - public function setLifetime($name, $lifetime) + public function setLifetime(string $name, int $lifetime): void { - $this->lifetimes[$name] = (int) $lifetime; + $this->lifetimes[$name] = $lifetime; } - /** - * @param string $regionName - * - * @return int - */ - public function getLockLifetime($regionName) + public function getLockLifetime(string $regionName): int { return $this->lockLifetimes[$regionName] ?? $this->defaultLockLifetime; } - /** - * @param string $name - * @param int $lifetime - * - * @return void - */ - public function setLockLifetime($name, $lifetime) + public function setLockLifetime(string $name, int $lifetime): void { - $this->lockLifetimes[$name] = (int) $lifetime; + $this->lockLifetimes[$name] = $lifetime; } } diff --git a/src/Cache/TimestampCacheEntry.php b/src/Cache/TimestampCacheEntry.php index f1a8bfa9486..60c9175c14e 100644 --- a/src/Cache/TimestampCacheEntry.php +++ b/src/Cache/TimestampCacheEntry.php @@ -6,21 +6,13 @@ use function microtime; -/** - * Timestamp cache entry - */ class TimestampCacheEntry implements CacheEntry { - /** - * @readonly Public only for performance reasons, it should be considered immutable. - * @var float - */ - public $time; + public readonly float $time; - /** @param float|null $time */ - public function __construct($time = null) + public function __construct(float|null $time = null) { - $this->time = $time ? (float) $time : microtime(true); + $this->time = $time ?? microtime(true); } /** @@ -29,10 +21,8 @@ public function __construct($time = null) * This method allow Doctrine\Common\Cache\PhpFileCache compatibility * * @param array $values array containing property values - * - * @return TimestampCacheEntry */ - public static function __set_state(array $values) + public static function __set_state(array $values): TimestampCacheEntry { return new self($values['time']); } diff --git a/src/Cache/TimestampCacheKey.php b/src/Cache/TimestampCacheKey.php index 9dff0020cf9..5aef4c5a05c 100644 --- a/src/Cache/TimestampCacheKey.php +++ b/src/Cache/TimestampCacheKey.php @@ -10,8 +10,8 @@ class TimestampCacheKey extends CacheKey { /** @param string $space Result cache id */ - public function __construct($space) + public function __construct(string $space) { - parent::__construct((string) $space); + parent::__construct($space); } } diff --git a/src/Cache/TimestampQueryCacheValidator.php b/src/Cache/TimestampQueryCacheValidator.php index 410242e02f0..98240888e0c 100644 --- a/src/Cache/TimestampQueryCacheValidator.php +++ b/src/Cache/TimestampQueryCacheValidator.php @@ -8,18 +8,11 @@ class TimestampQueryCacheValidator implements QueryCacheValidator { - /** @var TimestampRegion */ - private $timestampRegion; - - public function __construct(TimestampRegion $timestampRegion) + public function __construct(private readonly TimestampRegion $timestampRegion) { - $this->timestampRegion = $timestampRegion; } - /** - * {@inheritDoc} - */ - public function isValid(QueryCacheKey $key, QueryCacheEntry $entry) + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool { if ($this->regionUpdated($key, $entry)) { return false; diff --git a/src/Cache/TimestampRegion.php b/src/Cache/TimestampRegion.php index 4f72a159ee3..b74fa8d6bde 100644 --- a/src/Cache/TimestampRegion.php +++ b/src/Cache/TimestampRegion.php @@ -12,9 +12,7 @@ interface TimestampRegion extends Region /** * Update a specific key into the cache region. * - * @return void - * * @throws LockException Indicates a problem accessing the region. */ - public function update(CacheKey $key); + public function update(CacheKey $key): void; } diff --git a/src/Configuration.php b/src/Configuration.php index 86b2f08387c..361d146a50b 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -4,36 +4,15 @@ namespace Doctrine\ORM; -use BadMethodCallException; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\CachedReader; -use Doctrine\Common\Annotations\SimpleAnnotationReader; -use Doctrine\Common\Cache\ArrayCache; -use Doctrine\Common\Cache\Cache as CacheDriver; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Cache\CacheConfiguration; -use Doctrine\ORM\Cache\Exception\CacheException; -use Doctrine\ORM\Cache\Exception\MetadataCacheNotConfigured; -use Doctrine\ORM\Cache\Exception\MetadataCacheUsesNonPersistentCache; -use Doctrine\ORM\Cache\Exception\QueryCacheNotConfigured; -use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache; use Doctrine\ORM\Exception\InvalidEntityRepository; -use Doctrine\ORM\Exception\NamedNativeQueryNotFound; -use Doctrine\ORM\Exception\NamedQueryNotFound; -use Doctrine\ORM\Exception\NotSupported; -use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating; -use Doctrine\ORM\Exception\UnknownEntityNamespace; use Doctrine\ORM\Internal\Hydration\AbstractHydrator; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; use Doctrine\ORM\Mapping\DefaultNamingStrategy; use Doctrine\ORM\Mapping\DefaultQuoteStrategy; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\Mapping\EntityListenerResolver; use Doctrine\ORM\Mapping\NamingStrategy; use Doctrine\ORM\Mapping\QuoteStrategy; @@ -41,23 +20,15 @@ use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\Filter\SQLFilter; -use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Repository\DefaultRepositoryFactory; use Doctrine\ORM\Repository\RepositoryFactory; use Doctrine\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Persistence\ObjectRepository; -use Doctrine\Persistence\Reflection\RuntimeReflectionProperty; use LogicException; use Psr\Cache\CacheItemPoolInterface; -use Symfony\Component\VarExporter\LazyGhostTrait; use function class_exists; use function is_a; -use function method_exists; -use function sprintf; use function strtolower; -use function trait_exists; -use function trim; /** * Configuration container for all configuration options of Doctrine. @@ -68,7 +39,7 @@ class Configuration extends \Doctrine\DBAL\Configuration { /** @var mixed[] */ - protected $_attributes = []; + protected array $attributes = []; /** @phpstan-var array, ClassMetadata::GENERATOR_TYPE_*> */ private $identityGenerationPreferences = []; @@ -87,24 +58,18 @@ public function getIdentityGenerationPreferences(): array /** * Sets the directory where Doctrine generates any necessary proxy class files. - * - * @param string $dir - * - * @return void */ - public function setProxyDir($dir) + public function setProxyDir(string $dir): void { - $this->_attributes['proxyDir'] = $dir; + $this->attributes['proxyDir'] = $dir; } /** * Gets the directory where Doctrine generates any necessary proxy class files. - * - * @return string|null */ - public function getProxyDir() + public function getProxyDir(): string|null { - return $this->_attributes['proxyDir'] ?? null; + return $this->attributes['proxyDir'] ?? null; } /** @@ -112,180 +77,56 @@ public function getProxyDir() * * @return ProxyFactory::AUTOGENERATE_* */ - public function getAutoGenerateProxyClasses() + public function getAutoGenerateProxyClasses(): int { - return $this->_attributes['autoGenerateProxyClasses'] ?? ProxyFactory::AUTOGENERATE_ALWAYS; + return $this->attributes['autoGenerateProxyClasses'] ?? ProxyFactory::AUTOGENERATE_ALWAYS; } /** * Sets the strategy for automatically generating proxy classes. * * @param bool|ProxyFactory::AUTOGENERATE_* $autoGenerate True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER. - * - * @return void */ - public function setAutoGenerateProxyClasses($autoGenerate) + public function setAutoGenerateProxyClasses(bool|int $autoGenerate): void { - $this->_attributes['autoGenerateProxyClasses'] = (int) $autoGenerate; + $this->attributes['autoGenerateProxyClasses'] = (int) $autoGenerate; } /** * Gets the namespace where proxy classes reside. - * - * @return string|null */ - public function getProxyNamespace() + public function getProxyNamespace(): string|null { - return $this->_attributes['proxyNamespace'] ?? null; + return $this->attributes['proxyNamespace'] ?? null; } /** * Sets the namespace where proxy classes reside. - * - * @param string $ns - * - * @return void */ - public function setProxyNamespace($ns) + public function setProxyNamespace(string $ns): void { - $this->_attributes['proxyNamespace'] = $ns; + $this->attributes['proxyNamespace'] = $ns; } /** * Sets the cache driver implementation that is used for metadata caching. * - * @return void - * * @todo Force parameter to be a Closure to ensure lazy evaluation * (as soon as a metadata cache is in effect, the driver never needs to initialize). */ - public function setMetadataDriverImpl(MappingDriver $driverImpl) - { - $this->_attributes['metadataDriverImpl'] = $driverImpl; - } - - /** - * Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader - * is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported. - * - * @deprecated Use {@see ORMSetup::createDefaultAnnotationDriver()} instead. - * - * @param string|string[] $paths - * @param bool $useSimpleAnnotationReader - * @phpstan-param string|list $paths - * - * @return AnnotationDriver - */ - public function newDefaultAnnotationDriver($paths = [], $useSimpleAnnotationReader = true, bool $reportFieldsWhereDeclared = false) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9443', - '%s is deprecated, call %s::createDefaultAnnotationDriver() instead.', - __METHOD__, - ORMSetup::class - ); - - if (! class_exists(AnnotationReader::class)) { - throw new LogicException( - 'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library' - . ' is not installed. Please run "composer require doctrine/annotations" or choose a different' - . ' metadata driver.' - ); - } - - if ($useSimpleAnnotationReader) { - if (! class_exists(SimpleAnnotationReader::class)) { - throw new BadMethodCallException( - 'SimpleAnnotationReader has been removed in doctrine/annotations 2.' - . ' Downgrade to version 1 or set $useSimpleAnnotationReader to false.' - ); - } - - // Register the ORM Annotations in the AnnotationRegistry - $reader = new SimpleAnnotationReader(); - $reader->addNamespace('Doctrine\ORM\Mapping'); - } else { - $reader = new AnnotationReader(); - } - - if (class_exists(ArrayCache::class) && class_exists(CachedReader::class)) { - $reader = new CachedReader($reader, new ArrayCache()); - } - - return new AnnotationDriver( - $reader, - (array) $paths, - $reportFieldsWhereDeclared - ); - } - - /** - * Adds a namespace under a certain alias. - * - * @deprecated No replacement planned. - * - * @param string $alias - * @param string $namespace - * - * @return void - */ - public function addEntityNamespace($alias, $namespace) - { - if (class_exists(PersistentObject::class)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8818', - 'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.', - $alias - ); - } else { - throw NotSupported::createForPersistence3(sprintf( - 'Using short namespace alias "%s" by calling %s', - $alias, - __METHOD__ - )); - } - - $this->_attributes['entityNamespaces'][$alias] = $namespace; - } - - /** - * Resolves a registered namespace alias to the full namespace. - * - * @param string $entityNamespaceAlias - * - * @return string - * - * @throws UnknownEntityNamespace - */ - public function getEntityNamespace($entityNamespaceAlias) + public function setMetadataDriverImpl(MappingDriver $driverImpl): void { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8818', - 'Entity short namespace aliases such as "%s" are deprecated, use ::class constant instead.', - $entityNamespaceAlias - ); - - if (! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) { - // @phpstan-ignore staticMethod.deprecatedClass - throw UnknownEntityNamespace::fromNamespaceAlias($entityNamespaceAlias); - } - - return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\'); + $this->attributes['metadataDriverImpl'] = $driverImpl; } /** * Sets the entity alias map. * * @phpstan-param array $entityNamespaces - * - * @return void */ - public function setEntityNamespaces(array $entityNamespaces) + public function setEntityNamespaces(array $entityNamespaces): void { - $this->_attributes['entityNamespaces'] = $entityNamespaces; + $this->attributes['entityNamespaces'] = $entityNamespaces; } /** @@ -293,98 +134,25 @@ public function setEntityNamespaces(array $entityNamespaces) * * @phpstan-return array */ - public function getEntityNamespaces() + public function getEntityNamespaces(): array { - return $this->_attributes['entityNamespaces']; + return $this->attributes['entityNamespaces']; } /** * Gets the cache driver implementation that is used for the mapping metadata. - * - * @return MappingDriver|null */ - public function getMetadataDriverImpl() + public function getMetadataDriverImpl(): MappingDriver|null { - return $this->_attributes['metadataDriverImpl'] ?? null; - } - - /** - * Gets the cache driver implementation that is used for query result caching. - */ - public function getResultCache(): ?CacheItemPoolInterface - { - // Compatibility with DBAL 2 - if (! method_exists(parent::class, 'getResultCache')) { - // @phpstan-ignore method.deprecated - $cacheImpl = $this->getResultCacheImpl(); - - return $cacheImpl ? CacheAdapter::wrap($cacheImpl) : null; - } - - return parent::getResultCache(); - } - - /** - * Sets the cache driver implementation that is used for query result caching. - */ - public function setResultCache(CacheItemPoolInterface $cache): void - { - // Compatibility with DBAL 2 - if (! method_exists(parent::class, 'setResultCache')) { - // @phpstan-ignore method.deprecated - $this->setResultCacheImpl(DoctrineProvider::wrap($cache)); - - return; - } - - parent::setResultCache($cache); - } - - /** - * Gets the cache driver implementation that is used for the query cache (SQL cache). - * - * @deprecated Call {@see getQueryCache()} instead. - * - * @return CacheDriver|null - */ - public function getQueryCacheImpl() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9002', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getQueryCache() instead.', - __METHOD__ - ); - - return $this->_attributes['queryCacheImpl'] ?? null; - } - - /** - * Sets the cache driver implementation that is used for the query cache (SQL cache). - * - * @deprecated Call {@see setQueryCache()} instead. - * - * @return void - */ - public function setQueryCacheImpl(CacheDriver $cacheImpl) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9002', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use setQueryCache() instead.', - __METHOD__ - ); - - $this->_attributes['queryCache'] = CacheAdapter::wrap($cacheImpl); - $this->_attributes['queryCacheImpl'] = $cacheImpl; + return $this->attributes['metadataDriverImpl'] ?? null; } /** * Gets the cache driver implementation that is used for the query cache (SQL cache). */ - public function getQueryCache(): ?CacheItemPoolInterface + public function getQueryCache(): CacheItemPoolInterface|null { - return $this->_attributes['queryCache'] ?? null; + return $this->attributes['queryCache'] ?? null; } /** @@ -392,223 +160,27 @@ public function getQueryCache(): ?CacheItemPoolInterface */ public function setQueryCache(CacheItemPoolInterface $cache): void { - $this->_attributes['queryCache'] = $cache; - $this->_attributes['queryCacheImpl'] = DoctrineProvider::wrap($cache); + $this->attributes['queryCache'] = $cache; } - /** - * Gets the cache driver implementation that is used for the hydration cache (SQL cache). - * - * @deprecated Call {@see getHydrationCache()} instead. - * - * @return CacheDriver|null - */ - public function getHydrationCacheImpl() + public function getHydrationCache(): CacheItemPoolInterface|null { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9002', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getHydrationCache() instead.', - __METHOD__ - ); - - return $this->_attributes['hydrationCacheImpl'] ?? null; - } - - /** - * Sets the cache driver implementation that is used for the hydration cache (SQL cache). - * - * @deprecated Call {@see setHydrationCache()} instead. - * - * @return void - */ - public function setHydrationCacheImpl(CacheDriver $cacheImpl) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9002', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use setHydrationCache() instead.', - __METHOD__ - ); - - $this->_attributes['hydrationCache'] = CacheAdapter::wrap($cacheImpl); - $this->_attributes['hydrationCacheImpl'] = $cacheImpl; - } - - public function getHydrationCache(): ?CacheItemPoolInterface - { - return $this->_attributes['hydrationCache'] ?? null; + return $this->attributes['hydrationCache'] ?? null; } public function setHydrationCache(CacheItemPoolInterface $cache): void { - $this->_attributes['hydrationCache'] = $cache; - $this->_attributes['hydrationCacheImpl'] = DoctrineProvider::wrap($cache); - } - - /** - * Gets the cache driver implementation that is used for metadata caching. - * - * @deprecated Deprecated in favor of getMetadataCache - * - * @return CacheDriver|null - */ - public function getMetadataCacheImpl() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8650', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getMetadataCache() instead.', - __METHOD__ - ); - - if (isset($this->_attributes['metadataCacheImpl'])) { - return $this->_attributes['metadataCacheImpl']; - } - - return isset($this->_attributes['metadataCache']) ? DoctrineProvider::wrap($this->_attributes['metadataCache']) : null; - } - - /** - * Sets the cache driver implementation that is used for metadata caching. - * - * @deprecated Deprecated in favor of setMetadataCache - * - * @return void - */ - public function setMetadataCacheImpl(CacheDriver $cacheImpl) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8650', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use setMetadataCache() instead.', - __METHOD__ - ); - - $this->_attributes['metadataCacheImpl'] = $cacheImpl; - $this->_attributes['metadataCache'] = CacheAdapter::wrap($cacheImpl); + $this->attributes['hydrationCache'] = $cache; } - public function getMetadataCache(): ?CacheItemPoolInterface + public function getMetadataCache(): CacheItemPoolInterface|null { - return $this->_attributes['metadataCache'] ?? null; + return $this->attributes['metadataCache'] ?? null; } public function setMetadataCache(CacheItemPoolInterface $cache): void { - $this->_attributes['metadataCache'] = $cache; - $this->_attributes['metadataCacheImpl'] = DoctrineProvider::wrap($cache); - } - - /** - * Adds a named DQL query to the configuration. - * - * @param string $name The name of the query. - * @param string $dql The DQL query string. - * - * @return void - */ - public function addNamedQuery($name, $dql) - { - $this->_attributes['namedQueries'][$name] = $dql; - } - - /** - * Gets a previously registered named DQL query. - * - * @param string $name The name of the query. - * - * @return string The DQL query. - * - * @throws NamedQueryNotFound - */ - public function getNamedQuery($name) - { - if (! isset($this->_attributes['namedQueries'][$name])) { - throw NamedQueryNotFound::fromName($name); - } - - return $this->_attributes['namedQueries'][$name]; - } - - /** - * Adds a named native query to the configuration. - * - * @param string $name The name of the query. - * @param string $sql The native SQL query string. - * @param Query\ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. - * - * @return void - */ - public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm) - { - $this->_attributes['namedNativeQueries'][$name] = [$sql, $rsm]; - } - - /** - * Gets the components of a previously registered named native query. - * - * @param string $name The name of the query. - * - * @return mixed[] - * @phpstan-return array{string, ResultSetMapping} A tuple with the first element being the SQL string and the second - * element being the ResultSetMapping. - * - * @throws NamedNativeQueryNotFound - */ - public function getNamedNativeQuery($name) - { - if (! isset($this->_attributes['namedNativeQueries'][$name])) { - throw NamedNativeQueryNotFound::fromName($name); - } - - return $this->_attributes['namedNativeQueries'][$name]; - } - - /** - * Ensures that this Configuration instance contains settings that are - * suitable for a production environment. - * - * @deprecated - * - * @return void - * - * @throws ProxyClassesAlwaysRegenerating - * @throws CacheException If a configuration setting has a value that is not - * suitable for a production environment. - */ - public function ensureProductionSettings() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9074', - '%s is deprecated', - __METHOD__ - ); - - $queryCacheImpl = $this->getQueryCacheImpl(); - - if (! $queryCacheImpl) { - throw QueryCacheNotConfigured::create(); - } - - if ($queryCacheImpl instanceof ArrayCache) { - throw QueryCacheUsesNonPersistentCache::fromDriver($queryCacheImpl); - } - - if ($this->getAutoGenerateProxyClasses() !== ProxyFactory::AUTOGENERATE_NEVER) { - throw ProxyClassesAlwaysRegenerating::create(); - } - - if (! $this->getMetadataCache()) { - throw MetadataCacheNotConfigured::create(); - } - - $metadataCacheImpl = $this->getMetadataCacheImpl(); - - if ($metadataCacheImpl instanceof ArrayCache) { - throw MetadataCacheUsesNonPersistentCache::fromDriver($metadataCacheImpl); - } + $this->attributes['metadataCache'] = $cache; } /** @@ -618,30 +190,24 @@ public function ensureProductionSettings() * * DQL function names are case-insensitive. * - * @param string $name Function name. * @param class-string|callable $className Class name or a callable that returns the function. * @phpstan-param class-string|callable(string):FunctionNode $className - * - * @return void */ - public function addCustomStringFunction($name, $className) + public function addCustomStringFunction(string $name, string|callable $className): void { - $this->_attributes['customStringFunctions'][strtolower($name)] = $className; + $this->attributes['customStringFunctions'][strtolower($name)] = $className; } /** * Gets the implementation class name of a registered custom string DQL function. * - * @param string $name - * - * @return string|callable|null * @phpstan-return class-string|callable(string):FunctionNode|null */ - public function getCustomStringFunction($name) + public function getCustomStringFunction(string $name): string|callable|null { $name = strtolower($name); - return $this->_attributes['customStringFunctions'][$name] ?? null; + return $this->attributes['customStringFunctions'][$name] ?? null; } /** @@ -654,10 +220,8 @@ public function getCustomStringFunction($name) * * @phpstan-param array|callable(string):FunctionNode> $functions The map of custom * DQL string functions. - * - * @return void */ - public function setCustomStringFunctions(array $functions) + public function setCustomStringFunctions(array $functions): void { foreach ($functions as $name => $className) { $this->addCustomStringFunction($name, $className); @@ -671,29 +235,24 @@ public function setCustomStringFunctions(array $functions) * * DQL function names are case-insensitive. * - * @param string $name Function name. * @param class-string|callable $className Class name or a callable that returns the function. * @phpstan-param class-string|callable(string):FunctionNode $className - * - * @return void */ - public function addCustomNumericFunction($name, $className) + public function addCustomNumericFunction(string $name, string|callable $className): void { - $this->_attributes['customNumericFunctions'][strtolower($name)] = $className; + $this->attributes['customNumericFunctions'][strtolower($name)] = $className; } /** * Gets the implementation class name of a registered custom numeric DQL function. * - * @param string $name - * - * @return class-string|callable|null + * @phpstan-return class-string|callable(string):FunctionNode|null */ - public function getCustomNumericFunction($name) + public function getCustomNumericFunction(string $name): string|callable|null { $name = strtolower($name); - return $this->_attributes['customNumericFunctions'][$name] ?? null; + return $this->attributes['customNumericFunctions'][$name] ?? null; } /** @@ -705,11 +264,9 @@ public function getCustomNumericFunction($name) * Any previously added numeric functions are discarded. * * @param array $functions The map of custom - * DQL numeric functions. - * - * @return void + * DQL numeric functions. */ - public function setCustomNumericFunctions(array $functions) + public function setCustomNumericFunctions(array $functions): void { foreach ($functions as $name => $className) { $this->addCustomNumericFunction($name, $className); @@ -723,29 +280,24 @@ public function setCustomNumericFunctions(array $functions) * * DQL function names are case-insensitive. * - * @param string $name Function name. * @param string|callable $className Class name or a callable that returns the function. * @phpstan-param class-string|callable(string):FunctionNode $className - * - * @return void */ - public function addCustomDatetimeFunction($name, $className) + public function addCustomDatetimeFunction(string $name, string|callable $className): void { - $this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className; + $this->attributes['customDatetimeFunctions'][strtolower($name)] = $className; } /** * Gets the implementation class name of a registered custom date/time DQL function. * - * @param string $name - * * @return class-string|callable|null */ - public function getCustomDatetimeFunction($name) + public function getCustomDatetimeFunction(string $name): string|callable|null { $name = strtolower($name); - return $this->_attributes['customDatetimeFunctions'][$name] ?? null; + return $this->attributes['customDatetimeFunctions'][$name] ?? null; } /** @@ -758,10 +310,8 @@ public function getCustomDatetimeFunction($name) * * @param array $functions The map of custom DQL date/time functions. * @phpstan-param array|callable(string):FunctionNode> $functions - * - * @return void */ - public function setCustomDatetimeFunctions(array $functions) + public function setCustomDatetimeFunctions(array $functions): void { foreach ($functions as $name => $className) { $this->addCustomDatetimeFunction($name, $className); @@ -771,29 +321,27 @@ public function setCustomDatetimeFunctions(array $functions) /** * Sets a TypedFieldMapper for php typed fields to DBAL types auto-completion. */ - public function setTypedFieldMapper(?TypedFieldMapper $typedFieldMapper): void + public function setTypedFieldMapper(TypedFieldMapper|null $typedFieldMapper): void { - $this->_attributes['typedFieldMapper'] = $typedFieldMapper; + $this->attributes['typedFieldMapper'] = $typedFieldMapper; } /** * Gets a TypedFieldMapper for php typed fields to DBAL types auto-completion. */ - public function getTypedFieldMapper(): ?TypedFieldMapper + public function getTypedFieldMapper(): TypedFieldMapper|null { - return $this->_attributes['typedFieldMapper'] ?? null; + return $this->attributes['typedFieldMapper'] ?? null; } /** * Sets the custom hydrator modes in one pass. * * @param array> $modes An array of ($modeName => $hydrator). - * - * @return void */ - public function setCustomHydrationModes($modes) + public function setCustomHydrationModes(array $modes): void { - $this->_attributes['customHydrationModes'] = []; + $this->attributes['customHydrationModes'] = []; foreach ($modes as $modeName => $hydrator) { $this->addCustomHydrationMode($modeName, $hydrator); @@ -803,74 +351,62 @@ public function setCustomHydrationModes($modes) /** * Gets the hydrator class for the given hydration mode name. * - * @param string $modeName The hydration mode name. - * - * @return class-string|null The hydrator class name. + * @return class-string|null */ - public function getCustomHydrationMode($modeName) + public function getCustomHydrationMode(string $modeName): string|null { - return $this->_attributes['customHydrationModes'][$modeName] ?? null; + return $this->attributes['customHydrationModes'][$modeName] ?? null; } /** * Adds a custom hydration mode. * - * @param string $modeName The hydration mode name. - * @param class-string $hydrator The hydrator class name. - * - * @return void + * @param class-string $hydrator */ - public function addCustomHydrationMode($modeName, $hydrator) + public function addCustomHydrationMode(string $modeName, string $hydrator): void { - $this->_attributes['customHydrationModes'][$modeName] = $hydrator; + $this->attributes['customHydrationModes'][$modeName] = $hydrator; } /** * Sets a class metadata factory. * * @param class-string $cmfName - * - * @return void */ - public function setClassMetadataFactoryName($cmfName) + public function setClassMetadataFactoryName(string $cmfName): void { - $this->_attributes['classMetadataFactoryName'] = $cmfName; + $this->attributes['classMetadataFactoryName'] = $cmfName; } /** @return class-string */ - public function getClassMetadataFactoryName() + public function getClassMetadataFactoryName(): string { - if (! isset($this->_attributes['classMetadataFactoryName'])) { - $this->_attributes['classMetadataFactoryName'] = ClassMetadataFactory::class; + if (! isset($this->attributes['classMetadataFactoryName'])) { + $this->attributes['classMetadataFactoryName'] = ClassMetadataFactory::class; } - return $this->_attributes['classMetadataFactoryName']; + return $this->attributes['classMetadataFactoryName']; } /** * Adds a filter to the list of possible filters. * - * @param string $name The name of the filter. * @param class-string $className The class name of the filter. - * - * @return void */ - public function addFilter($name, $className) + public function addFilter(string $name, string $className): void { - $this->_attributes['filters'][$name] = $className; + $this->attributes['filters'][$name] = $className; } /** * Gets the class name for a given filter name. * - * @param string $name The name of the filter. - * * @return class-string|null The class name of the filter, * or null if it is not defined. */ - public function getFilterClassName($name) + public function getFilterClassName(string $name): string|null { - return $this->_attributes['filters'][$name] ?? null; + return $this->attributes['filters'][$name] ?? null; } /** @@ -878,27 +414,15 @@ public function getFilterClassName($name) * * @param class-string $className * - * @return void - * * @throws InvalidEntityRepository If $classname is not an ObjectRepository. */ - public function setDefaultRepositoryClassName($className) + public function setDefaultRepositoryClassName(string $className): void { - if (! class_exists($className) || ! is_a($className, ObjectRepository::class, true)) { + if (! class_exists($className) || ! is_a($className, EntityRepository::class, true)) { throw InvalidEntityRepository::fromClassName($className); } - if (! is_a($className, EntityRepository::class, true)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9533', - 'Configuring %s as default repository class is deprecated because it does not extend %s.', - $className, - EntityRepository::class - ); - } - - $this->_attributes['defaultRepositoryClassName'] = $className; + $this->attributes['defaultRepositoryClassName'] = $className; } /** @@ -906,133 +430,109 @@ public function setDefaultRepositoryClassName($className) * * @return class-string */ - public function getDefaultRepositoryClassName() + public function getDefaultRepositoryClassName(): string { - return $this->_attributes['defaultRepositoryClassName'] ?? EntityRepository::class; + return $this->attributes['defaultRepositoryClassName'] ?? EntityRepository::class; } /** * Sets naming strategy. - * - * @return void */ - public function setNamingStrategy(NamingStrategy $namingStrategy) + public function setNamingStrategy(NamingStrategy $namingStrategy): void { - $this->_attributes['namingStrategy'] = $namingStrategy; + $this->attributes['namingStrategy'] = $namingStrategy; } /** * Gets naming strategy.. - * - * @return NamingStrategy */ - public function getNamingStrategy() + public function getNamingStrategy(): NamingStrategy { - if (! isset($this->_attributes['namingStrategy'])) { - $this->_attributes['namingStrategy'] = new DefaultNamingStrategy(); + if (! isset($this->attributes['namingStrategy'])) { + $this->attributes['namingStrategy'] = new DefaultNamingStrategy(); } - return $this->_attributes['namingStrategy']; + return $this->attributes['namingStrategy']; } /** * Sets quote strategy. - * - * @return void */ - public function setQuoteStrategy(QuoteStrategy $quoteStrategy) + public function setQuoteStrategy(QuoteStrategy $quoteStrategy): void { - $this->_attributes['quoteStrategy'] = $quoteStrategy; + $this->attributes['quoteStrategy'] = $quoteStrategy; } /** * Gets quote strategy. - * - * @return QuoteStrategy */ - public function getQuoteStrategy() + public function getQuoteStrategy(): QuoteStrategy { - if (! isset($this->_attributes['quoteStrategy'])) { - $this->_attributes['quoteStrategy'] = new DefaultQuoteStrategy(); + if (! isset($this->attributes['quoteStrategy'])) { + $this->attributes['quoteStrategy'] = new DefaultQuoteStrategy(); } - return $this->_attributes['quoteStrategy']; + return $this->attributes['quoteStrategy']; } /** * Set the entity listener resolver. - * - * @return void */ - public function setEntityListenerResolver(EntityListenerResolver $resolver) + public function setEntityListenerResolver(EntityListenerResolver $resolver): void { - $this->_attributes['entityListenerResolver'] = $resolver; + $this->attributes['entityListenerResolver'] = $resolver; } /** * Get the entity listener resolver. - * - * @return EntityListenerResolver */ - public function getEntityListenerResolver() + public function getEntityListenerResolver(): EntityListenerResolver { - if (! isset($this->_attributes['entityListenerResolver'])) { - $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver(); + if (! isset($this->attributes['entityListenerResolver'])) { + $this->attributes['entityListenerResolver'] = new DefaultEntityListenerResolver(); } - return $this->_attributes['entityListenerResolver']; + return $this->attributes['entityListenerResolver']; } /** * Set the entity repository factory. - * - * @return void */ - public function setRepositoryFactory(RepositoryFactory $repositoryFactory) + public function setRepositoryFactory(RepositoryFactory $repositoryFactory): void { - $this->_attributes['repositoryFactory'] = $repositoryFactory; + $this->attributes['repositoryFactory'] = $repositoryFactory; } /** * Get the entity repository factory. - * - * @return RepositoryFactory */ - public function getRepositoryFactory() + public function getRepositoryFactory(): RepositoryFactory { - return $this->_attributes['repositoryFactory'] ?? new DefaultRepositoryFactory(); + return $this->attributes['repositoryFactory'] ?? new DefaultRepositoryFactory(); } - /** @return bool */ - public function isSecondLevelCacheEnabled() + public function isSecondLevelCacheEnabled(): bool { - return $this->_attributes['isSecondLevelCacheEnabled'] ?? false; + return $this->attributes['isSecondLevelCacheEnabled'] ?? false; } - /** - * @param bool $flag - * - * @return void - */ - public function setSecondLevelCacheEnabled($flag = true) + public function setSecondLevelCacheEnabled(bool $flag = true): void { - $this->_attributes['isSecondLevelCacheEnabled'] = (bool) $flag; + $this->attributes['isSecondLevelCacheEnabled'] = $flag; } - /** @return void */ - public function setSecondLevelCacheConfiguration(CacheConfiguration $cacheConfig) + public function setSecondLevelCacheConfiguration(CacheConfiguration $cacheConfig): void { - $this->_attributes['secondLevelCacheConfiguration'] = $cacheConfig; + $this->attributes['secondLevelCacheConfiguration'] = $cacheConfig; } - /** @return CacheConfiguration|null */ - public function getSecondLevelCacheConfiguration() + public function getSecondLevelCacheConfiguration(): CacheConfiguration|null { - if (! isset($this->_attributes['secondLevelCacheConfiguration']) && $this->isSecondLevelCacheEnabled()) { - $this->_attributes['secondLevelCacheConfiguration'] = new CacheConfiguration(); + if (! isset($this->attributes['secondLevelCacheConfiguration']) && $this->isSecondLevelCacheEnabled()) { + $this->attributes['secondLevelCacheConfiguration'] = new CacheConfiguration(); } - return $this->_attributes['secondLevelCacheConfiguration'] ?? null; + return $this->attributes['secondLevelCacheConfiguration'] ?? null; } /** @@ -1040,46 +540,37 @@ public function getSecondLevelCacheConfiguration() * * @phpstan-return array */ - public function getDefaultQueryHints() + public function getDefaultQueryHints(): array { - return $this->_attributes['defaultQueryHints'] ?? []; + return $this->attributes['defaultQueryHints'] ?? []; } /** * Sets array of query hints, which will be applied to every query in application * * @phpstan-param array $defaultQueryHints - * - * @return void */ - public function setDefaultQueryHints(array $defaultQueryHints) + public function setDefaultQueryHints(array $defaultQueryHints): void { - $this->_attributes['defaultQueryHints'] = $defaultQueryHints; + $this->attributes['defaultQueryHints'] = $defaultQueryHints; } /** * Gets the value of a default query hint. If the hint name is not recognized, FALSE is returned. * - * @param string $name The name of the hint. - * * @return mixed The value of the hint or FALSE, if the hint name is not recognized. */ - public function getDefaultQueryHint($name) + public function getDefaultQueryHint(string $name): mixed { - return $this->_attributes['defaultQueryHints'][$name] ?? false; + return $this->attributes['defaultQueryHints'][$name] ?? false; } /** * Sets a default query hint. If the hint name is not recognized, it is silently ignored. - * - * @param string $name The name of the hint. - * @param mixed $value The value of the hint. - * - * @return void */ - public function setDefaultQueryHint($name, $value) + public function setDefaultQueryHint(string $name, mixed $value): void { - $this->_attributes['defaultQueryHints'][$name] = $value; + $this->attributes['defaultQueryHints'][$name] = $value; } /** @@ -1089,7 +580,7 @@ public function setDefaultQueryHint($name, $value) */ public function getSchemaIgnoreClasses(): array { - return $this->_attributes['schemaIgnoreClasses'] ?? []; + return $this->attributes['schemaIgnoreClasses'] ?? []; } /** @@ -1099,50 +590,58 @@ public function getSchemaIgnoreClasses(): array */ public function setSchemaIgnoreClasses(array $schemaIgnoreClasses): void { - $this->_attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses; + $this->attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses; } + /** + * To be deprecated in 3.1.0 + * + * @return true + */ public function isLazyGhostObjectEnabled(): bool { - return $this->_attributes['isLazyGhostObjectEnabled'] ?? false; + return true; } + /** To be deprecated in 3.1.0 */ public function setLazyGhostObjectEnabled(bool $flag): void { - if ($flag && ! trait_exists(LazyGhostTrait::class)) { - throw new LogicException( - 'Lazy ghost objects cannot be enabled because the "symfony/var-exporter" library' - . ' version 6.2 or higher is not installed. Please run "composer require symfony/var-exporter:^6.2".' - ); - } - - if ($flag && ! class_exists(RuntimeReflectionProperty::class)) { - throw new LogicException( - 'Lazy ghost objects cannot be enabled because the "doctrine/persistence" library' - . ' version 3.1 or higher is not installed. Please run "composer update doctrine/persistence".' - ); + if (! $flag) { + throw new LogicException(<<<'EXCEPTION' + The lazy ghost object feature cannot be disabled anymore. + Please remove the call to setLazyGhostObjectEnabled(false). + EXCEPTION); } - - $this->_attributes['isLazyGhostObjectEnabled'] = $flag; } + /** To be deprecated in 3.1.0 */ public function setRejectIdCollisionInIdentityMap(bool $flag): void { - $this->_attributes['rejectIdCollisionInIdentityMap'] = $flag; + if (! $flag) { + throw new LogicException(<<<'EXCEPTION' + Rejecting ID collisions in the identity map cannot be disabled anymore. + Please remove the call to setRejectIdCollisionInIdentityMap(false). + EXCEPTION); + } } + /** + * To be deprecated in 3.1.0 + * + * @return true + */ public function isRejectIdCollisionInIdentityMapEnabled(): bool { - return $this->_attributes['rejectIdCollisionInIdentityMap'] ?? false; + return true; } public function setEagerFetchBatchSize(int $batchSize = 100): void { - $this->_attributes['fetchModeSubselectBatchSize'] = $batchSize; + $this->attributes['fetchModeSubselectBatchSize'] = $batchSize; } public function getEagerFetchBatchSize(): int { - return $this->_attributes['fetchModeSubselectBatchSize'] ?? 100; + return $this->attributes['fetchModeSubselectBatchSize'] ?? 100; } } diff --git a/src/Decorator/EntityManagerDecorator.php b/src/Decorator/EntityManagerDecorator.php index 6c37a19c392..6f1b0419686 100644 --- a/src/Decorator/EntityManagerDecorator.php +++ b/src/Decorator/EntityManagerDecorator.php @@ -4,21 +4,27 @@ namespace Doctrine\ORM\Decorator; -use Doctrine\Deprecations\Deprecation; +use DateTimeInterface; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Internal\Hydration\AbstractHydrator; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\NativeQuery; +use Doctrine\ORM\Proxy\ProxyFactory; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\FilterCollection; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\UnitOfWork; use Doctrine\Persistence\ObjectManagerDecorator; -use function func_get_arg; -use function func_num_args; -use function get_debug_type; -use function method_exists; -use function sprintf; -use function trigger_error; - -use const E_USER_NOTICE; - /** * Base class for EntityManager decorators * @@ -31,295 +37,137 @@ public function __construct(EntityManagerInterface $wrapped) $this->wrapped = $wrapped; } - /** - * {@inheritDoc} - */ - public function getConnection() + public function getRepository(string $className): EntityRepository { - return $this->wrapped->getConnection(); + return $this->wrapped->getRepository($className); } - /** - * {@inheritDoc} - */ - public function getExpressionBuilder() + public function getMetadataFactory(): ClassMetadataFactory { - return $this->wrapped->getExpressionBuilder(); + return $this->wrapped->getMetadataFactory(); } - /** - * {@inheritDoc} - * - * @param class-string $className - * - * @phpstan-return EntityRepository - * - * @template T of object - */ - public function getRepository($className) + public function getClassMetadata(string $className): ClassMetadata { - return $this->wrapped->getRepository($className); + return $this->wrapped->getClassMetadata($className); } - /** - * {@inheritDoc} - */ - public function getClassMetadata($className) + public function getConnection(): Connection { - return $this->wrapped->getClassMetadata($className); + return $this->wrapped->getConnection(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function getExpressionBuilder(): Expr { - $this->wrapped->beginTransaction(); + return $this->wrapped->getExpressionBuilder(); } - /** - * {@inheritDoc} - */ - public function transactional($func) + public function beginTransaction(): void { - return $this->wrapped->transactional($func); + $this->wrapped->beginTransaction(); } - /** - * {@inheritDoc} - */ - public function wrapInTransaction(callable $func) + public function wrapInTransaction(callable $func): mixed { - if (! method_exists($this->wrapped, 'wrapInTransaction')) { - trigger_error( - sprintf('Calling `transactional()` instead of `wrapInTransaction()` which is not implemented on %s', get_debug_type($this->wrapped)), - E_USER_NOTICE - ); - - // @phpstan-ignore method.deprecated - return $this->wrapped->transactional($func); - } - return $this->wrapped->wrapInTransaction($func); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { $this->wrapped->commit(); } - /** - * {@inheritDoc} - */ - public function rollback() + public function rollback(): void { $this->wrapped->rollback(); } - /** - * {@inheritDoc} - */ - public function createQuery($dql = '') + public function createQuery(string $dql = ''): Query { return $this->wrapped->createQuery($dql); } - /** - * {@inheritDoc} - */ - public function createNamedQuery($name) - { - return $this->wrapped->createNamedQuery($name); - } - - /** - * {@inheritDoc} - */ - public function createNativeQuery($sql, ResultSetMapping $rsm) + public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery { return $this->wrapped->createNativeQuery($sql, $rsm); } - /** - * {@inheritDoc} - */ - public function createNamedNativeQuery($name) - { - return $this->wrapped->createNamedNativeQuery($name); - } - - /** - * {@inheritDoc} - */ - public function createQueryBuilder() + public function createQueryBuilder(): QueryBuilder { return $this->wrapped->createQueryBuilder(); } - /** - * {@inheritDoc} - */ - public function getReference($entityName, $id) + public function getReference(string $entityName, mixed $id): object|null { return $this->wrapped->getReference($entityName, $id); } - /** - * {@inheritDoc} - */ - public function getPartialReference($entityName, $identifier) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10987', - 'Method %s is deprecated and will be removed in 3.0.', - __METHOD__ - ); - - return $this->wrapped->getPartialReference($entityName, $identifier); - } - - /** - * {@inheritDoc} - */ - public function close() + public function close(): void { $this->wrapped->close(); } - /** - * {@inheritDoc} - */ - public function copy($entity, $deep = false) - { - return $this->wrapped->copy($entity, $deep); - } - - /** - * {@inheritDoc} - */ - public function lock($entity, $lockMode, $lockVersion = null) + public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void { $this->wrapped->lock($entity, $lockMode, $lockVersion); } - /** - * {@inheritDoc} - */ - public function find($className, $id, $lockMode = null, $lockVersion = null) + public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null { return $this->wrapped->find($className, $id, $lockMode, $lockVersion); } - /** - * {@inheritDoc} - */ - public function flush($entity = null) + public function refresh(object $object, LockMode|int|null $lockMode = null): void { - $this->wrapped->flush($entity); - } - - /** - * {@inheritDoc} - */ - public function refresh($object) - { - $lockMode = null; - - if (func_num_args() > 1) { - $lockMode = func_get_arg(1); - } - $this->wrapped->refresh($object, $lockMode); } - /** - * {@inheritDoc} - */ - public function getEventManager() + public function getEventManager(): EventManager { return $this->wrapped->getEventManager(); } - /** - * {@inheritDoc} - */ - public function getConfiguration() + public function getConfiguration(): Configuration { return $this->wrapped->getConfiguration(); } - /** - * {@inheritDoc} - */ - public function isOpen() + public function isOpen(): bool { return $this->wrapped->isOpen(); } - /** - * {@inheritDoc} - */ - public function getUnitOfWork() + public function getUnitOfWork(): UnitOfWork { return $this->wrapped->getUnitOfWork(); } - /** - * {@inheritDoc} - */ - public function getHydrator($hydrationMode) - { - return $this->wrapped->getHydrator($hydrationMode); - } - - /** - * {@inheritDoc} - */ - public function newHydrator($hydrationMode) + public function newHydrator(string|int $hydrationMode): AbstractHydrator { return $this->wrapped->newHydrator($hydrationMode); } - /** - * {@inheritDoc} - */ - public function getProxyFactory() + public function getProxyFactory(): ProxyFactory { return $this->wrapped->getProxyFactory(); } - /** - * {@inheritDoc} - */ - public function getFilters() + public function getFilters(): FilterCollection { return $this->wrapped->getFilters(); } - /** - * {@inheritDoc} - */ - public function isFiltersStateClean() + public function isFiltersStateClean(): bool { return $this->wrapped->isFiltersStateClean(); } - /** - * {@inheritDoc} - */ - public function hasFilters() + public function hasFilters(): bool { return $this->wrapped->hasFilters(); } - /** - * {@inheritDoc} - */ - public function getCache() + public function getCache(): Cache|null { return $this->wrapped->getCache(); } diff --git a/src/EntityManager.php b/src/EntityManager.php index d31efee7a70..06b34689abf 100644 --- a/src/EntityManager.php +++ b/src/EntityManager.php @@ -5,22 +5,17 @@ namespace Doctrine\ORM; use BackedEnum; -use BadMethodCallException; -use Doctrine\Common\Cache\Psr6\CacheAdapter; +use DateTimeInterface; use Doctrine\Common\EventManager; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Exception\EntityManagerClosed; use Doctrine\ORM\Exception\InvalidHydrationMode; -use Doctrine\ORM\Exception\MismatchedEventManager; use Doctrine\ORM\Exception\MissingIdentifierField; use Doctrine\ORM\Exception\MissingMappingDriverImplementation; -use Doctrine\ORM\Exception\NotSupported; use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\UnrecognizedIdentifierFields; +use Doctrine\ORM\Internal\Hydration\AbstractHydrator; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; @@ -29,27 +24,18 @@ use Doctrine\ORM\Query\FilterCollection; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Repository\RepositoryFactory; -use Doctrine\Persistence\Mapping\MappingException; -use InvalidArgumentException; use function array_keys; -use function class_exists; -use function get_debug_type; -use function gettype; use function is_array; -use function is_callable; use function is_object; -use function is_string; use function ltrim; -use function sprintf; -use function strpos; +use function method_exists; /** * The EntityManager is the central access point to ORM functionality. * * It is a facade to all different ORM subsystems such as UnitOfWork, - * Query Language and Repository API. Instantiation is done through - * the static create() method. The quickest way to obtain a fully + * Query Language and Repository API. The quickest way to obtain a fully * configured EntityManager is: * * use Doctrine\ORM\Tools\ORMSetup; @@ -73,97 +59,71 @@ */ class EntityManager implements EntityManagerInterface { - /** - * The used Configuration. - * - * @var Configuration - */ - private $config; - - /** - * The database connection used by the EntityManager. - * - * @var Connection - */ - private $conn; - /** * The metadata factory, used to retrieve the ORM metadata of entity classes. - * - * @var ClassMetadataFactory */ - private $metadataFactory; + private ClassMetadataFactory $metadataFactory; /** * The UnitOfWork used to coordinate object-level transactions. - * - * @var UnitOfWork */ - private $unitOfWork; + private UnitOfWork $unitOfWork; /** * The event manager that is the central point of the event system. - * - * @var EventManager */ - private $eventManager; + private EventManager $eventManager; /** * The proxy factory used to create dynamic proxies. - * - * @var ProxyFactory */ - private $proxyFactory; + private ProxyFactory $proxyFactory; /** * The repository factory used to create dynamic repositories. - * - * @var RepositoryFactory */ - private $repositoryFactory; + private RepositoryFactory $repositoryFactory; /** * The expression builder instance used to generate query expressions. - * - * @var Expr|null */ - private $expressionBuilder; + private Expr|null $expressionBuilder = null; /** * Whether the EntityManager is closed or not. - * - * @var bool */ - private $closed = false; + private bool $closed = false; /** * Collection of query filters. - * - * @var FilterCollection|null */ - private $filterCollection; + private FilterCollection|null $filterCollection = null; /** * The second level cache regions API. - * - * @var Cache|null */ - private $cache; + private Cache|null $cache = null; /** * Creates a new EntityManager that operates on the given database connection * and uses the given Configuration and EventManager implementations. + * + * @param Connection $conn The database connection used by the EntityManager. */ - public function __construct(Connection $conn, Configuration $config, ?EventManager $eventManager = null) - { + public function __construct( + private Connection $conn, + private Configuration $config, + EventManager|null $eventManager = null, + ) { if (! $config->getMetadataDriverImpl()) { throw MissingMappingDriverImplementation::create(); } - $this->conn = $conn; - $this->config = $config; - // @phpstan-ignore method.deprecated - $this->eventManager = $eventManager ?? $conn->getEventManager(); + $this->eventManager = $eventManager + ?? (method_exists($conn, 'getEventManager') + ? $conn->getEventManager() + : new EventManager() + ); $metadataFactoryClassName = $config->getClassMetadataFactoryName(); @@ -178,7 +138,7 @@ public function __construct(Connection $conn, Configuration $config, ?EventManag $this, $config->getProxyDir(), $config->getProxyNamespace(), - $config->getAutoGenerateProxyClasses() + $config->getAutoGenerateProxyClasses(), ); if ($config->isSecondLevelCacheEnabled()) { @@ -188,88 +148,32 @@ public function __construct(Connection $conn, Configuration $config, ?EventManag } } - /** - * {@inheritDoc} - */ - public function getConnection() + public function getConnection(): Connection { return $this->conn; } - /** - * Gets the metadata factory used to gather the metadata of classes. - * - * @return ClassMetadataFactory - */ - public function getMetadataFactory() + public function getMetadataFactory(): ClassMetadataFactory { return $this->metadataFactory; } - /** - * {@inheritDoc} - */ - public function getExpressionBuilder() + public function getExpressionBuilder(): Expr { - if ($this->expressionBuilder === null) { - $this->expressionBuilder = new Query\Expr(); - } - - return $this->expressionBuilder; + return $this->expressionBuilder ??= new Expr(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): void { $this->conn->beginTransaction(); } - /** - * {@inheritDoc} - */ - public function getCache() + public function getCache(): Cache|null { return $this->cache; } - /** - * {@inheritDoc} - */ - public function transactional($func) - { - if (! is_callable($func)) { - throw new InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"'); - } - - $this->conn->beginTransaction(); - - $successful = false; - - try { - $return = $func($this); - - $this->flush(); - $this->conn->commit(); - - $successful = true; - - return $return ?: true; - } finally { - if (! $successful) { - $this->close(); - if ($this->conn->isTransactionActive()) { - $this->conn->rollBack(); - } - } - } - } - - /** - * {@inheritDoc} - */ - public function wrapInTransaction(callable $func) + public function wrapInTransaction(callable $func): mixed { $this->conn->beginTransaction(); @@ -294,18 +198,12 @@ public function wrapInTransaction(callable $func) } } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { $this->conn->commit(); } - /** - * {@inheritDoc} - */ - public function rollback() + public function rollback(): void { $this->conn->rollBack(); } @@ -313,26 +211,16 @@ public function rollback() /** * Returns the ORM metadata descriptor for a class. * - * The class name must be the fully-qualified class name without a leading backslash - * (as it is returned by get_class($obj)) or an aliased class name. - * - * Examples: - * MyProject\Domain\User - * sales:PriceRequest - * * Internal note: Performance-sensitive method. * * {@inheritDoc} */ - public function getClassMetadata($className) + public function getClassMetadata(string $className): Mapping\ClassMetadata { return $this->metadataFactory->getMetadataFor($className); } - /** - * {@inheritDoc} - */ - public function createQuery($dql = '') + public function createQuery(string $dql = ''): Query { $query = new Query($this); @@ -343,18 +231,7 @@ public function createQuery($dql = '') return $query; } - /** - * {@inheritDoc} - */ - public function createNamedQuery($name) - { - return $this->createQuery($this->config->getNamedQuery($name)); - } - - /** - * {@inheritDoc} - */ - public function createNativeQuery($sql, ResultSetMapping $rsm) + public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery { $query = new NativeQuery($this); @@ -364,20 +241,7 @@ public function createNativeQuery($sql, ResultSetMapping $rsm) return $query; } - /** - * {@inheritDoc} - */ - public function createNamedNativeQuery($name) - { - [$sql, $rsm] = $this->config->getNamedNativeQuery($name); - - return $this->createNativeQuery($sql, $rsm); - } - - /** - * {@inheritDoc} - */ - public function createQueryBuilder() + public function createQueryBuilder(): QueryBuilder { return new QueryBuilder($this); } @@ -390,52 +254,20 @@ public function createQueryBuilder() * If an entity is explicitly passed to this method only this entity and * the cascade-persist semantics + scheduled inserts/removals are synchronized. * - * @param object|mixed[]|null $entity - * - * @return void - * * @throws OptimisticLockException If a version check on an entity that * makes use of optimistic locking fails. * @throws ORMException */ - public function flush($entity = null) + public function flush(): void { - if ($entity !== null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8459', - 'Calling %s() with any arguments to flush specific entities is deprecated and will not be supported in Doctrine ORM 3.0.', - __METHOD__ - ); - } - $this->errorIfClosed(); - - $this->unitOfWork->commit($entity); + $this->unitOfWork->commit(); } /** - * Finds an Entity by its identifier. - * - * @param class-string $className The class name of the entity to find. - * @param mixed $id The identity of the entity to find. - * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants - * or NULL if no specific lock mode should be used - * during the search. - * @param int|null $lockVersion The version of the entity to find when using - * optimistic locking. - * @phpstan-param LockMode::*|null $lockMode - * - * @return T|null The entity instance or NULL if the entity can not be found. - * - * @throws OptimisticLockException - * @throws ORMInvalidArgumentException - * @throws TransactionRequiredException - * @throws ORMException - * - * @template T of object + * {@inheritDoc} */ - public function find($className, $id, $lockMode = null, $lockVersion = null) + public function find($className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null { $class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\')); @@ -531,10 +363,7 @@ public function find($className, $id, $lockMode = null, $lockVersion = null) } } - /** - * {@inheritDoc} - */ - public function getReference($entityName, $id) + public function getReference(string $entityName, mixed $id): object|null { $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); @@ -575,79 +404,16 @@ public function getReference($entityName, $id) return $entity; } - /** - * {@inheritDoc} - */ - public function getPartialReference($entityName, $identifier) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10987', - 'Method %s is deprecated and will be removed in 3.0.', - __METHOD__ - ); - $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); - - $entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName); - - // Check identity map first, if its already in there just return it. - if ($entity !== false) { - return $entity instanceof $class->name ? $entity : null; - } - - if (! is_array($identifier)) { - $identifier = [$class->identifier[0] => $identifier]; - } - - $entity = $class->newInstance(); - - $class->setIdentifierValues($entity, $identifier); - - $this->unitOfWork->registerManaged($entity, $identifier, []); - $this->unitOfWork->markReadOnly($entity); - - return $entity; - } - /** * Clears the EntityManager. All entities that are currently managed * by this EntityManager become detached. - * - * @param string|null $entityName if given, only entities of this type will get detached - * - * @return void - * - * @throws ORMInvalidArgumentException If a non-null non-string value is given. - * @throws MappingException If a $entityName is given, but that entity is not - * found in the mappings. */ - public function clear($entityName = null) + public function clear(): void { - if ($entityName !== null && ! is_string($entityName)) { - // @phpstan-ignore staticMethod.deprecated - throw ORMInvalidArgumentException::invalidEntityName($entityName); - } - - if ($entityName !== null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8460', - 'Calling %s() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.', - __METHOD__ - ); - } - - $this->unitOfWork->clear( - $entityName === null - ? null - : $this->metadataFactory->getMetadataFor($entityName)->getName() - ); + $this->unitOfWork->clear(); } - /** - * {@inheritDoc} - */ - public function close() + public function close(): void { $this->clear(); @@ -663,22 +429,14 @@ public function close() * NOTE: The persist operation always considers entities that are not yet known to * this EntityManager as NEW. Do not pass detached entities to the persist operation. * - * @param object $entity The instance to make managed and persistent. - * - * @return void - * * @throws ORMInvalidArgumentException * @throws ORMException */ - public function persist($entity) + public function persist(object $object): void { - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity); - } - $this->errorIfClosed(); - $this->unitOfWork->persist($entity); + $this->unitOfWork->persist($object); } /** @@ -687,46 +445,21 @@ public function persist($entity) * A removed entity will be removed from the database at or before transaction commit * or as a result of the flush operation. * - * @param object $entity The entity instance to remove. - * - * @return void - * * @throws ORMInvalidArgumentException * @throws ORMException */ - public function remove($entity) + public function remove(object $object): void { - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity); - } - $this->errorIfClosed(); - $this->unitOfWork->remove($entity); + $this->unitOfWork->remove($object); } - /** - * Refreshes the persistent state of an entity from the database, - * overriding any local changes that have not yet been persisted. - * - * @param object $entity The entity to refresh - * @phpstan-param LockMode::*|null $lockMode - * - * @return void - * - * @throws ORMInvalidArgumentException - * @throws ORMException - * @throws TransactionRequiredException - */ - public function refresh($entity, ?int $lockMode = null) + public function refresh(object $object, LockMode|int|null $lockMode = null): void { - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity); - } - $this->errorIfClosed(); - $this->unitOfWork->refresh($entity, $lockMode); + $this->unitOfWork->refresh($object, $lockMode); } /** @@ -736,74 +469,14 @@ public function refresh($entity, ?int $lockMode = null) * Entities which previously referenced the detached entity will continue to * reference it. * - * @param object $entity The entity to detach. - * - * @return void - * - * @throws ORMInvalidArgumentException - */ - public function detach($entity) - { - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()', $entity); - } - - $this->unitOfWork->detach($entity); - } - - /** - * Merges the state of a detached entity into the persistence context - * of this EntityManager and returns the managed copy of the entity. - * The entity passed to merge will not become associated/managed with this EntityManager. - * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement - * - * @param object $entity The detached entity to merge into the persistence context. - * - * @return object The managed copy of the entity. - * * @throws ORMInvalidArgumentException - * @throws ORMException - */ - public function merge($entity) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8461', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0.', - __METHOD__ - ); - - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()', $entity); - } - - $this->errorIfClosed(); - - return $this->unitOfWork->merge($entity); - } - - /** - * {@inheritDoc} - * - * @phpstan-return never */ - public function copy($entity, $deep = false) + public function detach(object $object): void { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8462', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0.', - __METHOD__ - ); - - throw new BadMethodCallException('Not implemented.'); + $this->unitOfWork->detach($object); } - /** - * {@inheritDoc} - */ - public function lock($entity, $lockMode, $lockVersion = null) + public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void { $this->unitOfWork->lock($entity, $lockMode, $lockVersion); } @@ -811,71 +484,35 @@ public function lock($entity, $lockMode, $lockVersion = null) /** * Gets the repository for an entity class. * - * @param class-string $entityName The name of the entity. + * @param class-string $className The name of the entity. * * @return EntityRepository The repository class. * * @template T of object */ - public function getRepository($entityName) + public function getRepository(string $className): EntityRepository { - if (strpos($entityName, ':') !== false) { - if (class_exists(PersistentObject::class)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8818', - 'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.', - $entityName - ); - } else { - throw NotSupported::createForPersistence3(sprintf( - 'Using short namespace alias "%s" when calling %s', - $entityName, - __METHOD__ - )); - } - } - - $repository = $this->repositoryFactory->getRepository($this, $entityName); - if (! $repository instanceof EntityRepository) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9533', - 'Not returning an instance of %s from %s::getRepository() is deprecated and will cause a TypeError on 3.0.', - EntityRepository::class, - get_debug_type($this->repositoryFactory) - ); - } - - return $repository; + return $this->repositoryFactory->getRepository($this, $className); } /** * Determines whether an entity instance is managed in this EntityManager. * - * @param object $entity - * * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise. */ - public function contains($entity) + public function contains(object $object): bool { - return $this->unitOfWork->isScheduledForInsert($entity) - || $this->unitOfWork->isInIdentityMap($entity) - && ! $this->unitOfWork->isScheduledForDelete($entity); + return $this->unitOfWork->isScheduledForInsert($object) + || $this->unitOfWork->isInIdentityMap($object) + && ! $this->unitOfWork->isScheduledForDelete($object); } - /** - * {@inheritDoc} - */ - public function getEventManager() + public function getEventManager(): EventManager { return $this->eventManager; } - /** - * {@inheritDoc} - */ - public function getConfiguration() + public function getConfiguration(): Configuration { return $this->config; } @@ -892,77 +529,35 @@ private function errorIfClosed(): void } } - /** - * {@inheritDoc} - */ - public function isOpen() + public function isOpen(): bool { return ! $this->closed; } - /** - * {@inheritDoc} - */ - public function getUnitOfWork() + public function getUnitOfWork(): UnitOfWork { return $this->unitOfWork; } - /** - * {@inheritDoc} - */ - public function getHydrator($hydrationMode) + public function newHydrator(string|int $hydrationMode): AbstractHydrator { - return $this->newHydrator($hydrationMode); + return match ($hydrationMode) { + Query::HYDRATE_OBJECT => new Internal\Hydration\ObjectHydrator($this), + Query::HYDRATE_ARRAY => new Internal\Hydration\ArrayHydrator($this), + Query::HYDRATE_SCALAR => new Internal\Hydration\ScalarHydrator($this), + Query::HYDRATE_SINGLE_SCALAR => new Internal\Hydration\SingleScalarHydrator($this), + Query::HYDRATE_SIMPLEOBJECT => new Internal\Hydration\SimpleObjectHydrator($this), + Query::HYDRATE_SCALAR_COLUMN => new Internal\Hydration\ScalarColumnHydrator($this), + default => $this->createCustomHydrator((string) $hydrationMode), + }; } - /** - * {@inheritDoc} - */ - public function newHydrator($hydrationMode) - { - switch ($hydrationMode) { - case Query::HYDRATE_OBJECT: - return new Internal\Hydration\ObjectHydrator($this); - - case Query::HYDRATE_ARRAY: - return new Internal\Hydration\ArrayHydrator($this); - - case Query::HYDRATE_SCALAR: - return new Internal\Hydration\ScalarHydrator($this); - - case Query::HYDRATE_SINGLE_SCALAR: - return new Internal\Hydration\SingleScalarHydrator($this); - - case Query::HYDRATE_SIMPLEOBJECT: - return new Internal\Hydration\SimpleObjectHydrator($this); - - case Query::HYDRATE_SCALAR_COLUMN: - return new Internal\Hydration\ScalarColumnHydrator($this); - - default: - $class = $this->config->getCustomHydrationMode($hydrationMode); - - if ($class !== null) { - return new $class($this); - } - } - - throw InvalidHydrationMode::fromMode((string) $hydrationMode); - } - - /** - * {@inheritDoc} - */ - public function getProxyFactory() + public function getProxyFactory(): ProxyFactory { return $this->proxyFactory; } - /** - * {@inheritDoc} - */ - public function initializeObject($obj) + public function initializeObject(object $obj): void { $this->unitOfWork->initializeObject($obj); } @@ -970,112 +565,22 @@ public function initializeObject($obj) /** * {@inheritDoc} */ - public function isUninitializedObject($obj): bool + public function isUninitializedObject($value): bool { - return $this->unitOfWork->isUninitializedObject($obj); + return $this->unitOfWork->isUninitializedObject($value); } - /** - * Factory method to create EntityManager instances. - * - * @deprecated Use {@see DriverManager::getConnection()} to bootstrap the connection and call the constructor. - * - * @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance. - * @param Configuration $config The Configuration instance to use. - * @param EventManager|null $eventManager The EventManager instance to use. - * @phpstan-param array|Connection $connection - * - * @return EntityManager The created EntityManager. - * - * @throws InvalidArgumentException - * @throws ORMException - */ - public static function create($connection, Configuration $config, ?EventManager $eventManager = null) + public function getFilters(): FilterCollection { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9961', - '%s() is deprecated. To bootstrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.', - __METHOD__, - DriverManager::class, - self::class - ); - - $connection = static::createConnection($connection, $config, $eventManager); - - return new EntityManager($connection, $config); + return $this->filterCollection ??= new FilterCollection($this); } - /** - * Factory method to create Connection instances. - * - * @deprecated Use {@see DriverManager::getConnection()} to bootstrap the connection. - * - * @param mixed[]|Connection $connection An array with the connection parameters or an existing Connection instance. - * @param Configuration $config The Configuration instance to use. - * @param EventManager|null $eventManager The EventManager instance to use. - * @phpstan-param array|Connection $connection - * - * @return Connection - * - * @throws InvalidArgumentException - * @throws ORMException - */ - protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9961', - '%s() is deprecated, call %s::getConnection() instead.', - __METHOD__, - DriverManager::class - ); - - if (is_array($connection)) { - return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager()); - } - - if (! $connection instanceof Connection) { - throw new InvalidArgumentException( - sprintf( - 'Invalid $connection argument of type %s given%s.', - get_debug_type($connection), - is_object($connection) ? '' : ': "' . $connection . '"' - ) - ); - } - - if ($eventManager !== null && $connection->getEventManager() !== $eventManager) { - throw MismatchedEventManager::create(); - } - - return $connection; - } - - /** - * {@inheritDoc} - */ - public function getFilters() - { - if ($this->filterCollection === null) { - $this->filterCollection = new FilterCollection($this); - } - - return $this->filterCollection; - } - - /** - * {@inheritDoc} - */ - public function isFiltersStateClean() + public function isFiltersStateClean(): bool { return $this->filterCollection === null || $this->filterCollection->isClean(); } - /** - * {@inheritDoc} - */ - public function hasFilters() + public function hasFilters(): bool { return $this->filterCollection !== null; } @@ -1086,7 +591,7 @@ public function hasFilters() * @throws OptimisticLockException * @throws TransactionRequiredException */ - private function checkLockRequirements(int $lockMode, ClassMetadata $class): void + private function checkLockRequirements(LockMode|int $lockMode, ClassMetadata $class): void { switch ($lockMode) { case LockMode::OPTIMISTIC: @@ -1107,23 +612,20 @@ private function configureMetadataCache(): void { $metadataCache = $this->config->getMetadataCache(); if (! $metadataCache) { - $this->configureLegacyMetadataCache(); - return; } $this->metadataFactory->setCache($metadataCache); } - private function configureLegacyMetadataCache(): void + private function createCustomHydrator(string $hydrationMode): AbstractHydrator { - // @phpstan-ignore method.deprecated - $metadataCache = $this->config->getMetadataCacheImpl(); - if (! $metadataCache) { - return; + $class = $this->config->getCustomHydrationMode($hydrationMode); + + if ($class !== null) { + return new $class($this); } - // Wrap doctrine/cache to provide PSR-6 interface - $this->metadataFactory->setCache(CacheAdapter::wrap($metadataCache)); + throw InvalidHydrationMode::fromMode($hydrationMode); } } diff --git a/src/EntityManagerInterface.php b/src/EntityManagerInterface.php index 588075960ae..03dbdbbea19 100644 --- a/src/EntityManagerInterface.php +++ b/src/EntityManagerInterface.php @@ -4,26 +4,19 @@ namespace Doctrine\ORM; -use BadMethodCallException; use DateTimeInterface; use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; use Doctrine\DBAL\LockMode; use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Internal\Hydration\AbstractHydrator; +use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\FilterCollection; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Persistence\ObjectManager; -/** - * EntityManager interface - * - * @method Mapping\ClassMetadataFactory getMetadataFactory() - * @method mixed wrapInTransaction(callable $func) - * @method void refresh(object $object, ?int $lockMode = null) - */ interface EntityManagerInterface extends ObjectManager { /** @@ -35,21 +28,19 @@ interface EntityManagerInterface extends ObjectManager * * @template T of object */ - public function getRepository($className); + public function getRepository(string $className): EntityRepository; /** * Returns the cache API for managing the second level cache regions or NULL if the cache is not enabled. - * - * @return Cache|null */ - public function getCache(); + public function getCache(): Cache|null; /** * Gets the database connection object used by the EntityManager. - * - * @return Connection */ - public function getConnection(); + public function getConnection(): Connection; + + public function getMetadataFactory(): ClassMetadataFactory; /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. @@ -62,35 +53,13 @@ public function getConnection(); * $qb->select('u')->from('User', 'u') * ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2))); * - * - * @return Expr */ - public function getExpressionBuilder(); + public function getExpressionBuilder(): Expr; /** * Starts a transaction on the underlying database connection. - * - * @return void - */ - public function beginTransaction(); - - /** - * Executes a function in a transaction. - * - * The function gets passed this EntityManager instance as an (optional) parameter. - * - * {@link flush} is invoked prior to transaction commit. - * - * If an exception occurs during execution of the function or flushing or transaction commit, - * the transaction is rolled back, the EntityManager closed and the exception re-thrown. - * - * @deprecated 2.10 Use {@link wrapInTransaction} instead. - * - * @param callable $func The function to execute transactionally. - * - * @return mixed The non-empty value returned from the closure or true instead. */ - public function transactional($func); + public function beginTransaction(): void; /** * Executes a function in a transaction. @@ -102,71 +71,81 @@ public function transactional($func); * If an exception occurs during execution of the function or flushing or transaction commit, * the transaction is rolled back, the EntityManager closed and the exception re-thrown. * - * @param callable(self): T $func The function to execute transactionally. + * @phpstan-param callable(self): T $func The function to execute transactionally. * - * @return T The value returned from the closure. + * @return mixed The value returned from the closure. + * @phpstan-return T * * @template T */ - // public function wrapInTransaction(callable $func); + public function wrapInTransaction(callable $func): mixed; /** * Commits a transaction on the underlying database connection. - * - * @return void */ - public function commit(); + public function commit(): void; /** * Performs a rollback on the underlying database connection. - * - * @return void */ - public function rollback(); + public function rollback(): void; /** * Creates a new Query object. * * @param string $dql The DQL string. - * - * @return Query */ - public function createQuery($dql = ''); + public function createQuery(string $dql = ''): Query; /** - * Creates a Query from a named query. - * - * @param string $name - * - * @return Query + * Creates a native SQL query. */ - public function createNamedQuery($name); + public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery; /** - * Creates a native SQL query. - * - * @param string $sql - * @param ResultSetMapping $rsm The ResultSetMapping to use. - * - * @return NativeQuery + * Create a QueryBuilder instance */ - public function createNativeQuery($sql, ResultSetMapping $rsm); + public function createQueryBuilder(): QueryBuilder; /** - * Creates a NativeQuery from a named native query. + * Finds an Entity by its identifier. + * + * @param string $className The class name of the entity to find. + * @param mixed $id The identity of the entity to find. + * @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @param int|null $lockVersion The version of the entity to find when using + * optimistic locking. + * @phpstan-param class-string $className + * @phpstan-param LockMode::*|null $lockMode * - * @param string $name + * @return object|null The entity instance or NULL if the entity can not be found. + * @phpstan-return T|null * - * @return NativeQuery + * @throws OptimisticLockException + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws ORMException + * + * @template T of object */ - public function createNamedNativeQuery($name); + public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null; /** - * Create a QueryBuilder instance + * Refreshes the persistent state of an object from the database, + * overriding any local changes that have not yet been persisted. * - * @return QueryBuilder + * @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @phpstan-param LockMode::*|null $lockMode + * + * @throws ORMInvalidArgumentException + * @throws ORMException + * @throws TransactionRequiredException */ - public function createQueryBuilder(); + public function refresh(object $object, LockMode|int|null $lockMode = null): void; /** * Gets a reference to the entity identified by the given type and identifier @@ -179,166 +158,84 @@ public function createQueryBuilder(); * * @throws ORMException * - * @template T - */ - public function getReference($entityName, $id); - - /** - * Gets a partial reference to the entity identified by the given type and identifier - * without actually loading it, if the entity is not yet loaded. - * - * The returned reference may be a partial object if the entity is not yet loaded/managed. - * If it is a partial object it will not initialize the rest of the entity state on access. - * Thus you can only ever safely access the identifier of an entity obtained through - * this method. - * - * The use-cases for partial references involve maintaining bidirectional associations - * without loading one side of the association or to update an entity without loading it. - * Note, however, that in the latter case the original (persistent) entity data will - * never be visible to the application (especially not event listeners) as it will - * never be loaded in the first place. - * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement - * - * @param class-string $entityName The name of the entity type. - * @param mixed $identifier The entity identifier. - * - * @return T|null The (partial) entity reference - * - * @template T + * @template T of object */ - public function getPartialReference($entityName, $identifier); + public function getReference(string $entityName, mixed $id): object|null; /** * Closes the EntityManager. All entities that are currently managed * by this EntityManager become detached. The EntityManager may no longer * be used after it is closed. - * - * @return void - */ - public function close(); - - /** - * Creates a copy of the given entity. Can create a shallow or a deep copy. - * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement - * - * @param object $entity The entity to copy. - * @param bool $deep FALSE for a shallow copy, TRUE for a deep copy. - * - * @return object The new entity. - * - * @throws BadMethodCallException */ - public function copy($entity, $deep = false); + public function close(): void; /** * Acquire a lock on the given entity. * - * @param object $entity - * @param int $lockMode - * @param int|DateTimeInterface|null $lockVersion * @phpstan-param LockMode::* $lockMode * - * @return void - * * @throws OptimisticLockException * @throws PessimisticLockException */ - public function lock($entity, $lockMode, $lockVersion = null); + public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void; /** * Gets the EventManager used by the EntityManager. - * - * @return EventManager */ - public function getEventManager(); + public function getEventManager(): EventManager; /** * Gets the Configuration used by the EntityManager. - * - * @return Configuration */ - public function getConfiguration(); + public function getConfiguration(): Configuration; /** * Check if the Entity manager is open or closed. - * - * @return bool */ - public function isOpen(); + public function isOpen(): bool; /** * Gets the UnitOfWork used by the EntityManager to coordinate operations. - * - * @return UnitOfWork */ - public function getUnitOfWork(); - - /** - * Gets a hydrator for the given hydration mode. - * - * This method caches the hydrator instances which is used for all queries that don't - * selectively iterate over the result. - * - * @deprecated - * - * @param string|int $hydrationMode - * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode - * - * @return AbstractHydrator - */ - public function getHydrator($hydrationMode); + public function getUnitOfWork(): UnitOfWork; /** * Create a new instance for the given hydration mode. * - * @param string|int $hydrationMode * @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode * - * @return AbstractHydrator - * * @throws ORMException */ - public function newHydrator($hydrationMode); + public function newHydrator(string|int $hydrationMode): AbstractHydrator; /** * Gets the proxy factory used by the EntityManager to create entity proxies. - * - * @return ProxyFactory */ - public function getProxyFactory(); + public function getProxyFactory(): ProxyFactory; /** * Gets the enabled filters. - * - * @return FilterCollection The active filter collection. */ - public function getFilters(); + public function getFilters(): FilterCollection; /** * Checks whether the state of the filter collection is clean. - * - * @return bool True, if the filter collection is clean. */ - public function isFiltersStateClean(); + public function isFiltersStateClean(): bool; /** * Checks whether the Entity Manager has filters. - * - * @return bool True, if the EM has a filter collection. */ - public function hasFilters(); + public function hasFilters(): bool; /** * {@inheritDoc} * * @param string|class-string $className * - * @return Mapping\ClassMetadata * @phpstan-return ($className is class-string ? Mapping\ClassMetadata : Mapping\ClassMetadata) * * @phpstan-template T of object */ - public function getClassMetadata($className); + public function getClassMetadata(string $className): Mapping\ClassMetadata; } diff --git a/src/EntityNotFoundException.php b/src/EntityNotFoundException.php index 76abf4dbc91..142dc8a4ab5 100644 --- a/src/EntityNotFoundException.php +++ b/src/EntityNotFoundException.php @@ -5,6 +5,7 @@ namespace Doctrine\ORM; use Doctrine\ORM\Exception\ORMException; +use RuntimeException; use function implode; use function sprintf; @@ -12,17 +13,14 @@ /** * Exception thrown when a Proxy fails to retrieve an Entity result. */ -class EntityNotFoundException extends ORMException +class EntityNotFoundException extends RuntimeException implements ORMException { /** * Static constructor. * - * @param string $className * @param string[] $id - * - * @return self */ - public static function fromClassNameAndIdentifier($className, array $id) + public static function fromClassNameAndIdentifier(string $className, array $id): self { $ids = []; @@ -31,7 +29,7 @@ public static function fromClassNameAndIdentifier($className, array $id) } return new self( - 'Entity of type \'' . $className . '\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found' + 'Entity of type \'' . $className . '\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found', ); } @@ -42,7 +40,7 @@ public static function noIdentifierFound(string $className): self { return new self(sprintf( 'Unable to find "%s" entity identifier associated with the UnitOfWork', - $className + $className, )); } } diff --git a/src/EntityRepository.php b/src/EntityRepository.php index f6ab954ebf7..fb1315b785d 100644 --- a/src/EntityRepository.php +++ b/src/EntityRepository.php @@ -8,19 +8,15 @@ use Doctrine\Common\Collections\AbstractLazyCollection; use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Selectable; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\Deprecation; use Doctrine\Inflector\Inflector; use Doctrine\Inflector\InflectorFactory; -use Doctrine\ORM\Exception\NotSupported; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall; use Doctrine\Persistence\ObjectRepository; use function array_slice; -use function class_exists; use function lcfirst; use function sprintf; use function str_starts_with; @@ -39,161 +35,55 @@ */ class EntityRepository implements ObjectRepository, Selectable { - /** - * @internal This property will be private in 3.0, call {@see getEntityName()} instead. - * - * @var class-string - */ - protected $_entityName; - - /** - * @internal This property will be private in 3.0, call {@see getEntityManager()} instead. - * - * @var EntityManagerInterface - */ - protected $_em; + /** @var class-string */ + private readonly string $entityName; + private static Inflector|null $inflector = null; - /** - * @internal This property will be private in 3.0, call {@see getClassMetadata()} instead. - * - * @var ClassMetadata - * @phpstan-var ClassMetadata - */ - protected $_class; - - /** @var Inflector|null */ - private static $inflector; - - /** @phpstan-param ClassMetadata $class */ - public function __construct(EntityManagerInterface $em, ClassMetadata $class) - { - $this->_entityName = $class->name; - $this->_em = $em; - $this->_class = $class; + /** @param ClassMetadata $class */ + public function __construct( + private readonly EntityManagerInterface $em, + private readonly ClassMetadata $class, + ) { + $this->entityName = $class->name; } /** * Creates a new QueryBuilder instance that is prepopulated for this entity name. - * - * @param string $alias - * @param string|null $indexBy The index for the from. - * - * @return QueryBuilder */ - public function createQueryBuilder($alias, $indexBy = null) + public function createQueryBuilder(string $alias, string|null $indexBy = null): QueryBuilder { - return $this->_em->createQueryBuilder() + return $this->em->createQueryBuilder() ->select($alias) - ->from($this->_entityName, $alias, $indexBy); + ->from($this->entityName, $alias, $indexBy); } /** * Creates a new result set mapping builder for this entity. * * The column naming strategy is "INCREMENT". - * - * @param string $alias - * - * @return ResultSetMappingBuilder */ - public function createResultSetMappingBuilder($alias) + public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder { - $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); - $rsm->addRootEntityFromClassMetadata($this->_entityName, $alias); + $rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); + $rsm->addRootEntityFromClassMetadata($this->entityName, $alias); return $rsm; } - /** - * Creates a new Query instance based on a predefined metadata named query. - * - * @deprecated - * - * @param string $queryName - * - * @return Query - */ - public function createNamedQuery($queryName) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8592', - 'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', - $queryName, - $this->_class->name - ); - - return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); - } - - /** - * Creates a native SQL query. - * - * @deprecated - * - * @param string $queryName - * - * @return NativeQuery - */ - public function createNativeNamedQuery($queryName) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8592', - 'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', - $queryName, - $this->_class->name - ); - - $queryMapping = $this->_class->getNamedNativeQuery($queryName); - $rsm = new Query\ResultSetMappingBuilder($this->_em); - $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping); - - return $this->_em->createNativeQuery($queryMapping['query'], $rsm); - } - - /** - * Clears the repository, causing all managed entities to become detached. - * - * @deprecated 2.8 This method is being removed from the ORM and won't have any replacement - * - * @return void - */ - public function clear() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8460', - 'Calling %s() is deprecated and will not be supported in Doctrine ORM 3.0.', - __METHOD__ - ); - - if (! class_exists(PersistentObject::class)) { - throw NotSupported::createForPersistence3(sprintf( - 'Partial clearing of entities for class %s', - $this->_class->rootEntityName - )); - } - - $this->_em->clear($this->_class->rootEntityName); - } - /** * Finds an entity by its primary key / identifier. * - * @param mixed $id The identifier. - * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants - * or NULL if no specific lock mode should be used - * during the search. - * @param int|null $lockVersion The lock version. + * @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. * @phpstan-param LockMode::*|null $lockMode * * @return object|null The entity instance or NULL if the entity can not be found. * @phpstan-return ?T */ - public function find($id, $lockMode = null, $lockVersion = null) + public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null { - return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion); + return $this->em->find($this->entityName, $id, $lockMode, $lockVersion); } /** @@ -201,7 +91,7 @@ public function find($id, $lockMode = null, $lockVersion = null) * * @phpstan-return list The entities. */ - public function findAll() + public function findAll(): array { return $this->findBy([]); } @@ -209,17 +99,13 @@ public function findAll() /** * Finds entities by a set of criteria. * - * @param int|null $limit - * @param int|null $offset - * @phpstan-param array $criteria - * @phpstan-param array|null $orderBy + * {@inheritDoc} * - * @return object[] The objects. * @phpstan-return list */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array { - $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName); return $persister->loadAll($criteria, $orderBy, $limit, $offset); } @@ -230,12 +116,11 @@ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $ * @phpstan-param array $criteria * @phpstan-param array|null $orderBy * - * @return object|null The entity instance or NULL if the entity can not be found. - * @phpstan-return ?T + * @phpstan-return T|null */ - public function findOneBy(array $criteria, ?array $orderBy = null) + public function findOneBy(array $criteria, array|null $orderBy = null): object|null { - $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName); return $persister->load($criteria, null, null, [], null, 1, $orderBy); } @@ -246,26 +131,24 @@ public function findOneBy(array $criteria, ?array $orderBy = null) * @phpstan-param array $criteria * * @return int The cardinality of the objects that match the given criteria. + * @phpstan-return 0|positive-int * * @todo Add this method to `ObjectRepository` interface in the next major release */ - public function count(array $criteria) + public function count(array $criteria = []): int { - return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->count($criteria); + return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria); } /** * Adds support for magic method calls. * - * @param string $method * @param mixed[] $arguments * @phpstan-param list $arguments * - * @return mixed The returned value from the resolved method. - * * @throws BadMethodCallException If the method called is invalid. */ - public function __call($method, $arguments) + public function __call(string $method, array $arguments): mixed { if (str_starts_with($method, 'findBy')) { return $this->resolveMagicCall('findBy', substr($method, 6), $arguments); @@ -282,49 +165,41 @@ public function __call($method, $arguments) throw new BadMethodCallException(sprintf( 'Undefined method "%s". The method name must start with ' . 'either findBy, findOneBy or countBy!', - $method + $method, )); } /** @return class-string */ - protected function getEntityName() + protected function getEntityName(): string { - return $this->_entityName; + return $this->entityName; } - /** - * {@inheritDoc} - */ - public function getClassName() + public function getClassName(): string { return $this->getEntityName(); } - /** @return EntityManagerInterface */ - protected function getEntityManager() + protected function getEntityManager(): EntityManagerInterface { - return $this->_em; + return $this->em; } - /** - * @return ClassMetadata - * @phpstan-return ClassMetadata - */ - protected function getClassMetadata() + /** @phpstan-return ClassMetadata */ + protected function getClassMetadata(): ClassMetadata { - return $this->_class; + return $this->class; } /** * Select all elements from a selectable that match the expression and * return a new collection containing these elements. * - * @return AbstractLazyCollection * @phpstan-return AbstractLazyCollection&Selectable */ - public function matching(Criteria $criteria) + public function matching(Criteria $criteria): AbstractLazyCollection&Selectable { - $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName); return new LazyCriteriaCollection($persister, $criteria); } @@ -336,28 +211,24 @@ public function matching(Criteria $criteria) * @param string $by The property name used as condition * @phpstan-param list $arguments The arguments to pass at method call * - * @return mixed - * * @throws InvalidMagicMethodCall If the method called is invalid or the * requested field/association does not exist. */ - private function resolveMagicCall(string $method, string $by, array $arguments) + private function resolveMagicCall(string $method, string $by, array $arguments): mixed { if (! $arguments) { throw InvalidMagicMethodCall::onMissingParameter($method . $by); } - if (self::$inflector === null) { - self::$inflector = InflectorFactory::create()->build(); - } + self::$inflector ??= InflectorFactory::create()->build(); $fieldName = lcfirst(self::$inflector->classify($by)); - if (! ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName))) { + if (! ($this->class->hasField($fieldName) || $this->class->hasAssociation($fieldName))) { throw InvalidMagicMethodCall::becauseFieldNotFoundIn( - $this->_entityName, + $this->entityName, $fieldName, - $method . $by + $method . $by, ); } diff --git a/src/Event/LifecycleEventArgs.php b/src/Event/LifecycleEventArgs.php deleted file mode 100644 index 5233316fa3c..00000000000 --- a/src/Event/LifecycleEventArgs.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -class LifecycleEventArgs extends BaseLifecycleEventArgs -{ - /** @param object $object */ - public function __construct($object, EntityManagerInterface $objectManager) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'The %s class is deprecated and will be removed in ORM 3.0. Use %s instead.', - self::class, - BaseLifecycleEventArgs::class - ); - - parent::__construct($object, $objectManager); - } - - /** - * Retrieves associated Entity. - * - * @deprecated 2.13. Use {@see getObject} instead. - * - * @return object - */ - public function getEntity() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObject() instead.', - __METHOD__ - ); - - return $this->getObject(); - } - - /** - * Retrieves associated EntityManager. - * - * @deprecated 2.13. Use {@see getObjectManager} instead. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.', - __METHOD__ - ); - - return $this->getObjectManager(); - } -} diff --git a/src/Event/ListenersInvoker.php b/src/Event/ListenersInvoker.php index ce7e0e683b1..b7e06912fbb 100644 --- a/src/Event/ListenersInvoker.php +++ b/src/Event/ListenersInvoker.php @@ -15,20 +15,16 @@ */ class ListenersInvoker { - public const INVOKE_NONE = 0; - public const INVOKE_LISTENERS = 1; - public const INVOKE_CALLBACKS = 2; - public const INVOKE_MANAGER = 4; + final public const INVOKE_NONE = 0; + final public const INVOKE_LISTENERS = 1; + final public const INVOKE_CALLBACKS = 2; + final public const INVOKE_MANAGER = 4; - /** @var EntityListenerResolver The Entity listener resolver. */ - private $resolver; + /** The Entity listener resolver. */ + private readonly EntityListenerResolver $resolver; - /** - * The EventManager used for dispatching events. - * - * @var EventManager - */ - private $eventManager; + /** The EventManager used for dispatching events. */ + private readonly EventManager $eventManager; public function __construct(EntityManagerInterface $em) { @@ -42,10 +38,9 @@ public function __construct(EntityManagerInterface $em) * @param ClassMetadata $metadata The entity metadata. * @param string $eventName The entity lifecycle event. * - * @return int Bitmask of subscribed event systems. - * @phpstan-return int-mask-of + * @phpstan-return int-mask-of Bitmask of subscribed event systems. */ - public function getSubscribedSystems(ClassMetadata $metadata, $eventName) + public function getSubscribedSystems(ClassMetadata $metadata, string $eventName): int { $invoke = self::INVOKE_NONE; @@ -71,13 +66,15 @@ public function getSubscribedSystems(ClassMetadata $metadata, $eventName) * @param string $eventName The entity lifecycle event. * @param object $entity The Entity on which the event occurred. * @param EventArgs $event The Event args. - * @param int $invoke Bitmask to invoke listeners. - * @phpstan-param int-mask-of $invoke - * - * @return void + * @phpstan-param int-mask-of $invoke Bitmask to invoke listeners. */ - public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke) - { + public function invoke( + ClassMetadata $metadata, + string $eventName, + object $entity, + EventArgs $event, + int $invoke, + ): void { if ($invoke & self::INVOKE_CALLBACKS) { foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) { $entity->$callback($event); diff --git a/src/Event/LoadClassMetadataEventArgs.php b/src/Event/LoadClassMetadataEventArgs.php index 94adb108c74..b45061687ea 100644 --- a/src/Event/LoadClassMetadataEventArgs.php +++ b/src/Event/LoadClassMetadataEventArgs.php @@ -17,10 +17,8 @@ class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs { /** * Retrieve associated EntityManager. - * - * @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { return $this->getObjectManager(); } diff --git a/src/Event/OnClassMetadataNotFoundEventArgs.php b/src/Event/OnClassMetadataNotFoundEventArgs.php index 8ef9e837786..762c08340a2 100644 --- a/src/Event/OnClassMetadataNotFoundEventArgs.php +++ b/src/Event/OnClassMetadataNotFoundEventArgs.php @@ -4,14 +4,11 @@ namespace Doctrine\ORM\Event; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Event\ManagerEventArgs; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; -use function func_num_args; - /** * Class that holds event arguments for a `onClassMetadataNotFound` event. * @@ -22,50 +19,30 @@ */ class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs { - /** @var string */ - private $className; - - /** @var ClassMetadata|null */ - private $foundMetadata; - - /** - * @param string $className - * @param EntityManagerInterface $objectManager - */ - public function __construct($className, ObjectManager $objectManager) - { - $this->className = (string) $className; + private ClassMetadata|null $foundMetadata = null; + /** @param EntityManagerInterface $objectManager */ + public function __construct( + private readonly string $className, + ObjectManager $objectManager, + ) { parent::__construct($objectManager); } - /** @return void */ - public function setFoundMetadata(?ClassMetadata $classMetadata = null) + public function setFoundMetadata(ClassMetadata|null $classMetadata): void { - if (func_num_args() < 1) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9791', - 'Calling %s without arguments is deprecated, pass null instead.', - __METHOD__ - ); - } - $this->foundMetadata = $classMetadata; } - /** @return ClassMetadata|null */ - public function getFoundMetadata() + public function getFoundMetadata(): ClassMetadata|null { return $this->foundMetadata; } /** * Retrieve class name for which a failed metadata fetch attempt was executed - * - * @return string */ - public function getClassName() + public function getClassName(): string { return $this->className; } diff --git a/src/Event/OnClearEventArgs.php b/src/Event/OnClearEventArgs.php index 2948744674e..29a42f24aad 100644 --- a/src/Event/OnClearEventArgs.php +++ b/src/Event/OnClearEventArgs.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Event; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Event\OnClearEventArgs as BaseOnClearEventArgs; @@ -17,57 +16,4 @@ */ class OnClearEventArgs extends BaseOnClearEventArgs { - /** @var string|null */ - private $entityClass; - - /** @param string|null $entityClass Optional entity class. */ - public function __construct(EntityManagerInterface $em, $entityClass = null) - { - parent::__construct($em); - - $this->entityClass = $entityClass; - } - - /** - * Retrieves associated EntityManager. - * - * @deprecated 2.13. Use {@see getObjectManager} instead. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.', - __METHOD__ - ); - - return $this->getObjectManager(); - } - - /** - * Name of the entity class that is cleared, or empty if all are cleared. - * - * @deprecated Clearing the entity manager partially is deprecated. This method will be removed in 3.0. - * - * @return string|null - */ - public function getEntityClass() - { - return $this->entityClass; - } - - /** - * Checks if event clears all entities. - * - * @deprecated Clearing the entity manager partially is deprecated. This method will be removed in 3.0. - * - * @return bool - */ - public function clearsAllEntities() - { - return $this->entityClass === null; - } } diff --git a/src/Event/OnFlushEventArgs.php b/src/Event/OnFlushEventArgs.php index 340719f78f5..b0594cac1b0 100644 --- a/src/Event/OnFlushEventArgs.php +++ b/src/Event/OnFlushEventArgs.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Event; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Event\ManagerEventArgs; @@ -17,22 +16,4 @@ */ class OnFlushEventArgs extends ManagerEventArgs { - /** - * Retrieve associated EntityManager. - * - * @deprecated 2.13. Use {@see getObjectManager} instead. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.', - __METHOD__ - ); - - return $this->getObjectManager(); - } } diff --git a/src/Event/PostFlushEventArgs.php b/src/Event/PostFlushEventArgs.php index ae3f52a4066..ca41ba8ee57 100644 --- a/src/Event/PostFlushEventArgs.php +++ b/src/Event/PostFlushEventArgs.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Event; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Event\ManagerEventArgs; @@ -17,22 +16,4 @@ */ class PostFlushEventArgs extends ManagerEventArgs { - /** - * Retrieves associated EntityManager. - * - * @deprecated 2.13. Use {@see getObjectManager} instead. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.', - __METHOD__ - ); - - return $this->getObjectManager(); - } } diff --git a/src/Event/PostLoadEventArgs.php b/src/Event/PostLoadEventArgs.php index 466bd019e0e..8344e681337 100644 --- a/src/Event/PostLoadEventArgs.php +++ b/src/Event/PostLoadEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PostLoadEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PostPersistEventArgs.php b/src/Event/PostPersistEventArgs.php index f7074f5f22b..926ac1c21c1 100644 --- a/src/Event/PostPersistEventArgs.php +++ b/src/Event/PostPersistEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PostPersistEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PostRemoveEventArgs.php b/src/Event/PostRemoveEventArgs.php index d6b2afa7282..8bf857e9fc1 100644 --- a/src/Event/PostRemoveEventArgs.php +++ b/src/Event/PostRemoveEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PostRemoveEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PostUpdateEventArgs.php b/src/Event/PostUpdateEventArgs.php index 448292b6098..c9ff00477d4 100644 --- a/src/Event/PostUpdateEventArgs.php +++ b/src/Event/PostUpdateEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PostUpdateEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PreFlushEventArgs.php b/src/Event/PreFlushEventArgs.php index b8bdb25538e..671535c960c 100644 --- a/src/Event/PreFlushEventArgs.php +++ b/src/Event/PreFlushEventArgs.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Event; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\Event\ManagerEventArgs; @@ -17,20 +16,4 @@ */ class PreFlushEventArgs extends ManagerEventArgs { - /** - * @deprecated 2.13. Use {@see getObjectManager} instead. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9875', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getObjectManager() instead.', - __METHOD__ - ); - - return $this->getObjectManager(); - } } diff --git a/src/Event/PrePersistEventArgs.php b/src/Event/PrePersistEventArgs.php index 96c7f82fde2..e70c3cf7366 100644 --- a/src/Event/PrePersistEventArgs.php +++ b/src/Event/PrePersistEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PrePersistEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PreRemoveEventArgs.php b/src/Event/PreRemoveEventArgs.php index b2fb51bd998..3af0d02930f 100644 --- a/src/Event/PreRemoveEventArgs.php +++ b/src/Event/PreRemoveEventArgs.php @@ -4,7 +4,10 @@ namespace Doctrine\ORM\Event; -/** @phpstan-ignore class.extendsDeprecatedClass */ +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Event\LifecycleEventArgs; + +/** @extends LifecycleEventArgs */ final class PreRemoveEventArgs extends LifecycleEventArgs { } diff --git a/src/Event/PreUpdateEventArgs.php b/src/Event/PreUpdateEventArgs.php index 4cd239459e2..d50033414cf 100644 --- a/src/Event/PreUpdateEventArgs.php +++ b/src/Event/PreUpdateEventArgs.php @@ -6,6 +6,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\PersistentCollection; +use Doctrine\Persistence\Event\LifecycleEventArgs; use InvalidArgumentException; use function get_debug_type; @@ -14,21 +15,19 @@ /** * Class that holds event arguments for a preUpdate event. * - * @phpstan-ignore class.extendsDeprecatedClass + * @extends LifecycleEventArgs */ class PreUpdateEventArgs extends LifecycleEventArgs { /** @var array */ - private $entityChangeSet; + private array $entityChangeSet; /** - * @param object $entity * @param mixed[][] $changeSet * @phpstan-param array $changeSet */ - public function __construct($entity, EntityManagerInterface $em, array &$changeSet) + public function __construct(object $entity, EntityManagerInterface $em, array &$changeSet) { - // @phpstan-ignore staticMethod.deprecatedClass parent::__construct($entity, $em); $this->entityChangeSet = &$changeSet; @@ -40,31 +39,23 @@ public function __construct($entity, EntityManagerInterface $em, array &$changeS * @return mixed[][] * @phpstan-return array */ - public function getEntityChangeSet() + public function getEntityChangeSet(): array { return $this->entityChangeSet; } /** * Checks if field has a changeset. - * - * @param string $field - * - * @return bool */ - public function hasChangedField($field) + public function hasChangedField(string $field): bool { return isset($this->entityChangeSet[$field]); } /** * Gets the old value of the changeset of the changed field. - * - * @param string $field - * - * @return mixed */ - public function getOldValue($field) + public function getOldValue(string $field): mixed { $this->assertValidField($field); @@ -73,12 +64,8 @@ public function getOldValue($field) /** * Gets the new value of the changeset of the changed field. - * - * @param string $field - * - * @return mixed */ - public function getNewValue($field) + public function getNewValue(string $field): mixed { $this->assertValidField($field); @@ -87,13 +74,8 @@ public function getNewValue($field) /** * Sets the new value of this field. - * - * @param string $field - * @param mixed $value - * - * @return void */ - public function setNewValue($field, $value) + public function setNewValue(string $field, mixed $value): void { $this->assertValidField($field); @@ -111,7 +93,7 @@ private function assertValidField(string $field): void throw new InvalidArgumentException(sprintf( 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', $field, - get_debug_type($this->getObject()) + get_debug_type($this->getObject()), )); } } diff --git a/src/Events.php b/src/Events.php index 4695a7fb772..740290b3dcb 100644 --- a/src/Events.php +++ b/src/Events.php @@ -82,7 +82,7 @@ private function __construct() /** * The loadClassMetadata event occurs after the mapping metadata for a class - * has been loaded from a mapping source (attributes/xml/yaml). + * has been loaded from a mapping source (attributes/xml). */ public const loadClassMetadata = 'loadClassMetadata'; diff --git a/src/Exception/ConfigurationException.php b/src/Exception/ConfigurationException.php index 06437fde514..45cf83f7d4e 100644 --- a/src/Exception/ConfigurationException.php +++ b/src/Exception/ConfigurationException.php @@ -4,8 +4,6 @@ namespace Doctrine\ORM\Exception; -use Throwable; - -interface ConfigurationException extends Throwable +interface ConfigurationException extends ORMException { } diff --git a/src/Exception/DuplicateFieldException.php b/src/Exception/DuplicateFieldException.php new file mode 100644 index 00000000000..ec7cb00593e --- /dev/null +++ b/src/Exception/DuplicateFieldException.php @@ -0,0 +1,17 @@ +alreadyDelegatedToGenerateId) { - throw new LogicException(sprintf( - 'Endless recursion detected in %s. Please implement generateId() without calling the parent implementation.', - get_debug_type($this) - )); - } - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9325', - '%s::generate() is deprecated, call generateId() instead.', - get_debug_type($this) - ); - - $this->alreadyDelegatedToGenerateId = true; - - try { - return $this->generateId($em, $entity); - } finally { - $this->alreadyDelegatedToGenerateId = false; - } - } - - /** - * Generates an identifier for an entity. - * - * @param object|null $entity - * - * @return mixed - */ - public function generateId(EntityManagerInterface $em, $entity) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9325', - 'Not implementing %s in %s is deprecated.', - __FUNCTION__, - get_debug_type($this) - ); - - if (! $em instanceof EntityManager) { - throw new InvalidArgumentException('Unsupported entity manager implementation.'); - } - - // @phpstan-ignore method.deprecated - return $this->generate($em, $entity); - } + abstract public function generateId(EntityManagerInterface $em, object|null $entity): mixed; /** * Gets whether this generator is a post-insert generator which means that @@ -84,10 +20,8 @@ public function generateId(EntityManagerInterface $em, $entity) * * By default, this method returns FALSE. Generators that have this requirement * must override this method and return TRUE. - * - * @return bool */ - public function isPostInsertGenerator() + public function isPostInsertGenerator(): bool { return false; } diff --git a/src/Id/AssignedGenerator.php b/src/Id/AssignedGenerator.php index 6a62dc44b3b..e11b341a7dc 100644 --- a/src/Id/AssignedGenerator.php +++ b/src/Id/AssignedGenerator.php @@ -7,8 +7,6 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\EntityMissingAssignedId; -use function get_class; - /** * Special generator for application-assigned identifiers (doesn't really generate anything). */ @@ -21,9 +19,9 @@ class AssignedGenerator extends AbstractIdGenerator * * @throws EntityMissingAssignedId */ - public function generateId(EntityManagerInterface $em, $entity) + public function generateId(EntityManagerInterface $em, object|null $entity): array { - $class = $em->getClassMetadata(get_class($entity)); + $class = $em->getClassMetadata($entity::class); $idFields = $class->getIdentifierFieldNames(); $identifier = []; diff --git a/src/Id/BigIntegerIdentityGenerator.php b/src/Id/BigIntegerIdentityGenerator.php index a6c30cf8840..762a7cb7070 100644 --- a/src/Id/BigIntegerIdentityGenerator.php +++ b/src/Id/BigIntegerIdentityGenerator.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Id; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; /** @@ -14,44 +13,12 @@ */ class BigIntegerIdentityGenerator extends AbstractIdGenerator { - /** - * The name of the sequence to pass to lastInsertId(), if any. - * - * @var string|null - */ - private $sequenceName; - - /** - * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() - * to obtain the last generated identifier within the current - * database session/connection, if any. - */ - public function __construct($sequenceName = null) - { - if ($sequenceName !== null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8850', - 'Passing a sequence name to the IdentityGenerator is deprecated in favor of using %s. $sequenceName will be removed in ORM 3.0', - SequenceGenerator::class - ); - } - - $this->sequenceName = $sequenceName; - } - - /** - * {@inheritDoc} - */ - public function generateId(EntityManagerInterface $em, $entity) + public function generateId(EntityManagerInterface $em, object|null $entity): string { - return (string) $em->getConnection()->lastInsertId($this->sequenceName); + return (string) $em->getConnection()->lastInsertId(); } - /** - * {@inheritDoc} - */ - public function isPostInsertGenerator() + public function isPostInsertGenerator(): bool { return true; } diff --git a/src/Id/IdentityGenerator.php b/src/Id/IdentityGenerator.php index 6f83b85693c..4610f66426b 100644 --- a/src/Id/IdentityGenerator.php +++ b/src/Id/IdentityGenerator.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Id; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; /** @@ -14,44 +13,12 @@ */ class IdentityGenerator extends AbstractIdGenerator { - /** - * The name of the sequence to pass to lastInsertId(), if any. - * - * @var string|null - */ - private $sequenceName; - - /** - * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() - * to obtain the last generated identifier within the current - * database session/connection, if any. - */ - public function __construct($sequenceName = null) - { - if ($sequenceName !== null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8850', - 'Passing a sequence name to the IdentityGenerator is deprecated in favor of using %s. $sequenceName will be removed in ORM 3.0', - SequenceGenerator::class - ); - } - - $this->sequenceName = $sequenceName; - } - - /** - * {@inheritDoc} - */ - public function generateId(EntityManagerInterface $em, $entity) + public function generateId(EntityManagerInterface $em, object|null $entity): int { - return (int) $em->getConnection()->lastInsertId($this->sequenceName); + return (int) $em->getConnection()->lastInsertId(); } - /** - * {@inheritDoc} - */ - public function isPostInsertGenerator() + public function isPostInsertGenerator(): bool { return true; } diff --git a/src/Id/SequenceGenerator.php b/src/Id/SequenceGenerator.php index fc91573e61d..659bb58b9e4 100644 --- a/src/Id/SequenceGenerator.php +++ b/src/Id/SequenceGenerator.php @@ -5,6 +5,7 @@ namespace Doctrine\ORM\Id; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; +use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Serializable; @@ -16,25 +17,8 @@ */ class SequenceGenerator extends AbstractIdGenerator implements Serializable { - /** - * The allocation size of the sequence. - * - * @var int - */ - private $allocationSize; - - /** - * The name of the sequence. - * - * @var string - */ - private $sequenceName; - - /** @var int */ - private $nextValue = 0; - - /** @var int|null */ - private $maxValue = null; + private int $nextValue = 0; + private int|null $maxValue = null; /** * Initializes a new sequence generator. @@ -42,16 +26,13 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable * @param string $sequenceName The name of the sequence. * @param int $allocationSize The allocation size of the sequence. */ - public function __construct($sequenceName, $allocationSize) - { - $this->sequenceName = $sequenceName; - $this->allocationSize = $allocationSize; + public function __construct( + private string $sequenceName, + private int $allocationSize, + ) { } - /** - * {@inheritDoc} - */ - public function generateId(EntityManagerInterface $em, $entity) + public function generateId(EntityManagerInterface $em, object|null $entity): int { if ($this->maxValue === null || $this->nextValue === $this->maxValue) { // Allocate new values @@ -71,31 +52,31 @@ public function generateId(EntityManagerInterface $em, $entity) /** * Gets the maximum value of the currently allocated bag of values. - * - * @return int|null */ - public function getCurrentMaxValue() + public function getCurrentMaxValue(): int|null { return $this->maxValue; } /** * Gets the next value that will be returned by generate(). - * - * @return int */ - public function getNextValue() + public function getNextValue(): int { return $this->nextValue; } - /** - * @return string - * - * @final - */ - public function serialize() + /** @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()); } @@ -108,15 +89,17 @@ public function __serialize(): array ]; } - /** - * @param string $serialized - * - * @return void - * - * @final - */ - public function unserialize($serialized) + /** @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)); } diff --git a/src/Id/TableGenerator.php b/src/Id/TableGenerator.php deleted file mode 100644 index dfc5af90a3d..00000000000 --- a/src/Id/TableGenerator.php +++ /dev/null @@ -1,83 +0,0 @@ -tableName = $tableName; - $this->sequenceName = $sequenceName; - $this->allocationSize = $allocationSize; - } - - /** - * {@inheritDoc} - */ - public function generateId( - EntityManagerInterface $em, - $entity - ) { - if ($this->maxValue === null || $this->nextValue === $this->maxValue) { - // Allocate new values - $conn = $em->getConnection(); - - if ($conn->getTransactionNestingLevel() === 0) { - // use select for update - $sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->tableName, $this->sequenceName); - $currentLevel = $conn->fetchOne($sql); - - if ($currentLevel !== null) { - $this->nextValue = $currentLevel; - $this->maxValue = $this->nextValue + $this->allocationSize; - - $updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql( - $this->tableName, - $this->sequenceName, - $this->allocationSize - ); - - if ($conn->executeStatement($updateSql, [1 => $currentLevel, 2 => $currentLevel + 1]) !== 1) { - // no affected rows, concurrency issue, throw exception - } - } else { - // no current level returned, TableGenerator seems to be broken, throw exception - } - } else { - // only table locks help here, implement this or throw exception? - // or do we want to work with table locks exclusively? - } - } - - return $this->nextValue++; - } -} diff --git a/src/Id/UuidGenerator.php b/src/Id/UuidGenerator.php deleted file mode 100644 index d24fcc1fb01..00000000000 --- a/src/Id/UuidGenerator.php +++ /dev/null @@ -1,56 +0,0 @@ -getConnection(); - $sql = 'SELECT ' . $connection->getDatabasePlatform()->getGuidExpression(); - - if ($connection instanceof PrimaryReadReplicaConnection) { - $connection->ensureConnectedToPrimary(); - } - - return $connection->executeQuery($sql)->fetchOne(); - } -} diff --git a/src/Internal/CriteriaOrderings.php b/src/Internal/CriteriaOrderings.php deleted file mode 100644 index 49c0145a178..00000000000 --- a/src/Internal/CriteriaOrderings.php +++ /dev/null @@ -1,51 +0,0 @@ - */ - private static function getCriteriaOrderings(Criteria $criteria): array - { - if (! method_exists(Criteria::class, 'orderings')) { - // @phpstan-ignore method.deprecated - return $criteria->getOrderings(); - } - - return array_map( - static function (Order $order): string { - return $order->value; - }, - $criteria->orderings() - ); - } - - /** - * @param array $orderings - * - * @return array|array - */ - private static function mapToOrderEnumIfAvailable(array $orderings): array - { - if (! class_exists(Order::class)) { - return $orderings; - } - - return array_map( - static function (string $order): Order { - return Order::from(strtoupper($order)); - }, - $orderings - ); - } -} diff --git a/src/Internal/Hydration/AbstractHydrator.php b/src/Internal/Hydration/AbstractHydrator.php index acfd69a62d8..0a44d3a02b5 100644 --- a/src/Internal/Hydration/AbstractHydrator.php +++ b/src/Internal/Hydration/AbstractHydrator.php @@ -5,12 +5,9 @@ namespace Doctrine\ORM\Internal\Hydration; use BackedEnum; -use Doctrine\DBAL\Driver\ResultStatement; -use Doctrine\DBAL\ForwardCompatibility\Result as ForwardCompatibilityResult; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; use Doctrine\ORM\Mapping\ClassMetadata; @@ -20,163 +17,88 @@ use Generator; use LogicException; use ReflectionClass; -use TypeError; use function array_map; use function array_merge; use function count; use function end; -use function get_debug_type; use function in_array; use function is_array; -use function sprintf; /** * Base class for all hydrators. A hydrator is a class that provides some form * of transformation of an SQL result set into another structure. + * + * @phpstan-consistent-constructor */ abstract class AbstractHydrator { /** * The ResultSetMapping. - * - * @var ResultSetMapping|null - */ - protected $_rsm; - - /** - * The EntityManager instance. - * - * @var EntityManagerInterface */ - protected $_em; + protected ResultSetMapping|null $rsm = null; /** * The dbms Platform instance. - * - * @var AbstractPlatform */ - protected $_platform; + protected AbstractPlatform $platform; /** * The UnitOfWork of the associated EntityManager. - * - * @var UnitOfWork */ - protected $_uow; + protected UnitOfWork $uow; /** * Local ClassMetadata cache to avoid going to the EntityManager all the time. * * @var array> */ - protected $_metadataCache = []; + protected array $metadataCache = []; /** * The cache used during row-by-row hydration. * * @var array */ - protected $_cache = []; + protected array $cache = []; /** * The statement that provides the data to hydrate. - * - * @var Result|null */ - protected $_stmt; + protected Result|null $stmt = null; /** * The query hints. * * @var array */ - protected $_hints = []; + protected array $hints = []; /** * Initializes a new instance of a class derived from AbstractHydrator. - * - * @param EntityManagerInterface $em The EntityManager to use. */ - public function __construct(EntityManagerInterface $em) + public function __construct(protected EntityManagerInterface $em) { - $this->_em = $em; - $this->_platform = $em->getConnection()->getDatabasePlatform(); - $this->_uow = $em->getUnitOfWork(); + $this->platform = $em->getConnection()->getDatabasePlatform(); + $this->uow = $em->getUnitOfWork(); } /** * Initiates a row-by-row hydration. * - * @deprecated - * - * @param Result|ResultStatement $stmt - * @param ResultSetMapping $resultSetMapping - * @phpstan-param array $hints - * - * @return IterableResult - */ - public function iterate($stmt, $resultSetMapping, array $hints = []) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8463', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.', - __METHOD__ - ); - - $this->_stmt = $stmt instanceof ResultStatement ? ForwardCompatibilityResult::ensure($stmt) : $stmt; - $this->_rsm = $resultSetMapping; - $this->_hints = $hints; - - $evm = $this->_em->getEventManager(); - - $evm->addEventListener([Events::onClear], $this); - - $this->prepare(); - - return new IterableResult($this); - } - - /** - * Initiates a row-by-row hydration. - * - * @param Result|ResultStatement $stmt * @phpstan-param array $hints * * @return Generator * * @final */ - public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable + final public function toIterable(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): Generator { - if (! $stmt instanceof Result) { - if (! $stmt instanceof ResultStatement) { - throw new TypeError(sprintf( - '%s: Expected parameter $stmt to be an instance of %s or %s, got %s', - __METHOD__, - Result::class, - ResultStatement::class, - get_debug_type($stmt) - )); - } + $this->stmt = $stmt; + $this->rsm = $resultSetMapping; + $this->hints = $hints; - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/8796', - '%s: Passing a result as $stmt that does not implement %s is deprecated and will cause a TypeError on 3.0', - __METHOD__, - Result::class - ); - - $stmt = ForwardCompatibilityResult::ensure($stmt); - } - - $this->_stmt = $stmt; - $this->_rsm = $resultSetMapping; - $this->_hints = $hints; - - $evm = $this->_em->getEventManager(); + $evm = $this->em->getEventManager(); $evm->addEventListener([Events::onClear], $this); @@ -212,60 +134,34 @@ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hin final protected function statement(): Result { - if ($this->_stmt === null) { + if ($this->stmt === null) { throw new LogicException('Uninitialized _stmt property'); } - return $this->_stmt; + return $this->stmt; } final protected function resultSetMapping(): ResultSetMapping { - if ($this->_rsm === null) { + if ($this->rsm === null) { throw new LogicException('Uninitialized _rsm property'); } - return $this->_rsm; + return $this->rsm; } /** * Hydrates all rows returned by the passed statement instance at once. * - * @param Result|ResultStatement $stmt - * @param ResultSetMapping $resultSetMapping * @phpstan-param array $hints - * - * @return mixed[] */ - public function hydrateAll($stmt, $resultSetMapping, array $hints = []) + public function hydrateAll(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): mixed { - if (! $stmt instanceof Result) { - if (! $stmt instanceof ResultStatement) { - throw new TypeError(sprintf( - '%s: Expected parameter $stmt to be an instance of %s or %s, got %s', - __METHOD__, - Result::class, - ResultStatement::class, - get_debug_type($stmt) - )); - } - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/8796', - '%s: Passing a result as $stmt that does not implement %s is deprecated and will cause a TypeError on 3.0', - __METHOD__, - Result::class - ); - - $stmt = ForwardCompatibilityResult::ensure($stmt); - } + $this->stmt = $stmt; + $this->rsm = $resultSetMapping; + $this->hints = $hints; - $this->_stmt = $stmt; - $this->_rsm = $resultSetMapping; - $this->_hints = $hints; - - $this->_em->getEventManager()->addEventListener([Events::onClear], $this); + $this->em->getEventManager()->addEventListener([Events::onClear], $this); $this->prepare(); try { @@ -277,77 +173,37 @@ public function hydrateAll($stmt, $resultSetMapping, array $hints = []) return $result; } - /** - * Hydrates a single row returned by the current statement instance during - * row-by-row hydration with {@link iterate()} or {@link toIterable()}. - * - * @deprecated - * - * @return mixed[]|false - */ - public function hydrateRow() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9072', - '%s is deprecated.', - __METHOD__ - ); - - $row = $this->statement()->fetchAssociative(); - - if ($row === false) { - $this->cleanup(); - - return false; - } - - $result = []; - - $this->hydrateRowData($row, $result); - - return $result; - } - /** * When executed in a hydrate() loop we have to clear internal state to * decrease memory consumption. - * - * @param mixed $eventArgs - * - * @return void */ - public function onClear($eventArgs) + public function onClear(mixed $eventArgs): void { } /** * Executes one-time preparation tasks, once each time hydration is started - * through {@link hydrateAll} or {@link iterate()}. - * - * @return void + * through {@link hydrateAll} or {@link toIterable()}. */ - protected function prepare() + protected function prepare(): void { } /** * Executes one-time cleanup tasks at the end of a hydration that was initiated - * through {@link hydrateAll} or {@link iterate()}. - * - * @return void + * through {@link hydrateAll} or {@link toIterable()}. */ - protected function cleanup() + protected function cleanup(): void { $this->statement()->free(); - $this->_stmt = null; - $this->_rsm = null; - $this->_cache = []; - $this->_metadataCache = []; + $this->stmt = null; + $this->rsm = null; + $this->cache = []; + $this->metadataCache = []; $this - ->_em + ->em ->getEventManager() ->removeEventListener([Events::onClear], $this); } @@ -364,21 +220,17 @@ protected function cleanupAfterRowIteration(): void * @param mixed[] $row The row data. * @param mixed[] $result The result to fill. * - * @return void - * * @throws HydrationException */ - protected function hydrateRowData(array $row, array &$result) + protected function hydrateRowData(array $row, array &$result): void { throw new HydrationException('hydrateRowData() not implemented by this hydrator.'); } /** * Hydrates all rows from the current statement instance at once. - * - * @return mixed[] */ - abstract protected function hydrateAllData(); + abstract protected function hydrateAllData(): mixed; /** * Processes a row of the result set. @@ -400,15 +252,16 @@ abstract protected function hydrateAllData(); * @phpstan-return array{ * data: array, * newObjects?: array, * scalars?: array * } */ - protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents) + protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents): array { - $rowData = ['data' => []]; + $rowData = ['data' => [], 'newObjects' => []]; foreach ($data as $key => $value) { $cacheKeyInfo = $this->hydrateColumnInfo($key); @@ -423,7 +276,7 @@ protected function gatherRowData(array $data, array &$id, array &$nonemptyCompon $argIndex = $cacheKeyInfo['argIndex']; $objIndex = $cacheKeyInfo['objIndex']; $type = $cacheKeyInfo['type']; - $value = $type->convertToPHPValue($value, $this->_platform); + $value = $type->convertToPHPValue($value, $this->platform); if ($value !== null && isset($cacheKeyInfo['enumType'])) { $value = $this->buildEnum($value, $cacheKeyInfo['enumType']); @@ -435,7 +288,7 @@ protected function gatherRowData(array $data, array &$id, array &$nonemptyCompon case isset($cacheKeyInfo['isScalar']): $type = $cacheKeyInfo['type']; - $value = $type->convertToPHPValue($value, $this->_platform); + $value = $type->convertToPHPValue($value, $this->platform); if ($value !== null && isset($cacheKeyInfo['enumType'])) { $value = $this->buildEnum($value, $cacheKeyInfo['enumType']); @@ -467,7 +320,7 @@ protected function gatherRowData(array $data, array &$id, array &$nonemptyCompon } $rowData['data'][$dqlAlias][$fieldName] = $type - ? $type->convertToPHPValue($value, $this->_platform) + ? $type->convertToPHPValue($value, $this->platform) : $value; if ($rowData['data'][$dqlAlias][$fieldName] !== null && isset($cacheKeyInfo['enumType'])) { @@ -483,6 +336,25 @@ protected function gatherRowData(array $data, array &$id, array &$nonemptyCompon } } + foreach ($this->resultSetMapping()->nestedNewObjectArguments as $objIndex => ['ownerIndex' => $ownerIndex, 'argIndex' => $argIndex]) { + if (! isset($rowData['newObjects'][$ownerIndex . ':' . $argIndex])) { + continue; + } + + $newObject = $rowData['newObjects'][$ownerIndex . ':' . $argIndex]; + unset($rowData['newObjects'][$ownerIndex . ':' . $argIndex]); + + $obj = $newObject['class']->newInstanceArgs($newObject['args']); + + $rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $obj; + } + + foreach ($rowData['newObjects'] as $objIndex => $newObject) { + $obj = $newObject['class']->newInstanceArgs($newObject['args']); + + $rowData['newObjects'][$objIndex]['obj'] = $obj; + } + return $rowData; } @@ -500,7 +372,7 @@ protected function gatherRowData(array $data, array &$id, array &$nonemptyCompon * @return mixed[] The processed row. * @phpstan-return array */ - protected function gatherScalarRowData(&$data) + protected function gatherScalarRowData(array &$data): array { $rowData = []; @@ -516,7 +388,7 @@ protected function gatherScalarRowData(&$data) // erroneous behavior exists since 2.0 and we're forced to keep compatibility. if (! isset($cacheKeyInfo['isScalar'])) { $type = $cacheKeyInfo['type']; - $value = $type ? $type->convertToPHPValue($value, $this->_platform) : $value; + $value = $type ? $type->convertToPHPValue($value, $this->platform) : $value; $fieldName = $cacheKeyInfo['dqlAlias'] . '_' . $fieldName; } @@ -535,91 +407,91 @@ protected function gatherScalarRowData(&$data) * @return mixed[]|null * @phpstan-return array|null */ - protected function hydrateColumnInfo($key) + protected function hydrateColumnInfo(string $key): array|null { - if (isset($this->_cache[$key])) { - return $this->_cache[$key]; + if (isset($this->cache[$key])) { + return $this->cache[$key]; } switch (true) { // NOTE: Most of the times it's a field mapping, so keep it first!!! - case isset($this->_rsm->fieldMappings[$key]): - $classMetadata = $this->getClassMetadata($this->_rsm->declaringClasses[$key]); - $fieldName = $this->_rsm->fieldMappings[$key]; + case isset($this->rsm->fieldMappings[$key]): + $classMetadata = $this->getClassMetadata($this->rsm->declaringClasses[$key]); + $fieldName = $this->rsm->fieldMappings[$key]; $fieldMapping = $classMetadata->fieldMappings[$fieldName]; - $ownerMap = $this->_rsm->columnOwnerMap[$key]; + $ownerMap = $this->rsm->columnOwnerMap[$key]; $columnInfo = [ 'isIdentifier' => in_array($fieldName, $classMetadata->identifier, true), 'fieldName' => $fieldName, - 'type' => Type::getType($fieldMapping['type']), + 'type' => Type::getType($fieldMapping->type), 'dqlAlias' => $ownerMap, - 'enumType' => $this->_rsm->enumMappings[$key] ?? null, + 'enumType' => $this->rsm->enumMappings[$key] ?? null, ]; // the current discriminator value must be saved in order to disambiguate fields hydration, // should there be field name collisions - if ($classMetadata->parentClasses && isset($this->_rsm->discriminatorColumns[$ownerMap])) { - return $this->_cache[$key] = array_merge( + if ($classMetadata->parentClasses && isset($this->rsm->discriminatorColumns[$ownerMap])) { + return $this->cache[$key] = array_merge( $columnInfo, [ - 'discriminatorColumn' => $this->_rsm->discriminatorColumns[$ownerMap], + 'discriminatorColumn' => $this->rsm->discriminatorColumns[$ownerMap], 'discriminatorValue' => $classMetadata->discriminatorValue, 'discriminatorValues' => $this->getDiscriminatorValues($classMetadata), - ] + ], ); } - return $this->_cache[$key] = $columnInfo; + return $this->cache[$key] = $columnInfo; - case isset($this->_rsm->newObjectMappings[$key]): + case isset($this->rsm->newObjectMappings[$key]): // WARNING: A NEW object is also a scalar, so it must be declared before! - $mapping = $this->_rsm->newObjectMappings[$key]; + $mapping = $this->rsm->newObjectMappings[$key]; - return $this->_cache[$key] = [ + return $this->cache[$key] = [ 'isScalar' => true, 'isNewObjectParameter' => true, - 'fieldName' => $this->_rsm->scalarMappings[$key], - 'type' => Type::getType($this->_rsm->typeMappings[$key]), + 'fieldName' => $this->rsm->scalarMappings[$key], + 'type' => Type::getType($this->rsm->typeMappings[$key]), 'argIndex' => $mapping['argIndex'], 'objIndex' => $mapping['objIndex'], 'class' => new ReflectionClass($mapping['className']), - 'enumType' => $this->_rsm->enumMappings[$key] ?? null, + 'enumType' => $this->rsm->enumMappings[$key] ?? null, ]; - case isset($this->_rsm->scalarMappings[$key], $this->_hints[LimitSubqueryWalker::FORCE_DBAL_TYPE_CONVERSION]): - return $this->_cache[$key] = [ - 'fieldName' => $this->_rsm->scalarMappings[$key], - 'type' => Type::getType($this->_rsm->typeMappings[$key]), + case isset($this->rsm->scalarMappings[$key], $this->hints[LimitSubqueryWalker::FORCE_DBAL_TYPE_CONVERSION]): + return $this->cache[$key] = [ + 'fieldName' => $this->rsm->scalarMappings[$key], + 'type' => Type::getType($this->rsm->typeMappings[$key]), 'dqlAlias' => '', - 'enumType' => $this->_rsm->enumMappings[$key] ?? null, + 'enumType' => $this->rsm->enumMappings[$key] ?? null, ]; - case isset($this->_rsm->scalarMappings[$key]): - return $this->_cache[$key] = [ + case isset($this->rsm->scalarMappings[$key]): + return $this->cache[$key] = [ 'isScalar' => true, - 'fieldName' => $this->_rsm->scalarMappings[$key], - 'type' => Type::getType($this->_rsm->typeMappings[$key]), - 'enumType' => $this->_rsm->enumMappings[$key] ?? null, + 'fieldName' => $this->rsm->scalarMappings[$key], + 'type' => Type::getType($this->rsm->typeMappings[$key]), + 'enumType' => $this->rsm->enumMappings[$key] ?? null, ]; - case isset($this->_rsm->metaMappings[$key]): + case isset($this->rsm->metaMappings[$key]): // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). - $fieldName = $this->_rsm->metaMappings[$key]; - $dqlAlias = $this->_rsm->columnOwnerMap[$key]; - $type = isset($this->_rsm->typeMappings[$key]) - ? Type::getType($this->_rsm->typeMappings[$key]) + $fieldName = $this->rsm->metaMappings[$key]; + $dqlAlias = $this->rsm->columnOwnerMap[$key]; + $type = isset($this->rsm->typeMappings[$key]) + ? Type::getType($this->rsm->typeMappings[$key]) : null; // Cache metadata fetch - $this->getClassMetadata($this->_rsm->aliasMap[$dqlAlias]); + $this->getClassMetadata($this->rsm->aliasMap[$dqlAlias]); - return $this->_cache[$key] = [ - 'isIdentifier' => isset($this->_rsm->isIdentifierColumn[$dqlAlias][$key]), + return $this->cache[$key] = [ + 'isIdentifier' => isset($this->rsm->isIdentifierColumn[$dqlAlias][$key]), 'isMetaColumn' => true, 'fieldName' => $fieldName, 'type' => $type, 'dqlAlias' => $dqlAlias, - 'enumType' => $this->_rsm->enumMappings[$key] ?? null, + 'enumType' => $this->rsm->enumMappings[$key] ?? null, ]; } @@ -635,10 +507,8 @@ protected function hydrateColumnInfo($key) private function getDiscriminatorValues(ClassMetadata $classMetadata): array { $values = array_map( - function (string $subClass): string { - return (string) $this->getClassMetadata($subClass)->discriminatorValue; - }, - $classMetadata->subClasses + fn (string $subClass): string => (string) $this->getClassMetadata($subClass)->discriminatorValue, + $classMetadata->subClasses, ); $values[] = (string) $classMetadata->discriminatorValue; @@ -648,64 +518,57 @@ function (string $subClass): string { /** * Retrieve ClassMetadata associated to entity class name. - * - * @param string $className - * - * @return ClassMetadata */ - protected function getClassMetadata($className) + protected function getClassMetadata(string $className): ClassMetadata { - if (! isset($this->_metadataCache[$className])) { - $this->_metadataCache[$className] = $this->_em->getClassMetadata($className); + if (! isset($this->metadataCache[$className])) { + $this->metadataCache[$className] = $this->em->getClassMetadata($className); } - return $this->_metadataCache[$className]; + return $this->metadataCache[$className]; } /** * Register entity as managed in UnitOfWork. * - * @param object $entity * @param mixed[] $data * - * @return void - * * @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow */ - protected function registerManaged(ClassMetadata $class, $entity, array $data) + protected function registerManaged(ClassMetadata $class, object $entity, array $data): void { if ($class->isIdentifierComposite) { $id = []; foreach ($class->identifier as $fieldName) { - $id[$fieldName] = isset($class->associationMappings[$fieldName]) - ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + $id[$fieldName] = isset($class->associationMappings[$fieldName]) && $class->associationMappings[$fieldName]->isToOneOwningSide() + ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name] : $data[$fieldName]; } } else { $fieldName = $class->identifier[0]; $id = [ - $fieldName => isset($class->associationMappings[$fieldName]) - ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + $fieldName => isset($class->associationMappings[$fieldName]) && $class->associationMappings[$fieldName]->isToOneOwningSide() + ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name] : $data[$fieldName], ]; } - $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); + $this->em->getUnitOfWork()->registerManaged($entity, $id, $data); } /** - * @param mixed $value * @param class-string $enumType * * @return BackedEnum|array */ - final protected function buildEnum($value, string $enumType) + final protected function buildEnum(mixed $value, string $enumType): BackedEnum|array { if (is_array($value)) { - return array_map(static function ($value) use ($enumType): BackedEnum { - return $enumType::from($value); - }, $value); + return array_map( + static fn ($value) => $enumType::from($value), + $value, + ); } return $enumType::from($value); diff --git a/src/Internal/Hydration/ArrayHydrator.php b/src/Internal/Hydration/ArrayHydrator.php index ff4cebdc4cc..576b89174d3 100644 --- a/src/Internal/Hydration/ArrayHydrator.php +++ b/src/Internal/Hydration/ArrayHydrator.php @@ -4,10 +4,8 @@ namespace Doctrine\ORM\Internal\Hydration; -use Doctrine\ORM\Mapping\ClassMetadata; - +use function array_key_last; use function count; -use function end; use function is_array; use function key; use function reset; @@ -19,27 +17,22 @@ class ArrayHydrator extends AbstractHydrator { /** @var array */ - private $rootAliases = []; + private array $rootAliases = []; - /** @var bool */ - private $isSimpleQuery = false; + private bool $isSimpleQuery = false; /** @var mixed[] */ - private $identifierMap = []; + private array $identifierMap = []; /** @var mixed[] */ - private $resultPointers = []; + private array $resultPointers = []; /** @var array */ - private $idTemplate = []; + private array $idTemplate = []; - /** @var int */ - private $resultCounter = 0; + private int $resultCounter = 0; - /** - * {@inheritDoc} - */ - protected function prepare() + protected function prepare(): void { $this->isSimpleQuery = count($this->resultSetMapping()->aliasMap) <= 1; @@ -53,7 +46,7 @@ protected function prepare() /** * {@inheritDoc} */ - protected function hydrateAllData() + protected function hydrateAllData(): array { $result = []; @@ -67,7 +60,7 @@ protected function hydrateAllData() /** * {@inheritDoc} */ - protected function hydrateRowData(array $row, array &$result) + protected function hydrateRowData(array $row, array &$result): void { // 1) Initialize $id = $this->idTemplate; // initialize the id-memory @@ -104,11 +97,11 @@ protected function hydrateRowData(array $row, array &$result) } $relationAlias = $this->resultSetMapping()->relationMap[$dqlAlias]; - $parentClass = $this->_metadataCache[$this->resultSetMapping()->aliasMap[$parent]]; + $parentClass = $this->metadataCache[$this->resultSetMapping()->aliasMap[$parent]]; $relation = $parentClass->associationMappings[$relationAlias]; // Check the type of the relation (many or single-valued) - if (! ($relation['type'] & ClassMetadata::TO_ONE)) { + if (! $relation->isToOne()) { $oneToOne = false; if (! isset($baseElement[$relationAlias])) { @@ -129,9 +122,7 @@ protected function hydrateRowData(array $row, array &$result) $baseElement[$relationAlias][] = $element; } - end($baseElement[$relationAlias]); - - $this->identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]); + $this->identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = array_key_last($baseElement[$relationAlias]); } } } else { @@ -223,9 +214,8 @@ protected function hydrateRowData(array $row, array &$result) $scalarCount = (isset($rowData['scalars']) ? count($rowData['scalars']) : 0); foreach ($rowData['newObjects'] as $objIndex => $newObject) { - $class = $newObject['class']; - $args = $newObject['args']; - $obj = $class->newInstanceArgs($args); + $args = $newObject['args']; + $obj = $newObject['obj']; if (count($args) === $scalarCount || ($scalarCount === 0 && count($rowData['newObjects']) === 1)) { $result[$resultKey] = $obj; @@ -242,15 +232,15 @@ protected function hydrateRowData(array $row, array &$result) * Updates the result pointer for an Entity. The result pointers point to the * last seen instance of each Entity type. This is used for graph construction. * - * @param mixed[]|null $coll The element. - * @param bool|int $index Index of the element in the collection. - * @param bool $oneToOne Whether it is a single-valued association or not. + * @param mixed[]|null $coll The element. + * @param string|int|false $index Index of the element in the collection. + * @param bool $oneToOne Whether it is a single-valued association or not. */ private function updateResultPointer( - ?array &$coll, - $index, + array|null &$coll, + string|int|false $index, string $dqlAlias, - bool $oneToOne + bool $oneToOne, ): void { if ($coll === null) { unset($this->resultPointers[$dqlAlias]); // Ticket #1228 @@ -274,7 +264,6 @@ private function updateResultPointer( return; } - end($coll); - $this->resultPointers[$dqlAlias] =& $coll[key($coll)]; + $this->resultPointers[$dqlAlias] =& $coll[array_key_last($coll)]; } } diff --git a/src/Internal/Hydration/HydrationException.php b/src/Internal/Hydration/HydrationException.php index aa70bd36783..a59644300b4 100644 --- a/src/Internal/Hydration/HydrationException.php +++ b/src/Internal/Hydration/HydrationException.php @@ -5,40 +5,29 @@ namespace Doctrine\ORM\Internal\Hydration; use Doctrine\ORM\Exception\ORMException; +use Exception; use function implode; use function sprintf; -class HydrationException extends ORMException +class HydrationException extends Exception implements ORMException { - /** @return HydrationException */ - public static function nonUniqueResult() + public static function nonUniqueResult(): self { return new self('The result returned by the query was not unique.'); } - /** - * @param string $alias - * @param string $parentAlias - * - * @return HydrationException - */ - public static function parentObjectOfRelationNotFound($alias, $parentAlias) + public static function parentObjectOfRelationNotFound(string $alias, string $parentAlias): self { return new self(sprintf( "The parent object of entity result with alias '%s' was not found." . " The parent alias is '%s'.", $alias, - $parentAlias + $parentAlias, )); } - /** - * @param string $dqlAlias - * - * @return HydrationException - */ - public static function emptyDiscriminatorValue($dqlAlias) + public static function emptyDiscriminatorValue(string $dqlAlias): self { return new self("The DQL alias '" . $dqlAlias . "' contains an entity " . 'of an inheritance hierarchy with an empty discriminator value. This means ' . @@ -46,52 +35,38 @@ public static function emptyDiscriminatorValue($dqlAlias) 'discriminator value in a table row.'); } - /** - * @param string $entityName - * @param string $discrColumnName - * @param string $dqlAlias - * - * @return HydrationException - */ - public static function missingDiscriminatorColumn($entityName, $discrColumnName, $dqlAlias) + public static function missingDiscriminatorColumn(string $entityName, string $discrColumnName, string $dqlAlias): self { return new self(sprintf( 'The discriminator column "%s" is missing for "%s" using the DQL alias "%s".', $discrColumnName, $entityName, - $dqlAlias + $dqlAlias, )); } - /** - * @param string $entityName - * @param string $discrColumnName - * @param string $dqlAlias - * - * @return HydrationException - */ - public static function missingDiscriminatorMetaMappingColumn($entityName, $discrColumnName, $dqlAlias) + public static function missingDiscriminatorMetaMappingColumn(string $entityName, string $discrColumnName, string $dqlAlias): self { return new self(sprintf( 'The meta mapping for the discriminator column "%s" is missing for "%s" using the DQL alias "%s".', $discrColumnName, $entityName, - $dqlAlias + $dqlAlias, )); } - /** - * @param string $discrValue - * @param list $discrValues - * - * @return HydrationException - */ - public static function invalidDiscriminatorValue($discrValue, $discrValues) + /** @param list $discrValues */ + public static function invalidDiscriminatorValue(string $discrValue, array $discrValues): self { return new self(sprintf( 'The discriminator value "%s" is invalid. It must be one of "%s".', $discrValue, - implode('", "', $discrValues) + implode('", "', $discrValues), )); } + + public static function partialObjectHydrationDisallowed(): self + { + return new self('Hydration of entity objects is not allowed when DQL PARTIAL keyword is used.'); + } } diff --git a/src/Internal/Hydration/IterableResult.php b/src/Internal/Hydration/IterableResult.php deleted file mode 100644 index 58a662f3021..00000000000 --- a/src/Internal/Hydration/IterableResult.php +++ /dev/null @@ -1,86 +0,0 @@ -hydrator = $hydrator; - } - - /** - * @return void - * - * @throws HydrationException - */ - #[ReturnTypeWillChange] - public function rewind() - { - if ($this->rewinded === true) { - throw new HydrationException('Can only iterate a Result once.'); - } - - $this->current = $this->next(); - $this->rewinded = true; - } - - /** - * Gets the next set of results. - * - * @return mixed[]|false - */ - #[ReturnTypeWillChange] - public function next() - { - $this->current = $this->hydrator->hydrateRow(); - $this->key++; - - return $this->current; - } - - /** @return mixed */ - #[ReturnTypeWillChange] - public function current() - { - return $this->current; - } - - /** @return int */ - #[ReturnTypeWillChange] - public function key() - { - return $this->key; - } - - /** @return bool */ - #[ReturnTypeWillChange] - public function valid() - { - return $this->current !== false; - } -} diff --git a/src/Internal/Hydration/ObjectHydrator.php b/src/Internal/Hydration/ObjectHydrator.php index 738adc68f44..21383e8c16e 100644 --- a/src/Internal/Hydration/ObjectHydrator.php +++ b/src/Internal/Hydration/ObjectHydrator.php @@ -14,6 +14,7 @@ use function array_fill_keys; use function array_keys; use function array_map; +use function assert; use function count; use function is_array; use function key; @@ -28,36 +29,32 @@ class ObjectHydrator extends AbstractHydrator { /** @var mixed[] */ - private $identifierMap = []; + private array $identifierMap = []; /** @var mixed[] */ - private $resultPointers = []; + private array $resultPointers = []; /** @var mixed[] */ - private $idTemplate = []; + private array $idTemplate = []; - /** @var int */ - private $resultCounter = 0; + private int $resultCounter = 0; /** @var mixed[] */ - private $rootAliases = []; + private array $rootAliases = []; /** @var mixed[] */ - private $initializedCollections = []; + private array $initializedCollections = []; /** @var array */ - private $uninitializedCollections = []; + private array $uninitializedCollections = []; /** @var mixed[] */ - private $existingCollections = []; + private array $existingCollections = []; - /** - * {@inheritDoc} - */ - protected function prepare() + protected function prepare(): void { - if (! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { - $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; + if (! isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { + $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; } foreach ($this->resultSetMapping()->aliasMap as $dqlAlias => $className) { @@ -80,39 +77,36 @@ protected function prepare() $sourceClass = $this->getClassMetadata($sourceClassName); $assoc = $sourceClass->associationMappings[$this->resultSetMapping()->relationMap[$dqlAlias]]; - $this->_hints['fetched'][$parent][$assoc['fieldName']] = true; + $this->hints['fetched'][$parent][$assoc->fieldName] = true; - if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { + if ($assoc->isManyToMany()) { continue; } // Mark any non-collection opposite sides as fetched, too. - if ($assoc['mappedBy']) { - $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true; + if (! $assoc->isOwningSide()) { + $this->hints['fetched'][$dqlAlias][$assoc->mappedBy] = true; continue; } // handle fetch-joined owning side bi-directional one-to-one associations - if ($assoc['inversedBy']) { + if ($assoc->inversedBy !== null) { $class = $this->getClassMetadata($className); - $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; + $inverseAssoc = $class->associationMappings[$assoc->inversedBy]; - if (! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { + if (! $inverseAssoc->isToOne()) { continue; } - $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true; + $this->hints['fetched'][$dqlAlias][$inverseAssoc->fieldName] = true; } } } - /** - * {@inheritDoc} - */ - protected function cleanup() + protected function cleanup(): void { - $eagerLoad = isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD]) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] === true; + $eagerLoad = isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD]) && $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] === true; parent::cleanup(); @@ -123,10 +117,10 @@ protected function cleanup() $this->resultPointers = []; if ($eagerLoad) { - $this->_uow->triggerEagerLoads(); + $this->uow->triggerEagerLoads(); } - $this->_uow->hydrationComplete(); + $this->uow->hydrationComplete(); } protected function cleanupAfterRowIteration(): void @@ -141,7 +135,7 @@ protected function cleanupAfterRowIteration(): void /** * {@inheritDoc} */ - protected function hydrateAllData() + protected function hydrateAllData(): array { $result = []; @@ -166,15 +160,14 @@ protected function hydrateAllData() /** * Initializes a related collection. * - * @param object $entity The entity to which the collection belongs. * @param string $fieldName The name of the field on the entity that holds the collection. * @param string $parentDqlAlias Alias of the parent fetch joining this collection. */ private function initRelatedCollection( - $entity, + object $entity, ClassMetadata $class, string $fieldName, - string $parentDqlAlias + string $parentDqlAlias, ): PersistentCollection { $oid = spl_object_id($entity); $relation = $class->associationMappings[$fieldName]; @@ -185,20 +178,21 @@ private function initRelatedCollection( } if (! $value instanceof PersistentCollection) { + assert($relation->isToMany()); $value = new PersistentCollection( - $this->_em, - $this->_metadataCache[$relation['targetEntity']], - $value + $this->em, + $this->metadataCache[$relation->targetEntity], + $value, ); $value->setOwner($entity, $relation); $class->reflFields[$fieldName]->setValue($entity, $value); - $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); + $this->uow->setOriginalEntityProperty($oid, $fieldName, $value); $this->initializedCollections[$oid . $fieldName] = $value; } elseif ( - isset($this->_hints[Query::HINT_REFRESH]) || - isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && + isset($this->hints[Query::HINT_REFRESH]) || + isset($this->hints['fetched'][$parentDqlAlias][$fieldName]) && ! $value->isInitialized() ) { // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! @@ -221,11 +215,9 @@ private function initRelatedCollection( * @param string $dqlAlias The DQL alias of the entity's class. * @phpstan-param array $data The instance data. * - * @return object - * * @throws HydrationException */ - private function getEntity(array $data, string $dqlAlias) + private function getEntity(array $data, string $dqlAlias): object { $className = $this->resultSetMapping()->aliasMap[$dqlAlias]; @@ -246,7 +238,7 @@ private function getEntity(array $data, string $dqlAlias) throw HydrationException::emptyDiscriminatorValue($dqlAlias); } - $discrMap = $this->_metadataCache[$className]->discriminatorMap; + $discrMap = $this->metadataCache[$className]->discriminatorMap; $discriminatorValue = $data[$discrColumn]; if ($discriminatorValue instanceof BackedEnum) { $discriminatorValue = $discriminatorValue->value; @@ -263,45 +255,44 @@ private function getEntity(array $data, string $dqlAlias) unset($data[$discrColumn]); } - if (isset($this->_hints[Query::HINT_REFRESH_ENTITY], $this->rootAliases[$dqlAlias])) { - $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + if (isset($this->hints[Query::HINT_REFRESH_ENTITY], $this->rootAliases[$dqlAlias])) { + $this->registerManaged($this->metadataCache[$className], $this->hints[Query::HINT_REFRESH_ENTITY], $data); } - $this->_hints['fetchAlias'] = $dqlAlias; + $this->hints['fetchAlias'] = $dqlAlias; - return $this->_uow->createEntity($className, $data, $this->_hints); + return $this->uow->createEntity($className, $data, $this->hints); } /** * @param class-string $className * @phpstan-param array $data - * - * @return mixed */ - private function getEntityFromIdentityMap(string $className, array $data) + private function getEntityFromIdentityMap(string $className, array $data): object|bool { // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? - $class = $this->_metadataCache[$className]; + $class = $this->metadataCache[$className]; if ($class->isIdentifierComposite) { $idHash = UnitOfWork::getIdHashByIdentifier( array_map( /** @return mixed */ - static function (string $fieldName) use ($data, $class) { - return isset($class->associationMappings[$fieldName]) - ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] - : $data[$fieldName]; - }, - $class->identifier - ) + static fn (string $fieldName) => isset($class->associationMappings[$fieldName]) && assert($class->associationMappings[$fieldName]->isToOneOwningSide()) + ? $data[$class->associationMappings[$fieldName]->joinColumns[0]->name] + : $data[$fieldName], + $class->identifier, + ), ); - return $this->_uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName); + return $this->uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName); } elseif (isset($class->associationMappings[$class->identifier[0]])) { - return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName); + $association = $class->associationMappings[$class->identifier[0]]; + assert($association->isToOneOwningSide()); + + return $this->uow->tryGetByIdHash($data[$association->joinColumns[0]->name], $class->rootEntityName); } - return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); + return $this->uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); } /** @@ -322,10 +313,8 @@ static function (string $fieldName) use ($data, $class) { * * @param mixed[] $row The data of the row to process. * @param mixed[] $result The result array to fill. - * - * @return void */ - protected function hydrateRowData(array $row, array &$result) + protected function hydrateRowData(array $row, array &$result): void { // Initialize $id = $this->idTemplate; // initialize the id-memory @@ -354,7 +343,7 @@ protected function hydrateRowData(array $row, array &$result) continue; } - $parentClass = $this->_metadataCache[$this->resultSetMapping()->aliasMap[$parentAlias]]; + $parentClass = $this->metadataCache[$this->resultSetMapping()->aliasMap[$parentAlias]]; $relationField = $this->resultSetMapping()->relationMap[$dqlAlias]; $relation = $parentClass->associationMappings[$relationField]; $reflField = $parentClass->reflFields[$relationField]; @@ -378,14 +367,14 @@ protected function hydrateRowData(array $row, array &$result) } // Mark as not-fetched again - unset($this->_hints['fetched'][$parentAlias][$relationField]); + unset($this->hints['fetched'][$parentAlias][$relationField]); continue; } $oid = spl_object_id($parentObject); // Check the type of the relation (many or single-valued) - if (! ($relation['type'] & ClassMetadata::TO_ONE)) { + if (! $relation->isToOne()) { // PATH A: Collection-valued association $reflFieldValue = $reflField->getValue($parentObject); @@ -442,35 +431,35 @@ protected function hydrateRowData(array $row, array &$result) // PATH B: Single-valued association $reflFieldValue = $reflField->getValue($parentObject); - if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || $this->_uow->isUninitializedObject($reflFieldValue)) { + if (! $reflFieldValue || isset($this->hints[Query::HINT_REFRESH]) || $this->uow->isUninitializedObject($reflFieldValue)) { // we only need to take action if this value is null, // we refresh the entity or its an uninitialized proxy. if (isset($nonemptyComponents[$dqlAlias])) { $element = $this->getEntity($data, $dqlAlias); $reflField->setValue($parentObject, $element); - $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); - $targetClass = $this->_metadataCache[$relation['targetEntity']]; + $this->uow->setOriginalEntityProperty($oid, $relationField, $element); + $targetClass = $this->metadataCache[$relation->targetEntity]; - if ($relation['isOwningSide']) { + if ($relation->isOwningSide()) { // TODO: Just check hints['fetched'] here? // If there is an inverse mapping on the target class its bidirectional - if ($relation['inversedBy']) { - $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; - if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { - $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); - $this->_uow->setOriginalEntityProperty(spl_object_id($element), $inverseAssoc['fieldName'], $parentObject); + if ($relation->inversedBy !== null) { + $inverseAssoc = $targetClass->associationMappings[$relation->inversedBy]; + if ($inverseAssoc->isToOne()) { + $targetClass->reflFields[$inverseAssoc->fieldName]->setValue($element, $parentObject); + $this->uow->setOriginalEntityProperty(spl_object_id($element), $inverseAssoc->fieldName, $parentObject); } } } else { // For sure bidirectional, as there is no inverse side in unidirectional mappings - $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); - $this->_uow->setOriginalEntityProperty(spl_object_id($element), $relation['mappedBy'], $parentObject); + $targetClass->reflFields[$relation->mappedBy]->setValue($element, $parentObject); + $this->uow->setOriginalEntityProperty(spl_object_id($element), $relation->mappedBy, $parentObject); } // Update result pointer $this->resultPointers[$dqlAlias] = $element; } else { - $this->_uow->setOriginalEntityProperty($oid, $relationField, null); + $this->uow->setOriginalEntityProperty($oid, $relationField, null); $reflField->setValue($parentObject, null); } // else leave $reflFieldValue null for single-valued associations @@ -508,8 +497,8 @@ protected function hydrateRowData(array $row, array &$result) if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) { $resultKey = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]]; - if (isset($this->_hints['collection'])) { - $this->_hints['collection']->hydrateSet($resultKey, $element); + if (isset($this->hints['collection'])) { + $this->hints['collection']->hydrateSet($resultKey, $element); } $result[$resultKey] = $element; @@ -517,8 +506,8 @@ protected function hydrateRowData(array $row, array &$result) $resultKey = $this->resultCounter; ++$this->resultCounter; - if (isset($this->_hints['collection'])) { - $this->_hints['collection']->hydrateAdd($element); + if (isset($this->hints['collection'])) { + $this->hints['collection']->hydrateAdd($element); } $result[] = $element; @@ -536,8 +525,8 @@ protected function hydrateRowData(array $row, array &$result) } } - if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { - $this->_uow->hydrationComplete(); + if (isset($this->hints[Query::HINT_INTERNAL_ITERATION]) && $this->hints[Query::HINT_INTERNAL_ITERATION]) { + $this->uow->hydrationComplete(); } } @@ -567,9 +556,7 @@ protected function hydrateRowData(array $row, array &$result) $scalarCount = (isset($rowData['scalars']) ? count($rowData['scalars']) : 0); foreach ($rowData['newObjects'] as $objIndex => $newObject) { - $class = $newObject['class']; - $args = $newObject['args']; - $obj = $class->newInstanceArgs($args); + $obj = $newObject['obj']; if ($scalarCount === 0 && count($rowData['newObjects']) === 1) { $result[$resultKey] = $obj; @@ -585,12 +572,8 @@ protected function hydrateRowData(array $row, array &$result) /** * When executed in a hydrate() loop we may have to clear internal state to * decrease memory consumption. - * - * @param mixed $eventArgs - * - * @return void */ - public function onClear($eventArgs) + public function onClear(mixed $eventArgs): void { parent::onClear($eventArgs); diff --git a/src/Internal/Hydration/ScalarHydrator.php b/src/Internal/Hydration/ScalarHydrator.php index 364eb7ac7f5..15f3e7eadb2 100644 --- a/src/Internal/Hydration/ScalarHydrator.php +++ b/src/Internal/Hydration/ScalarHydrator.php @@ -14,7 +14,7 @@ class ScalarHydrator extends AbstractHydrator /** * {@inheritDoc} */ - protected function hydrateAllData() + protected function hydrateAllData(): array { $result = []; @@ -28,7 +28,7 @@ protected function hydrateAllData() /** * {@inheritDoc} */ - protected function hydrateRowData(array $row, array &$result) + protected function hydrateRowData(array $row, array &$result): void { $result[] = $this->gatherScalarRowData($row); } diff --git a/src/Internal/Hydration/SimpleObjectHydrator.php b/src/Internal/Hydration/SimpleObjectHydrator.php index 16c0c25ccaa..6f808f82fb1 100644 --- a/src/Internal/Hydration/SimpleObjectHydrator.php +++ b/src/Internal/Hydration/SimpleObjectHydrator.php @@ -14,6 +14,7 @@ use function array_keys; use function array_search; +use function assert; use function count; use function in_array; use function is_array; @@ -25,13 +26,9 @@ class SimpleObjectHydrator extends AbstractHydrator { use SQLResultCasing; - /** @var ClassMetadata */ - private $class; + private ClassMetadata|null $class = null; - /** - * {@inheritDoc} - */ - protected function prepare() + protected function prepare(): void { if (count($this->resultSetMapping()->aliasMap) !== 1) { throw new RuntimeException('Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.'); @@ -44,21 +41,18 @@ protected function prepare() $this->class = $this->getClassMetadata(reset($this->resultSetMapping()->aliasMap)); } - /** - * {@inheritDoc} - */ - protected function cleanup() + protected function cleanup(): void { parent::cleanup(); - $this->_uow->triggerEagerLoads(); - $this->_uow->hydrationComplete(); + $this->uow->triggerEagerLoads(); + $this->uow->hydrationComplete(); } /** * {@inheritDoc} */ - protected function hydrateAllData() + protected function hydrateAllData(): array { $result = []; @@ -66,7 +60,7 @@ protected function hydrateAllData() $this->hydrateRowData($row, $result); } - $this->_em->getUnitOfWork()->triggerEagerLoads(); + $this->em->getUnitOfWork()->triggerEagerLoads(); return $result; } @@ -74,8 +68,9 @@ protected function hydrateAllData() /** * {@inheritDoc} */ - protected function hydrateRowData(array $row, array &$result) + protected function hydrateRowData(array $row, array &$result): void { + assert($this->class !== null); $entityName = $this->class->name; $data = []; $discrColumnValue = null; @@ -83,7 +78,7 @@ protected function hydrateRowData(array $row, array &$result) // We need to find the correct entity class name if we have inheritance in resultset if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { $discrColumn = $this->class->getDiscriminatorColumn(); - $discrColumnName = $this->getSQLResultCasing($this->_platform, $discrColumn['name']); + $discrColumnName = $this->getSQLResultCasing($this->platform, $discrColumn->name); // Find mapped discriminator column from the result set. $metaMappingDiscrColumnName = array_search($discrColumnName, $this->resultSetMapping()->metaMappings, true); @@ -95,13 +90,13 @@ protected function hydrateRowData(array $row, array &$result) throw HydrationException::missingDiscriminatorColumn( $entityName, $discrColumnName, - key($this->resultSetMapping()->aliasMap) + key($this->resultSetMapping()->aliasMap), ); } if ($row[$discrColumnName] === '') { throw HydrationException::emptyDiscriminatorValue(key( - $this->resultSetMapping()->aliasMap + $this->resultSetMapping()->aliasMap, )); } @@ -140,7 +135,7 @@ protected function hydrateRowData(array $row, array &$result) // Convert field to a valid PHP value if (isset($cacheKeyInfo['type'])) { $type = $cacheKeyInfo['type']; - $value = $type->convertToPHPValue($value, $this->_platform); + $value = $type->convertToPHPValue($value, $this->platform); } if ($value !== null && isset($cacheKeyInfo['enumType'])) { @@ -160,7 +155,7 @@ protected function hydrateRowData(array $row, array &$result) $cacheKeyInfo['fieldName'], (string) $currentValue, $cacheKeyInfo['enumType'], - $e + $e, ); } } @@ -173,17 +168,17 @@ protected function hydrateRowData(array $row, array &$result) } } - if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) { - $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + if (isset($this->hints[Query::HINT_REFRESH_ENTITY])) { + $this->registerManaged($this->class, $this->hints[Query::HINT_REFRESH_ENTITY], $data); } - $uow = $this->_em->getUnitOfWork(); - $entity = $uow->createEntity($entityName, $data, $this->_hints); + $uow = $this->em->getUnitOfWork(); + $entity = $uow->createEntity($entityName, $data, $this->hints); $result[] = $entity; - if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { - $this->_uow->hydrationComplete(); + if (isset($this->hints[Query::HINT_INTERNAL_ITERATION]) && $this->hints[Query::HINT_INTERNAL_ITERATION]) { + $this->uow->hydrationComplete(); } } } diff --git a/src/Internal/Hydration/SingleScalarHydrator.php b/src/Internal/Hydration/SingleScalarHydrator.php index d62c2ad6e86..2787bbcf21d 100644 --- a/src/Internal/Hydration/SingleScalarHydrator.php +++ b/src/Internal/Hydration/SingleScalarHydrator.php @@ -16,10 +16,7 @@ */ class SingleScalarHydrator extends AbstractHydrator { - /** - * {@inheritDoc} - */ - protected function hydrateAllData() + protected function hydrateAllData(): mixed { $data = $this->statement()->fetchAllAssociative(); $numRows = count($data); diff --git a/src/Internal/HydrationCompleteHandler.php b/src/Internal/HydrationCompleteHandler.php index 393255c0c50..e0fe34230c2 100644 --- a/src/Internal/HydrationCompleteHandler.php +++ b/src/Internal/HydrationCompleteHandler.php @@ -16,30 +16,19 @@ */ final class HydrationCompleteHandler { - /** @var ListenersInvoker */ - private $listenersInvoker; - - /** @var EntityManagerInterface */ - private $em; - /** @var mixed[][] */ - private $deferredPostLoadInvocations = []; + private array $deferredPostLoadInvocations = []; - /** - * Constructor for this object - */ - public function __construct(ListenersInvoker $listenersInvoker, EntityManagerInterface $em) - { - $this->listenersInvoker = $listenersInvoker; - $this->em = $em; + public function __construct( + private readonly ListenersInvoker $listenersInvoker, + private readonly EntityManagerInterface $em, + ) { } /** * Method schedules invoking of postLoad entity to the very end of current hydration cycle. - * - * @param object $entity */ - public function deferPostLoadInvoking(ClassMetadata $class, $entity): void + public function deferPostLoadInvoking(ClassMetadata $class, object $entity): void { $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad); @@ -68,7 +57,7 @@ public function hydrationComplete(): void Events::postLoad, $entity, new PostLoadEventArgs($entity, $this->em), - $invoke + $invoke, ); } } diff --git a/src/Internal/NoUnknownNamedArguments.php b/src/Internal/NoUnknownNamedArguments.php new file mode 100644 index 00000000000..a3843307f27 --- /dev/null +++ b/src/Internal/NoUnknownNamedArguments.php @@ -0,0 +1,55 @@ + $parameter + */ + private static function validateVariadicParameter(array $parameter): void + { + if (array_is_list($parameter)) { + return; + } + + [, $trace] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + assert(isset($trace['class'])); + + $additionalArguments = array_values(array_filter( + array_keys($parameter), + is_string(...), + )); + + throw new BadMethodCallException(sprintf( + 'Invalid call to %s::%s(), unknown named arguments: %s', + $trace['class'], + $trace['function'], + implode(', ', $additionalArguments), + )); + } +} diff --git a/src/Internal/QueryType.php b/src/Internal/QueryType.php new file mode 100644 index 00000000000..b5e60c7e828 --- /dev/null +++ b/src/Internal/QueryType.php @@ -0,0 +1,13 @@ +getSQLResultCasing($column); - } - return $column; } } diff --git a/src/Internal/StronglyConnectedComponents.php b/src/Internal/StronglyConnectedComponents.php index 0e184c30fc6..dd4fc98a8f4 100644 --- a/src/Internal/StronglyConnectedComponents.php +++ b/src/Internal/StronglyConnectedComponents.php @@ -34,7 +34,7 @@ final class StronglyConnectedComponents * * @var array */ - private $nodes = []; + private array $nodes = []; /** * DFS state for the different nodes, indexed by node object id and using one of @@ -42,7 +42,7 @@ final class StronglyConnectedComponents * * @var array */ - private $states = []; + private array $states = []; /** * Edges between the nodes. The first-level key is the object id of the outgoing @@ -50,41 +50,39 @@ final class StronglyConnectedComponents * * @var array> */ - private $edges = []; + private array $edges = []; /** * DFS numbers, by object ID * * @var array */ - private $dfs = []; + private array $dfs = []; /** * lowlink numbers, by object ID * * @var array */ - private $lowlink = []; + private array $lowlink = []; - /** @var int */ - private $maxdfs = 0; + private int $maxdfs = 0; /** * Nodes representing the SCC another node is in, indexed by lookup-node object ID * * @var array */ - private $representingNodes = []; + private array $representingNodes = []; /** * Stack with OIDs of nodes visited in the current state of the DFS * * @var list */ - private $stack = []; + private array $stack = []; - /** @param object $node */ - public function addNode($node): void + public function addNode(object $node): void { $id = spl_object_id($node); $this->nodes[$id] = $node; @@ -92,19 +90,15 @@ public function addNode($node): void $this->edges[$id] = []; } - /** @param object $node */ - public function hasNode($node): bool + public function hasNode(object $node): bool { return isset($this->nodes[spl_object_id($node)]); } /** * Adds a new edge between two nodes to the graph - * - * @param object $from - * @param object $to */ - public function addEdge($from, $to): void + public function addEdge(object $from, object $to): void { $fromId = spl_object_id($from); $toId = spl_object_id($to); @@ -152,12 +146,7 @@ private function tarjan(int $oid): void } } - /** - * @param object $node - * - * @return object - */ - public function getNodeRepresentingStronglyConnectedComponent($node) + public function getNodeRepresentingStronglyConnectedComponent(object $node): object { $oid = spl_object_id($node); diff --git a/src/Internal/TopologicalSort.php b/src/Internal/TopologicalSort.php index aed71f3b206..808bc0f5a42 100644 --- a/src/Internal/TopologicalSort.php +++ b/src/Internal/TopologicalSort.php @@ -29,7 +29,7 @@ final class TopologicalSort * * @var array */ - private $nodes = []; + private array $nodes = []; /** * DFS state for the different nodes, indexed by node object id and using one of @@ -37,7 +37,7 @@ final class TopologicalSort * * @var array */ - private $states = []; + private array $states = []; /** * Edges between the nodes. The first-level key is the object id of the outgoing @@ -46,17 +46,16 @@ final class TopologicalSort * * @var array> */ - private $edges = []; + private array $edges = []; /** * Builds up the result during the DFS. * * @var list */ - private $sortResult = []; + private array $sortResult = []; - /** @param object $node */ - public function addNode($node): void + public function addNode(object $node): void { $id = spl_object_id($node); $this->nodes[$id] = $node; @@ -64,8 +63,7 @@ public function addNode($node): void $this->edges[$id] = []; } - /** @param object $node */ - public function hasNode($node): bool + public function hasNode(object $node): bool { return isset($this->nodes[spl_object_id($node)]); } @@ -73,11 +71,9 @@ public function hasNode($node): bool /** * Adds a new edge between two nodes to the graph * - * @param object $from - * @param object $to - * @param bool $optional This indicates whether the edge may be ignored during the topological sort if it is necessary to break cycles. + * @param bool $optional This indicates whether the edge may be ignored during the topological sort if it is necessary to break cycles. */ - public function addEdge($from, $to, bool $optional): void + public function addEdge(object $from, object $to, bool $optional): void { $fromId = spl_object_id($from); $toId = spl_object_id($to); diff --git a/src/Internal/TopologicalSort/CycleDetectedException.php b/src/Internal/TopologicalSort/CycleDetectedException.php index 9b0bc49d257..3af5329fcb0 100644 --- a/src/Internal/TopologicalSort/CycleDetectedException.php +++ b/src/Internal/TopologicalSort/CycleDetectedException.php @@ -11,25 +11,18 @@ class CycleDetectedException extends RuntimeException { /** @var list */ - private $cycle; - - /** @var object */ - private $startNode; + private array $cycle; /** * Do we have the complete cycle collected? - * - * @var bool */ - private $cycleCollected = false; + private bool $cycleCollected = false; - /** @param object $startNode */ - public function __construct($startNode) + public function __construct(private readonly object $startNode) { parent::__construct('A cycle has been detected, so a topological sort is not possible. The getCycle() method provides the list of nodes that form the cycle.'); - $this->startNode = $startNode; - $this->cycle = [$startNode]; + $this->cycle = [$startNode]; } /** @return list */ @@ -38,8 +31,7 @@ public function getCycle(): array return $this->cycle; } - /** @param object $node */ - public function addToCycle($node): void + public function addToCycle(object $node): void { array_unshift($this->cycle, $node); diff --git a/src/LazyCriteriaCollection.php b/src/LazyCriteriaCollection.php index 48040922616..ca67914edec 100644 --- a/src/LazyCriteriaCollection.php +++ b/src/LazyCriteriaCollection.php @@ -7,9 +7,9 @@ use Doctrine\Common\Collections\AbstractLazyCollection; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ReadableCollection; use Doctrine\Common\Collections\Selectable; use Doctrine\ORM\Persisters\Entity\EntityPersister; -use ReturnTypeWillChange; use function assert; @@ -26,28 +26,18 @@ */ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable { - /** @var EntityPersister */ - protected $entityPersister; + private int|null $count = null; - /** @var Criteria */ - protected $criteria; - - /** @var int|null */ - private $count; - - public function __construct(EntityPersister $entityPersister, Criteria $criteria) - { - $this->entityPersister = $entityPersister; - $this->criteria = $criteria; + public function __construct( + protected EntityPersister $entityPersister, + protected Criteria $criteria, + ) { } /** * Do an efficient count on the collection - * - * @return int */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { if ($this->isInitialized()) { return $this->collection->count(); @@ -63,10 +53,8 @@ public function count() /** * check if collection is empty without loading it - * - * @return bool TRUE if the collection is empty, FALSE otherwise. */ - public function isEmpty() + public function isEmpty(): bool { if ($this->isInitialized()) { return $this->collection->isEmpty(); @@ -82,7 +70,7 @@ public function isEmpty() * * @return bool TRUE if the collection contains $element, FALSE otherwise. */ - public function contains($element) + public function contains(mixed $element): bool { if ($this->isInitialized()) { return $this->collection->contains($element); @@ -91,10 +79,8 @@ public function contains($element) return $this->entityPersister->exists($element, $this->criteria); } - /** - * {@inheritDoc} - */ - public function matching(Criteria $criteria) + /** @return ReadableCollection&Selectable */ + public function matching(Criteria $criteria): ReadableCollection&Selectable { $this->initialize(); assert($this->collection instanceof Selectable); @@ -102,10 +88,7 @@ public function matching(Criteria $criteria) return $this->collection->matching($criteria); } - /** - * {@inheritDoc} - */ - protected function doInitialize() + protected function doInitialize(): void { $elements = $this->entityPersister->loadCriteria($this->criteria); $this->collection = new ArrayCollection($elements); diff --git a/src/Mapping/Annotation.php b/src/Mapping/Annotation.php deleted file mode 100644 index 65223a070f6..00000000000 --- a/src/Mapping/Annotation.php +++ /dev/null @@ -1,10 +0,0 @@ -fieldMappings[$fieldName]['columnName']; + public function getColumnName( + string $fieldName, + ClassMetadata $class, + AbstractPlatform $platform, + ): string { + return $class->fieldMappings[$fieldName]->columnName; } - /** - * {@inheritDoc} - */ - public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string { return $class->table['name']; } @@ -34,48 +31,46 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string { return $definition['sequenceName']; } - /** - * {@inheritDoc} - */ - public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string { - return $joinColumn['name']; + return $joinColumn->name; } - /** - * {@inheritDoc} - */ - public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) - { - return $joinColumn['referencedColumnName']; + public function getReferencedJoinColumnName( + JoinColumnMapping $joinColumn, + ClassMetadata $class, + AbstractPlatform $platform, + ): string { + return $joinColumn->referencedColumnName; } - /** - * {@inheritDoc} - */ - public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) - { - return $association['joinTable']['name']; + public function getJoinTableName( + ManyToManyOwningSideMapping $association, + ClassMetadata $class, + AbstractPlatform $platform, + ): string { + return $association->joinTable->name; } /** * {@inheritDoc} */ - public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array { return $class->identifier; } - /** - * {@inheritDoc} - */ - public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ?ClassMetadata $class = null) - { + public function getColumnAlias( + string $columnName, + int $counter, + AbstractPlatform $platform, + ClassMetadata|null $class = null, + ): string { return $this->getSQLResultCasing($platform, $columnName . '_' . $counter); } } diff --git a/src/Mapping/ArrayAccessImplementation.php b/src/Mapping/ArrayAccessImplementation.php new file mode 100644 index 00000000000..3fd0988cf1b --- /dev/null +++ b/src/Mapping/ArrayAccessImplementation.php @@ -0,0 +1,70 @@ +$offset); + } + + /** @param string $offset */ + public function offsetGet(mixed $offset): mixed + { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + + if (! property_exists($this, $offset)) { + throw new InvalidArgumentException('Undefined property: ' . $offset); + } + + return $this->$offset; + } + + /** @param string $offset */ + public function offsetSet(mixed $offset, mixed $value): void + { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + + $this->$offset = $value; + } + + /** @param string $offset */ + public function offsetUnset(mixed $offset): void + { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + + $this->$offset = null; + } +} diff --git a/src/Mapping/AssociationMapping.php b/src/Mapping/AssociationMapping.php new file mode 100644 index 00000000000..74bef09f9de --- /dev/null +++ b/src/Mapping/AssociationMapping.php @@ -0,0 +1,359 @@ + */ +abstract class AssociationMapping implements ArrayAccess +{ + /** + * The names of persistence operations to cascade on the association. + * + * @var list<'persist'|'remove'|'detach'|'refresh'|'all'> + */ + public array $cascade = []; + + /** + * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. + * + * @var ClassMetadata::FETCH_*|null + */ + public int|null $fetch = null; + + /** + * This is set when the association is inherited by this class from another + * (inheritance) parent entity class. The value is the FQCN of the + * topmost entity class that contains this association. (If there are + * transient classes in the class hierarchy, these are ignored, so the + * class property may in fact come from a class further up in the PHP class + * hierarchy.) To-many associations initially declared in mapped + * superclasses are not considered 'inherited' in the nearest + * entity subclasses. + * + * @var class-string|null + */ + public string|null $inherited = null; + + /** + * This is set when the association does not appear in the current class + * for the first time, but is initially declared in another parent + * entity or mapped superclass. The value is the FQCN of the + * topmost non-transient class that contains association information for + * this relationship. + * + * @var class-string|null + */ + public string|null $declared = null; + + public array|null $cache = null; + + public bool|null $id = null; + + public bool|null $isOnDeleteCascade = null; + + /** @var class-string|null */ + public string|null $originalClass = null; + + public string|null $originalField = null; + + public bool $orphanRemoval = false; + + public bool|null $unique = null; + + /** + * @param string $fieldName The name of the field in the entity + * the association is mapped to. + * @param class-string $sourceEntity The class name of the source entity. + * In the case of to-many associations + * initially present in mapped + * superclasses, the nearest + * entity subclasses will be + * considered the respective source + * entities. + * @param class-string $targetEntity The class name of the target entity. + * If it is fully-qualified it is used as + * is. If it is a simple, unqualified + * class name the namespace is assumed to + * be the same as the namespace of the + * source entity. + */ + final public function __construct( + public readonly string $fieldName, + public string $sourceEntity, + public readonly string $targetEntity, + ) { + } + + /** + * @param mixed[] $mappingArray + * @phpstan-param array{ + * fieldName: string, + * sourceEntity: class-string, + * targetEntity: class-string, + * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): static + { + unset($mappingArray['isOwningSide'], $mappingArray['type']); + $mapping = new static( + $mappingArray['fieldName'], + $mappingArray['sourceEntity'], + $mappingArray['targetEntity'], + ); + unset($mappingArray['fieldName'], $mappingArray['sourceEntity'], $mappingArray['targetEntity']); + + foreach ($mappingArray as $key => $value) { + if ($key === 'joinTable') { + assert($mapping instanceof ManyToManyAssociationMapping); + + if ($value === [] || $value === null) { + continue; + } + + assert($mapping instanceof ManyToManyOwningSideMapping); + + $mapping->joinTable = JoinTableMapping::fromMappingArray($value); + + continue; + } + + if (property_exists($mapping, $key)) { + $mapping->$key = $value; + } else { + throw new OutOfRangeException('Unknown property ' . $key . ' on class ' . static::class); + } + } + + return $mapping; + } + + /** + * @phpstan-assert-if-true OwningSideMapping $this + * @phpstan-assert-if-false InverseSideMapping $this + */ + final public function isOwningSide(): bool + { + return $this instanceof OwningSideMapping; + } + + /** @phpstan-assert-if-true ToOneAssociationMapping $this */ + final public function isToOne(): bool + { + return $this instanceof ToOneAssociationMapping; + } + + /** @phpstan-assert-if-true ToManyAssociationMapping $this */ + final public function isToMany(): bool + { + return $this instanceof ToManyAssociationMapping; + } + + /** @phpstan-assert-if-true OneToOneOwningSideMapping $this */ + final public function isOneToOneOwningSide(): bool + { + return $this->isOneToOne() && $this->isOwningSide(); + } + + /** @phpstan-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */ + final public function isToOneOwningSide(): bool + { + return $this->isToOne() && $this->isOwningSide(); + } + + /** @phpstan-assert-if-true ManyToManyOwningSideMapping $this */ + final public function isManyToManyOwningSide(): bool + { + return $this instanceof ManyToManyOwningSideMapping; + } + + /** @phpstan-assert-if-true OneToOneAssociationMapping $this */ + final public function isOneToOne(): bool + { + return $this instanceof OneToOneAssociationMapping; + } + + /** @phpstan-assert-if-true OneToManyAssociationMapping $this */ + final public function isOneToMany(): bool + { + return $this instanceof OneToManyAssociationMapping; + } + + /** @phpstan-assert-if-true ManyToOneAssociationMapping $this */ + final public function isManyToOne(): bool + { + return $this instanceof ManyToOneAssociationMapping; + } + + /** @phpstan-assert-if-true ManyToManyAssociationMapping $this */ + final public function isManyToMany(): bool + { + return $this instanceof ManyToManyAssociationMapping; + } + + /** @phpstan-assert-if-true ToManyAssociationMapping $this */ + final public function isOrdered(): bool + { + return $this->isToMany() && $this->orderBy() !== []; + } + + /** @phpstan-assert-if-true ToManyAssociationMapping $this */ + public function isIndexed(): bool + { + return false; + } + + final public function type(): int + { + return match (true) { + $this instanceof OneToOneAssociationMapping => ClassMetadata::ONE_TO_ONE, + $this instanceof OneToManyAssociationMapping => ClassMetadata::ONE_TO_MANY, + $this instanceof ManyToOneAssociationMapping => ClassMetadata::MANY_TO_ONE, + $this instanceof ManyToManyAssociationMapping => ClassMetadata::MANY_TO_MANY, + default => throw new Exception('Cannot determine type for ' . static::class), + }; + } + + /** @param string $offset */ + public function offsetExists(mixed $offset): bool + { + return isset($this->$offset) || in_array($offset, ['isOwningSide', 'type'], true); + } + + final public function offsetGet(mixed $offset): mixed + { + return match ($offset) { + 'isOwningSide' => $this->isOwningSide(), + 'type' => $this->type(), + 'isCascadeRemove' => $this->isCascadeRemove(), + 'isCascadePersist' => $this->isCascadePersist(), + 'isCascadeRefresh' => $this->isCascadeRefresh(), + 'isCascadeDetach' => $this->isCascadeDetach(), + default => property_exists($this, $offset) ? $this->$offset : throw new OutOfRangeException(sprintf( + 'Unknown property "%s" on class %s', + $offset, + static::class, + )), + }; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + assert($offset !== null); + if (! property_exists($this, $offset)) { + throw new OutOfRangeException(sprintf( + 'Unknown property "%s" on class %s', + $offset, + static::class, + )); + } + + if ($offset === 'joinTable') { + $value = JoinTableMapping::fromMappingArray($value); + } + + $this->$offset = $value; + } + + /** @param string $offset */ + public function offsetUnset(mixed $offset): void + { + if (! property_exists($this, $offset)) { + throw new OutOfRangeException(sprintf( + 'Unknown property "%s" on class %s', + $offset, + static::class, + )); + } + + $this->$offset = null; + } + + final public function isCascadeRemove(): bool + { + return in_array('remove', $this->cascade, true); + } + + final public function isCascadePersist(): bool + { + return in_array('persist', $this->cascade, true); + } + + final public function isCascadeRefresh(): bool + { + return in_array('refresh', $this->cascade, true); + } + + final public function isCascadeDetach(): bool + { + return in_array('detach', $this->cascade, true); + } + + /** @return array */ + public function toArray(): array + { + $array = (array) $this; + + $array['isOwningSide'] = $this->isOwningSide(); + $array['type'] = $this->type(); + + return $array; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = ['fieldName', 'sourceEntity', 'targetEntity']; + + if (count($this->cascade) > 0) { + $serialized[] = 'cascade'; + } + + foreach ( + [ + 'fetch', + 'inherited', + 'declared', + 'cache', + 'originalClass', + 'originalField', + ] as $stringOrArrayProperty + ) { + if ($this->$stringOrArrayProperty !== null) { + $serialized[] = $stringOrArrayProperty; + } + } + + foreach (['id', 'orphanRemoval', 'isOnDeleteCascade', 'unique'] as $boolProperty) { + if ($this->$boolProperty) { + $serialized[] = $boolProperty; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/AssociationOverride.php b/src/Mapping/AssociationOverride.php index 4697f842299..a10372dbd2b 100644 --- a/src/Mapping/AssociationOverride.php +++ b/src/Mapping/AssociationOverride.php @@ -4,77 +4,38 @@ namespace Doctrine\ORM\Mapping; -/** - * This attribute is used to override association mapping of property for an entity relationship. - * - * @Annotation - * @NamedArgumentConstructor - * @Target("ANNOTATION") - */ +/** This attribute is used to override association mapping of property for an entity relationship. */ final class AssociationOverride implements MappingAttribute { - /** - * The name of the relationship property whose mapping is being overridden. - * - * @var string - * @readonly - */ - public $name; - /** * The join column that is being mapped to the persistent attribute. * * @var array|null - * @readonly */ - public $joinColumns; + public readonly array|null $joinColumns; /** * The join column that is being mapped to the persistent attribute. * * @var array|null - * @readonly - */ - public $inverseJoinColumns; - - /** - * The join table that maps the relationship. - * - * @var JoinTable|null - * @readonly - */ - public $joinTable; - - /** - * The name of the association-field on the inverse-side. - * - * @var string|null - * @readonly - */ - public $inversedBy; - - /** - * The fetching strategy to use for the association. - * - * @var string|null - * @phpstan-var 'LAZY'|'EAGER'|'EXTRA_LAZY'|null - * @readonly - * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) */ - public $fetch; + public readonly array|null $inverseJoinColumns; /** + * @param string $name The name of the relationship property whose mapping is being overridden. * @param JoinColumn|array $joinColumns * @param JoinColumn|array $inverseJoinColumns + * @param JoinTable|null $joinTable The join table that maps the relationship. + * @param string|null $inversedBy The name of the association-field on the inverse-side. * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch */ public function __construct( - string $name, - $joinColumns = null, - $inverseJoinColumns = null, - ?JoinTable $joinTable = null, - ?string $inversedBy = null, - ?string $fetch = null + public readonly string $name, + array|JoinColumn|null $joinColumns = null, + array|JoinColumn|null $inverseJoinColumns = null, + public readonly JoinTable|null $joinTable = null, + public readonly string|null $inversedBy = null, + public readonly string|null $fetch = null, ) { if ($joinColumns instanceof JoinColumn) { $joinColumns = [$joinColumns]; @@ -84,11 +45,7 @@ public function __construct( $inverseJoinColumns = [$inverseJoinColumns]; } - $this->name = $name; $this->joinColumns = $joinColumns; $this->inverseJoinColumns = $inverseJoinColumns; - $this->joinTable = $joinTable; - $this->inversedBy = $inversedBy; - $this->fetch = $fetch; } } diff --git a/src/Mapping/AssociationOverrides.php b/src/Mapping/AssociationOverrides.php index 2b283a93f95..9fc6807b7b8 100644 --- a/src/Mapping/AssociationOverrides.php +++ b/src/Mapping/AssociationOverrides.php @@ -9,13 +9,7 @@ use function array_values; use function is_array; -/** - * This attribute is used to override association mappings of relationship properties. - * - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ +/** This attribute is used to override association mappings of relationship properties. */ #[Attribute(Attribute::TARGET_CLASS)] final class AssociationOverrides implements MappingAttribute { @@ -23,12 +17,11 @@ final class AssociationOverrides implements MappingAttribute * Mapping overrides of relationship properties. * * @var list - * @readonly */ - public $overrides = []; + public readonly array $overrides; /** @param array|AssociationOverride $overrides */ - public function __construct($overrides) + public function __construct(array|AssociationOverride $overrides) { if (! is_array($overrides)) { $overrides = [$overrides]; diff --git a/src/Mapping/AttributeOverride.php b/src/Mapping/AttributeOverride.php index fb3c6e74713..8f0e70c7295 100644 --- a/src/Mapping/AttributeOverride.php +++ b/src/Mapping/AttributeOverride.php @@ -4,34 +4,12 @@ namespace Doctrine\ORM\Mapping; -/** - * This attribute is used to override the mapping of a entity property. - * - * @Annotation - * @NamedArgumentConstructor - * @Target("ANNOTATION") - */ +/** This attribute is used to override the mapping of a entity property. */ final class AttributeOverride implements MappingAttribute { - /** - * The name of the property whose mapping is being overridden. - * - * @var string - * @readonly - */ - public $name; - - /** - * The column definition. - * - * @var Column - * @readonly - */ - public $column; - - public function __construct(string $name, Column $column) - { - $this->name = $name; - $this->column = $column; + public function __construct( + public string $name, + public Column $column, + ) { } } diff --git a/src/Mapping/AttributeOverrides.php b/src/Mapping/AttributeOverrides.php index a746e436e90..9c7b9dbc8f6 100644 --- a/src/Mapping/AttributeOverrides.php +++ b/src/Mapping/AttributeOverrides.php @@ -9,13 +9,7 @@ use function array_values; use function is_array; -/** - * This attribute is used to override the mapping of a entity property. - * - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ +/** This attribute is used to override the mapping of a entity property. */ #[Attribute(Attribute::TARGET_CLASS)] final class AttributeOverrides implements MappingAttribute { @@ -23,12 +17,11 @@ final class AttributeOverrides implements MappingAttribute * One or more field or property mapping overrides. * * @var list - * @readonly */ - public $overrides = []; + public readonly array $overrides; /** @param array|AttributeOverride $overrides */ - public function __construct($overrides) + public function __construct(array|AttributeOverride $overrides) { if (! is_array($overrides)) { $overrides = [$overrides]; diff --git a/src/Mapping/Builder/AssociationBuilder.php b/src/Mapping/Builder/AssociationBuilder.php index 2fd713ddec4..ea9e13c53b1 100644 --- a/src/Mapping/Builder/AssociationBuilder.php +++ b/src/Mapping/Builder/AssociationBuilder.php @@ -9,47 +9,27 @@ class AssociationBuilder { - /** @var ClassMetadataBuilder */ - protected $builder; - - /** @var mixed[] */ - protected $mapping; - /** @var mixed[]|null */ - protected $joinColumns; + protected array|null $joinColumns = null; - /** @var int */ - protected $type; - - /** - * @param mixed[] $mapping - * @param int $type - */ - public function __construct(ClassMetadataBuilder $builder, array $mapping, $type) - { - $this->builder = $builder; - $this->mapping = $mapping; - $this->type = $type; + /** @param mixed[] $mapping */ + public function __construct( + protected readonly ClassMetadataBuilder $builder, + protected array $mapping, + protected readonly int $type, + ) { } - /** - * @param string $fieldName - * - * @return $this - */ - public function mappedBy($fieldName) + /** @return $this */ + public function mappedBy(string $fieldName): static { $this->mapping['mappedBy'] = $fieldName; return $this; } - /** - * @param string $fieldName - * - * @return $this - */ - public function inversedBy($fieldName) + /** @return $this */ + public function inversedBy(string $fieldName): static { $this->mapping['inversedBy'] = $fieldName; @@ -57,7 +37,7 @@ public function inversedBy($fieldName) } /** @return $this */ - public function cascadeAll() + public function cascadeAll(): static { $this->mapping['cascade'] = ['ALL']; @@ -65,7 +45,7 @@ public function cascadeAll() } /** @return $this */ - public function cascadePersist() + public function cascadePersist(): static { $this->mapping['cascade'][] = 'persist'; @@ -73,7 +53,7 @@ public function cascadePersist() } /** @return $this */ - public function cascadeRemove() + public function cascadeRemove(): static { $this->mapping['cascade'][] = 'remove'; @@ -81,15 +61,7 @@ public function cascadeRemove() } /** @return $this */ - public function cascadeMerge() - { - $this->mapping['cascade'][] = 'merge'; - - return $this; - } - - /** @return $this */ - public function cascadeDetach() + public function cascadeDetach(): static { $this->mapping['cascade'][] = 'detach'; @@ -97,7 +69,7 @@ public function cascadeDetach() } /** @return $this */ - public function cascadeRefresh() + public function cascadeRefresh(): static { $this->mapping['cascade'][] = 'refresh'; @@ -105,7 +77,7 @@ public function cascadeRefresh() } /** @return $this */ - public function fetchExtraLazy() + public function fetchExtraLazy(): static { $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; @@ -113,7 +85,7 @@ public function fetchExtraLazy() } /** @return $this */ - public function fetchEager() + public function fetchEager(): static { $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER; @@ -121,7 +93,7 @@ public function fetchEager() } /** @return $this */ - public function fetchLazy() + public function fetchLazy(): static { $this->mapping['fetch'] = ClassMetadata::FETCH_LAZY; @@ -131,17 +103,16 @@ public function fetchLazy() /** * Add Join Columns. * - * @param string $columnName - * @param string $referencedColumnName - * @param bool $nullable - * @param bool $unique - * @param string|null $onDelete - * @param string|null $columnDef - * * @return $this */ - public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) - { + public function addJoinColumn( + string $columnName, + string $referencedColumnName, + bool $nullable = true, + bool $unique = false, + string|null $onDelete = null, + string|null $columnDef = null, + ): static { $this->joinColumns[] = [ 'name' => $columnName, 'referencedColumnName' => $referencedColumnName, @@ -159,7 +130,7 @@ public function addJoinColumn($columnName, $referencedColumnName, $nullable = tr * * @return $this */ - public function makePrimaryKey() + public function makePrimaryKey(): static { $this->mapping['id'] = true; @@ -171,19 +142,15 @@ public function makePrimaryKey() * * @return $this */ - public function orphanRemoval() + public function orphanRemoval(): static { $this->mapping['orphanRemoval'] = true; return $this; } - /** - * @return ClassMetadataBuilder - * - * @throws InvalidArgumentException - */ - public function build() + /** @throws InvalidArgumentException */ + public function build(): ClassMetadataBuilder { $mapping = $this->mapping; if ($this->joinColumns) { diff --git a/src/Mapping/Builder/ClassMetadataBuilder.php b/src/Mapping/Builder/ClassMetadataBuilder.php index 29675aa22a9..2e9fca1a9d2 100644 --- a/src/Mapping/Builder/ClassMetadataBuilder.php +++ b/src/Mapping/Builder/ClassMetadataBuilder.php @@ -5,11 +5,7 @@ namespace Doctrine\ORM\Mapping\Builder; use BackedEnum; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; - -use function get_class; /** * Builder Object for ClassMetadata @@ -18,27 +14,12 @@ */ class ClassMetadataBuilder { - /** @var ClassMetadataInfo */ - private $cm; - - public function __construct(ClassMetadataInfo $cm) - { - if (! $cm instanceof ClassMetadata) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/249', - 'Passing an instance of %s to %s is deprecated, please pass a ClassMetadata instance instead.', - get_class($cm), - __METHOD__, - ClassMetadata::class - ); - } - - $this->cm = $cm; + public function __construct( + private readonly ClassMetadata $cm, + ) { } - /** @return ClassMetadataInfo */ - public function getClassMetadata() + public function getClassMetadata(): ClassMetadata { return $this->cm; } @@ -48,7 +29,7 @@ public function getClassMetadata() * * @return $this */ - public function setMappedSuperClass() + public function setMappedSuperClass(): static { $this->cm->isMappedSuperclass = true; $this->cm->isEmbeddedClass = false; @@ -61,7 +42,7 @@ public function setMappedSuperClass() * * @return $this */ - public function setEmbeddable() + public function setEmbeddable(): static { $this->cm->isEmbeddedClass = true; $this->cm->isMappedSuperclass = false; @@ -72,20 +53,18 @@ public function setEmbeddable() /** * Adds and embedded class * - * @param string $fieldName - * @param string $class - * @param string|false|null $columnPrefix + * @param class-string $class * * @return $this */ - public function addEmbedded($fieldName, $class, $columnPrefix = null) + public function addEmbedded(string $fieldName, string $class, string|false|null $columnPrefix = null): static { $this->cm->mapEmbedded( [ 'fieldName' => $fieldName, 'class' => $class, 'columnPrefix' => $columnPrefix, - ] + ], ); return $this; @@ -94,11 +73,9 @@ public function addEmbedded($fieldName, $class, $columnPrefix = null) /** * Sets custom Repository class name. * - * @param string $repositoryClassName - * * @return $this */ - public function setCustomRepositoryClass($repositoryClassName) + public function setCustomRepositoryClass(string $repositoryClassName): static { $this->cm->setCustomRepositoryClass($repositoryClassName); @@ -110,7 +87,7 @@ public function setCustomRepositoryClass($repositoryClassName) * * @return $this */ - public function setReadOnly() + public function setReadOnly(): static { $this->cm->markReadOnly(); @@ -120,11 +97,9 @@ public function setReadOnly() /** * Sets the table name. * - * @param string $name - * * @return $this */ - public function setTable($name) + public function setTable(string $name): static { $this->cm->setPrimaryTable(['name' => $name]); @@ -134,12 +109,11 @@ public function setTable($name) /** * Adds Index. * - * @param string $name * @phpstan-param list $columns * * @return $this */ - public function addIndex(array $columns, $name) + public function addIndex(array $columns, string $name): static { if (! isset($this->cm->table['indexes'])) { $this->cm->table['indexes'] = []; @@ -153,12 +127,11 @@ public function addIndex(array $columns, $name) /** * Adds Unique Constraint. * - * @param string $name * @phpstan-param list $columns * * @return $this */ - public function addUniqueConstraint(array $columns, $name) + public function addUniqueConstraint(array $columns, string $name): static { if (! isset($this->cm->table['uniqueConstraints'])) { $this->cm->table['uniqueConstraints'] = []; @@ -169,34 +142,12 @@ public function addUniqueConstraint(array $columns, $name) return $this; } - /** - * Adds named query. - * - * @deprecated - * - * @param string $name - * @param string $dqlQuery - * - * @return $this - */ - public function addNamedQuery($name, $dqlQuery) - { - $this->cm->addNamedQuery( - [ - 'name' => $name, - 'query' => $dqlQuery, - ] - ); - - return $this; - } - /** * Sets class as root of a joined table inheritance hierarchy. * * @return $this */ - public function setJoinedTableInheritance() + public function setJoinedTableInheritance(): static { $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); @@ -208,7 +159,7 @@ public function setJoinedTableInheritance() * * @return $this */ - public function setSingleTableInheritance() + public function setSingleTableInheritance(): static { $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); @@ -218,16 +169,19 @@ public function setSingleTableInheritance() /** * Sets the discriminator column details. * - * @param string $name - * @param string $type - * @param int $length * @param class-string|null $enumType * @param array $options * * @return $this */ - public function setDiscriminatorColumn($name, $type = 'string', $length = 255, ?string $columnDefinition = null, ?string $enumType = null, array $options = []) - { + public function setDiscriminatorColumn( + string $name, + string $type = 'string', + int $length = 255, + string|null $columnDefinition = null, + string|null $enumType = null, + array $options = [], + ): static { $this->cm->setDiscriminatorColumn( [ 'name' => $name, @@ -236,7 +190,7 @@ public function setDiscriminatorColumn($name, $type = 'string', $length = 255, ? 'columnDefinition' => $columnDefinition, 'enumType' => $enumType, 'options' => $options, - ] + ], ); return $this; @@ -245,12 +199,9 @@ public function setDiscriminatorColumn($name, $type = 'string', $length = 255, ? /** * Adds a subclass to this inheritance hierarchy. * - * @param string $name - * @param string $class - * * @return $this */ - public function addDiscriminatorMapClass($name, $class) + public function addDiscriminatorMapClass(string $name, string $class): static { $this->cm->addDiscriminatorMapClass($name, $class); @@ -262,34 +213,19 @@ public function addDiscriminatorMapClass($name, $class) * * @return $this */ - public function setChangeTrackingPolicyDeferredExplicit() + public function setChangeTrackingPolicyDeferredExplicit(): static { $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); return $this; } - /** - * Sets notify change tracking policy. - * - * @return $this - */ - public function setChangeTrackingPolicyNotify() - { - $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); - - return $this; - } - /** * Adds lifecycle event. * - * @param string $methodName - * @param string $event - * * @return $this */ - public function addLifecycleEvent($methodName, $event) + public function addLifecycleEvent(string $methodName, string $event): static { $this->cm->addLifecycleCallback($methodName, $event); @@ -299,13 +235,11 @@ public function addLifecycleEvent($methodName, $event) /** * Adds Field. * - * @param string $name - * @param string $type * @phpstan-param array $mapping * * @return $this */ - public function addField($name, $type, array $mapping = []) + public function addField(string $name, string $type, array $mapping = []): static { $mapping['fieldName'] = $name; $mapping['type'] = $type; @@ -317,32 +251,22 @@ public function addField($name, $type, array $mapping = []) /** * Creates a field builder. - * - * @param string $name - * @param string $type - * - * @return FieldBuilder */ - public function createField($name, $type) + public function createField(string $name, string $type): FieldBuilder { return new FieldBuilder( $this, [ 'fieldName' => $name, 'type' => $type, - ] + ], ); } /** * Creates an embedded builder. - * - * @param string $fieldName - * @param string $class - * - * @return EmbeddedBuilder */ - public function createEmbedded($fieldName, $class) + public function createEmbedded(string $fieldName, string $class): EmbeddedBuilder { return new EmbeddedBuilder( $this, @@ -350,24 +274,21 @@ public function createEmbedded($fieldName, $class) 'fieldName' => $fieldName, 'class' => $class, 'columnPrefix' => null, - ] + ], ); } /** * Adds a simple many to one association, optionally with the inversed by field. - * - * @param string $name - * @param string $targetEntity - * @param string|null $inversedBy - * - * @return ClassMetadataBuilder */ - public function addManyToOne($name, $targetEntity, $inversedBy = null) - { + public function addManyToOne( + string $name, + string $targetEntity, + string|null $inversedBy = null, + ): ClassMetadataBuilder { $builder = $this->createManyToOne($name, $targetEntity); - if ($inversedBy) { + if ($inversedBy !== null) { $builder->inversedBy($inversedBy); } @@ -378,13 +299,8 @@ public function addManyToOne($name, $targetEntity, $inversedBy = null) * Creates a ManyToOne Association Builder. * * Note: This method does not add the association, you have to call build() on the AssociationBuilder. - * - * @param string $name - * @param string $targetEntity - * - * @return AssociationBuilder */ - public function createManyToOne($name, $targetEntity) + public function createManyToOne(string $name, string $targetEntity): AssociationBuilder { return new AssociationBuilder( $this, @@ -392,19 +308,14 @@ public function createManyToOne($name, $targetEntity) 'fieldName' => $name, 'targetEntity' => $targetEntity, ], - ClassMetadata::MANY_TO_ONE + ClassMetadata::MANY_TO_ONE, ); } /** * Creates a OneToOne Association Builder. - * - * @param string $name - * @param string $targetEntity - * - * @return AssociationBuilder */ - public function createOneToOne($name, $targetEntity) + public function createOneToOne(string $name, string $targetEntity): AssociationBuilder { return new AssociationBuilder( $this, @@ -412,20 +323,14 @@ public function createOneToOne($name, $targetEntity) 'fieldName' => $name, 'targetEntity' => $targetEntity, ], - ClassMetadata::ONE_TO_ONE + ClassMetadata::ONE_TO_ONE, ); } /** * Adds simple inverse one-to-one association. - * - * @param string $name - * @param string $targetEntity - * @param string $mappedBy - * - * @return ClassMetadataBuilder */ - public function addInverseOneToOne($name, $targetEntity, $mappedBy) + public function addInverseOneToOne(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder { $builder = $this->createOneToOne($name, $targetEntity); $builder->mappedBy($mappedBy); @@ -435,18 +340,15 @@ public function addInverseOneToOne($name, $targetEntity, $mappedBy) /** * Adds simple owning one-to-one association. - * - * @param string $name - * @param string $targetEntity - * @param string|null $inversedBy - * - * @return ClassMetadataBuilder */ - public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) - { + public function addOwningOneToOne( + string $name, + string $targetEntity, + string|null $inversedBy = null, + ): ClassMetadataBuilder { $builder = $this->createOneToOne($name, $targetEntity); - if ($inversedBy) { + if ($inversedBy !== null) { $builder->inversedBy($inversedBy); } @@ -455,13 +357,8 @@ public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) /** * Creates a ManyToMany Association Builder. - * - * @param string $name - * @param string $targetEntity - * - * @return ManyToManyAssociationBuilder */ - public function createManyToMany($name, $targetEntity) + public function createManyToMany(string $name, string $targetEntity): ManyToManyAssociationBuilder { return new ManyToManyAssociationBuilder( $this, @@ -469,24 +366,21 @@ public function createManyToMany($name, $targetEntity) 'fieldName' => $name, 'targetEntity' => $targetEntity, ], - ClassMetadata::MANY_TO_MANY + ClassMetadata::MANY_TO_MANY, ); } /** * Adds a simple owning many to many association. - * - * @param string $name - * @param string $targetEntity - * @param string|null $inversedBy - * - * @return ClassMetadataBuilder */ - public function addOwningManyToMany($name, $targetEntity, $inversedBy = null) - { + public function addOwningManyToMany( + string $name, + string $targetEntity, + string|null $inversedBy = null, + ): ClassMetadataBuilder { $builder = $this->createManyToMany($name, $targetEntity); - if ($inversedBy) { + if ($inversedBy !== null) { $builder->inversedBy($inversedBy); } @@ -495,14 +389,8 @@ public function addOwningManyToMany($name, $targetEntity, $inversedBy = null) /** * Adds a simple inverse many to many association. - * - * @param string $name - * @param string $targetEntity - * @param string $mappedBy - * - * @return ClassMetadataBuilder */ - public function addInverseManyToMany($name, $targetEntity, $mappedBy) + public function addInverseManyToMany(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder { $builder = $this->createManyToMany($name, $targetEntity); $builder->mappedBy($mappedBy); @@ -512,13 +400,8 @@ public function addInverseManyToMany($name, $targetEntity, $mappedBy) /** * Creates a one to many association builder. - * - * @param string $name - * @param string $targetEntity - * - * @return OneToManyAssociationBuilder */ - public function createOneToMany($name, $targetEntity) + public function createOneToMany(string $name, string $targetEntity): OneToManyAssociationBuilder { return new OneToManyAssociationBuilder( $this, @@ -526,20 +409,14 @@ public function createOneToMany($name, $targetEntity) 'fieldName' => $name, 'targetEntity' => $targetEntity, ], - ClassMetadata::ONE_TO_MANY + ClassMetadata::ONE_TO_MANY, ); } /** * Adds simple OneToMany association. - * - * @param string $name - * @param string $targetEntity - * @param string $mappedBy - * - * @return ClassMetadataBuilder */ - public function addOneToMany($name, $targetEntity, $mappedBy) + public function addOneToMany(string $name, string $targetEntity, string $mappedBy): ClassMetadataBuilder { $builder = $this->createOneToMany($name, $targetEntity); $builder->mappedBy($mappedBy); diff --git a/src/Mapping/Builder/EmbeddedBuilder.php b/src/Mapping/Builder/EmbeddedBuilder.php index a8508ab3bbe..b9d2127393b 100644 --- a/src/Mapping/Builder/EmbeddedBuilder.php +++ b/src/Mapping/Builder/EmbeddedBuilder.php @@ -11,27 +11,19 @@ */ class EmbeddedBuilder { - /** @var ClassMetadataBuilder */ - private $builder; - - /** @var mixed[] */ - private $mapping; - /** @param mixed[] $mapping */ - public function __construct(ClassMetadataBuilder $builder, array $mapping) - { - $this->builder = $builder; - $this->mapping = $mapping; + public function __construct( + private readonly ClassMetadataBuilder $builder, + private array $mapping, + ) { } /** * Sets the column prefix for all of the embedded columns. * - * @param string $columnPrefix - * * @return $this */ - public function setColumnPrefix($columnPrefix) + public function setColumnPrefix(string $columnPrefix): static { $this->mapping['columnPrefix'] = $columnPrefix; @@ -42,10 +34,8 @@ public function setColumnPrefix($columnPrefix) * Finalizes this embeddable and attach it to the ClassMetadata. * * Without this call an EmbeddedBuilder has no effect on the ClassMetadata. - * - * @return ClassMetadataBuilder */ - public function build() + public function build(): ClassMetadataBuilder { $cm = $this->builder->getClassMetadata(); diff --git a/src/Mapping/Builder/EntityListenerBuilder.php b/src/Mapping/Builder/EntityListenerBuilder.php index 1ba8c7a787a..a0b14b954c5 100644 --- a/src/Mapping/Builder/EntityListenerBuilder.php +++ b/src/Mapping/Builder/EntityListenerBuilder.php @@ -16,8 +16,8 @@ */ class EntityListenerBuilder { - /** @var array Hash-map to handle event names. */ - private static $events = [ + /** Hash-map to handle event names. */ + private const EVENTS = [ Events::preRemove => true, Events::postRemove => true, Events::prePersist => true, @@ -34,11 +34,9 @@ class EntityListenerBuilder * @param ClassMetadata $metadata The entity metadata. * @param string $className The listener class name. * - * @return void - * * @throws MappingException When the listener class not found. */ - public static function bindEntityListener(ClassMetadata $metadata, $className) + public static function bindEntityListener(ClassMetadata $metadata, string $className): void { $class = $metadata->fullyQualifiedClassName($className); @@ -47,7 +45,7 @@ public static function bindEntityListener(ClassMetadata $metadata, $className) } foreach (get_class_methods($class) as $method) { - if (! isset(self::$events[$method])) { + if (! isset(self::EVENTS[$method])) { continue; } diff --git a/src/Mapping/Builder/FieldBuilder.php b/src/Mapping/Builder/FieldBuilder.php index 5cbf997774f..8326ff5017c 100644 --- a/src/Mapping/Builder/FieldBuilder.php +++ b/src/Mapping/Builder/FieldBuilder.php @@ -13,39 +13,27 @@ */ class FieldBuilder { - /** @var ClassMetadataBuilder */ - private $builder; + private bool $version = false; + private string|null $generatedValue = null; - /** @var mixed[] */ - private $mapping; + /** @var mixed[]|null */ + private array|null $sequenceDef = null; - /** @var bool */ - private $version; - - /** @var string */ - private $generatedValue; - - /** @var mixed[] */ - private $sequenceDef; - - /** @var string|null */ - private $customIdGenerator; + private string|null $customIdGenerator = null; /** @param mixed[] $mapping */ - public function __construct(ClassMetadataBuilder $builder, array $mapping) - { - $this->builder = $builder; - $this->mapping = $mapping; + public function __construct( + private readonly ClassMetadataBuilder $builder, + private array $mapping, + ) { } /** * Sets length. * - * @param int $length - * * @return $this */ - public function length($length) + public function length(int $length): static { $this->mapping['length'] = $length; @@ -55,13 +43,11 @@ public function length($length) /** * Sets nullable. * - * @param bool $flag - * * @return $this */ - public function nullable($flag = true) + public function nullable(bool $flag = true): static { - $this->mapping['nullable'] = (bool) $flag; + $this->mapping['nullable'] = $flag; return $this; } @@ -69,13 +55,11 @@ public function nullable($flag = true) /** * Sets Unique. * - * @param bool $flag - * * @return $this */ - public function unique($flag = true) + public function unique(bool $flag = true): static { - $this->mapping['unique'] = (bool) $flag; + $this->mapping['unique'] = $flag; return $this; } @@ -83,11 +67,9 @@ public function unique($flag = true) /** * Sets column name. * - * @param string $name - * * @return $this */ - public function columnName($name) + public function columnName(string $name): static { $this->mapping['columnName'] = $name; @@ -97,11 +79,9 @@ public function columnName($name) /** * Sets Precision. * - * @param int $p - * * @return $this */ - public function precision($p) + public function precision(int $p): static { $this->mapping['precision'] = $p; @@ -139,35 +119,21 @@ public function updatable(bool $flag = true): self /** * Sets scale. * - * @param int $s - * * @return $this */ - public function scale($s) + public function scale(int $s): static { $this->mapping['scale'] = $s; return $this; } - /** - * Sets field as primary key. - * - * @deprecated Use makePrimaryKey() instead - * - * @return FieldBuilder - */ - public function isPrimaryKey() - { - return $this->makePrimaryKey(); - } - /** * Sets field as primary key. * * @return $this */ - public function makePrimaryKey() + public function makePrimaryKey(): static { $this->mapping['id'] = true; @@ -177,24 +143,17 @@ public function makePrimaryKey() /** * Sets an option. * - * @param string $name - * @param mixed $value - * * @return $this */ - public function option($name, $value) + public function option(string $name, mixed $value): static { $this->mapping['options'][$name] = $value; return $this; } - /** - * @param string $strategy - * - * @return $this - */ - public function generatedValue($strategy = 'AUTO') + /** @return $this */ + public function generatedValue(string $strategy = 'AUTO'): static { $this->generatedValue = $strategy; @@ -206,7 +165,7 @@ public function generatedValue($strategy = 'AUTO') * * @return $this */ - public function isVersionField() + public function isVersionField(): static { $this->version = true; @@ -216,13 +175,9 @@ public function isVersionField() /** * Sets Sequence Generator. * - * @param string $sequenceName - * @param int $allocationSize - * @param int $initialValue - * * @return $this */ - public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1) + public function setSequenceGenerator(string $sequenceName, int $allocationSize = 1, int $initialValue = 1): static { $this->sequenceDef = [ 'sequenceName' => $sequenceName, @@ -236,11 +191,9 @@ public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initia /** * Sets column definition. * - * @param string $def - * * @return $this */ - public function columnDefinition($def) + public function columnDefinition(string $def): static { $this->mapping['columnDefinition'] = $def; @@ -251,13 +204,11 @@ public function columnDefinition($def) * Set the FQCN of the custom ID generator. * This class must extend \Doctrine\ORM\Id\AbstractIdGenerator. * - * @param string $customIdGenerator - * * @return $this */ - public function setCustomIdGenerator($customIdGenerator) + public function setCustomIdGenerator(string $customIdGenerator): static { - $this->customIdGenerator = (string) $customIdGenerator; + $this->customIdGenerator = $customIdGenerator; return $this; } @@ -266,10 +217,8 @@ public function setCustomIdGenerator($customIdGenerator) * Finalizes this field and attach it to the ClassMetadata. * * Without this call a FieldBuilder has no effect on the ClassMetadata. - * - * @return ClassMetadataBuilder */ - public function build() + public function build(): ClassMetadataBuilder { $cm = $this->builder->getClassMetadata(); if ($this->generatedValue) { diff --git a/src/Mapping/Builder/ManyToManyAssociationBuilder.php b/src/Mapping/Builder/ManyToManyAssociationBuilder.php index 5da24eb4b90..b83a8baef9b 100644 --- a/src/Mapping/Builder/ManyToManyAssociationBuilder.php +++ b/src/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -11,18 +11,13 @@ */ class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder { - /** @var string|null */ - private $joinTableName; + private string|null $joinTableName = null; /** @var mixed[] */ - private $inverseJoinColumns = []; + private array $inverseJoinColumns = []; - /** - * @param string $name - * - * @return $this - */ - public function setJoinTable($name) + /** @return $this */ + public function setJoinTable(string $name): static { $this->joinTableName = $name; @@ -32,17 +27,16 @@ public function setJoinTable($name) /** * Adds Inverse Join Columns. * - * @param string $columnName - * @param string $referencedColumnName - * @param bool $nullable - * @param bool $unique - * @param string|null $onDelete - * @param string|null $columnDef - * * @return $this */ - public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) - { + public function addInverseJoinColumn( + string $columnName, + string $referencedColumnName, + bool $nullable = true, + bool $unique = false, + string|null $onDelete = null, + string|null $columnDef = null, + ): static { $this->inverseJoinColumns[] = [ 'name' => $columnName, 'referencedColumnName' => $referencedColumnName, @@ -55,8 +49,7 @@ public function addInverseJoinColumn($columnName, $referencedColumnName, $nullab return $this; } - /** @return ClassMetadataBuilder */ - public function build() + public function build(): ClassMetadataBuilder { $mapping = $this->mapping; $mapping['joinTable'] = []; diff --git a/src/Mapping/Builder/OneToManyAssociationBuilder.php b/src/Mapping/Builder/OneToManyAssociationBuilder.php index 0ddfc2399ad..0b0e1aac9e1 100644 --- a/src/Mapping/Builder/OneToManyAssociationBuilder.php +++ b/src/Mapping/Builder/OneToManyAssociationBuilder.php @@ -16,27 +16,22 @@ class OneToManyAssociationBuilder extends AssociationBuilder * * @return $this */ - public function setOrderBy(array $fieldNames) + public function setOrderBy(array $fieldNames): static { $this->mapping['orderBy'] = $fieldNames; return $this; } - /** - * @param string $fieldName - * - * @return $this - */ - public function setIndexBy($fieldName) + /** @return $this */ + public function setIndexBy(string $fieldName): static { $this->mapping['indexBy'] = $fieldName; return $this; } - /** @return ClassMetadataBuilder */ - public function build() + public function build(): ClassMetadataBuilder { $mapping = $this->mapping; if ($this->joinColumns) { diff --git a/src/Mapping/Cache.php b/src/Mapping/Cache.php index 635982f75aa..5eda277c90a 100644 --- a/src/Mapping/Cache.php +++ b/src/Mapping/Cache.php @@ -5,34 +5,15 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * Caching to an entity or a collection. - * - * @Annotation - * @NamedArgumentConstructor() - * @Target({"CLASS","PROPERTY"}) - */ +/** Caching to an entity or a collection. */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)] final class Cache implements MappingAttribute { - /** - * The concurrency strategy. - * - * @Enum({"READ_ONLY", "NONSTRICT_READ_WRITE", "READ_WRITE"}) - * @var string - * @phpstan-var 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' - */ - public $usage = 'READ_ONLY'; - - /** @var string|null Cache region name. */ - public $region; - /** @phpstan-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */ - public function __construct(string $usage = 'READ_ONLY', ?string $region = null) - { - $this->usage = $usage; - $this->region = $region; + public function __construct( + public readonly string $usage = 'READ_ONLY', + public readonly string|null $region = null, + ) { } } diff --git a/src/Mapping/ChainTypedFieldMapper.php b/src/Mapping/ChainTypedFieldMapper.php index 5790fd33ceb..ed1ba93706c 100644 --- a/src/Mapping/ChainTypedFieldMapper.php +++ b/src/Mapping/ChainTypedFieldMapper.php @@ -4,18 +4,20 @@ namespace Doctrine\ORM\Mapping; +use Doctrine\ORM\Internal\NoUnknownNamedArguments; use ReflectionProperty; final class ChainTypedFieldMapper implements TypedFieldMapper { - /** - * @readonly - * @var TypedFieldMapper[] $typedFieldMappers - */ - private array $typedFieldMappers; + use NoUnknownNamedArguments; + + /** @var list $typedFieldMappers */ + private readonly array $typedFieldMappers; public function __construct(TypedFieldMapper ...$typedFieldMappers) { + self::validateVariadicParameter($typedFieldMappers); + $this->typedFieldMappers = $typedFieldMappers; } diff --git a/src/Mapping/ChangeTrackingPolicy.php b/src/Mapping/ChangeTrackingPolicy.php index 271023a6752..f72fa5c550e 100644 --- a/src/Mapping/ChangeTrackingPolicy.php +++ b/src/Mapping/ChangeTrackingPolicy.php @@ -5,29 +5,13 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class ChangeTrackingPolicy implements MappingAttribute { - /** - * The change tracking policy. - * - * @var string - * @phpstan-var 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT'|'NOTIFY' - * @readonly - * @Enum({"DEFERRED_IMPLICIT", "DEFERRED_EXPLICIT", "NOTIFY"}) - */ - public $value; - - /** @phpstan-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT'|'NOTIFY' $value */ - public function __construct(string $value) - { - $this->value = $value; + /** @phpstan-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */ + public function __construct( + public readonly string $value, + ) { } } diff --git a/src/Mapping/ClassMetadata.php b/src/Mapping/ClassMetadata.php index cfb6a2f5165..e1cbd6306a5 100644 --- a/src/Mapping/ClassMetadata.php +++ b/src/Mapping/ClassMetadata.php @@ -5,114 +5,2680 @@ namespace Doctrine\ORM\Mapping; use BackedEnum; +use BadMethodCallException; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\Types; +use Doctrine\Deprecations\Deprecation; +use Doctrine\Instantiator\Instantiator; +use Doctrine\Instantiator\InstantiatorInterface; +use Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation; +use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Id\AbstractIdGenerator; +use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata; +use Doctrine\Persistence\Mapping\ReflectionService; +use Doctrine\Persistence\Reflection\EnumReflectionProperty; +use InvalidArgumentException; +use LogicException; +use ReflectionClass; +use ReflectionNamedType; +use ReflectionProperty; +use Stringable; + +use function array_column; +use function array_count_values; +use function array_diff; +use function array_filter; +use function array_flip; +use function array_intersect; +use function array_key_exists; +use function array_keys; +use function array_map; +use function array_merge; +use function array_pop; +use function array_values; +use function assert; +use function class_exists; +use function count; +use function defined; +use function enum_exists; +use function explode; +use function implode; +use function in_array; +use function interface_exists; +use function is_string; +use function is_subclass_of; +use function ltrim; +use function method_exists; +use function spl_object_id; +use function sprintf; +use function str_contains; +use function str_replace; +use function strtolower; +use function trait_exists; +use function trim; + +use const PHP_VERSION_ID; /** - * {@inheritDoc} + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and its associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: * - * @todo remove or rename ClassMetadataInfo to ClassMetadata + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @phpstan-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping * @template-covariant T of object - * @template-extends ClassMetadataInfo - * @phpstan-type FieldMapping = array{ - * type: string, - * fieldName: string, - * columnName: string, - * length?: int, - * id?: bool, - * nullable?: bool, - * notInsertable?: bool, - * notUpdatable?: bool, - * generated?: int, - * enumType?: class-string, - * columnDefinition?: string, - * precision?: int, - * scale?: int, - * unique?: bool, - * inherited?: class-string, - * originalClass?: class-string, - * originalField?: string, - * quoted?: bool, - * requireSQLConversion?: bool, - * declared?: class-string, - * declaredField?: string, - * options?: array, - * version?: string, - * default?: string|int, - * } - * @phpstan-type JoinColumnData = array{ - * name: string, - * referencedColumnName: string, - * unique?: bool, - * quoted?: bool, - * fieldName?: string, - * onDelete?: string, - * columnDefinition?: string, - * nullable?: bool, - * } - * @phpstan-type AssociationMapping = array{ - * cache?: array, - * cascade: array, - * declared?: class-string, - * fetch: mixed, - * fieldName: string, - * id?: bool, - * inherited?: class-string, - * indexBy?: string, - * inversedBy: string|null, - * isCascadeRemove: bool, - * isCascadePersist: bool, - * isCascadeRefresh: bool, - * isCascadeMerge: bool, - * isCascadeDetach: bool, - * isOnDeleteCascade?: bool, - * isOwningSide: bool, - * joinColumns?: array, - * joinColumnFieldNames?: array, - * joinTable?: array, - * joinTableColumns?: list, - * mappedBy: string|null, - * orderBy?: array, - * originalClass?: class-string, - * originalField?: string, - * orphanRemoval?: bool, - * relationToSourceKeyColumns?: array, - * relationToTargetKeyColumns?: array, - * sourceEntity: class-string, - * sourceToTargetKeyColumns?: array, - * targetEntity: class-string, - * targetToSourceKeyColumns?: array, - * type: int, - * unique?: bool, - * } - * @phpstan-type DiscriminatorColumnMapping = array{ - * name: string, - * fieldName: string, - * type: string, - * length?: int, - * columnDefinition?: string|null, - * enumType?: class-string|null, - * options?: array, - * } - * @phpstan-type EmbeddedClassMapping = array{ - * class: class-string, - * columnPrefix: string|null, - * declaredField: string|null, - * originalField: string|null, - * inherited?: class-string, - * declared?: class-string, - * } + * @template-implements PersistenceClassMetadata */ -class ClassMetadata extends ClassMetadataInfo +class ClassMetadata implements PersistenceClassMetadata, Stringable { + use GetReflectionClassImplementation; + + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + public const INHERITANCE_TYPE_NONE = 1; + + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ + public const INHERITANCE_TYPE_JOINED = 2; + + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ + public const INHERITANCE_TYPE_SINGLE_TABLE = 3; + + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ + public const GENERATOR_TYPE_AUTO = 1; + + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ + public const GENERATOR_TYPE_SEQUENCE = 2; + + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ + public const GENERATOR_TYPE_IDENTITY = 4; + + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural, manually assigned id. + */ + public const GENERATOR_TYPE_NONE = 5; + + /** + * CUSTOM means that customer will use own ID generator that supposedly work + */ + public const GENERATOR_TYPE_CUSTOM = 7; + + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + public const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through persist() or a cascade). + */ + public const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + + /** + * Specifies that an association is to be fetched when it is first accessed. + */ + public const FETCH_LAZY = 2; + + /** + * Specifies that an association is to be fetched when the owner of the + * association is fetched. + */ + public const FETCH_EAGER = 3; + + /** + * Specifies that an association is to be fetched lazy (on first access) and that + * commands such as Collection#count, Collection#slice are issued directly against + * the database if the collection is not yet initialized. + */ + public const FETCH_EXTRA_LAZY = 4; + + /** + * Identifies a one-to-one association. + */ + public const ONE_TO_ONE = 1; + + /** + * Identifies a many-to-one association. + */ + public const MANY_TO_ONE = 2; + + /** + * Identifies a one-to-many association. + */ + public const ONE_TO_MANY = 4; + + /** + * Identifies a many-to-many association. + */ + public const MANY_TO_MANY = 8; + + /** + * Combined bitmask for to-one (single-valued) associations. + */ + public const TO_ONE = 3; + + /** + * Combined bitmask for to-many (collection-valued) associations. + */ + public const TO_MANY = 12; + + /** + * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks, + */ + public const CACHE_USAGE_READ_ONLY = 1; + + /** + * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes. + */ + public const CACHE_USAGE_NONSTRICT_READ_WRITE = 2; + + /** + * Read Write Attempts to lock the entity before update/delete. + */ + public const CACHE_USAGE_READ_WRITE = 3; + + /** + * The value of this column is never generated by the database. + */ + public const GENERATED_NEVER = 0; + + /** + * The value of this column is generated by the database on INSERT, but not on UPDATE. + */ + public const GENERATED_INSERT = 1; + + /** + * The value of this column is generated by the database on both INSERT and UDPATE statements. + */ + public const GENERATED_ALWAYS = 2; + + /** + * READ-ONLY: The namespace the entity class is contained in. + * + * @todo Not really needed. Usage could be localized. + */ + public string|null $namespace = null; + + /** + * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance + * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same + * as {@link $name}. + * + * @phpstan-var class-string + */ + public string $rootEntityName; + + /** + * READ-ONLY: The definition of custom generator. Only used for CUSTOM + * generator type + * + * The definition has the following structure: + * + * array( + * 'class' => 'ClassName', + * ) + * + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + * @var array|null + */ + public array|null $customGeneratorDefinition = null; + + /** + * The name of the custom repository class used for the entity class. + * (Optional). + * + * @phpstan-var ?class-string + */ + public string|null $customRepositoryClassName = null; + + /** + * READ-ONLY: Whether this class describes the mapping of a mapped superclass. + */ + public bool $isMappedSuperclass = false; + + /** + * READ-ONLY: Whether this class describes the mapping of an embeddable class. + */ + public bool $isEmbeddedClass = false; + + /** + * READ-ONLY: The names of the parent entity classes (ancestors), starting with the + * nearest one and ending with the root entity class. + * + * @phpstan-var list + */ + public array $parentClasses = []; + + /** + * READ-ONLY: For classes in inheritance mapping hierarchies, this field contains the names of all + * entity subclasses of this class. These may also be abstract classes. + * + * This list is used, for example, to enumerate all necessary tables in JTI when querying for root + * or subclass entities, or to gather all fields comprised in an entity inheritance tree. + * + * For classes that do not use STI/JTI, this list is empty. + * + * Implementation note: + * + * In PHP, there is no general way to discover all subclasses of a given class at runtime. For that + * reason, the list of classes given in the discriminator map at the root entity is considered + * authoritative. The discriminator map must contain all concrete classes that can + * appear in the particular inheritance hierarchy tree. Since there can be no instances of abstract + * entity classes, users are not required to list such classes with a discriminator value. + * + * The possibly remaining "gaps" for abstract entity classes are filled after the class metadata for the + * root entity has been loaded. + * + * For subclasses of such root entities, the list can be reused/passed downwards, it only needs to + * be filtered accordingly (only keep remaining subclasses) + * + * @phpstan-var list + */ + public array $subClasses = []; + + /** + * READ-ONLY: The names of all embedded classes based on properties. + * + * @phpstan-var array + */ + public array $embeddedClasses = []; + + /** + * READ-ONLY: The field names of all fields that are part of the identifier/primary key + * of the mapped entity class. + * + * @phpstan-var list + */ + public array $identifier = []; + + /** + * READ-ONLY: The inheritance mapping type used by the class. + * + * @phpstan-var self::INHERITANCE_TYPE_* + */ + public int $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * READ-ONLY: The Id generator type used by the class. + * + * @phpstan-var self::GENERATOR_TYPE_* + */ + public int $generatorType = self::GENERATOR_TYPE_NONE; + + /** + * READ-ONLY: The field mappings of the class. + * Keys are field names and values are FieldMapping instances + * + * @var array + */ + public array $fieldMappings = []; + + /** + * READ-ONLY: An array of field names. Used to look up field names from column names. + * Keys are column names and values are field names. + * + * @phpstan-var array + */ + public array $fieldNames = []; + + /** + * READ-ONLY: A map of field names to column names. Keys are field names and values column names. + * Used to look up column names from field names. + * This is the reverse lookup map of $_fieldNames. + * + * @deprecated 3.0 Remove this. + * + * @var mixed[] + */ + public array $columnNames = []; + + /** + * READ-ONLY: The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @see discriminatorColumn + */ + public mixed $discriminatorValue = null; + + /** + * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @see discriminatorColumn + * + * @var array + * + * @phpstan-var array + */ + public array $discriminatorMap = []; + + /** + * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE + * inheritance mappings. + */ + public DiscriminatorColumnMapping|null $discriminatorColumn = null; + + /** + * READ-ONLY: The primary table definition. The definition is an array with the + * following entries: + * + * name => + * schema => + * indexes => array + * uniqueConstraints => array + * + * @var mixed[] + * @phpstan-var array{ + * name: string, + * schema?: string, + * indexes?: array, + * uniqueConstraints?: array, + * options?: array, + * quoted?: bool + * } + */ + public array $table; + + /** + * READ-ONLY: The registered lifecycle callbacks for entities of this class. + * + * @phpstan-var array> + */ + public array $lifecycleCallbacks = []; + + /** + * READ-ONLY: The registered entity listeners. + * + * @phpstan-var array> + */ + public array $entityListeners = []; + + /** + * READ-ONLY: The association mappings of this class. + * + * A join table definition has the following structure: + *
+     * array(
+     *     'name' => ,
+     *      'joinColumns' => array(),
+     *      'inverseJoinColumns' => array()
+     * )
+     * 
+ * + * @phpstan-var array + */ + public array $associationMappings = []; + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite. + */ + public bool $isIdentifierComposite = false; + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association. + * + * This flag is necessary because some code blocks require special treatment of this cases. + */ + public bool $containsForeignIdentifier = false; + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one ENUM type. + * + * This flag is necessary because some code blocks require special treatment of this cases. + */ + public bool $containsEnumIdentifier = false; + /** - * Repeating the ClassMetadataInfo constructor to infer correctly the template with PHPStan + * READ-ONLY: The ID generator used for generating IDs for this class. * - * @see https://github.com/doctrine/orm/issues/8709 + * @todo Remove! + */ + public AbstractIdGenerator $idGenerator; + + /** + * READ-ONLY: The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => '20', + * 'initialValue' => '1' + * ) + * + * + * @var array|null + * @phpstan-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public array|null $sequenceGeneratorDefinition = null; + + /** + * READ-ONLY: The policy used for change-tracking on entities of this class. + */ + public int $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * READ-ONLY: A Flag indicating whether one or more columns of this class + * have to be reloaded after insert / update operations. + */ + public bool $requiresFetchAfterChange = false; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to be versioned + * with optimistic locking. + */ + public bool $isVersioned = false; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + */ + public string|null $versionField = null; + + /** @var mixed[]|null */ + public array|null $cache = null; + + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass|null + */ + public ReflectionClass|null $reflClass = null; + + /** + * Is this entity marked as "read-only"? + * + * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance + * optimization for entities that are immutable, either in your domain or through the relation database + * (coming from a view, or a history table for example). + */ + public bool $isReadOnly = false; + + /** + * NamingStrategy determining the default column and table names. + */ + protected NamingStrategy $namingStrategy; + + /** + * The ReflectionProperty instances of the mapped class. * - * @param string $entityName The name of the entity class the new instance is used for. - * @phpstan-param class-string $entityName + * @var array */ - public function __construct($entityName, ?NamingStrategy $namingStrategy = null, ?TypedFieldMapper $typedFieldMapper = null) + public array $reflFields = []; + + private InstantiatorInterface|null $instantiator = null; + + private readonly TypedFieldMapper $typedFieldMapper; + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $name The name of the entity class the new instance is used for. + * @phpstan-param class-string $name + */ + public function __construct(public string $name, NamingStrategy|null $namingStrategy = null, TypedFieldMapper|null $typedFieldMapper = null) + { + $this->rootEntityName = $name; + $this->namingStrategy = $namingStrategy ?? new DefaultNamingStrategy(); + $this->instantiator = new Instantiator(); + $this->typedFieldMapper = $typedFieldMapper ?? new DefaultTypedFieldMapper(); + } + + /** + * Gets the ReflectionProperties of the mapped class. + * + * @return ReflectionProperty[]|null[] An array of ReflectionProperty instances. + * @phpstan-return array + */ + public function getReflectionProperties(): array + { + return $this->reflFields; + } + + /** + * Gets a ReflectionProperty for a specific field of the mapped class. + */ + public function getReflectionProperty(string $name): ReflectionProperty|null + { + return $this->reflFields[$name]; + } + + /** + * Gets the ReflectionProperty for the single identifier field. + * + * @throws BadMethodCallException If the class has a composite identifier. + */ + public function getSingleIdReflectionProperty(): ReflectionProperty|null + { + if ($this->isIdentifierComposite) { + throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.'); + } + + return $this->reflFields[$this->identifier[0]]; + } + + /** + * Extracts the identifier values of an entity of this class. + * + * For composite identifiers, the identifier values are returned as an array + * with the same order as the field order in {@link identifier}. + * + * @return array + */ + public function getIdentifierValues(object $entity): array { - parent::__construct($entityName, $namingStrategy, $typedFieldMapper); + if ($this->isIdentifierComposite) { + $id = []; + + foreach ($this->identifier as $idField) { + $value = $this->reflFields[$idField]->getValue($entity); + + if ($value !== null) { + $id[$idField] = $value; + } + } + + return $id; + } + + $id = $this->identifier[0]; + $value = $this->reflFields[$id]->getValue($entity); + + if ($value === null) { + return []; + } + + return [$id => $value]; + } + + /** + * Populates the entity identifier of an entity. + * + * @phpstan-param array $id + * + * @todo Rename to assignIdentifier() + */ + public function setIdentifierValues(object $entity, array $id): void + { + foreach ($id as $idField => $idValue) { + $this->reflFields[$idField]->setValue($entity, $idValue); + } + } + + /** + * Sets the specified field to the specified value on the given entity. + */ + public function setFieldValue(object $entity, string $field, mixed $value): void + { + $this->reflFields[$field]->setValue($entity, $value); + } + + /** + * Gets the specified field's value off the given entity. + */ + public function getFieldValue(object $entity, string $field): mixed + { + return $this->reflFields[$field]->getValue($entity); + } + + /** + * Creates a string representation of this instance. + * + * @return string The string representation of this instance. + * + * @todo Construct meaningful string representation. + */ + public function __toString(): string + { + return self::class . '@' . spl_object_id($this); + } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * Parts that are also NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return string[] The names of all the fields that should be serialized. + */ + public function __sleep(): array + { + // This metadata is always serialized/cached. + $serialized = [ + 'associationMappings', + 'columnNames', //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName'] + 'fieldMappings', + 'fieldNames', + 'embeddedClasses', + 'identifier', + 'isIdentifierComposite', // TODO: REMOVE + 'name', + 'namespace', // TODO: REMOVE + 'table', + 'rootEntityName', + 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. + ]; + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy !== self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType !== self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorColumn'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->generatorType !== self::GENERATOR_TYPE_NONE) { + $serialized[] = 'generatorType'; + if ($this->generatorType === self::GENERATOR_TYPE_SEQUENCE) { + $serialized[] = 'sequenceGeneratorDefinition'; + } + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->isEmbeddedClass) { + $serialized[] = 'isEmbeddedClass'; + } + + if ($this->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->containsEnumIdentifier) { + $serialized[] = 'containsEnumIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->entityListeners) { + $serialized[] = 'entityListeners'; + } + + if ($this->isReadOnly) { + $serialized[] = 'isReadOnly'; + } + + if ($this->customGeneratorDefinition) { + $serialized[] = 'customGeneratorDefinition'; + } + + if ($this->cache) { + $serialized[] = 'cache'; + } + + if ($this->requiresFetchAfterChange) { + $serialized[] = 'requiresFetchAfterChange'; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + */ + public function newInstance(): object + { + return $this->instantiator->instantiate($this->name); + } + + /** + * Restores some state that can not be serialized/unserialized. + */ + public function wakeupReflection(ReflectionService $reflService): void + { + // Restore ReflectionClass and properties + $this->reflClass = $reflService->getClass($this->name); + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $parentReflFields = []; + + foreach ($this->embeddedClasses as $property => $embeddedClass) { + if (isset($embeddedClass->declaredField)) { + assert($embeddedClass->originalField !== null); + $childProperty = $this->getAccessibleProperty( + $reflService, + $this->embeddedClasses[$embeddedClass->declaredField]->class, + $embeddedClass->originalField, + ); + assert($childProperty !== null); + $parentReflFields[$property] = new ReflectionEmbeddedProperty( + $parentReflFields[$embeddedClass->declaredField], + $childProperty, + $this->embeddedClasses[$embeddedClass->declaredField]->class, + ); + + continue; + } + + $fieldRefl = $this->getAccessibleProperty( + $reflService, + $embeddedClass->declared ?? $this->name, + $property, + ); + + $parentReflFields[$property] = $fieldRefl; + $this->reflFields[$property] = $fieldRefl; + } + + foreach ($this->fieldMappings as $field => $mapping) { + if (isset($mapping->declaredField) && isset($parentReflFields[$mapping->declaredField])) { + assert($mapping->originalField !== null); + assert($mapping->originalClass !== null); + $childProperty = $this->getAccessibleProperty($reflService, $mapping->originalClass, $mapping->originalField); + assert($childProperty !== null); + + if (isset($mapping->enumType)) { + $childProperty = new EnumReflectionProperty( + $childProperty, + $mapping->enumType, + ); + } + + $this->reflFields[$field] = new ReflectionEmbeddedProperty( + $parentReflFields[$mapping->declaredField], + $childProperty, + $mapping->originalClass, + ); + continue; + } + + $this->reflFields[$field] = isset($mapping->declared) + ? $this->getAccessibleProperty($reflService, $mapping->declared, $field) + : $this->getAccessibleProperty($reflService, $this->name, $field); + + if (isset($mapping->enumType) && $this->reflFields[$field] !== null) { + $this->reflFields[$field] = new EnumReflectionProperty( + $this->reflFields[$field], + $mapping->enumType, + ); + } + } + + foreach ($this->associationMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping->declared) + ? $this->getAccessibleProperty($reflService, $mapping->declared, $field) + : $this->getAccessibleProperty($reflService, $this->name, $field); + } + } + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param ReflectionService $reflService The reflection service. + */ + public function initializeReflection(ReflectionService $reflService): void + { + $this->reflClass = $reflService->getClass($this->name); + $this->namespace = $reflService->getClassNamespace($this->name); + + if ($this->reflClass) { + $this->name = $this->rootEntityName = $this->reflClass->name; + } + + $this->table['name'] = $this->namingStrategy->classToTableName($this->name); + } + + /** + * Validates Identifier. + * + * @throws MappingException + */ + public function validateIdentifier(): void + { + if ($this->isMappedSuperclass || $this->isEmbeddedClass) { + return; + } + + // Verify & complete identifier mapping + if (! $this->identifier) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validates association targets actually exist. + * + * @throws MappingException + */ + public function validateAssociations(): void + { + foreach ($this->associationMappings as $mapping) { + if ( + ! class_exists($mapping->targetEntity) + && ! interface_exists($mapping->targetEntity) + && ! trait_exists($mapping->targetEntity) + ) { + throw MappingException::invalidTargetEntityClass($mapping->targetEntity, $this->name, $mapping->fieldName); + } + } + } + + /** + * Validates lifecycle callbacks. + * + * @throws MappingException + */ + public function validateLifecycleCallbacks(ReflectionService $reflService): void + { + foreach ($this->lifecycleCallbacks as $callbacks) { + foreach ($callbacks as $callbackFuncName) { + if (! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { + throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); + } + } + } + } + + /** @phpstan-param array{usage?: mixed, region?: mixed} $cache */ + public function enableCache(array $cache): void + { + if (! isset($cache['usage'])) { + $cache['usage'] = self::CACHE_USAGE_READ_ONLY; + } + + if (! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)); + } + + $this->cache = $cache; + } + + /** @phpstan-param array{usage?: int, region?: string} $cache */ + public function enableAssociationCache(string $fieldName, array $cache): void + { + $this->associationMappings[$fieldName]->cache = $this->getAssociationCacheDefaults($fieldName, $cache); + } + + /** + * @phpstan-param array{usage?: int, region?: string|null} $cache + * + * @return int[]|string[] + * @phpstan-return array{usage: int, region: string|null} + */ + public function getAssociationCacheDefaults(string $fieldName, array $cache): array + { + if (! isset($cache['usage'])) { + $cache['usage'] = $this->cache['usage'] ?? self::CACHE_USAGE_READ_ONLY; + } + + if (! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; + } + + return $cache; + } + + /** + * Sets the change tracking policy used by this class. + */ + public function setChangeTrackingPolicy(int $policy): void + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + */ + public function isChangeTrackingDeferredExplicit(): bool + { + return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + */ + public function isChangeTrackingDeferredImplicit(): bool + { + return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + */ + public function isIdentifier(string $fieldName): bool + { + if (! $this->identifier) { + return false; + } + + if (! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + + return in_array($fieldName, $this->identifier, true); + } + + public function isUniqueField(string $fieldName): bool + { + $mapping = $this->getFieldMapping($fieldName); + + return $mapping !== false && isset($mapping->unique) && $mapping->unique; + } + + public function isNullable(string $fieldName): bool + { + $mapping = $this->getFieldMapping($fieldName); + + return $mapping !== false && isset($mapping->nullable) && $mapping->nullable; + } + + /** + * Gets a column name for a field name. + * If the column name for the field cannot be found, the given field name + * is returned. + */ + public function getColumnName(string $fieldName): string + { + // @phpstan-ignore property.deprecated + return $this->columnNames[$fieldName] ?? $fieldName; + } + + /** + * Gets the mapping of a (regular) field that holds some data but not a + * reference to another object. + * + * @throws MappingException + */ + public function getFieldMapping(string $fieldName): FieldMapping + { + if (! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + + return $this->fieldMappings[$fieldName]; + } + + /** + * Gets the mapping of an association. + * + * @see ClassMetadata::$associationMappings + * + * @param string $fieldName The field name that represents the association in + * the object model. + * + * @throws MappingException + */ + public function getAssociationMapping(string $fieldName): AssociationMapping + { + if (! isset($this->associationMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + + return $this->associationMappings[$fieldName]; + } + + /** + * Gets all association mappings of the class. + * + * @phpstan-return array + */ + public function getAssociationMappings(): array + { + return $this->associationMappings; + } + + /** + * Gets the field name for a column name. + * If no field name can be found the column name is returned. + * + * @return string The column alias. + */ + public function getFieldName(string $columnName): string + { + return $this->fieldNames[$columnName] ?? $columnName; + } + + /** + * Checks whether given property has type + */ + private function isTypedProperty(string $name): bool + { + return isset($this->reflClass) + && $this->reflClass->hasProperty($name) + && $this->reflClass->getProperty($name)->hasType(); + } + + /** + * Validates & completes the given field mapping based on typed property. + * + * @param array{fieldName: string, type?: string} $mapping The field mapping to validate & complete. + * + * @return array{fieldName: string, enumType?: class-string, type?: string} The updated mapping. + */ + private function validateAndCompleteTypedFieldMapping(array $mapping): array + { + $field = $this->reflClass->getProperty($mapping['fieldName']); + + return $this->typedFieldMapper->validateAndComplete($mapping, $field); + } + + /** + * Validates & completes the basic mapping information based on typed property. + * + * @param array{type: self::ONE_TO_ONE|self::MANY_TO_ONE|self::ONE_TO_MANY|self::MANY_TO_MANY, fieldName: string, targetEntity?: class-string} $mapping The mapping. + * + * @return mixed[] The updated mapping. + */ + private function validateAndCompleteTypedAssociationMapping(array $mapping): array + { + $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); + + if ($type === null || ($mapping['type'] & self::TO_ONE) === 0) { + return $mapping; + } + + if (! isset($mapping['targetEntity']) && $type instanceof ReflectionNamedType) { + $mapping['targetEntity'] = $type->getName(); + } + + return $mapping; + } + + /** + * Validates & completes the given field mapping. + * + * @phpstan-param array{ + * fieldName?: string, + * columnName?: string, + * id?: bool, + * generated?: self::GENERATED_*, + * enumType?: class-string, + * } $mapping The field mapping to validate & complete. + * + * @return FieldMapping The updated mapping. + * + * @throws MappingException + */ + protected function validateAndCompleteFieldMapping(array $mapping): FieldMapping + { + // Check mandatory fields + if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) { + throw MappingException::missingFieldName($this->name); + } + + if ($this->isTypedProperty($mapping['fieldName'])) { + $mapping = $this->validateAndCompleteTypedFieldMapping($mapping); + } + + if (! isset($mapping['type'])) { + // Default to string + $mapping['type'] = 'string'; + } + + // Complete fieldName and columnName mapping + if (! isset($mapping['columnName'])) { + $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); + } + + $mapping = FieldMapping::fromMappingArray($mapping); + + if ($mapping->columnName[0] === '`') { + $mapping->columnName = trim($mapping->columnName, '`'); + $mapping->quoted = true; + } + + // @phpstan-ignore property.deprecated + $this->columnNames[$mapping->fieldName] = $mapping->columnName; + + if (isset($this->fieldNames[$mapping->columnName]) || ($this->discriminatorColumn && $this->discriminatorColumn->name === $mapping->columnName)) { + throw MappingException::duplicateColumnName($this->name, $mapping->columnName); + } + + $this->fieldNames[$mapping->columnName] = $mapping->fieldName; + + // Complete id mapping + if (isset($mapping->id) && $mapping->id === true) { + if ($this->versionField === $mapping->fieldName) { + throw MappingException::cannotVersionIdField($this->name, $mapping->fieldName); + } + + if (! in_array($mapping->fieldName, $this->identifier, true)) { + $this->identifier[] = $mapping->fieldName; + } + + // Check for composite key + if (! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + if (isset($mapping->generated)) { + if (! in_array($mapping->generated, [self::GENERATED_NEVER, self::GENERATED_INSERT, self::GENERATED_ALWAYS])) { + throw MappingException::invalidGeneratedMode($mapping->generated); + } + + if ($mapping->generated === self::GENERATED_NEVER) { + unset($mapping->generated); + } + } + + if (isset($mapping->enumType)) { + if (! enum_exists($mapping->enumType)) { + throw MappingException::nonEnumTypeMapped($this->name, $mapping->fieldName, $mapping->enumType); + } + + if (! empty($mapping->id)) { + $this->containsEnumIdentifier = true; + } + + if ( + defined('Doctrine\DBAL\Types\Types::ENUM') + && $mapping->type === Types::ENUM + && ! isset($mapping->options['values']) + ) { + $mapping->options['values'] = array_column($mapping->enumType::cases(), 'value'); + } + } + + return $mapping; + } + + /** + * Validates & completes the basic mapping information that is common to all + * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). + * + * @phpstan-param array $mapping The mapping. + * + * @return ConcreteAssociationMapping + * + * @throws MappingException If something is wrong with the mapping. + */ + protected function _validateAndCompleteAssociationMapping(array $mapping): AssociationMapping + { + if (array_key_exists('mappedBy', $mapping) && $mapping['mappedBy'] === null) { + unset($mapping['mappedBy']); + } + + if (array_key_exists('inversedBy', $mapping) && $mapping['inversedBy'] === null) { + unset($mapping['inversedBy']); + } + + if (array_key_exists('joinColumns', $mapping) && in_array($mapping['joinColumns'], [null, []], true)) { + unset($mapping['joinColumns']); + } + + $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy + + if (empty($mapping['indexBy'])) { + unset($mapping['indexBy']); + } + + // If targetEntity is unqualified, assume it is in the same namespace as + // the sourceEntity. + $mapping['sourceEntity'] = $this->name; + + if ($this->isTypedProperty($mapping['fieldName'])) { + $mapping = $this->validateAndCompleteTypedAssociationMapping($mapping); + } + + if (isset($mapping['targetEntity'])) { + $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']); + $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); + } + + if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { + throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); + } + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { + throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); + } + + if (! in_array($mapping['fieldName'], $this->identifier, true)) { + if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) { + throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( + $mapping['targetEntity'], + $this->name, + $mapping['fieldName'], + ); + } + + assert(is_string($mapping['fieldName'])); + $this->identifier[] = $mapping['fieldName']; + $this->containsForeignIdentifier = true; + } + + // Check for composite key + if (! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + + if ($this->cache && ! isset($mapping['cache'])) { + throw NonCacheableEntityAssociation::fromEntityAndField( + $this->name, + $mapping['fieldName'], + ); + } + } + + // Mandatory attributes for both sides + // Mandatory: fieldName, targetEntity + if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) { + throw MappingException::missingFieldName($this->name); + } + + if (! isset($mapping['targetEntity'])) { + throw MappingException::missingTargetEntity($mapping['fieldName']); + } + + // Mandatory and optional attributes for either side + if (! isset($mapping['mappedBy'])) { + if (isset($mapping['joinTable'])) { + if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } + } + } else { + $mapping['isOwningSide'] = false; + } + + if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { + throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']); + } + + // Fetch mode. Default fetch mode to LAZY, if not set. + if (! isset($mapping['fetch'])) { + $mapping['fetch'] = self::FETCH_LAZY; + } + + // Cascades + $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : []; + + $allCascades = ['remove', 'persist', 'refresh', 'detach']; + if (in_array('all', $cascades, true)) { + $cascades = $allCascades; + } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) { + throw MappingException::invalidCascadeOption( + array_diff($cascades, $allCascades), + $this->name, + $mapping['fieldName'], + ); + } + + $mapping['cascade'] = $cascades; + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + if (isset($mapping['joinColumns']) && $mapping['joinColumns'] && ! $mapping['isOwningSide']) { + throw MappingException::joinColumnNotAllowedOnOneToOneInverseSide( + $this->name, + $mapping['fieldName'], + ); + } + + return $mapping['isOwningSide'] ? + OneToOneOwningSideMapping::fromMappingArrayAndName( + $mapping, + $this->namingStrategy, + $this->name, + $this->table ?? null, + $this->isInheritanceTypeSingleTable(), + ) : + OneToOneInverseSideMapping::fromMappingArrayAndName($mapping, $this->name); + + case self::MANY_TO_ONE: + return ManyToOneAssociationMapping::fromMappingArrayAndName( + $mapping, + $this->namingStrategy, + $this->name, + $this->table ?? null, + $this->isInheritanceTypeSingleTable(), + ); + + case self::ONE_TO_MANY: + return OneToManyAssociationMapping::fromMappingArrayAndName($mapping, $this->name); + + case self::MANY_TO_MANY: + if (isset($mapping['joinColumns'])) { + unset($mapping['joinColumns']); + } + + return $mapping['isOwningSide'] ? + ManyToManyOwningSideMapping::fromMappingArrayAndNamingStrategy($mapping, $this->namingStrategy) : + ManyToManyInverseSideMapping::fromMappingArray($mapping); + + default: + throw MappingException::invalidAssociationType( + $this->name, + $mapping['fieldName'], + $mapping['type'], + ); + } + } + + /** + * {@inheritDoc} + */ + public function getIdentifierFieldNames(): array + { + return $this->identifier; + } + + /** + * Gets the name of the single id field. Note that this only works on + * entity classes that have a single-field pk. + * + * @throws MappingException If the class doesn't have an identifier or it has a composite primary key. + */ + public function getSingleIdentifierFieldName(): string + { + if ($this->isIdentifierComposite) { + throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); + } + + if (! isset($this->identifier[0])) { + throw MappingException::noIdDefined($this->name); + } + + return $this->identifier[0]; + } + + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @throws MappingException If the class doesn't have an identifier or it has a composite primary key. + */ + public function getSingleIdentifierColumnName(): string + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + + /** + * INTERNAL: + * Sets the mapped identifier/primary key fields of this class. + * Mainly used by the ClassMetadataFactory to assign inherited identifiers. + * + * @phpstan-param list $identifier + */ + public function setIdentifier(array $identifier): void + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier(): array + { + return $this->identifier; + } + + public function hasField(string $fieldName): bool + { + return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]); + } + + /** + * Gets an array containing all the column names. + * + * @phpstan-param list|null $fieldNames + * + * @return mixed[] + * @phpstan-return list + */ + public function getColumnNames(array|null $fieldNames = null): array + { + if ($fieldNames === null) { + return array_keys($this->fieldNames); + } + + return array_values(array_map($this->getColumnName(...), $fieldNames)); + } + + /** + * Returns an array with all the identifier column names. + * + * @phpstan-return list + */ + public function getIdentifierColumnNames(): array + { + $columnNames = []; + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $columnNames[] = $this->fieldMappings[$idProperty]->columnName; + + continue; + } + + // Association defined as Id field + assert($this->associationMappings[$idProperty]->isToOneOwningSide()); + $joinColumns = $this->associationMappings[$idProperty]->joinColumns; + $assocColumnNames = array_map(static fn (JoinColumnMapping $joinColumn): string => $joinColumn->name, $joinColumns); + + $columnNames = array_merge($columnNames, $assocColumnNames); + } + + return $columnNames; + } + + /** + * Sets the type of Id generator to use for the mapped class. + * + * @phpstan-param self::GENERATOR_TYPE_* $generatorType + */ + public function setIdGeneratorType(int $generatorType): void + { + $this->generatorType = $generatorType; + } + + /** + * Checks whether the mapped class uses an Id generator. + */ + public function usesIdGenerator(): bool + { + return $this->generatorType !== self::GENERATOR_TYPE_NONE; + } + + public function isInheritanceTypeNone(): bool + { + return $this->inheritanceType === self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the JOINED inheritance mapping strategy. + * + * @return bool TRUE if the class participates in a JOINED inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeJoined(): bool + { + return $this->inheritanceType === self::INHERITANCE_TYPE_JOINED; + } + + /** + * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. + * + * @return bool TRUE if the class participates in a SINGLE_TABLE inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleTable(): bool + { + return $this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + /** + * Checks whether the class uses an identity column for the Id generation. + */ + public function isIdGeneratorIdentity(): bool + { + return $this->generatorType === self::GENERATOR_TYPE_IDENTITY; + } + + /** + * Checks whether the class uses a sequence for id generation. + * + * @phpstan-assert-if-true !null $this->sequenceGeneratorDefinition + */ + public function isIdGeneratorSequence(): bool + { + return $this->generatorType === self::GENERATOR_TYPE_SEQUENCE; + } + + /** + * Checks whether the class has a natural identifier/pk (which means it does + * not use any Id generator. + */ + public function isIdentifierNatural(): bool + { + return $this->generatorType === self::GENERATOR_TYPE_NONE; + } + + /** + * Gets the type of a field. + * + * @todo 3.0 Remove this. PersisterHelper should fix it somehow + */ + public function getTypeOfField(string $fieldName): string|null + { + return isset($this->fieldMappings[$fieldName]) + ? $this->fieldMappings[$fieldName]->type + : null; + } + + /** + * Gets the name of the primary table. + */ + public function getTableName(): string + { + return $this->table['name']; + } + + /** + * Gets primary table's schema name. + */ + public function getSchemaName(): string|null + { + return $this->table['schema'] ?? null; + } + + /** + * Gets the table name to use for temporary identifier tables of this class. + */ + public function getTemporaryIdTableName(): string + { + // replace dots with underscores because PostgreSQL creates temporary tables in a special schema + return str_replace('.', '_', $this->getTableName() . '_id_tmp'); + } + + /** + * Sets the mapped subclasses of this class. + * + * @phpstan-param list $subclasses The names of all mapped subclasses. + */ + public function setSubclasses(array $subclasses): void + { + foreach ($subclasses as $subclass) { + $this->subClasses[] = $this->fullyQualifiedClassName($subclass); + } + } + + /** + * Sets the parent class names. Only entity classes may be given. + * + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + * + * @phpstan-param list $classNames + */ + public function setParentClasses(array $classNames): void + { + $this->parentClasses = $classNames; + + if (count($classNames) > 0) { + $this->rootEntityName = array_pop($classNames); + } + } + + /** + * Sets the inheritance type used by the class and its subclasses. + * + * @phpstan-param self::INHERITANCE_TYPE_* $type + * + * @throws MappingException + */ + public function setInheritanceType(int $type): void + { + if (! $this->isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($this->name, $type); + } + + $this->inheritanceType = $type; + } + + /** + * Sets the association to override association mapping of property for an entity relationship. + * + * @phpstan-param array{joinColumns?: array, inversedBy?: ?string, joinTable?: array, fetch?: ?string, cascade?: string[]} $overrideMapping + * + * @throws MappingException + */ + public function setAssociationOverride(string $fieldName, array $overrideMapping): void + { + if (! isset($this->associationMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->associationMappings[$fieldName]->toArray(); + + if (isset($mapping['inherited'])) { + throw MappingException::illegalOverrideOfInheritedProperty( + $this->name, + $fieldName, + $mapping['inherited'], + ); + } + + if (isset($overrideMapping['joinColumns'])) { + $mapping['joinColumns'] = $overrideMapping['joinColumns']; + } + + if (isset($overrideMapping['inversedBy'])) { + $mapping['inversedBy'] = $overrideMapping['inversedBy']; + } + + if (isset($overrideMapping['joinTable'])) { + $mapping['joinTable'] = $overrideMapping['joinTable']; + } + + if (isset($overrideMapping['fetch'])) { + $mapping['fetch'] = $overrideMapping['fetch']; + } + + if (isset($overrideMapping['cascade'])) { + $mapping['cascade'] = $overrideMapping['cascade']; + } + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + case self::MANY_TO_ONE: + $mapping['joinColumnFieldNames'] = []; + $mapping['sourceToTargetKeyColumns'] = []; + break; + case self::MANY_TO_MANY: + $mapping['relationToSourceKeyColumns'] = []; + $mapping['relationToTargetKeyColumns'] = []; + break; + } + + $this->associationMappings[$fieldName] = $this->_validateAndCompleteAssociationMapping($mapping); + } + + /** + * Sets the override for a mapped field. + * + * @phpstan-param array $overrideMapping + * + * @throws MappingException + */ + public function setAttributeOverride(string $fieldName, array $overrideMapping): void + { + if (! isset($this->fieldMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->fieldMappings[$fieldName]; + + if (isset($mapping->inherited)) { + throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName, $mapping->inherited); + } + + if (isset($mapping->id)) { + $overrideMapping['id'] = $mapping->id; + } + + if (isset($mapping->declared)) { + $overrideMapping['declared'] = $mapping->declared; + } + + if (! isset($overrideMapping['type'])) { + $overrideMapping['type'] = $mapping->type; + } + + if (! isset($overrideMapping['fieldName'])) { + $overrideMapping['fieldName'] = $mapping->fieldName; + } + + if ($overrideMapping['type'] !== $mapping->type) { + throw MappingException::invalidOverrideFieldType($this->name, $fieldName); + } + + unset($this->fieldMappings[$fieldName]); + unset($this->fieldNames[$mapping->columnName]); + // @phpstan-ignore property.deprecated + unset($this->columnNames[$mapping->fieldName]); + + $overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping); + + $this->fieldMappings[$fieldName] = $overrideMapping; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + */ + public function isInheritedField(string $fieldName): bool + { + return isset($this->fieldMappings[$fieldName]->inherited); + } + + /** + * Checks if this entity is the root in any entity-inheritance-hierarchy. + */ + public function isRootEntity(): bool + { + return $this->name === $this->rootEntityName; + } + + /** + * Checks whether a mapped association field is inherited from a superclass. + */ + public function isInheritedAssociation(string $fieldName): bool + { + return isset($this->associationMappings[$fieldName]->inherited); + } + + public function isInheritedEmbeddedClass(string $fieldName): bool + { + return isset($this->embeddedClasses[$fieldName]->inherited); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @deprecated Use {@link setPrimaryTable}. + */ + public function setTableName(string $tableName): void + { + $this->table['name'] = $tableName; + } + + /** + * Sets the primary table definition. The provided array supports the + * following structure: + * + * name => (optional, defaults to class name) + * indexes => array of indexes (optional) + * uniqueConstraints => array of constraints (optional) + * + * If a key is omitted, the current value is kept. + * + * @phpstan-param array $table The table description. + */ + public function setPrimaryTable(array $table): void + { + if (isset($table['name'])) { + // Split schema and table name from a table name like "myschema.mytable" + if (str_contains($table['name'], '.')) { + [$this->table['schema'], $table['name']] = explode('.', $table['name'], 2); + } + + if ($table['name'][0] === '`') { + $table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } + + $this->table['name'] = $table['name']; + } + + if (isset($table['quoted'])) { + $this->table['quoted'] = $table['quoted']; + } + + if (isset($table['schema'])) { + $this->table['schema'] = $table['schema']; + } + + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; + } + + if (isset($table['options'])) { + $this->table['options'] = $table['options']; + } + } + + /** + * Checks whether the given type identifies an inheritance type. + */ + private function isInheritanceType(int $type): bool + { + return $type === self::INHERITANCE_TYPE_NONE || + $type === self::INHERITANCE_TYPE_SINGLE_TABLE || + $type === self::INHERITANCE_TYPE_JOINED; + } + + /** + * Adds a mapped field to the class. + * + * @phpstan-param array $mapping The field mapping. + * + * @throws MappingException + */ + public function mapField(array $mapping): void + { + $mapping = $this->validateAndCompleteFieldMapping($mapping); + $this->assertFieldNotMapped($mapping->fieldName); + + if (isset($mapping->generated)) { + $this->requiresFetchAfterChange = true; + } + + $this->fieldMappings[$mapping->fieldName] = $mapping; + } + + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param ConcreteAssociationMapping $mapping + * + * @throws MappingException + */ + public function addInheritedAssociationMapping(AssociationMapping $mapping/*, $owningClassName = null*/): void + { + if (isset($this->associationMappings[$mapping->fieldName])) { + throw MappingException::duplicateAssociationMapping($this->name, $mapping->fieldName); + } + + $this->associationMappings[$mapping->fieldName] = $mapping; + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + */ + public function addInheritedFieldMapping(FieldMapping $fieldMapping): void + { + $this->fieldMappings[$fieldMapping->fieldName] = $fieldMapping; + // @phpstan-ignore property.deprecated + $this->columnNames[$fieldMapping->fieldName] = $fieldMapping->columnName; + $this->fieldNames[$fieldMapping->columnName] = $fieldMapping->fieldName; + + if (isset($fieldMapping->generated)) { + $this->requiresFetchAfterChange = true; + } + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + */ + public function mapOneToOne(array $mapping): void + { + $mapping['type'] = self::ONE_TO_ONE; + + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a one-to-many mapping. + * + * @phpstan-param array $mapping The mapping. + */ + public function mapOneToMany(array $mapping): void + { + $mapping['type'] = self::ONE_TO_MANY; + + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-one mapping. + * + * @phpstan-param array $mapping The mapping. + */ + public function mapManyToOne(array $mapping): void + { + $mapping['type'] = self::MANY_TO_ONE; + + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-many mapping. + * + * @phpstan-param array $mapping The mapping. + */ + public function mapManyToMany(array $mapping): void + { + $mapping['type'] = self::MANY_TO_MANY; + + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + $this->_storeAssociationMapping($mapping); + } + + /** + * Stores the association mapping. + * + * @param ConcreteAssociationMapping $assocMapping + * + * @throws MappingException + */ + protected function _storeAssociationMapping(AssociationMapping $assocMapping): void + { + $sourceFieldName = $assocMapping->fieldName; + + $this->assertFieldNotMapped($sourceFieldName); + + $this->associationMappings[$sourceFieldName] = $assocMapping; + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string|null $repositoryClassName The class name of the custom mapper. + * @phpstan-param class-string|null $repositoryClassName + */ + public function setCustomRepositoryClass(string|null $repositoryClassName): void + { + if ($repositoryClassName === null) { + $this->customRepositoryClassName = null; + + return; + } + + $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker + * + * @param string $lifecycleEvent The lifecycle event. + */ + public function invokeLifecycleCallbacks(string $lifecycleEvent, object $entity): void + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + $entity->$callback(); + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + */ + public function hasLifecycleCallbacks(string $lifecycleEvent): bool + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @return string[] + * @phpstan-return list + */ + public function getLifecycleCallbacks(string $event): array + { + return $this->lifecycleCallbacks[$event] ?? []; + } + + /** + * Adds a lifecycle callback for entities of this class. + */ + public function addLifecycleCallback(string $callback, string $event): void + { + if ($this->isEmbeddedClass) { + throw MappingException::illegalLifecycleCallbackOnEmbeddedClass($callback, $this->name); + } + + if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) { + return; + } + + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @phpstan-param array> $callbacks + */ + public function setLifecycleCallbacks(array $callbacks): void + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Adds a entity listener for entities of this class. + * + * @param string $eventName The entity lifecycle event. + * @param string $class The listener class. + * @param string $method The listener callback method. + * + * @throws MappingException + */ + public function addEntityListener(string $eventName, string $class, string $method): void + { + $class = $this->fullyQualifiedClassName($class); + + $listener = [ + 'class' => $class, + 'method' => $method, + ]; + + if (! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $this->name); + } + + if (! method_exists($class, $method)) { + throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); + } + + if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) { + throw MappingException::duplicateEntityListener($class, $method, $this->name); + } + + $this->entityListeners[$eventName][] = $listener; + } + + /** + * Sets the discriminator column definition. + * + * @see getDiscriminatorColumn() + * + * @param DiscriminatorColumnMapping|mixed[]|null $columnDef + * @phpstan-param DiscriminatorColumnMapping|array{ + * name: string|null, + * fieldName?: string|null, + * type?: string|null, + * length?: int|null, + * columnDefinition?: string|null, + * enumType?: class-string|null, + * options?: array|null + * }|null $columnDef + * + * @throws MappingException + */ + public function setDiscriminatorColumn(DiscriminatorColumnMapping|array|null $columnDef): void + { + if ($columnDef instanceof DiscriminatorColumnMapping) { + $this->discriminatorColumn = $columnDef; + + return; + } + + if ($columnDef !== null) { + if (! isset($columnDef['name'])) { + throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); + } + + if (isset($this->fieldNames[$columnDef['name']])) { + throw MappingException::duplicateColumnName($this->name, $columnDef['name']); + } + + $columnDef['fieldName'] ??= $columnDef['name']; + $columnDef['type'] ??= 'string'; + $columnDef['options'] ??= []; + + if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], true)) { + throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); + } + + $this->discriminatorColumn = DiscriminatorColumnMapping::fromMappingArray($columnDef); + } + } + + final public function getDiscriminatorColumn(): DiscriminatorColumnMapping + { + if ($this->discriminatorColumn === null) { + throw new LogicException('The discriminator column was not set.'); + } + + return $this->discriminatorColumn; + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + */ + public function setDiscriminatorMap(array $map): void + { + if (count(array_flip($map)) !== count($map)) { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/issues/3519', + <<<'DEPRECATION' + Mapping a class to multiple discriminator values is deprecated, + and the discriminator mapping of %s contains duplicate values + for the following discriminator values: %s. + DEPRECATION, + $this->name, + implode(', ', array_keys(array_filter(array_count_values($map), static function (int $value): bool { + return $value > 1; + }))), + ); + } + + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Adds one entry of the discriminator map with a new class and corresponding name. + * + * @throws MappingException + */ + public function addDiscriminatorMapClass(int|string $name, string $className): void + { + $className = $this->fullyQualifiedClassName($className); + $className = ltrim($className, '\\'); + + $this->discriminatorMap[$name] = $className; + + if ($this->name === $className) { + $this->discriminatorValue = $name; + + return; + } + + if (! (class_exists($className) || interface_exists($className))) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + + $this->addSubClass($className); + } + + /** @param array $classes */ + public function addSubClasses(array $classes): void + { + foreach ($classes as $className) { + $this->addSubClass($className); + } + } + + public function addSubClass(string $className): void + { + // By ignoring classes that are not subclasses of the current class, we simplify inheriting + // the subclass list from a parent class at the beginning of \Doctrine\ORM\Mapping\ClassMetadataFactory::doLoadMetadata. + + if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses, true)) { + $this->subClasses[] = $className; + } + } + + public function hasAssociation(string $fieldName): bool + { + return isset($this->associationMappings[$fieldName]); + } + + public function isSingleValuedAssociation(string $fieldName): bool + { + return isset($this->associationMappings[$fieldName]) + && ($this->associationMappings[$fieldName]->isToOne()); + } + + public function isCollectionValuedAssociation(string $fieldName): bool + { + return isset($this->associationMappings[$fieldName]) + && ! $this->associationMappings[$fieldName]->isToOne(); + } + + /** + * Is this an association that only has a single join column? + */ + public function isAssociationWithSingleJoinColumn(string $fieldName): bool + { + return isset($this->associationMappings[$fieldName]) + && isset($this->associationMappings[$fieldName]->joinColumns[0]) + && ! isset($this->associationMappings[$fieldName]->joinColumns[1]); + } + + /** + * Returns the single association join column (if any). + * + * @throws MappingException + */ + public function getSingleAssociationJoinColumnName(string $fieldName): string + { + if (! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + + $assoc = $this->associationMappings[$fieldName]; + + assert($assoc->isToOneOwningSide()); + + return $assoc->joinColumns[0]->name; + } + + /** + * Returns the single association referenced join column name (if any). + * + * @throws MappingException + */ + public function getSingleAssociationReferencedJoinColumnName(string $fieldName): string + { + if (! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + + $assoc = $this->associationMappings[$fieldName]; + + assert($assoc->isToOneOwningSide()); + + return $assoc->joinColumns[0]->referencedColumnName; + } + + /** + * Used to retrieve a fieldname for either field or association from a given column. + * + * This method is used in foreign-key as primary-key contexts. + * + * @throws MappingException + */ + public function getFieldForColumn(string $columnName): string + { + if (isset($this->fieldNames[$columnName])) { + return $this->fieldNames[$columnName]; + } + + foreach ($this->associationMappings as $assocName => $mapping) { + if ( + $this->isAssociationWithSingleJoinColumn($assocName) && + assert($this->associationMappings[$assocName]->isToOneOwningSide()) && + $this->associationMappings[$assocName]->joinColumns[0]->name === $columnName + ) { + return $assocName; + } + } + + throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); + } + + /** + * Sets the ID generator used to generate IDs for instances of this class. + */ + public function setIdGenerator(AbstractIdGenerator $generator): void + { + $this->idGenerator = $generator; + } + + /** + * Sets definition. + * + * @phpstan-param array $definition + */ + public function setCustomGeneratorDefinition(array $definition): void + { + $this->customGeneratorDefinition = $definition; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * 'quoted' => 1 + * ) + * + * + * @phpstan-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition + * + * @throws MappingException + */ + public function setSequenceGeneratorDefinition(array $definition): void + { + if (! isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') { + throw MappingException::missingSequenceName($this->name); + } + + if ($definition['sequenceName'][0] === '`') { + $definition['sequenceName'] = trim($definition['sequenceName'], '`'); + $definition['quoted'] = true; + } + + if (! isset($definition['allocationSize']) || trim((string) $definition['allocationSize']) === '') { + $definition['allocationSize'] = '1'; + } + + if (! isset($definition['initialValue']) || trim((string) $definition['initialValue']) === '') { + $definition['initialValue'] = '1'; + } + + $definition['allocationSize'] = (string) $definition['allocationSize']; + $definition['initialValue'] = (string) $definition['initialValue']; + + $this->sequenceGeneratorDefinition = $definition; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @phpstan-param array $mapping The version field mapping array. + * + * @throws MappingException + */ + public function setVersionMapping(array &$mapping): void + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + $this->requiresFetchAfterChange = true; + + if (! isset($mapping['default'])) { + if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'], true)) { + $mapping['default'] = 1; + } elseif ($mapping['type'] === 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); + } + } + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + */ + public function setVersioned(bool $bool): void + { + $this->isVersioned = $bool; + + if ($bool) { + $this->requiresFetchAfterChange = true; + } + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + */ + public function setVersionField(string|null $versionField): void + { + $this->versionField = $versionField; + } + + /** + * Marks this class as read only, no change tracking is applied to it. + */ + public function markReadOnly(): void + { + $this->isReadOnly = true; + } + + /** + * {@inheritDoc} + */ + public function getFieldNames(): array + { + return array_keys($this->fieldMappings); + } + + /** + * {@inheritDoc} + */ + public function getAssociationNames(): array + { + return array_keys($this->associationMappings); + } + + /** + * {@inheritDoc} + * + * @phpstan-return class-string + * + * @throws InvalidArgumentException + */ + public function getAssociationTargetClass(string $assocName): string + { + return $this->associationMappings[$assocName]->targetEntity + ?? throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association."); + } + + public function getName(): string + { + return $this->name; + } + + public function isAssociationInverseSide(string $assocName): bool + { + return isset($this->associationMappings[$assocName]) + && ! $this->associationMappings[$assocName]->isOwningSide(); + } + + public function getAssociationMappedByTargetField(string $assocName): string + { + $assoc = $this->getAssociationMapping($assocName); + + if (! $assoc instanceof InverseSideMapping) { + throw new LogicException(sprintf( + <<<'EXCEPTION' + Context: Calling %s() with "%s", which is the owning side of an association. + Problem: The owning side of an association has no "mappedBy" field. + Solution: Call %s::isAssociationInverseSide() to check first. + EXCEPTION, + __METHOD__, + $assocName, + self::class, + )); + } + + return $assoc->mappedBy; + } + + /** + * @param C $className + * + * @return string|null null if and only if the input value is null + * @phpstan-return (C is class-string ? class-string : (C is string ? string : null)) + * + * @template C of string|null + */ + public function fullyQualifiedClassName(string|null $className): string|null + { + if ($className === null) { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11294', + 'Passing null to %s is deprecated and will not be supported in Doctrine ORM 4.0', + __METHOD__, + ); + + return null; + } + + if (! str_contains($className, '\\') && $this->namespace) { + return $this->namespace . '\\' . $className; + } + + return $className; + } + + public function getMetadataValue(string $name): mixed + { + return $this->$name ?? null; + } + + /** + * Map Embedded Class + * + * @phpstan-param array{ + * fieldName: string, + * class?: class-string, + * declaredField?: string, + * columnPrefix?: string|false|null, + * originalField?: string + * } $mapping + * + * @throws MappingException + */ + public function mapEmbedded(array $mapping): void + { + $this->assertFieldNotMapped($mapping['fieldName']); + + if (! isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) { + $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); + if ($type instanceof ReflectionNamedType) { + $mapping['class'] = $type->getName(); + } + } + + if (! (isset($mapping['class']) && $mapping['class'])) { + throw MappingException::missingEmbeddedClass($mapping['fieldName']); + } + + $this->embeddedClasses[$mapping['fieldName']] = EmbeddedClassMapping::fromMappingArray([ + 'class' => $this->fullyQualifiedClassName($mapping['class']), + 'columnPrefix' => $mapping['columnPrefix'] ?? null, + 'declaredField' => $mapping['declaredField'] ?? null, + 'originalField' => $mapping['originalField'] ?? null, + ]); + } + + /** + * Inline the embeddable class + */ + public function inlineEmbeddable(string $property, ClassMetadata $embeddable): void + { + foreach ($embeddable->fieldMappings as $originalFieldMapping) { + $fieldMapping = (array) $originalFieldMapping; + $fieldMapping['originalClass'] ??= $embeddable->name; + $fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) + ? $property . '.' . $fieldMapping['declaredField'] + : $property; + $fieldMapping['originalField'] ??= $fieldMapping['fieldName']; + $fieldMapping['fieldName'] = $property . '.' . $fieldMapping['fieldName']; + + if (! empty($this->embeddedClasses[$property]->columnPrefix)) { + $fieldMapping['columnName'] = $this->embeddedClasses[$property]->columnPrefix . $fieldMapping['columnName']; + } elseif ($this->embeddedClasses[$property]->columnPrefix !== false) { + assert($this->reflClass !== null); + assert($embeddable->reflClass !== null); + $fieldMapping['columnName'] = $this->namingStrategy + ->embeddedFieldToColumnName( + $property, + $fieldMapping['columnName'], + $this->reflClass->name, + $embeddable->reflClass->name, + ); + } + + $this->mapField($fieldMapping); + } + } + + /** @throws MappingException */ + private function assertFieldNotMapped(string $fieldName): void + { + if ( + isset($this->fieldMappings[$fieldName]) || + isset($this->associationMappings[$fieldName]) || + isset($this->embeddedClasses[$fieldName]) + ) { + throw MappingException::duplicateFieldMapping($this->name, $fieldName); + } + } + + /** + * Gets the sequence name based on class metadata. + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequenceName(AbstractPlatform $platform): string + { + $sequencePrefix = $this->getSequencePrefix($platform); + $columnName = $this->getSingleIdentifierColumnName(); + + return $sequencePrefix . '_' . $columnName . '_seq'; + } + + /** + * Gets the sequence name prefix based on class metadata. + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequencePrefix(AbstractPlatform $platform): string + { + $tableName = $this->getTableName(); + $sequencePrefix = $tableName; + + // Prepend the schema name to the table name if there is one + $schemaName = $this->getSchemaName(); + if ($schemaName) { + $sequencePrefix = $schemaName . '.' . $tableName; + } + + return $sequencePrefix; + } + + /** @phpstan-param class-string $class */ + private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ReflectionProperty|null + { + $reflectionProperty = $reflService->getAccessibleProperty($class, $field); + if ($reflectionProperty?->isReadOnly()) { + $declaringClass = $reflectionProperty->class; + if ($declaringClass !== $class) { + $reflectionProperty = $reflService->getAccessibleProperty($declaringClass, $field); + } + + if ($reflectionProperty !== null) { + $reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty); + } + } + + if (PHP_VERSION_ID >= 80400 && $reflectionProperty !== null && count($reflectionProperty->getHooks()) > 0) { + throw new LogicException('Doctrine ORM does not support property hooks in this version. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.'); + } + + return $reflectionProperty; } } diff --git a/src/Mapping/ClassMetadataFactory.php b/src/Mapping/ClassMetadataFactory.php index 5965b09b459..b29f20c67b1 100644 --- a/src/Mapping/ClassMetadataFactory.php +++ b/src/Mapping/ClassMetadataFactory.php @@ -7,9 +7,6 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Platforms; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\MySQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; @@ -20,8 +17,6 @@ use Doctrine\ORM\Id\BigIntegerIdentityGenerator; use Doctrine\ORM\Id\IdentityGenerator; use Doctrine\ORM\Id\SequenceGenerator; -use Doctrine\ORM\Id\UuidGenerator; -use Doctrine\ORM\Mapping\Exception\CannotGenerateIds; use Doctrine\ORM\Mapping\Exception\InvalidCustomGenerator; use Doctrine\ORM\Mapping\Exception\UnknownGeneratorType; use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; @@ -37,10 +32,10 @@ use function count; use function end; use function explode; -use function get_class; use function in_array; use function is_a; use function is_subclass_of; +use function method_exists; use function str_contains; use function strlen; use function strtolower; @@ -52,35 +47,22 @@ * to a relational database. * * @extends AbstractClassMetadataFactory - * @phpstan-import-type AssociationMapping from ClassMetadata - * @phpstan-import-type EmbeddedClassMapping from ClassMetadata - * @phpstan-import-type FieldMapping from ClassMetadata */ class ClassMetadataFactory extends AbstractClassMetadataFactory { - /** @var EntityManagerInterface|null */ - private $em; - - /** @var AbstractPlatform|null */ - private $targetPlatform; - - /** @var MappingDriver */ - private $driver; - - /** @var EventManager */ - private $evm; + private EntityManagerInterface|null $em = null; + private AbstractPlatform|null $targetPlatform = null; + private MappingDriver|null $driver = null; + private EventManager|null $evm = null; /** @var mixed[] */ - private $embeddablesActiveNesting = []; + private array $embeddablesActiveNesting = []; private const NON_IDENTITY_DEFAULT_STRATEGY = [ - 'Doctrine\DBAL\Platforms\PostgreSqlPlatform' => ClassMetadata::GENERATOR_TYPE_SEQUENCE, Platforms\OraclePlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE, - Platforms\PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE, ]; - /** @return void */ - public function setEntityManager(EntityManagerInterface $em) + public function setEntityManager(EntityManagerInterface $em): void { parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver()); @@ -88,19 +70,47 @@ public function setEntityManager(EntityManagerInterface $em) } /** - * {@inheritDoc} + * @param A $maybeOwningSide + * + * @return (A is ManyToManyAssociationMapping ? ManyToManyOwningSideMapping : ( + * A is OneToOneAssociationMapping ? OneToOneOwningSideMapping : ( + * A is OneToManyAssociationMapping ? ManyToOneAssociationMapping : ( + * A is ManyToOneAssociationMapping ? ManyToOneAssociationMapping : + * ManyToManyOwningSideMapping|OneToOneOwningSideMapping|ManyToOneAssociationMapping + * )))) + * + * @template A of AssociationMapping */ - protected function initialize() + final public function getOwningSide(AssociationMapping $maybeOwningSide): OwningSideMapping + { + if ($maybeOwningSide instanceof OwningSideMapping) { + assert($maybeOwningSide instanceof ManyToManyOwningSideMapping || + $maybeOwningSide instanceof OneToOneOwningSideMapping || + $maybeOwningSide instanceof ManyToOneAssociationMapping); + + return $maybeOwningSide; + } + + assert($maybeOwningSide instanceof InverseSideMapping); + + $owningSide = $this->getMetadataFor($maybeOwningSide->targetEntity) + ->associationMappings[$maybeOwningSide->mappedBy]; + + assert($owningSide instanceof ManyToManyOwningSideMapping || + $owningSide instanceof OneToOneOwningSideMapping || + $owningSide instanceof ManyToOneAssociationMapping); + + return $owningSide; + } + + protected function initialize(): void { $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); $this->evm = $this->em->getEventManager(); $this->initialized = true; } - /** - * {@inheritDoc} - */ - protected function onNotFoundMetadata($className) + protected function onNotFoundMetadata(string $className): ClassMetadata|null { if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { return null; @@ -118,11 +128,15 @@ protected function onNotFoundMetadata($className) /** * {@inheritDoc} */ - protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) - { + protected function doLoadMetadata( + ClassMetadataInterface $class, + ClassMetadataInterface|null $parent, + bool $rootEntityFound, + array $nonSuperclassParents, + ): void { if ($parent) { $class->setInheritanceType($parent->inheritanceType); - $class->setDiscriminatorColumn($parent->discriminatorColumn); + $class->setDiscriminatorColumn($parent->discriminatorColumn === null ? null : clone $parent->discriminatorColumn); $class->setIdGeneratorType($parent->generatorType); $this->addInheritedFields($class, $parent); $this->addInheritedRelations($class, $parent); @@ -162,27 +176,21 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS if (! $class->isMappedSuperclass) { if ($rootEntityFound && $class->isInheritanceTypeNone()) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10431', - "Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared. This is a misconfiguration and will be an error in Doctrine ORM 3.0.", - $class->name, - end($nonSuperclassParents) - ); + throw MappingException::missingInheritanceTypeDeclaration(end($nonSuperclassParents), $class->name); } foreach ($class->embeddedClasses as $property => $embeddableClass) { - if (isset($embeddableClass['inherited'])) { + if (isset($embeddableClass->inherited)) { continue; } - if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) { + if (isset($this->embeddablesActiveNesting[$embeddableClass->class])) { throw MappingException::infiniteEmbeddableNesting($class->name, $property); } $this->embeddablesActiveNesting[$class->name] = true; - $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + $embeddableMetadata = $this->getMetadataFor($embeddableClass->class); if ($embeddableMetadata->isEmbeddedClass) { $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property); @@ -219,18 +227,6 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS $class->containsEnumIdentifier = true; } - if (! empty($parent->namedQueries)) { - $this->addInheritedNamedQueries($class, $parent); - } - - if (! empty($parent->namedNativeQueries)) { - $this->addInheritedNamedNativeQueries($class, $parent); - } - - if (! empty($parent->sqlResultSetMappings)) { - $this->addInheritedSqlResultSetMappings($class, $parent); - } - if (! empty($parent->entityListeners) && empty($class->entityListeners)) { $class->entityListeners = $parent->entityListeners; } @@ -252,29 +248,15 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS $this->findAbstractEntityClassesNotListedInDiscriminatorMap($class); - if ($class->changeTrackingPolicy === ClassMetadata::CHANGETRACKING_NOTIFY) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8383', - 'NOTIFY Change Tracking policy used in "%s" is deprecated, use deferred explicit instead.', - $class->name - ); - } - $this->validateRuntimeMetadata($class, $parent); } /** * Validate runtime metadata is correctly defined. * - * @param ClassMetadata $class - * @param ClassMetadataInterface|null $parent - * - * @return void - * * @throws MappingException */ - protected function validateRuntimeMetadata($class, $parent) + protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadataInterface|null $parent): void { if (! $class->reflClass) { // only validate if there is a reflection class instance @@ -302,7 +284,7 @@ protected function validateRuntimeMetadata($class, $parent) } } } else { - assert($parent instanceof ClassMetadataInfo); // https://github.com/doctrine/orm/issues/8746 + assert($parent instanceof ClassMetadata); // https://github.com/doctrine/orm/issues/8746 if ( ! $class->reflClass->isAbstract() && ! in_array($class->name, $class->discriminatorMap, true) @@ -316,15 +298,12 @@ protected function validateRuntimeMetadata($class, $parent) } } - /** - * {@inheritDoc} - */ - protected function newClassMetadataInstance($className) + protected function newClassMetadataInstance(string $className): ClassMetadata { return new ClassMetadata( $className, $this->em->getConfiguration()->getNamingStrategy(), - $this->em->getConfiguration()->getTypedFieldMapper() + $this->em->getConfiguration()->getTypedFieldMapper(), ); } @@ -412,7 +391,7 @@ private function peekIfIsMappedSuperclass(string $className): bool $class = $this->newClassMetadataInstance($className); $this->initializeReflection($class, $reflService); - $this->driver->loadMetadataForClass($className, $class); + $this->getDriver()->loadMetadataForClass($className, $class); return $class->isMappedSuperclass; } @@ -436,17 +415,17 @@ private function getShortName(string $className): string /** * Puts the `inherited` and `declared` values into mapping information for fields, associations * and embedded classes. - * - * @param AssociationMapping|EmbeddedClassMapping|FieldMapping $mapping */ - private function addMappingInheritanceInformation(array &$mapping, ClassMetadata $parentClass): void - { - if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { - $mapping['inherited'] = $parentClass->name; + private function addMappingInheritanceInformation( + AssociationMapping|EmbeddedClassMapping|FieldMapping $mapping, + ClassMetadata $parentClass, + ): void { + if (! isset($mapping->inherited) && ! $parentClass->isMappedSuperclass) { + $mapping->inherited = $parentClass->name; } - if (! isset($mapping['declared'])) { - $mapping['declared'] = $parentClass->name; + if (! isset($mapping->declared)) { + $mapping->declared = $parentClass->name; } } @@ -456,8 +435,9 @@ private function addMappingInheritanceInformation(array &$mapping, ClassMetadata private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass): void { foreach ($parentClass->fieldMappings as $mapping) { - $this->addMappingInheritanceInformation($mapping, $parentClass); - $subClass->addInheritedFieldMapping($mapping); + $subClassMapping = clone $mapping; + $this->addMappingInheritanceInformation($subClassMapping, $parentClass); + $subClass->addInheritedFieldMapping($subClassMapping); } foreach ($parentClass->reflFields as $name => $field) { @@ -473,26 +453,28 @@ private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $pare private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass): void { foreach ($parentClass->associationMappings as $field => $mapping) { - $this->addMappingInheritanceInformation($mapping, $parentClass); + $subClassMapping = clone $mapping; + $this->addMappingInheritanceInformation($subClassMapping, $parentClass); // When the class inheriting the relation ($subClass) is the first entity class since the // relation has been defined in a mapped superclass (or in a chain // of mapped superclasses) above, then declare this current entity class as the source of // the relationship. // According to the definitions given in https://github.com/doctrine/orm/pull/10396/, // this is the case <=> ! isset($mapping['inherited']). - if (! isset($mapping['inherited'])) { - $mapping['sourceEntity'] = $subClass->name; + if (! isset($subClassMapping->inherited)) { + $subClassMapping->sourceEntity = $subClass->name; } - $subClass->addInheritedAssociationMapping($mapping); + $subClass->addInheritedAssociationMapping($subClassMapping); } } private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass): void { foreach ($parentClass->embeddedClasses as $field => $embeddedClass) { - $this->addMappingInheritanceInformation($embeddedClass, $parentClass); - $subClass->embeddedClasses[$field] = $embeddedClass; + $subClassMapping = clone $embeddedClass; + $this->addMappingInheritanceInformation($subClassMapping, $parentClass); + $subClass->embeddedClasses[$field] = $subClassMapping; } } @@ -506,25 +488,25 @@ private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetad private function addNestedEmbeddedClasses( ClassMetadata $subClass, ClassMetadata $parentClass, - string $prefix + string $prefix, ): void { foreach ($subClass->embeddedClasses as $property => $embeddableClass) { - if (isset($embeddableClass['inherited'])) { + if (isset($embeddableClass->inherited)) { continue; } - $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + $embeddableMetadata = $this->getMetadataFor($embeddableClass->class); $parentClass->mapEmbedded( [ 'fieldName' => $prefix . '.' . $property, 'class' => $embeddableMetadata->name, - 'columnPrefix' => $embeddableClass['columnPrefix'], - 'declaredField' => $embeddableClass['declaredField'] - ? $prefix . '.' . $embeddableClass['declaredField'] + 'columnPrefix' => $embeddableClass->columnPrefix, + 'declaredField' => $embeddableClass->declaredField + ? $prefix . '.' . $embeddableClass->declaredField : $prefix, - 'originalField' => $embeddableClass['originalField'] ?: $property, - ] + 'originalField' => $embeddableClass->originalField ?: $property, + ], ); } } @@ -551,80 +533,13 @@ private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $par } } - /** - * Adds inherited named queries to the subclass mapping. - */ - private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass): void - { - foreach ($parentClass->namedQueries as $name => $query) { - if (! isset($subClass->namedQueries[$name])) { - // @phpstan-ignore method.deprecated - $subClass->addNamedQuery( - [ - 'name' => $query['name'], - 'query' => $query['query'], - ] - ); - } - } - } - - /** - * Adds inherited named native queries to the subclass mapping. - */ - private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass): void - { - foreach ($parentClass->namedNativeQueries as $name => $query) { - if (! isset($subClass->namedNativeQueries[$name])) { - // @phpstan-ignore method.deprecated - $subClass->addNamedNativeQuery( - [ - 'name' => $query['name'], - 'query' => $query['query'], - 'isSelfClass' => $query['isSelfClass'], - 'resultSetMapping' => $query['resultSetMapping'], - 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass'], - ] - ); - } - } - } - - /** - * Adds inherited sql result set mappings to the subclass mapping. - */ - private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass): void - { - foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { - if (! isset($subClass->sqlResultSetMappings[$name])) { - $entities = []; - foreach ($mapping['entities'] as $entity) { - $entities[] = [ - 'fields' => $entity['fields'], - 'isSelfClass' => $entity['isSelfClass'], - 'discriminatorColumn' => $entity['discriminatorColumn'], - 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'], - ]; - } - - $subClass->addSqlResultSetMapping( - [ - 'name' => $mapping['name'], - 'columns' => $mapping['columns'], - 'entities' => $entities, - ] - ); - } - } - } - /** * Completes the ID generator mapping. If "auto" is specified we choose the generator * most appropriate for the targeted database platform. * * @throws ORMException */ - private function completeIdGeneratorMapping(ClassMetadataInfo $class): void + private function completeIdGeneratorMapping(ClassMetadata $class): void { $idGenType = $class->generatorType; if ($idGenType === ClassMetadata::GENERATOR_TYPE_AUTO) { @@ -638,44 +553,9 @@ private function completeIdGeneratorMapping(ClassMetadataInfo $class): void $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; $platform = $this->getTargetPlatform(); - // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. - // @phpstan-ignore method.deprecated - if (! $platform instanceof MySQLPlatform && ! $platform instanceof SqlitePlatform && ! $platform instanceof SQLServerPlatform && $platform->usesSequenceEmulatedIdentityColumns()) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8850', - <<<'DEPRECATION' -Context: Loading metadata for class %s -Problem: Using identity columns emulated with a sequence is deprecated and will not be possible in Doctrine ORM 3.0. -Solution: Use the SEQUENCE generator strategy instead. -DEPRECATION - , - $class->name, - get_class($this->getTargetPlatform()) - ); - $columnName = $class->getSingleIdentifierColumnName(); - $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); - $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform()); - // @phpstan-ignore method.deprecated - $sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName); - $definition = [ - 'sequenceName' => $this->truncateSequenceName($sequenceName), - ]; - - if ($quoted) { - $definition['quoted'] = true; - } - - $sequenceName = $this - ->em - ->getConfiguration() - ->getQuoteStrategy() - ->getSequenceName($definition, $class, $this->getTargetPlatform()); - } - - $generator = $fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint' - ? new BigIntegerIdentityGenerator($sequenceName) - : new IdentityGenerator($sequenceName); + $generator = $fieldName && $class->fieldMappings[$fieldName]->type === 'bigint' + ? new BigIntegerIdentityGenerator() + : new IdentityGenerator(); $class->setIdGenerator($generator); @@ -688,7 +568,7 @@ private function completeIdGeneratorMapping(ClassMetadataInfo $class): void if (! $definition) { $fieldName = $class->getSingleIdentifierFieldName(); $sequenceName = $class->getSequenceName($this->getTargetPlatform()); - $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + $quoted = isset($class->fieldMappings[$fieldName]->quoted) || isset($class->table['quoted']); $definition = [ 'sequenceName' => $this->truncateSequenceName($sequenceName), @@ -705,7 +585,7 @@ private function completeIdGeneratorMapping(ClassMetadataInfo $class): void $sequenceGenerator = new SequenceGenerator( $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()), - (int) $definition['allocationSize'] + (int) $definition['allocationSize'], ); $class->setIdGenerator($sequenceGenerator); break; @@ -714,18 +594,6 @@ private function completeIdGeneratorMapping(ClassMetadataInfo $class): void $class->setIdGenerator(new AssignedGenerator()); break; - // @phpstan-ignore classConstant.deprecated - case ClassMetadata::GENERATOR_TYPE_UUID: - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/7312', - 'Mapping for %s: the "UUID" id generator strategy is deprecated with no replacement', - $class->name - ); - // @phpstan-ignore new.deprecated - $class->setIdGenerator(new UuidGenerator()); - break; - case ClassMetadata::GENERATOR_TYPE_CUSTOM: $definition = $class->customGeneratorDefinition; if ($definition === null) { @@ -754,25 +622,36 @@ private function determineIdGeneratorStrategy(AbstractPlatform $platform): int } } - foreach (self::NON_IDENTITY_DEFAULT_STRATEGY as $platformFamily => $strategy) { + $nonIdentityDefaultStrategy = self::NON_IDENTITY_DEFAULT_STRATEGY; + + // DBAL 3 + if (method_exists($platform, 'getIdentitySequenceName')) { + $nonIdentityDefaultStrategy[Platforms\PostgreSQLPlatform::class] = ClassMetadata::GENERATOR_TYPE_SEQUENCE; + } + + foreach ($nonIdentityDefaultStrategy as $platformFamily => $strategy) { if (is_a($platform, $platformFamily)) { - if ($platform instanceof Platforms\PostgreSQLPlatform || is_a($platform, 'Doctrine\DBAL\Platforms\PostgreSqlPlatform')) { + if ($platform instanceof Platforms\PostgreSQLPlatform) { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/orm/issues/8893', <<<'DEPRECATION' -Relying on non-optimal defaults for ID generation is deprecated, and IDENTITY -results in SERIAL, which is not recommended. -Instead, configure identifier generation strategies explicitly through -configuration. -We currently recommend "SEQUENCE" for "%s", so you should use -$configuration->setIdentityGenerationPreferences([ - "%s" => ClassMetadata::GENERATOR_TYPE_SEQUENCE, -]); -DEPRECATION - , + Relying on non-optimal defaults for ID generation is deprecated, and IDENTITY + results in SERIAL, which is not recommended. + Instead, configure identifier generation strategies explicitly through + configuration. + We currently recommend "SEQUENCE" for "%s", when using DBAL 3, + and "IDENTITY" when using DBAL 4, + so you should probably use the following configuration before upgrading to DBAL 4, + and remove it after deploying that upgrade: + + $configuration->setIdentityGenerationPreferences([ + "%s" => ClassMetadata::GENERATOR_TYPE_SEQUENCE, + ]); + + DEPRECATION, + $platformFamily, $platformFamily, - $platformFamily ); } @@ -780,21 +659,13 @@ private function determineIdGeneratorStrategy(AbstractPlatform $platform): int } } - if ($platform->supportsIdentityColumns()) { - return ClassMetadata::GENERATOR_TYPE_IDENTITY; - } - - if ($platform->supportsSequences()) { - return ClassMetadata::GENERATOR_TYPE_SEQUENCE; - } - - throw CannotGenerateIds::withPlatform($platform); + return ClassMetadata::GENERATOR_TYPE_IDENTITY; } private function truncateSequenceName(string $schemaElementName): string { $platform = $this->getTargetPlatform(); - if (! $platform instanceof Platforms\OraclePlatform && ! $platform instanceof Platforms\SQLAnywherePlatform) { + if (! $platform instanceof Platforms\OraclePlatform) { return $schemaElementName; } @@ -810,7 +681,7 @@ private function truncateSequenceName(string $schemaElementName): string /** * Inherits the ID generator mapping from a parent class. */ - private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent): void + private function inheritIdGeneratorMapping(ClassMetadata $class, ClassMetadata $parent): void { if ($parent->isIdGeneratorSequence()) { $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); @@ -820,52 +691,29 @@ private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetada $class->setIdGeneratorType($parent->generatorType); } - if ($parent->idGenerator) { + if ($parent->idGenerator ?? null) { $class->setIdGenerator($parent->idGenerator); } } - /** - * {@inheritDoc} - */ - protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) + protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService): void { $class->wakeupReflection($reflService); } - /** - * {@inheritDoc} - */ - protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) + protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService): void { $class->initializeReflection($reflService); } - /** - * @deprecated This method will be removed in ORM 3.0. - * - * @return class-string - */ - protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) + protected function getDriver(): MappingDriver { - /** @var class-string $classString */ - $classString = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + assert($this->driver !== null); - return $classString; - } - - /** - * {@inheritDoc} - */ - protected function getDriver() - { return $this->driver; } - /** - * {@inheritDoc} - */ - protected function isEntity(ClassMetadataInterface $class) + protected function isEntity(ClassMetadataInterface $class): bool { return ! $class->isMappedSuperclass; } diff --git a/src/Mapping/ClassMetadataInfo.php b/src/Mapping/ClassMetadataInfo.php deleted file mode 100644 index 335d9e9ca2e..00000000000 --- a/src/Mapping/ClassMetadataInfo.php +++ /dev/null @@ -1,3899 +0,0 @@ -ClassMetadata instance holds all the object-relational mapping metadata - * of an entity and its associations. - * - * Once populated, ClassMetadata instances are usually cached in a serialized form. - * - * IMPORTANT NOTE: - * - * The fields of this class are only public for 2 reasons: - * 1) To allow fast READ access. - * 2) To drastically reduce the size of a serialized instance (private/protected members - * get the whole class name, namespace inclusive, prepended to every property in - * the serialized representation). - * - * @template-covariant T of object - * @template-implements ClassMetadata - * @phpstan-import-type AssociationMapping from \Doctrine\ORM\Mapping\ClassMetadata - * @phpstan-import-type FieldMapping from \Doctrine\ORM\Mapping\ClassMetadata - * @phpstan-import-type EmbeddedClassMapping from \Doctrine\ORM\Mapping\ClassMetadata - * @phpstan-import-type JoinColumnData from \Doctrine\ORM\Mapping\ClassMetadata - * @phpstan-import-type DiscriminatorColumnMapping from \Doctrine\ORM\Mapping\ClassMetadata - */ -class ClassMetadataInfo implements ClassMetadata -{ - /* The inheritance mapping types */ - /** - * NONE means the class does not participate in an inheritance hierarchy - * and therefore does not need an inheritance mapping type. - */ - public const INHERITANCE_TYPE_NONE = 1; - - /** - * JOINED means the class will be persisted according to the rules of - * Class Table Inheritance. - */ - public const INHERITANCE_TYPE_JOINED = 2; - - /** - * SINGLE_TABLE means the class will be persisted according to the rules of - * Single Table Inheritance. - */ - public const INHERITANCE_TYPE_SINGLE_TABLE = 3; - - /** - * TABLE_PER_CLASS means the class will be persisted according to the rules - * of Concrete Table Inheritance. - * - * @deprecated - */ - public const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; - - /* The Id generator types. */ - /** - * AUTO means the generator type will depend on what the used platform prefers. - * Offers full portability. - */ - public const GENERATOR_TYPE_AUTO = 1; - - /** - * SEQUENCE means a separate sequence object will be used. Platforms that do - * not have native sequence support may emulate it. Full portability is currently - * not guaranteed. - */ - public const GENERATOR_TYPE_SEQUENCE = 2; - - /** - * TABLE means a separate table is used for id generation. - * Offers full portability (in that it results in an exception being thrown - * no matter the platform). - * - * @deprecated no replacement planned - */ - public const GENERATOR_TYPE_TABLE = 3; - - /** - * IDENTITY means an identity column is used for id generation. The database - * will fill in the id column on insertion. Platforms that do not support - * native identity columns may emulate them. Full portability is currently - * not guaranteed. - */ - public const GENERATOR_TYPE_IDENTITY = 4; - - /** - * NONE means the class does not have a generated id. That means the class - * must have a natural, manually assigned id. - */ - public const GENERATOR_TYPE_NONE = 5; - - /** - * UUID means that a UUID/GUID expression is used for id generation. Full - * portability is currently not guaranteed. - * - * @deprecated use an application-side generator instead - */ - public const GENERATOR_TYPE_UUID = 6; - - /** - * CUSTOM means that customer will use own ID generator that supposedly work - */ - public const GENERATOR_TYPE_CUSTOM = 7; - - /** - * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time - * by doing a property-by-property comparison with the original data. This will - * be done for all entities that are in MANAGED state at commit-time. - * - * This is the default change tracking policy. - */ - public const CHANGETRACKING_DEFERRED_IMPLICIT = 1; - - /** - * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time - * by doing a property-by-property comparison with the original data. This will - * be done only for entities that were explicitly saved (through persist() or a cascade). - */ - public const CHANGETRACKING_DEFERRED_EXPLICIT = 2; - - /** - * NOTIFY means that Doctrine relies on the entities sending out notifications - * when their properties change. Such entity classes must implement - * the NotifyPropertyChanged interface. - */ - public const CHANGETRACKING_NOTIFY = 3; - - /** - * Specifies that an association is to be fetched when it is first accessed. - */ - public const FETCH_LAZY = 2; - - /** - * Specifies that an association is to be fetched when the owner of the - * association is fetched. - */ - public const FETCH_EAGER = 3; - - /** - * Specifies that an association is to be fetched lazy (on first access) and that - * commands such as Collection#count, Collection#slice are issued directly against - * the database if the collection is not yet initialized. - */ - public const FETCH_EXTRA_LAZY = 4; - - /** - * Identifies a one-to-one association. - */ - public const ONE_TO_ONE = 1; - - /** - * Identifies a many-to-one association. - */ - public const MANY_TO_ONE = 2; - - /** - * Identifies a one-to-many association. - */ - public const ONE_TO_MANY = 4; - - /** - * Identifies a many-to-many association. - */ - public const MANY_TO_MANY = 8; - - /** - * Combined bitmask for to-one (single-valued) associations. - */ - public const TO_ONE = 3; - - /** - * Combined bitmask for to-many (collection-valued) associations. - */ - public const TO_MANY = 12; - - /** - * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks, - */ - public const CACHE_USAGE_READ_ONLY = 1; - - /** - * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes. - */ - public const CACHE_USAGE_NONSTRICT_READ_WRITE = 2; - - /** - * Read Write Attempts to lock the entity before update/delete. - */ - public const CACHE_USAGE_READ_WRITE = 3; - - /** - * The value of this column is never generated by the database. - */ - public const GENERATED_NEVER = 0; - - /** - * The value of this column is generated by the database on INSERT, but not on UPDATE. - */ - public const GENERATED_INSERT = 1; - - /** - * The value of this column is generated by the database on both INSERT and UDPATE statements. - */ - public const GENERATED_ALWAYS = 2; - - /** - * READ-ONLY: The name of the entity class. - * - * @var class-string - */ - public $name; - - /** - * READ-ONLY: The namespace the entity class is contained in. - * - * @var string - * @todo Not really needed. Usage could be localized. - */ - public $namespace; - - /** - * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance - * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same - * as {@link $name}. - * - * @var class-string - */ - public $rootEntityName; - - /** - * READ-ONLY: The definition of custom generator. Only used for CUSTOM - * generator type - * - * The definition has the following structure: - * - * array( - * 'class' => 'ClassName', - * ) - * - * - * @todo Merge with tableGeneratorDefinition into generic generatorDefinition - * @var array|null - */ - public $customGeneratorDefinition; - - /** - * The name of the custom repository class used for the entity class. - * (Optional). - * - * @var class-string|null - */ - public $customRepositoryClassName; - - /** - * READ-ONLY: Whether this class describes the mapping of a mapped superclass. - * - * @var bool - */ - public $isMappedSuperclass = false; - - /** - * READ-ONLY: Whether this class describes the mapping of an embeddable class. - * - * @var bool - */ - public $isEmbeddedClass = false; - - /** - * READ-ONLY: The names of the parent entity classes (ancestors), starting with the - * nearest one and ending with the root entity class. - * - * @var list - */ - public $parentClasses = []; - - /** - * READ-ONLY: For classes in inheritance mapping hierarchies, this field contains the names of all - * entity subclasses of this class. These may also be abstract classes. - * - * This list is used, for example, to enumerate all necessary tables in JTI when querying for root - * or subclass entities, or to gather all fields comprised in an entity inheritance tree. - * - * For classes that do not use STI/JTI, this list is empty. - * - * Implementation note: - * - * In PHP, there is no general way to discover all subclasses of a given class at runtime. For that - * reason, the list of classes given in the discriminator map at the root entity is considered - * authoritative. The discriminator map must contain all concrete classes that can - * appear in the particular inheritance hierarchy tree. Since there can be no instances of abstract - * entity classes, users are not required to list such classes with a discriminator value. - * - * The possibly remaining "gaps" for abstract entity classes are filled after the class metadata for the - * root entity has been loaded. - * - * For subclasses of such root entities, the list can be reused/passed downwards, it only needs to - * be filtered accordingly (only keep remaining subclasses) - * - * @var list - */ - public $subClasses = []; - - /** - * READ-ONLY: The names of all embedded classes based on properties. - * - * The value (definition) array may contain, among others, the following values: - * - * - 'inherited' (string, optional) - * This is set when this embedded-class field is inherited by this class from another (inheritance) parent - * entity class. The value is the FQCN of the topmost entity class that contains - * mapping information for this field. (If there are transient classes in the - * class hierarchy, these are ignored, so the class property may in fact come - * from a class further up in the PHP class hierarchy.) - * Fields initially declared in mapped superclasses are - * not considered 'inherited' in the nearest entity subclasses. - * - * - 'declared' (string, optional) - * This is set when the embedded-class field does not appear for the first time in this class, but is originally - * declared in another parent entity or mapped superclass. The value is the FQCN - * of the topmost non-transient class that contains mapping information for this field. - * - * @phpstan-var array - */ - public $embeddedClasses = []; - - /** - * READ-ONLY: The named queries allowed to be called directly from Repository. - * - * @phpstan-var array> - */ - public $namedQueries = []; - - /** - * READ-ONLY: The named native queries allowed to be called directly from Repository. - * - * A native SQL named query definition has the following structure: - *
-     * array(
-     *     'name'               => ,
-     *     'query'              => ,
-     *     'resultClass'        => ,
-     *     'resultSetMapping'   => 
-     * )
-     * 
- * - * @phpstan-var array> - */ - public $namedNativeQueries = []; - - /** - * READ-ONLY: The mappings of the results of native SQL queries. - * - * A native result mapping definition has the following structure: - *
-     * array(
-     *     'name'               => ,
-     *     'entities'           => array(),
-     *     'columns'            => array()
-     * )
-     * 
- * - * @phpstan-var array - */ - public $sqlResultSetMappings = []; - - /** - * READ-ONLY: The field names of all fields that are part of the identifier/primary key - * of the mapped entity class. - * - * @phpstan-var list - */ - public $identifier = []; - - /** - * READ-ONLY: The inheritance mapping type used by the class. - * - * @var int - * @phpstan-var self::INHERITANCE_TYPE_* - */ - public $inheritanceType = self::INHERITANCE_TYPE_NONE; - - /** - * READ-ONLY: The Id generator type used by the class. - * - * @var int - * @phpstan-var self::GENERATOR_TYPE_* - */ - public $generatorType = self::GENERATOR_TYPE_NONE; - - /** - * READ-ONLY: The field mappings of the class. - * Keys are field names and values are mapping definitions. - * - * The mapping definition array has the following values: - * - * - fieldName (string) - * The name of the field in the Entity. - * - * - type (string) - * The type name of the mapped field. Can be one of Doctrine's mapping types - * or a custom mapping type. - * - * - columnName (string, optional) - * The column name. Optional. Defaults to the field name. - * - * - length (integer, optional) - * The database length of the column. Optional. Default value taken from - * the type. - * - * - id (boolean, optional) - * Marks the field as the primary key of the entity. Multiple fields of an - * entity can have the id attribute, forming a composite key. - * - * - nullable (boolean, optional) - * Whether the column is nullable. Defaults to FALSE. - * - * - 'notInsertable' (boolean, optional) - * Whether the column is not insertable. Optional. Is only set if value is TRUE. - * - * - 'notUpdatable' (boolean, optional) - * Whether the column is updatable. Optional. Is only set if value is TRUE. - * - * - columnDefinition (string, optional, schema-only) - * The SQL fragment that is used when generating the DDL for the column. - * - * - precision (integer, optional, schema-only) - * The precision of a decimal column. Only valid if the column type is decimal. - * - * - scale (integer, optional, schema-only) - * The scale of a decimal column. Only valid if the column type is decimal. - * - * - 'unique' (boolean, optional, schema-only) - * Whether a unique constraint should be generated for the column. - * - * - 'inherited' (string, optional) - * This is set when the field is inherited by this class from another (inheritance) parent - * entity class. The value is the FQCN of the topmost entity class that contains - * mapping information for this field. (If there are transient classes in the - * class hierarchy, these are ignored, so the class property may in fact come - * from a class further up in the PHP class hierarchy.) - * Fields initially declared in mapped superclasses are - * not considered 'inherited' in the nearest entity subclasses. - * - * - 'declared' (string, optional) - * This is set when the field does not appear for the first time in this class, but is originally - * declared in another parent entity or mapped superclass. The value is the FQCN - * of the topmost non-transient class that contains mapping information for this field. - * - * @var mixed[] - * @phpstan-var array - */ - public $fieldMappings = []; - - /** - * READ-ONLY: An array of field names. Used to look up field names from column names. - * Keys are column names and values are field names. - * - * @phpstan-var array - */ - public $fieldNames = []; - - /** - * READ-ONLY: A map of field names to column names. Keys are field names and values column names. - * Used to look up column names from field names. - * This is the reverse lookup map of $_fieldNames. - * - * @deprecated 3.0 Remove this. - * - * @var mixed[] - */ - public $columnNames = []; - - /** - * READ-ONLY: The discriminator value of this class. - * - * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies - * where a discriminator column is used. - * - * @see discriminatorColumn - * - * @var mixed - */ - public $discriminatorValue; - - /** - * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. - * - * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies - * where a discriminator column is used. - * - * @see discriminatorColumn - * - * @var array - */ - public $discriminatorMap = []; - - /** - * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE - * inheritance mappings. - * - * @var array - * @phpstan-var DiscriminatorColumnMapping|null - */ - public $discriminatorColumn; - - /** - * READ-ONLY: The primary table definition. The definition is an array with the - * following entries: - * - * name => - * schema => - * indexes => array - * uniqueConstraints => array - * - * @var mixed[] - * @phpstan-var array{ - * name: string, - * schema?: string, - * indexes?: array, - * uniqueConstraints?: array, - * options?: array, - * quoted?: bool - * } - */ - public $table; - - /** - * READ-ONLY: The registered lifecycle callbacks for entities of this class. - * - * @phpstan-var array> - */ - public $lifecycleCallbacks = []; - - /** - * READ-ONLY: The registered entity listeners. - * - * @phpstan-var array> - */ - public $entityListeners = []; - - /** - * READ-ONLY: The association mappings of this class. - * - * The mapping definition array supports the following keys: - * - * - fieldName (string) - * The name of the field in the entity the association is mapped to. - * - * - sourceEntity (string) - * The class name of the source entity. In the case of to-many associations initially - * present in mapped superclasses, the nearest entity subclasses will be - * considered the respective source entities. - * - * - targetEntity (string) - * The class name of the target entity. If it is fully-qualified it is used as is. - * If it is a simple, unqualified class name the namespace is assumed to be the same - * as the namespace of the source entity. - * - * - mappedBy (string, required for bidirectional associations) - * The name of the field that completes the bidirectional association on the owning side. - * This key must be specified on the inverse side of a bidirectional association. - * - * - inversedBy (string, required for bidirectional associations) - * The name of the field that completes the bidirectional association on the inverse side. - * This key must be specified on the owning side of a bidirectional association. - * - * - cascade (array, optional) - * The names of persistence operations to cascade on the association. The set of possible - * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others). - * - * - orderBy (array, one-to-many/many-to-many only) - * A map of field names (of the target entity) to sorting directions (ASC/DESC). - * Example: array('priority' => 'desc') - * - * - fetch (integer, optional) - * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. - * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY. - * - * - joinTable (array, optional, many-to-many only) - * Specification of the join table and its join columns (foreign keys). - * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped - * through a join table by simply mapping the association as many-to-many with a unique - * constraint on the join table. - * - * - indexBy (string, optional, to-many only) - * Specification of a field on target-entity that is used to index the collection by. - * This field HAS to be either the primary key or a unique column. Otherwise the collection - * does not contain all the entities that are actually related. - * - * - 'inherited' (string, optional) - * This is set when the association is inherited by this class from another (inheritance) parent - * entity class. The value is the FQCN of the topmost entity class that contains - * this association. (If there are transient classes in the - * class hierarchy, these are ignored, so the class property may in fact come - * from a class further up in the PHP class hierarchy.) - * To-many associations initially declared in mapped superclasses are - * not considered 'inherited' in the nearest entity subclasses. - * - * - 'declared' (string, optional) - * This is set when the association does not appear in the current class for the first time, but - * is initially declared in another parent entity or mapped superclass. The value is the FQCN - * of the topmost non-transient class that contains association information for this relationship. - * - * A join table definition has the following structure: - *
-     * array(
-     *     'name' => ,
-     *      'joinColumns' => array(),
-     *      'inverseJoinColumns' => array()
-     * )
-     * 
- * - * @phpstan-var array - */ - public $associationMappings = []; - - /** - * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite. - * - * @var bool - */ - public $isIdentifierComposite = false; - - /** - * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association. - * - * This flag is necessary because some code blocks require special treatment of this cases. - * - * @var bool - */ - public $containsForeignIdentifier = false; - - /** - * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one ENUM type. - * - * This flag is necessary because some code blocks require special treatment of this cases. - * - * @var bool - */ - public $containsEnumIdentifier = false; - - /** - * READ-ONLY: The ID generator used for generating IDs for this class. - * - * @var AbstractIdGenerator - * @todo Remove! - */ - public $idGenerator; - - /** - * READ-ONLY: The definition of the sequence generator of this class. Only used for the - * SEQUENCE generation strategy. - * - * The definition has the following structure: - * - * array( - * 'sequenceName' => 'name', - * 'allocationSize' => '20', - * 'initialValue' => '1' - * ) - * - * - * @var array|null - * @phpstan-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null - * @todo Merge with tableGeneratorDefinition into generic generatorDefinition - */ - public $sequenceGeneratorDefinition; - - /** - * READ-ONLY: The definition of the table generator of this class. Only used for the - * TABLE generation strategy. - * - * @deprecated - * - * @var array - */ - public $tableGeneratorDefinition; - - /** - * READ-ONLY: The policy used for change-tracking on entities of this class. - * - * @var int - */ - public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; - - /** - * READ-ONLY: A Flag indicating whether one or more columns of this class - * have to be reloaded after insert / update operations. - * - * @var bool - */ - public $requiresFetchAfterChange = false; - - /** - * READ-ONLY: A flag for whether or not instances of this class are to be versioned - * with optimistic locking. - * - * @var bool - */ - public $isVersioned = false; - - /** - * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). - * - * @var string|null - */ - public $versionField; - - /** @var mixed[]|null */ - public $cache; - - /** - * The ReflectionClass instance of the mapped class. - * - * @var ReflectionClass|null - */ - public $reflClass; - - /** - * Is this entity marked as "read-only"? - * - * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance - * optimization for entities that are immutable, either in your domain or through the relation database - * (coming from a view, or a history table for example). - * - * @var bool - */ - public $isReadOnly = false; - - /** - * NamingStrategy determining the default column and table names. - * - * @var NamingStrategy - */ - protected $namingStrategy; - - /** - * The ReflectionProperty instances of the mapped class. - * - * @var array - */ - public $reflFields = []; - - /** @var InstantiatorInterface|null */ - private $instantiator; - - /** @var TypedFieldMapper $typedFieldMapper */ - private $typedFieldMapper; - - /** - * Initializes a new ClassMetadata instance that will hold the object-relational mapping - * metadata of the class with the given name. - * - * @param class-string $entityName The name of the entity class the new instance is used for. - */ - public function __construct($entityName, ?NamingStrategy $namingStrategy = null, ?TypedFieldMapper $typedFieldMapper = null) - { - $this->name = $entityName; - $this->rootEntityName = $entityName; - $this->namingStrategy = $namingStrategy ?? new DefaultNamingStrategy(); - $this->instantiator = new Instantiator(); - $this->typedFieldMapper = $typedFieldMapper ?? new DefaultTypedFieldMapper(); - } - - /** - * Gets the ReflectionProperties of the mapped class. - * - * @return ReflectionProperty[]|null[] An array of ReflectionProperty instances. - * @phpstan-return array - */ - public function getReflectionProperties() - { - return $this->reflFields; - } - - /** - * Gets a ReflectionProperty for a specific field of the mapped class. - * - * @param string $name - * - * @return ReflectionProperty - */ - public function getReflectionProperty($name) - { - return $this->reflFields[$name]; - } - - /** - * Gets the ReflectionProperty for the single identifier field. - * - * @return ReflectionProperty - * - * @throws BadMethodCallException If the class has a composite identifier. - */ - public function getSingleIdReflectionProperty() - { - if ($this->isIdentifierComposite) { - throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.'); - } - - return $this->reflFields[$this->identifier[0]]; - } - - /** - * Extracts the identifier values of an entity of this class. - * - * For composite identifiers, the identifier values are returned as an array - * with the same order as the field order in {@link identifier}. - * - * @param object $entity - * - * @return array - */ - public function getIdentifierValues($entity) - { - if ($this->isIdentifierComposite) { - $id = []; - - foreach ($this->identifier as $idField) { - $value = $this->reflFields[$idField]->getValue($entity); - - if ($value !== null) { - $id[$idField] = $value; - } - } - - return $id; - } - - $id = $this->identifier[0]; - $value = $this->reflFields[$id]->getValue($entity); - - if ($value === null) { - return []; - } - - return [$id => $value]; - } - - /** - * Populates the entity identifier of an entity. - * - * @param object $entity - * @phpstan-param array $id - * - * @return void - * - * @todo Rename to assignIdentifier() - */ - public function setIdentifierValues($entity, array $id) - { - foreach ($id as $idField => $idValue) { - $this->reflFields[$idField]->setValue($entity, $idValue); - } - } - - /** - * Sets the specified field to the specified value on the given entity. - * - * @param object $entity - * @param string $field - * @param mixed $value - * - * @return void - */ - public function setFieldValue($entity, $field, $value) - { - $this->reflFields[$field]->setValue($entity, $value); - } - - /** - * Gets the specified field's value off the given entity. - * - * @param object $entity - * @param string $field - * - * @return mixed - */ - public function getFieldValue($entity, $field) - { - return $this->reflFields[$field]->getValue($entity); - } - - /** - * Creates a string representation of this instance. - * - * @return string The string representation of this instance. - * - * @todo Construct meaningful string representation. - */ - public function __toString() - { - return self::class . '@' . spl_object_id($this); - } - - /** - * Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * That means any metadata properties that are not set or empty or simply have - * their default value are NOT serialized. - * - * Parts that are also NOT serialized because they can not be properly unserialized: - * - reflClass (ReflectionClass) - * - reflFields (ReflectionProperty array) - * - * @return string[] The names of all the fields that should be serialized. - */ - public function __sleep() - { - // This metadata is always serialized/cached. - $serialized = [ - 'associationMappings', - 'columnNames', //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName'] - 'fieldMappings', - 'fieldNames', - 'embeddedClasses', - 'identifier', - 'isIdentifierComposite', // TODO: REMOVE - 'name', - 'namespace', // TODO: REMOVE - 'table', - 'rootEntityName', - 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. - ]; - - // The rest of the metadata is only serialized if necessary. - if ($this->changeTrackingPolicy !== self::CHANGETRACKING_DEFERRED_IMPLICIT) { - $serialized[] = 'changeTrackingPolicy'; - } - - if ($this->customRepositoryClassName) { - $serialized[] = 'customRepositoryClassName'; - } - - if ($this->inheritanceType !== self::INHERITANCE_TYPE_NONE) { - $serialized[] = 'inheritanceType'; - $serialized[] = 'discriminatorColumn'; - $serialized[] = 'discriminatorValue'; - $serialized[] = 'discriminatorMap'; - $serialized[] = 'parentClasses'; - $serialized[] = 'subClasses'; - } - - if ($this->generatorType !== self::GENERATOR_TYPE_NONE) { - $serialized[] = 'generatorType'; - if ($this->generatorType === self::GENERATOR_TYPE_SEQUENCE) { - $serialized[] = 'sequenceGeneratorDefinition'; - } - } - - if ($this->isMappedSuperclass) { - $serialized[] = 'isMappedSuperclass'; - } - - if ($this->isEmbeddedClass) { - $serialized[] = 'isEmbeddedClass'; - } - - if ($this->containsForeignIdentifier) { - $serialized[] = 'containsForeignIdentifier'; - } - - if ($this->containsEnumIdentifier) { - $serialized[] = 'containsEnumIdentifier'; - } - - if ($this->isVersioned) { - $serialized[] = 'isVersioned'; - $serialized[] = 'versionField'; - } - - if ($this->lifecycleCallbacks) { - $serialized[] = 'lifecycleCallbacks'; - } - - if ($this->entityListeners) { - $serialized[] = 'entityListeners'; - } - - if ($this->namedQueries) { - $serialized[] = 'namedQueries'; - } - - if ($this->namedNativeQueries) { - $serialized[] = 'namedNativeQueries'; - } - - if ($this->sqlResultSetMappings) { - $serialized[] = 'sqlResultSetMappings'; - } - - if ($this->isReadOnly) { - $serialized[] = 'isReadOnly'; - } - - if ($this->customGeneratorDefinition) { - $serialized[] = 'customGeneratorDefinition'; - } - - if ($this->cache) { - $serialized[] = 'cache'; - } - - if ($this->requiresFetchAfterChange) { - $serialized[] = 'requiresFetchAfterChange'; - } - - return $serialized; - } - - /** - * Creates a new instance of the mapped class, without invoking the constructor. - * - * @return object - */ - public function newInstance() - { - return $this->instantiator->instantiate($this->name); - } - - /** - * Restores some state that can not be serialized/unserialized. - * - * @param ReflectionService $reflService - * - * @return void - */ - public function wakeupReflection($reflService) - { - // Restore ReflectionClass and properties - $this->reflClass = $reflService->getClass($this->name); - $this->instantiator = $this->instantiator ?: new Instantiator(); - - $parentReflFields = []; - - foreach ($this->embeddedClasses as $property => $embeddedClass) { - if (isset($embeddedClass['declaredField'])) { - assert($embeddedClass['originalField'] !== null); - $childProperty = $this->getAccessibleProperty( - $reflService, - $this->embeddedClasses[$embeddedClass['declaredField']]['class'], - $embeddedClass['originalField'] - ); - assert($childProperty !== null); - $parentReflFields[$property] = new ReflectionEmbeddedProperty( - $parentReflFields[$embeddedClass['declaredField']], - $childProperty, - $this->embeddedClasses[$embeddedClass['declaredField']]['class'] - ); - - continue; - } - - $fieldRefl = $this->getAccessibleProperty( - $reflService, - $embeddedClass['declared'] ?? $this->name, - $property - ); - - $parentReflFields[$property] = $fieldRefl; - $this->reflFields[$property] = $fieldRefl; - } - - foreach ($this->fieldMappings as $field => $mapping) { - if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) { - $childProperty = $this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']); - assert($childProperty !== null); - - if (isset($mapping['enumType'])) { - $childProperty = new ReflectionEnumProperty( - $childProperty, - $mapping['enumType'] - ); - } - - $this->reflFields[$field] = new ReflectionEmbeddedProperty( - $parentReflFields[$mapping['declaredField']], - $childProperty, - $mapping['originalClass'] - ); - continue; - } - - $this->reflFields[$field] = isset($mapping['declared']) - ? $this->getAccessibleProperty($reflService, $mapping['declared'], $field) - : $this->getAccessibleProperty($reflService, $this->name, $field); - - if (isset($mapping['enumType']) && $this->reflFields[$field] !== null) { - $this->reflFields[$field] = new ReflectionEnumProperty( - $this->reflFields[$field], - $mapping['enumType'] - ); - } - } - - foreach ($this->associationMappings as $field => $mapping) { - $this->reflFields[$field] = isset($mapping['declared']) - ? $this->getAccessibleProperty($reflService, $mapping['declared'], $field) - : $this->getAccessibleProperty($reflService, $this->name, $field); - } - } - - /** - * Initializes a new ClassMetadata instance that will hold the object-relational mapping - * metadata of the class with the given name. - * - * @param ReflectionService $reflService The reflection service. - * - * @return void - */ - public function initializeReflection($reflService) - { - $this->reflClass = $reflService->getClass($this->name); - $this->namespace = $reflService->getClassNamespace($this->name); - - if ($this->reflClass) { - $this->name = $this->rootEntityName = $this->reflClass->name; - } - - $this->table['name'] = $this->namingStrategy->classToTableName($this->name); - } - - /** - * Validates Identifier. - * - * @return void - * - * @throws MappingException - */ - public function validateIdentifier() - { - if ($this->isMappedSuperclass || $this->isEmbeddedClass) { - return; - } - - // Verify & complete identifier mapping - if (! $this->identifier) { - throw MappingException::identifierRequired($this->name); - } - - if ($this->usesIdGenerator() && $this->isIdentifierComposite) { - throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); - } - } - - /** - * Validates association targets actually exist. - * - * @return void - * - * @throws MappingException - */ - public function validateAssociations() - { - foreach ($this->associationMappings as $mapping) { - if ( - ! class_exists($mapping['targetEntity']) - && ! interface_exists($mapping['targetEntity']) - && ! trait_exists($mapping['targetEntity']) - ) { - throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); - } - } - } - - /** - * Validates lifecycle callbacks. - * - * @param ReflectionService $reflService - * - * @return void - * - * @throws MappingException - */ - public function validateLifecycleCallbacks($reflService) - { - foreach ($this->lifecycleCallbacks as $callbacks) { - foreach ($callbacks as $callbackFuncName) { - if (! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { - throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); - } - } - } - } - - /** - * {@inheritDoc} - */ - public function getReflectionClass() - { - return $this->reflClass; - } - - /** - * @phpstan-param array{usage?: mixed, region?: mixed} $cache - * - * @return void - */ - public function enableCache(array $cache) - { - if (! isset($cache['usage'])) { - $cache['usage'] = self::CACHE_USAGE_READ_ONLY; - } - - if (! isset($cache['region'])) { - $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)); - } - - $this->cache = $cache; - } - - /** - * @param string $fieldName - * @phpstan-param array{usage?: int, region?: string} $cache - * - * @return void - */ - public function enableAssociationCache($fieldName, array $cache) - { - $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults($fieldName, $cache); - } - - /** - * @param string $fieldName - * @param array $cache - * @phpstan-param array{usage?: int|null, region?: string|null} $cache - * - * @return int[]|string[] - * @phpstan-return array{usage: int, region: string|null} - */ - public function getAssociationCacheDefaults($fieldName, array $cache) - { - if (! isset($cache['usage'])) { - $cache['usage'] = $this->cache['usage'] ?? self::CACHE_USAGE_READ_ONLY; - } - - if (! isset($cache['region'])) { - $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; - } - - return $cache; - } - - /** - * Sets the change tracking policy used by this class. - * - * @param int $policy - * - * @return void - */ - public function setChangeTrackingPolicy($policy) - { - $this->changeTrackingPolicy = $policy; - } - - /** - * Whether the change tracking policy of this class is "deferred explicit". - * - * @return bool - */ - public function isChangeTrackingDeferredExplicit() - { - return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT; - } - - /** - * Whether the change tracking policy of this class is "deferred implicit". - * - * @return bool - */ - public function isChangeTrackingDeferredImplicit() - { - return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; - } - - /** - * Whether the change tracking policy of this class is "notify". - * - * @return bool - */ - public function isChangeTrackingNotify() - { - return $this->changeTrackingPolicy === self::CHANGETRACKING_NOTIFY; - } - - /** - * Checks whether a field is part of the identifier/primary key field(s). - * - * @param string $fieldName The field name. - * - * @return bool TRUE if the field is part of the table identifier/primary key field(s), - * FALSE otherwise. - */ - public function isIdentifier($fieldName) - { - if (! $this->identifier) { - return false; - } - - if (! $this->isIdentifierComposite) { - return $fieldName === $this->identifier[0]; - } - - return in_array($fieldName, $this->identifier, true); - } - - /** - * Checks if the field is unique. - * - * @param string $fieldName The field name. - * - * @return bool TRUE if the field is unique, FALSE otherwise. - */ - public function isUniqueField($fieldName) - { - $mapping = $this->getFieldMapping($fieldName); - - return $mapping !== false && isset($mapping['unique']) && $mapping['unique']; - } - - /** - * Checks if the field is not null. - * - * @param string $fieldName The field name. - * - * @return bool TRUE if the field is not null, FALSE otherwise. - */ - public function isNullable($fieldName) - { - $mapping = $this->getFieldMapping($fieldName); - - return $mapping !== false && isset($mapping['nullable']) && $mapping['nullable']; - } - - /** - * Gets a column name for a field name. - * If the column name for the field cannot be found, the given field name - * is returned. - * - * @param string $fieldName The field name. - * - * @return string The column name. - */ - public function getColumnName($fieldName) - { - // @phpstan-ignore property.deprecated - return $this->columnNames[$fieldName] ?? $fieldName; - } - - /** - * Gets the mapping of a (regular) field that holds some data but not a - * reference to another object. - * - * @param string $fieldName The field name. - * - * @return mixed[] The field mapping. - * @phpstan-return FieldMapping - * - * @throws MappingException - */ - public function getFieldMapping($fieldName) - { - if (! isset($this->fieldMappings[$fieldName])) { - throw MappingException::mappingNotFound($this->name, $fieldName); - } - - return $this->fieldMappings[$fieldName]; - } - - /** - * Gets the mapping of an association. - * - * @see ClassMetadataInfo::$associationMappings - * - * @param string $fieldName The field name that represents the association in - * the object model. - * - * @return mixed[] The mapping. - * @phpstan-return AssociationMapping - * - * @throws MappingException - */ - public function getAssociationMapping($fieldName) - { - if (! isset($this->associationMappings[$fieldName])) { - throw MappingException::mappingNotFound($this->name, $fieldName); - } - - return $this->associationMappings[$fieldName]; - } - - /** - * Gets all association mappings of the class. - * - * @phpstan-return array - */ - public function getAssociationMappings() - { - return $this->associationMappings; - } - - /** - * Gets the field name for a column name. - * If no field name can be found the column name is returned. - * - * @param string $columnName The column name. - * - * @return string The column alias. - */ - public function getFieldName($columnName) - { - return $this->fieldNames[$columnName] ?? $columnName; - } - - /** - * Gets the named query. - * - * @see ClassMetadataInfo::$namedQueries - * - * @param string $queryName The query name. - * - * @return string - * - * @throws MappingException - */ - public function getNamedQuery($queryName) - { - if (! isset($this->namedQueries[$queryName])) { - throw MappingException::queryNotFound($this->name, $queryName); - } - - return $this->namedQueries[$queryName]['dql']; - } - - /** - * Gets all named queries of the class. - * - * @return mixed[][] - * @phpstan-return array> - */ - public function getNamedQueries() - { - return $this->namedQueries; - } - - /** - * Gets the named native query. - * - * @see ClassMetadataInfo::$namedNativeQueries - * - * @param string $queryName The query name. - * - * @return mixed[] - * @phpstan-return array - * - * @throws MappingException - */ - public function getNamedNativeQuery($queryName) - { - if (! isset($this->namedNativeQueries[$queryName])) { - throw MappingException::queryNotFound($this->name, $queryName); - } - - return $this->namedNativeQueries[$queryName]; - } - - /** - * Gets all named native queries of the class. - * - * @phpstan-return array> - */ - public function getNamedNativeQueries() - { - return $this->namedNativeQueries; - } - - /** - * Gets the result set mapping. - * - * @see ClassMetadataInfo::$sqlResultSetMappings - * - * @param string $name The result set mapping name. - * - * @return mixed[] - * @phpstan-return array{name: string, entities: array, columns: array} - * - * @throws MappingException - */ - public function getSqlResultSetMapping($name) - { - if (! isset($this->sqlResultSetMappings[$name])) { - throw MappingException::resultMappingNotFound($this->name, $name); - } - - return $this->sqlResultSetMappings[$name]; - } - - /** - * Gets all sql result set mappings of the class. - * - * @return mixed[] - * @phpstan-return array - */ - public function getSqlResultSetMappings() - { - return $this->sqlResultSetMappings; - } - - /** - * Checks whether given property has type - * - * @param string $name Property name - */ - private function isTypedProperty(string $name): bool - { - return PHP_VERSION_ID >= 70400 - && isset($this->reflClass) - && $this->reflClass->hasProperty($name) - && $this->reflClass->getProperty($name)->hasType(); - } - - /** - * Validates & completes the given field mapping based on typed property. - * - * @param array{fieldName: string, type?: string} $mapping The field mapping to validate & complete. - * - * @return array{fieldName: string, enumType?: class-string, type?: string} The updated mapping. - */ - private function validateAndCompleteTypedFieldMapping(array $mapping): array - { - $field = $this->reflClass->getProperty($mapping['fieldName']); - - $mapping = $this->typedFieldMapper->validateAndComplete($mapping, $field); - - return $mapping; - } - - /** - * Validates & completes the basic mapping information based on typed property. - * - * @param array{type: self::ONE_TO_ONE|self::MANY_TO_ONE|self::ONE_TO_MANY|self::MANY_TO_MANY, fieldName: string, targetEntity?: class-string} $mapping The mapping. - * - * @return mixed[] The updated mapping. - */ - private function validateAndCompleteTypedAssociationMapping(array $mapping): array - { - $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); - - if ($type === null || ($mapping['type'] & self::TO_ONE) === 0) { - return $mapping; - } - - if (! isset($mapping['targetEntity']) && $type instanceof ReflectionNamedType) { - $mapping['targetEntity'] = $type->getName(); - } - - return $mapping; - } - - /** - * Validates & completes the given field mapping. - * - * @phpstan-param array{ - * fieldName?: string, - * columnName?: string, - * id?: bool, - * generated?: int, - * enumType?: class-string, - * } $mapping The field mapping to validate & complete. - * - * @return FieldMapping The updated mapping. - * - * @throws MappingException - */ - protected function validateAndCompleteFieldMapping(array $mapping): array - { - // Check mandatory fields - if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) { - throw MappingException::missingFieldName($this->name); - } - - if ($this->isTypedProperty($mapping['fieldName'])) { - $mapping = $this->validateAndCompleteTypedFieldMapping($mapping); - } - - if (! isset($mapping['type'])) { - // Default to string - $mapping['type'] = 'string'; - } - - // Complete fieldName and columnName mapping - if (! isset($mapping['columnName'])) { - $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); - } - - if ($mapping['columnName'][0] === '`') { - $mapping['columnName'] = trim($mapping['columnName'], '`'); - $mapping['quoted'] = true; - } - - // @phpstan-ignore property.deprecated - $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; - - if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn && $this->discriminatorColumn['name'] === $mapping['columnName'])) { - throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); - } - - $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; - - // Complete id mapping - if (isset($mapping['id']) && $mapping['id'] === true) { - if ($this->versionField === $mapping['fieldName']) { - throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); - } - - if (! in_array($mapping['fieldName'], $this->identifier, true)) { - $this->identifier[] = $mapping['fieldName']; - } - - // Check for composite key - if (! $this->isIdentifierComposite && count($this->identifier) > 1) { - $this->isIdentifierComposite = true; - } - } - - // @phpstan-ignore method.deprecated - if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) { - if (isset($mapping['id']) && $mapping['id'] === true) { - throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']); - } - - $mapping['requireSQLConversion'] = true; - } - - if (isset($mapping['generated'])) { - if (! in_array($mapping['generated'], [self::GENERATED_NEVER, self::GENERATED_INSERT, self::GENERATED_ALWAYS])) { - throw MappingException::invalidGeneratedMode($mapping['generated']); - } - - if ($mapping['generated'] === self::GENERATED_NEVER) { - unset($mapping['generated']); - } - } - - if (isset($mapping['enumType'])) { - if (PHP_VERSION_ID < 80100) { - throw MappingException::enumsRequirePhp81($this->name, $mapping['fieldName']); - } - - if (! enum_exists($mapping['enumType'])) { - throw MappingException::nonEnumTypeMapped($this->name, $mapping['fieldName'], $mapping['enumType']); - } - - if (! empty($mapping['id'])) { - $this->containsEnumIdentifier = true; - } - } - - return $mapping; - } - - /** - * Validates & completes the basic mapping information that is common to all - * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). - * - * @phpstan-param array $mapping The mapping. - * - * @return mixed[] The updated mapping. - * @phpstan-return AssociationMapping - * - * @throws MappingException If something is wrong with the mapping. - */ - protected function _validateAndCompleteAssociationMapping(array $mapping) - { - if (! isset($mapping['mappedBy'])) { - $mapping['mappedBy'] = null; - } - - if (! isset($mapping['inversedBy'])) { - $mapping['inversedBy'] = null; - } - - $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy - - if (empty($mapping['indexBy'])) { - unset($mapping['indexBy']); - } - - // If targetEntity is unqualified, assume it is in the same namespace as - // the sourceEntity. - $mapping['sourceEntity'] = $this->name; - - if ($this->isTypedProperty($mapping['fieldName'])) { - $mapping = $this->validateAndCompleteTypedAssociationMapping($mapping); - } - - if (isset($mapping['targetEntity'])) { - $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']); - $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); - } - - if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { - throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); - } - - // Complete id mapping - if (isset($mapping['id']) && $mapping['id'] === true) { - if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { - throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); - } - - if (! in_array($mapping['fieldName'], $this->identifier, true)) { - if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) { - throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( - $mapping['targetEntity'], - $this->name, - $mapping['fieldName'] - ); - } - - $this->identifier[] = $mapping['fieldName']; - $this->containsForeignIdentifier = true; - } - - // Check for composite key - if (! $this->isIdentifierComposite && count($this->identifier) > 1) { - $this->isIdentifierComposite = true; - } - - if ($this->cache && ! isset($mapping['cache'])) { - throw NonCacheableEntityAssociation::fromEntityAndField( - $this->name, - $mapping['fieldName'] - ); - } - } - - // Mandatory attributes for both sides - // Mandatory: fieldName, targetEntity - if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) { - throw MappingException::missingFieldName($this->name); - } - - if (! isset($mapping['targetEntity'])) { - throw MappingException::missingTargetEntity($mapping['fieldName']); - } - - // Mandatory and optional attributes for either side - if (! $mapping['mappedBy']) { - if (isset($mapping['joinTable']) && $mapping['joinTable']) { - if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') { - $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); - $mapping['joinTable']['quoted'] = true; - } - } - } else { - $mapping['isOwningSide'] = false; - } - - if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { - throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']); - } - - // Fetch mode. Default fetch mode to LAZY, if not set. - if (! isset($mapping['fetch'])) { - $mapping['fetch'] = self::FETCH_LAZY; - } - - // Cascades - $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : []; - - $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach']; - if (in_array('all', $cascades, true)) { - $cascades = $allCascades; - } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) { - throw MappingException::invalidCascadeOption( - array_diff($cascades, $allCascades), - $this->name, - $mapping['fieldName'] - ); - } - - $mapping['cascade'] = $cascades; - $mapping['isCascadeRemove'] = in_array('remove', $cascades, true); - $mapping['isCascadePersist'] = in_array('persist', $cascades, true); - $mapping['isCascadeRefresh'] = in_array('refresh', $cascades, true); - $mapping['isCascadeMerge'] = in_array('merge', $cascades, true); - $mapping['isCascadeDetach'] = in_array('detach', $cascades, true); - - return $mapping; - } - - /** - * Validates & completes a one-to-one association mapping. - * - * @phpstan-param array $mapping The mapping to validate & complete. - * - * @return mixed[] The validated & completed mapping. - * @phpstan-return AssociationMapping - * - * @throws RuntimeException - * @throws MappingException - */ - protected function _validateAndCompleteOneToOneMapping(array $mapping) - { - $mapping = $this->_validateAndCompleteAssociationMapping($mapping); - - if (isset($mapping['joinColumns']) && $mapping['joinColumns'] && ! $mapping['isOwningSide']) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10654', - 'JoinColumn configuration is not allowed on the inverse side of one-to-one associations, and will throw a MappingException in Doctrine ORM 3.0' - ); - } - - if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { - $mapping['isOwningSide'] = true; - } - - if ($mapping['isOwningSide']) { - if (empty($mapping['joinColumns'])) { - // Apply default join column - $mapping['joinColumns'] = [ - [ - 'name' => $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name), - 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), - ], - ]; - } - - $uniqueConstraintColumns = []; - - foreach ($mapping['joinColumns'] as &$joinColumn) { - if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { - if (count($mapping['joinColumns']) === 1) { - if (empty($mapping['id'])) { - $joinColumn['unique'] = true; - } - } else { - $uniqueConstraintColumns[] = $joinColumn['name']; - } - } - - if (empty($joinColumn['name'])) { - $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name); - } - - if (empty($joinColumn['referencedColumnName'])) { - $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); - } - - if ($joinColumn['name'][0] === '`') { - $joinColumn['name'] = trim($joinColumn['name'], '`'); - $joinColumn['quoted'] = true; - } - - if ($joinColumn['referencedColumnName'][0] === '`') { - $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); - $joinColumn['quoted'] = true; - } - - $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; - $mapping['joinColumnFieldNames'][$joinColumn['name']] = $joinColumn['fieldName'] ?? $joinColumn['name']; - } - - if ($uniqueConstraintColumns) { - if (! $this->table) { - throw new RuntimeException('ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.'); - } - - $this->table['uniqueConstraints'][$mapping['fieldName'] . '_uniq'] = ['columns' => $uniqueConstraintColumns]; - } - - $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); - } - - $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; - $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; - - if ($mapping['orphanRemoval']) { - unset($mapping['unique']); - } - - if (isset($mapping['id']) && $mapping['id'] === true && ! $mapping['isOwningSide']) { - throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); - } - - return $mapping; - } - - /** - * Validates & completes a one-to-many association mapping. - * - * @phpstan-param array $mapping The mapping to validate and complete. - * - * @return mixed[] The validated and completed mapping. - * @phpstan-return AssociationMapping - * - * @throws MappingException - * @throws InvalidArgumentException - */ - protected function _validateAndCompleteOneToManyMapping(array $mapping) - { - $mapping = $this->_validateAndCompleteAssociationMapping($mapping); - - // OneToMany-side MUST be inverse (must have mappedBy) - if (! isset($mapping['mappedBy'])) { - throw MappingException::oneToManyRequiresMappedBy($this->name, $mapping['fieldName']); - } - - $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; - $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; - - $this->assertMappingOrderBy($mapping); - - return $mapping; - } - - /** - * Validates & completes a many-to-many association mapping. - * - * @phpstan-param array $mapping The mapping to validate & complete. - * - * @return mixed[] The validated & completed mapping. - * @phpstan-return AssociationMapping - * - * @throws InvalidArgumentException - */ - protected function _validateAndCompleteManyToManyMapping(array $mapping) - { - $mapping = $this->_validateAndCompleteAssociationMapping($mapping); - - if ($mapping['isOwningSide']) { - // owning side MUST have a join table - if (! isset($mapping['joinTable']['name'])) { - $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); - } - - $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] === $mapping['targetEntity'] - && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns']))); - - if (! isset($mapping['joinTable']['joinColumns'])) { - $mapping['joinTable']['joinColumns'] = [ - [ - 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), - 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), - 'onDelete' => 'CASCADE', - ], - ]; - } - - if (! isset($mapping['joinTable']['inverseJoinColumns'])) { - $mapping['joinTable']['inverseJoinColumns'] = [ - [ - 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), - 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), - 'onDelete' => 'CASCADE', - ], - ]; - } - - $mapping['joinTableColumns'] = []; - - foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { - if (empty($joinColumn['name'])) { - $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']); - } - - if (empty($joinColumn['referencedColumnName'])) { - $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); - } - - if ($joinColumn['name'][0] === '`') { - $joinColumn['name'] = trim($joinColumn['name'], '`'); - $joinColumn['quoted'] = true; - } - - if ($joinColumn['referencedColumnName'][0] === '`') { - $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); - $joinColumn['quoted'] = true; - } - - if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) === 'cascade') { - $mapping['isOnDeleteCascade'] = true; - } - - $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; - $mapping['joinTableColumns'][] = $joinColumn['name']; - } - - foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { - if (empty($inverseJoinColumn['name'])) { - $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']); - } - - if (empty($inverseJoinColumn['referencedColumnName'])) { - $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); - } - - if ($inverseJoinColumn['name'][0] === '`') { - $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); - $inverseJoinColumn['quoted'] = true; - } - - if ($inverseJoinColumn['referencedColumnName'][0] === '`') { - $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`'); - $inverseJoinColumn['quoted'] = true; - } - - if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) === 'cascade') { - $mapping['isOnDeleteCascade'] = true; - } - - $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; - $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; - } - } - - $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; - - $this->assertMappingOrderBy($mapping); - - return $mapping; - } - - /** - * {@inheritDoc} - */ - public function getIdentifierFieldNames() - { - return $this->identifier; - } - - /** - * Gets the name of the single id field. Note that this only works on - * entity classes that have a single-field pk. - * - * @return string - * - * @throws MappingException If the class doesn't have an identifier or it has a composite primary key. - */ - public function getSingleIdentifierFieldName() - { - if ($this->isIdentifierComposite) { - throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); - } - - if (! isset($this->identifier[0])) { - throw MappingException::noIdDefined($this->name); - } - - return $this->identifier[0]; - } - - /** - * Gets the column name of the single id column. Note that this only works on - * entity classes that have a single-field pk. - * - * @return string - * - * @throws MappingException If the class doesn't have an identifier or it has a composite primary key. - */ - public function getSingleIdentifierColumnName() - { - return $this->getColumnName($this->getSingleIdentifierFieldName()); - } - - /** - * INTERNAL: - * Sets the mapped identifier/primary key fields of this class. - * Mainly used by the ClassMetadataFactory to assign inherited identifiers. - * - * @phpstan-param list $identifier - * - * @return void - */ - public function setIdentifier(array $identifier) - { - $this->identifier = $identifier; - $this->isIdentifierComposite = (count($this->identifier) > 1); - } - - /** - * {@inheritDoc} - */ - public function getIdentifier() - { - return $this->identifier; - } - - /** - * {@inheritDoc} - */ - public function hasField($fieldName) - { - return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]); - } - - /** - * Gets an array containing all the column names. - * - * @phpstan-param list|null $fieldNames - * - * @return mixed[] - * @phpstan-return list - */ - public function getColumnNames(?array $fieldNames = null) - { - if ($fieldNames === null) { - return array_keys($this->fieldNames); - } - - return array_values(array_map([$this, 'getColumnName'], $fieldNames)); - } - - /** - * Returns an array with all the identifier column names. - * - * @phpstan-return list - */ - public function getIdentifierColumnNames() - { - $columnNames = []; - - foreach ($this->identifier as $idProperty) { - if (isset($this->fieldMappings[$idProperty])) { - $columnNames[] = $this->fieldMappings[$idProperty]['columnName']; - - continue; - } - - // Association defined as Id field - $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; - $assocColumnNames = array_map(static function ($joinColumn) { - return $joinColumn['name']; - }, $joinColumns); - - $columnNames = array_merge($columnNames, $assocColumnNames); - } - - return $columnNames; - } - - /** - * Sets the type of Id generator to use for the mapped class. - * - * @param int $generatorType - * @phpstan-param self::GENERATOR_TYPE_* $generatorType - * - * @return void - */ - public function setIdGeneratorType($generatorType) - { - $this->generatorType = $generatorType; - } - - /** - * Checks whether the mapped class uses an Id generator. - * - * @return bool TRUE if the mapped class uses an Id generator, FALSE otherwise. - */ - public function usesIdGenerator() - { - return $this->generatorType !== self::GENERATOR_TYPE_NONE; - } - - /** @return bool */ - public function isInheritanceTypeNone() - { - return $this->inheritanceType === self::INHERITANCE_TYPE_NONE; - } - - /** - * Checks whether the mapped class uses the JOINED inheritance mapping strategy. - * - * @return bool TRUE if the class participates in a JOINED inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeJoined() - { - return $this->inheritanceType === self::INHERITANCE_TYPE_JOINED; - } - - /** - * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. - * - * @return bool TRUE if the class participates in a SINGLE_TABLE inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeSingleTable() - { - return $this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_TABLE; - } - - /** - * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. - * - * @deprecated - * - * @return bool TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeTablePerClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10414/', - 'Concrete table inheritance has never been implemented, and its stubs will be removed in Doctrine ORM 3.0 with no replacement' - ); - - return $this->inheritanceType === self::INHERITANCE_TYPE_TABLE_PER_CLASS; - } - - /** - * Checks whether the class uses an identity column for the Id generation. - * - * @return bool TRUE if the class uses the IDENTITY generator, FALSE otherwise. - */ - public function isIdGeneratorIdentity() - { - return $this->generatorType === self::GENERATOR_TYPE_IDENTITY; - } - - /** - * Checks whether the class uses a sequence for id generation. - * - * @return bool TRUE if the class uses the SEQUENCE generator, FALSE otherwise. - * - * @phpstan-assert-if-true !null $this->sequenceGeneratorDefinition - */ - public function isIdGeneratorSequence() - { - return $this->generatorType === self::GENERATOR_TYPE_SEQUENCE; - } - - /** - * Checks whether the class uses a table for id generation. - * - * @deprecated - * - * @return false - */ - public function isIdGeneratorTable() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9046', - '%s is deprecated', - __METHOD__ - ); - - return false; - } - - /** - * Checks whether the class has a natural identifier/pk (which means it does - * not use any Id generator. - * - * @return bool - */ - public function isIdentifierNatural() - { - return $this->generatorType === self::GENERATOR_TYPE_NONE; - } - - /** - * Checks whether the class use a UUID for id generation. - * - * @deprecated - * - * @return bool - */ - public function isIdentifierUuid() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9046', - '%s is deprecated', - __METHOD__ - ); - - return $this->generatorType === self::GENERATOR_TYPE_UUID; - } - - /** - * Gets the type of a field. - * - * @param string $fieldName - * - * @return string|null - * - * @todo 3.0 Remove this. PersisterHelper should fix it somehow - */ - public function getTypeOfField($fieldName) - { - return isset($this->fieldMappings[$fieldName]) - ? $this->fieldMappings[$fieldName]['type'] - : null; - } - - /** - * Gets the type of a column. - * - * @deprecated 3.0 remove this. this method is bogus and unreliable, since it cannot resolve the type of a column - * that is derived by a referenced field on a different entity. - * - * @param string $columnName - * - * @return string|null - */ - public function getTypeOfColumn($columnName) - { - return $this->getTypeOfField($this->getFieldName($columnName)); - } - - /** - * Gets the name of the primary table. - * - * @return string - */ - public function getTableName() - { - return $this->table['name']; - } - - /** - * Gets primary table's schema name. - * - * @return string|null - */ - public function getSchemaName() - { - return $this->table['schema'] ?? null; - } - - /** - * Gets the table name to use for temporary identifier tables of this class. - * - * @return string - */ - public function getTemporaryIdTableName() - { - // replace dots with underscores because PostgreSQL creates temporary tables in a special schema - return str_replace('.', '_', $this->getTableName() . '_id_tmp'); - } - - /** - * Sets the mapped subclasses of this class. - * - * @phpstan-param list $subclasses The names of all mapped subclasses. - * - * @return void - */ - public function setSubclasses(array $subclasses) - { - foreach ($subclasses as $subclass) { - $this->subClasses[] = $this->fullyQualifiedClassName($subclass); - } - } - - /** - * Sets the parent class names. Only entity classes may be given. - * - * Assumes that the class names in the passed array are in the order: - * directParent -> directParentParent -> directParentParentParent ... -> root. - * - * @param list $classNames - * - * @return void - */ - public function setParentClasses(array $classNames) - { - $this->parentClasses = $classNames; - - if (count($classNames) > 0) { - $this->rootEntityName = array_pop($classNames); - } - } - - /** - * Sets the inheritance type used by the class and its subclasses. - * - * @param int $type - * @phpstan-param self::INHERITANCE_TYPE_* $type - * - * @return void - * - * @throws MappingException - */ - public function setInheritanceType($type) - { - if (! $this->isInheritanceType($type)) { - throw MappingException::invalidInheritanceType($this->name, $type); - } - - $this->inheritanceType = $type; - } - - /** - * Sets the association to override association mapping of property for an entity relationship. - * - * @param string $fieldName - * @phpstan-param array $overrideMapping - * - * @return void - * - * @throws MappingException - */ - public function setAssociationOverride($fieldName, array $overrideMapping) - { - if (! isset($this->associationMappings[$fieldName])) { - throw MappingException::invalidOverrideFieldName($this->name, $fieldName); - } - - $mapping = $this->associationMappings[$fieldName]; - - if (isset($mapping['inherited'])) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10470', - 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s. This is a misconfiguration and will be an error in Doctrine ORM 3.0.', - $this->name, - $fieldName, - $mapping['inherited'] - ); - } - - if (isset($overrideMapping['joinColumns'])) { - $mapping['joinColumns'] = $overrideMapping['joinColumns']; - } - - if (isset($overrideMapping['inversedBy'])) { - $mapping['inversedBy'] = $overrideMapping['inversedBy']; - } - - if (isset($overrideMapping['joinTable'])) { - $mapping['joinTable'] = $overrideMapping['joinTable']; - } - - if (isset($overrideMapping['fetch'])) { - $mapping['fetch'] = $overrideMapping['fetch']; - } - - $mapping['joinColumnFieldNames'] = null; - $mapping['joinTableColumns'] = null; - $mapping['sourceToTargetKeyColumns'] = null; - $mapping['relationToSourceKeyColumns'] = null; - $mapping['relationToTargetKeyColumns'] = null; - - switch ($mapping['type']) { - case self::ONE_TO_ONE: - $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); - break; - case self::ONE_TO_MANY: - $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); - break; - case self::MANY_TO_ONE: - $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); - break; - case self::MANY_TO_MANY: - $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); - break; - } - - $this->associationMappings[$fieldName] = $mapping; - } - - /** - * Sets the override for a mapped field. - * - * @param string $fieldName - * @phpstan-param array $overrideMapping - * - * @return void - * - * @throws MappingException - */ - public function setAttributeOverride($fieldName, array $overrideMapping) - { - if (! isset($this->fieldMappings[$fieldName])) { - throw MappingException::invalidOverrideFieldName($this->name, $fieldName); - } - - $mapping = $this->fieldMappings[$fieldName]; - - if (isset($mapping['inherited'])) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10470', - 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s. This is a misconfiguration and will be an error in Doctrine ORM 3.0.', - $this->name, - $fieldName, - $mapping['inherited'] - ); - //throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName); - } - - if (isset($mapping['id'])) { - $overrideMapping['id'] = $mapping['id']; - } - - if (isset($mapping['declared'])) { - $overrideMapping['declared'] = $mapping['declared']; - } - - if (! isset($overrideMapping['type'])) { - $overrideMapping['type'] = $mapping['type']; - } - - if (! isset($overrideMapping['fieldName'])) { - $overrideMapping['fieldName'] = $mapping['fieldName']; - } - - if ($overrideMapping['type'] !== $mapping['type']) { - throw MappingException::invalidOverrideFieldType($this->name, $fieldName); - } - - unset($this->fieldMappings[$fieldName]); - unset($this->fieldNames[$mapping['columnName']]); - // @phpstan-ignore property.deprecated - unset($this->columnNames[$mapping['fieldName']]); - - $overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping); - - $this->fieldMappings[$fieldName] = $overrideMapping; - } - - /** - * Checks whether a mapped field is inherited from an entity superclass. - * - * @param string $fieldName - * - * @return bool TRUE if the field is inherited, FALSE otherwise. - */ - public function isInheritedField($fieldName) - { - return isset($this->fieldMappings[$fieldName]['inherited']); - } - - /** - * Checks if this entity is the root in any entity-inheritance-hierarchy. - * - * @return bool - */ - public function isRootEntity() - { - return $this->name === $this->rootEntityName; - } - - /** - * Checks whether a mapped association field is inherited from a superclass. - * - * @param string $fieldName - * - * @return bool TRUE if the field is inherited, FALSE otherwise. - */ - public function isInheritedAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]['inherited']); - } - - /** - * @param string $fieldName - * - * @return bool - */ - public function isInheritedEmbeddedClass($fieldName) - { - return isset($this->embeddedClasses[$fieldName]['inherited']); - } - - /** - * Sets the name of the primary table the class is mapped to. - * - * @deprecated Use {@link setPrimaryTable}. - * - * @param string $tableName The table name. - * - * @return void - */ - public function setTableName($tableName) - { - $this->table['name'] = $tableName; - } - - /** - * Sets the primary table definition. The provided array supports the - * following structure: - * - * name => (optional, defaults to class name) - * indexes => array of indexes (optional) - * uniqueConstraints => array of constraints (optional) - * - * If a key is omitted, the current value is kept. - * - * @phpstan-param array $table The table description. - * - * @return void - */ - public function setPrimaryTable(array $table) - { - if (isset($table['name'])) { - // Split schema and table name from a table name like "myschema.mytable" - if (str_contains($table['name'], '.')) { - [$this->table['schema'], $table['name']] = explode('.', $table['name'], 2); - } - - if ($table['name'][0] === '`') { - $table['name'] = trim($table['name'], '`'); - $this->table['quoted'] = true; - } - - $this->table['name'] = $table['name']; - } - - if (isset($table['quoted'])) { - $this->table['quoted'] = $table['quoted']; - } - - if (isset($table['schema'])) { - $this->table['schema'] = $table['schema']; - } - - if (isset($table['indexes'])) { - $this->table['indexes'] = $table['indexes']; - } - - if (isset($table['uniqueConstraints'])) { - $this->table['uniqueConstraints'] = $table['uniqueConstraints']; - } - - if (isset($table['options'])) { - $this->table['options'] = $table['options']; - } - } - - /** - * Checks whether the given type identifies an inheritance type. - * - * @return bool TRUE if the given type identifies an inheritance type, FALSE otherwise. - */ - private function isInheritanceType(int $type): bool - { - // @phpstan-ignore classConstant.deprecated - if ($type === self::INHERITANCE_TYPE_TABLE_PER_CLASS) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10414/', - 'Concrete table inheritance has never been implemented, and its stubs will be removed in Doctrine ORM 3.0 with no replacement' - ); - } - - return $type === self::INHERITANCE_TYPE_NONE || - $type === self::INHERITANCE_TYPE_SINGLE_TABLE || - $type === self::INHERITANCE_TYPE_JOINED || - // @phpstan-ignore classConstant.deprecated - $type === self::INHERITANCE_TYPE_TABLE_PER_CLASS; - } - - /** - * Adds a mapped field to the class. - * - * @phpstan-param array $mapping The field mapping. - * - * @return void - * - * @throws MappingException - */ - public function mapField(array $mapping) - { - $mapping = $this->validateAndCompleteFieldMapping($mapping); - $this->assertFieldNotMapped($mapping['fieldName']); - - if (isset($mapping['generated'])) { - $this->requiresFetchAfterChange = true; - } - - $this->fieldMappings[$mapping['fieldName']] = $mapping; - } - - /** - * INTERNAL: - * Adds an association mapping without completing/validating it. - * This is mainly used to add inherited association mappings to derived classes. - * - * @phpstan-param AssociationMapping $mapping - * - * @return void - * - * @throws MappingException - */ - public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/) - { - if (isset($this->associationMappings[$mapping['fieldName']])) { - throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); - } - - $this->associationMappings[$mapping['fieldName']] = $mapping; - } - - /** - * INTERNAL: - * Adds a field mapping without completing/validating it. - * This is mainly used to add inherited field mappings to derived classes. - * - * @phpstan-param FieldMapping $fieldMapping - * - * @return void - */ - public function addInheritedFieldMapping(array $fieldMapping) - { - $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; - // @phpstan-ignore property.deprecated - $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; - $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; - - if (isset($fieldMapping['generated'])) { - $this->requiresFetchAfterChange = true; - } - } - - /** - * INTERNAL: - * Adds a named query to this class. - * - * @deprecated - * - * @phpstan-param array $queryMapping - * - * @return void - * - * @throws MappingException - */ - public function addNamedQuery(array $queryMapping) - { - if (! isset($queryMapping['name'])) { - throw MappingException::nameIsMandatoryForQueryMapping($this->name); - } - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8592', - 'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', - $queryMapping['name'], - $this->name - ); - - if (isset($this->namedQueries[$queryMapping['name']])) { - throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); - } - - if (! isset($queryMapping['query'])) { - throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); - } - - $name = $queryMapping['name']; - $query = $queryMapping['query']; - $dql = str_replace('__CLASS__', $this->name, $query); - - $this->namedQueries[$name] = [ - 'name' => $name, - 'query' => $query, - 'dql' => $dql, - ]; - } - - /** - * INTERNAL: - * Adds a named native query to this class. - * - * @deprecated - * - * @phpstan-param array $queryMapping - * - * @return void - * - * @throws MappingException - */ - public function addNamedNativeQuery(array $queryMapping) - { - if (! isset($queryMapping['name'])) { - throw MappingException::nameIsMandatoryForQueryMapping($this->name); - } - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8592', - 'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', - $queryMapping['name'], - $this->name - ); - - if (isset($this->namedNativeQueries[$queryMapping['name']])) { - throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); - } - - if (! isset($queryMapping['query'])) { - throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); - } - - if (! isset($queryMapping['resultClass']) && ! isset($queryMapping['resultSetMapping'])) { - throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); - } - - $queryMapping['isSelfClass'] = false; - - if (isset($queryMapping['resultClass'])) { - if ($queryMapping['resultClass'] === '__CLASS__') { - $queryMapping['isSelfClass'] = true; - $queryMapping['resultClass'] = $this->name; - } - - $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']); - $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); - } - - $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; - } - - /** - * INTERNAL: - * Adds a sql result set mapping to this class. - * - * @phpstan-param array $resultMapping - * - * @return void - * - * @throws MappingException - */ - public function addSqlResultSetMapping(array $resultMapping) - { - if (! isset($resultMapping['name'])) { - throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); - } - - if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { - throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); - } - - if (isset($resultMapping['entities'])) { - foreach ($resultMapping['entities'] as $key => $entityResult) { - if (! isset($entityResult['entityClass'])) { - throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); - } - - $entityResult['isSelfClass'] = false; - if ($entityResult['entityClass'] === '__CLASS__') { - $entityResult['isSelfClass'] = true; - $entityResult['entityClass'] = $this->name; - } - - $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); - - $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); - $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; - - if (isset($entityResult['fields'])) { - foreach ($entityResult['fields'] as $k => $field) { - if (! isset($field['name'])) { - throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); - } - - if (! isset($field['column'])) { - $fieldName = $field['name']; - if (str_contains($fieldName, '.')) { - [, $fieldName] = explode('.', $fieldName); - } - - $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; - } - } - } - } - } - - $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; - } - - /** - * Adds a one-to-one mapping. - * - * @param array $mapping The mapping. - * - * @return void - */ - public function mapOneToOne(array $mapping) - { - $mapping['type'] = self::ONE_TO_ONE; - - $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); - - $this->_storeAssociationMapping($mapping); - } - - /** - * Adds a one-to-many mapping. - * - * @phpstan-param array $mapping The mapping. - * - * @return void - */ - public function mapOneToMany(array $mapping) - { - $mapping['type'] = self::ONE_TO_MANY; - - $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); - - $this->_storeAssociationMapping($mapping); - } - - /** - * Adds a many-to-one mapping. - * - * @phpstan-param array $mapping The mapping. - * - * @return void - */ - public function mapManyToOne(array $mapping) - { - $mapping['type'] = self::MANY_TO_ONE; - - // A many-to-one mapping is essentially a one-one backreference - $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); - - $this->_storeAssociationMapping($mapping); - } - - /** - * Adds a many-to-many mapping. - * - * @phpstan-param array $mapping The mapping. - * - * @return void - */ - public function mapManyToMany(array $mapping) - { - $mapping['type'] = self::MANY_TO_MANY; - - $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); - - $this->_storeAssociationMapping($mapping); - } - - /** - * Stores the association mapping. - * - * @phpstan-param AssociationMapping $assocMapping - * - * @return void - * - * @throws MappingException - */ - protected function _storeAssociationMapping(array $assocMapping) - { - $sourceFieldName = $assocMapping['fieldName']; - - $this->assertFieldNotMapped($sourceFieldName); - - $this->associationMappings[$sourceFieldName] = $assocMapping; - } - - /** - * Registers a custom repository class for the entity class. - * - * @param class-string|null $repositoryClassName The class name of the custom mapper. - * - * @return void - */ - public function setCustomRepositoryClass($repositoryClassName) - { - $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); - } - - /** - * Dispatches the lifecycle event of the given entity to the registered - * lifecycle callbacks and lifecycle listeners. - * - * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker - * - * @param string $lifecycleEvent The lifecycle event. - * @param object $entity The Entity on which the event occurred. - * - * @return void - */ - public function invokeLifecycleCallbacks($lifecycleEvent, $entity) - { - foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { - $entity->$callback(); - } - } - - /** - * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. - * - * @param string $lifecycleEvent - * - * @return bool - */ - public function hasLifecycleCallbacks($lifecycleEvent) - { - return isset($this->lifecycleCallbacks[$lifecycleEvent]); - } - - /** - * Gets the registered lifecycle callbacks for an event. - * - * @param string $event - * - * @return string[] - * @phpstan-return list - */ - public function getLifecycleCallbacks($event) - { - return $this->lifecycleCallbacks[$event] ?? []; - } - - /** - * Adds a lifecycle callback for entities of this class. - * - * @param string $callback - * @param string $event - * - * @return void - */ - public function addLifecycleCallback($callback, $event) - { - if ($this->isEmbeddedClass) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/8381', - 'Registering lifecycle callback %s on Embedded class %s is not doing anything and will throw exception in 3.0', - $event, - $this->name - ); - } - - if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) { - return; - } - - $this->lifecycleCallbacks[$event][] = $callback; - } - - /** - * Sets the lifecycle callbacks for entities of this class. - * Any previously registered callbacks are overwritten. - * - * @phpstan-param array> $callbacks - * - * @return void - */ - public function setLifecycleCallbacks(array $callbacks) - { - $this->lifecycleCallbacks = $callbacks; - } - - /** - * Adds a entity listener for entities of this class. - * - * @param string $eventName The entity lifecycle event. - * @param string $class The listener class. - * @param string $method The listener callback method. - * - * @return void - * - * @throws MappingException - */ - public function addEntityListener($eventName, $class, $method) - { - $class = $this->fullyQualifiedClassName($class); - - $listener = [ - 'class' => $class, - 'method' => $method, - ]; - - if (! class_exists($class)) { - throw MappingException::entityListenerClassNotFound($class, $this->name); - } - - if (! method_exists($class, $method)) { - throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); - } - - if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) { - throw MappingException::duplicateEntityListener($class, $method, $this->name); - } - - $this->entityListeners[$eventName][] = $listener; - } - - /** - * Sets the discriminator column definition. - * - * @see getDiscriminatorColumn() - * - * @param mixed[]|null $columnDef - * @phpstan-param array{name: string|null, fieldName?: string, type?: string, length?: int, columnDefinition?: string|null, enumType?: class-string|null, options?: array}|null $columnDef - * - * @return void - * - * @throws MappingException - */ - public function setDiscriminatorColumn($columnDef) - { - if ($columnDef !== null) { - if (! isset($columnDef['name'])) { - throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); - } - - if (isset($this->fieldNames[$columnDef['name']])) { - throw MappingException::duplicateColumnName($this->name, $columnDef['name']); - } - - if (! isset($columnDef['fieldName'])) { - $columnDef['fieldName'] = $columnDef['name']; - } - - if (! isset($columnDef['type'])) { - $columnDef['type'] = 'string'; - } - - if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], true)) { - throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); - } - - $this->discriminatorColumn = $columnDef; - } - } - - /** - * @return array - * @phpstan-return DiscriminatorColumnMapping - */ - final public function getDiscriminatorColumn(): array - { - if ($this->discriminatorColumn === null) { - throw new LogicException('The discriminator column was not set.'); - } - - return $this->discriminatorColumn; - } - - /** - * Sets the discriminator values used by this class. - * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. - * - * @param array $map - * - * @return void - */ - public function setDiscriminatorMap(array $map) - { - foreach ($map as $value => $className) { - $this->addDiscriminatorMapClass($value, $className); - } - } - - /** - * Adds one entry of the discriminator map with a new class and corresponding name. - * - * @param int|string $name - * @param string $className - * - * @return void - * - * @throws MappingException - */ - public function addDiscriminatorMapClass($name, $className) - { - $className = $this->fullyQualifiedClassName($className); - $className = ltrim($className, '\\'); - - $this->discriminatorMap[$name] = $className; - - if ($this->name === $className) { - $this->discriminatorValue = $name; - - return; - } - - if (! (class_exists($className) || interface_exists($className))) { - throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); - } - - $this->addSubClass($className); - } - - /** @param array $classes */ - public function addSubClasses(array $classes): void - { - foreach ($classes as $className) { - $this->addSubClass($className); - } - } - - public function addSubClass(string $className): void - { - // By ignoring classes that are not subclasses of the current class, we simplify inheriting - // the subclass list from a parent class at the beginning of \Doctrine\ORM\Mapping\ClassMetadataFactory::doLoadMetadata. - if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses, true)) { - $this->subClasses[] = $className; - } - } - - /** - * Checks whether the class has a named query with the given query name. - * - * @param string $queryName - * - * @return bool - */ - public function hasNamedQuery($queryName) - { - return isset($this->namedQueries[$queryName]); - } - - /** - * Checks whether the class has a named native query with the given query name. - * - * @param string $queryName - * - * @return bool - */ - public function hasNamedNativeQuery($queryName) - { - return isset($this->namedNativeQueries[$queryName]); - } - - /** - * Checks whether the class has a named native query with the given query name. - * - * @param string $name - * - * @return bool - */ - public function hasSqlResultSetMapping($name) - { - return isset($this->sqlResultSetMappings[$name]); - } - - /** - * {@inheritDoc} - */ - public function hasAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]); - } - - /** - * {@inheritDoc} - */ - public function isSingleValuedAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]) - && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); - } - - /** - * {@inheritDoc} - */ - public function isCollectionValuedAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]) - && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); - } - - /** - * Is this an association that only has a single join column? - * - * @param string $fieldName - * - * @return bool - */ - public function isAssociationWithSingleJoinColumn($fieldName) - { - return isset($this->associationMappings[$fieldName]) - && isset($this->associationMappings[$fieldName]['joinColumns'][0]) - && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]); - } - - /** - * Returns the single association join column (if any). - * - * @param string $fieldName - * - * @return string - * - * @throws MappingException - */ - public function getSingleAssociationJoinColumnName($fieldName) - { - if (! $this->isAssociationWithSingleJoinColumn($fieldName)) { - throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); - } - - return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; - } - - /** - * Returns the single association referenced join column name (if any). - * - * @param string $fieldName - * - * @return string - * - * @throws MappingException - */ - public function getSingleAssociationReferencedJoinColumnName($fieldName) - { - if (! $this->isAssociationWithSingleJoinColumn($fieldName)) { - throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); - } - - return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; - } - - /** - * Used to retrieve a fieldname for either field or association from a given column. - * - * This method is used in foreign-key as primary-key contexts. - * - * @param string $columnName - * - * @return string - * - * @throws MappingException - */ - public function getFieldForColumn($columnName) - { - if (isset($this->fieldNames[$columnName])) { - return $this->fieldNames[$columnName]; - } - - foreach ($this->associationMappings as $assocName => $mapping) { - if ( - $this->isAssociationWithSingleJoinColumn($assocName) && - $this->associationMappings[$assocName]['joinColumns'][0]['name'] === $columnName - ) { - return $assocName; - } - } - - throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); - } - - /** - * Sets the ID generator used to generate IDs for instances of this class. - * - * @param AbstractIdGenerator $generator - * - * @return void - */ - public function setIdGenerator($generator) - { - $this->idGenerator = $generator; - } - - /** - * Sets definition. - * - * @phpstan-param array $definition - * - * @return void - */ - public function setCustomGeneratorDefinition(array $definition) - { - $this->customGeneratorDefinition = $definition; - } - - /** - * Sets the definition of the sequence ID generator for this class. - * - * The definition must have the following structure: - * - * array( - * 'sequenceName' => 'name', - * 'allocationSize' => 20, - * 'initialValue' => 1 - * 'quoted' => 1 - * ) - * - * - * @phpstan-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition - * - * @return void - * - * @throws MappingException - */ - public function setSequenceGeneratorDefinition(array $definition) - { - if (! isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') { - throw MappingException::missingSequenceName($this->name); - } - - if ($definition['sequenceName'][0] === '`') { - $definition['sequenceName'] = trim($definition['sequenceName'], '`'); - $definition['quoted'] = true; - } - - if (! isset($definition['allocationSize']) || trim((string) $definition['allocationSize']) === '') { - $definition['allocationSize'] = '1'; - } - - if (! isset($definition['initialValue']) || trim((string) $definition['initialValue']) === '') { - $definition['initialValue'] = '1'; - } - - $definition['allocationSize'] = (string) $definition['allocationSize']; - $definition['initialValue'] = (string) $definition['initialValue']; - - $this->sequenceGeneratorDefinition = $definition; - } - - /** - * Sets the version field mapping used for versioning. Sets the default - * value to use depending on the column type. - * - * @phpstan-param array $mapping The version field mapping array. - * - * @return void - * - * @throws MappingException - */ - public function setVersionMapping(array &$mapping) - { - $this->isVersioned = true; - $this->versionField = $mapping['fieldName']; - $this->requiresFetchAfterChange = true; - - if (! isset($mapping['default'])) { - if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'], true)) { - $mapping['default'] = 1; - } elseif ($mapping['type'] === 'datetime') { - $mapping['default'] = 'CURRENT_TIMESTAMP'; - } else { - throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); - } - } - } - - /** - * Sets whether this class is to be versioned for optimistic locking. - * - * @param bool $bool - * - * @return void - */ - public function setVersioned($bool) - { - $this->isVersioned = $bool; - - if ($bool) { - $this->requiresFetchAfterChange = true; - } - } - - /** - * Sets the name of the field that is to be used for versioning if this class is - * versioned for optimistic locking. - * - * @param string|null $versionField - * - * @return void - */ - public function setVersionField($versionField) - { - $this->versionField = $versionField; - } - - /** - * Marks this class as read only, no change tracking is applied to it. - * - * @return void - */ - public function markReadOnly() - { - $this->isReadOnly = true; - } - - /** - * {@inheritDoc} - */ - public function getFieldNames() - { - return array_keys($this->fieldMappings); - } - - /** - * {@inheritDoc} - */ - public function getAssociationNames() - { - return array_keys($this->associationMappings); - } - - /** - * {@inheritDoc} - * - * @param string $assocName - * - * @return class-string - * - * @throws InvalidArgumentException - */ - public function getAssociationTargetClass($assocName) - { - if (! isset($this->associationMappings[$assocName])) { - throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association."); - } - - return $this->associationMappings[$assocName]['targetEntity']; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return $this->name; - } - - /** - * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. - * - * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy - * - * @param AbstractPlatform $platform - * - * @return string[] - * @phpstan-return list - */ - public function getQuotedIdentifierColumnNames($platform) - { - $quotedColumnNames = []; - - foreach ($this->identifier as $idProperty) { - if (isset($this->fieldMappings[$idProperty])) { - $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted']) - ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName']) - : $this->fieldMappings[$idProperty]['columnName']; - - continue; - } - - // Association defined as Id field - $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; - $assocQuotedColumnNames = array_map( - static function ($joinColumn) use ($platform) { - return isset($joinColumn['quoted']) - ? $platform->quoteIdentifier($joinColumn['name']) - : $joinColumn['name']; - }, - $joinColumns - ); - - $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); - } - - return $quotedColumnNames; - } - - /** - * Gets the (possibly quoted) column name of a mapped field for safe use in an SQL statement. - * - * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy - * - * @param string $field - * @param AbstractPlatform $platform - * - * @return string - */ - public function getQuotedColumnName($field, $platform) - { - return isset($this->fieldMappings[$field]['quoted']) - ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) - : $this->fieldMappings[$field]['columnName']; - } - - /** - * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement. - * - * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy - * - * @param AbstractPlatform $platform - * - * @return string - */ - public function getQuotedTableName($platform) - { - return isset($this->table['quoted']) - ? $platform->quoteIdentifier($this->table['name']) - : $this->table['name']; - } - - /** - * Gets the (possibly quoted) name of the join table. - * - * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy - * - * @param mixed[] $assoc - * @param AbstractPlatform $platform - * - * @return string - */ - public function getQuotedJoinTableName(array $assoc, $platform) - { - return isset($assoc['joinTable']['quoted']) - ? $platform->quoteIdentifier($assoc['joinTable']['name']) - : $assoc['joinTable']['name']; - } - - /** - * {@inheritDoc} - */ - public function isAssociationInverseSide($fieldName) - { - return isset($this->associationMappings[$fieldName]) - && ! $this->associationMappings[$fieldName]['isOwningSide']; - } - - /** - * {@inheritDoc} - */ - public function getAssociationMappedByTargetField($fieldName) - { - if (! $this->isAssociationInverseSide($fieldName)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/11309', - 'Calling %s with owning side field %s is deprecated and will no longer be supported in Doctrine ORM 3.0. Call %s::isAssociationInverseSide() to check first.', - __METHOD__, - $fieldName, - self::class - ); - } - - return $this->associationMappings[$fieldName]['mappedBy']; - } - - /** - * @param string $targetClass - * - * @return mixed[][] - * @phpstan-return array> - */ - public function getAssociationsByTargetClass($targetClass) - { - $relations = []; - - foreach ($this->associationMappings as $mapping) { - if ($mapping['targetEntity'] === $targetClass) { - $relations[$mapping['fieldName']] = $mapping; - } - } - - return $relations; - } - - /** - * @param string|null $className - * - * @return string|null null if the input value is null - */ - public function fullyQualifiedClassName($className) - { - if (empty($className)) { - return $className; - } - - if (! str_contains($className, '\\') && $this->namespace) { - return $this->namespace . '\\' . $className; - } - - return $className; - } - - /** - * @param string $name - * - * @return mixed - */ - public function getMetadataValue($name) - { - if (isset($this->$name)) { - return $this->$name; - } - - return null; - } - - /** - * Map Embedded Class - * - * @phpstan-param array $mapping - * - * @return void - * - * @throws MappingException - */ - public function mapEmbedded(array $mapping) - { - $this->assertFieldNotMapped($mapping['fieldName']); - - if (! isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) { - $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); - if ($type instanceof ReflectionNamedType) { - $mapping['class'] = $type->getName(); - } - } - - if (! (isset($mapping['class']) && $mapping['class'])) { - throw MappingException::missingEmbeddedClass($mapping['fieldName']); - } - - $fqcn = $this->fullyQualifiedClassName($mapping['class']); - - assert($fqcn !== null); - - $this->embeddedClasses[$mapping['fieldName']] = [ - 'class' => $fqcn, - 'columnPrefix' => $mapping['columnPrefix'] ?? null, - 'declaredField' => $mapping['declaredField'] ?? null, - 'originalField' => $mapping['originalField'] ?? null, - ]; - } - - /** - * Inline the embeddable class - * - * @param string $property - * - * @return void - */ - public function inlineEmbeddable($property, ClassMetadataInfo $embeddable) - { - foreach ($embeddable->fieldMappings as $fieldMapping) { - $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->name; - $fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) - ? $property . '.' . $fieldMapping['declaredField'] - : $property; - $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldMapping['fieldName']; - $fieldMapping['fieldName'] = $property . '.' . $fieldMapping['fieldName']; - - if (! empty($this->embeddedClasses[$property]['columnPrefix'])) { - $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName']; - } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) { - $fieldMapping['columnName'] = $this->namingStrategy - ->embeddedFieldToColumnName( - $property, - $fieldMapping['columnName'], - $this->reflClass->name, - $embeddable->reflClass->name - ); - } - - $this->mapField($fieldMapping); - } - } - - /** @throws MappingException */ - private function assertFieldNotMapped(string $fieldName): void - { - if ( - isset($this->fieldMappings[$fieldName]) || - isset($this->associationMappings[$fieldName]) || - isset($this->embeddedClasses[$fieldName]) - ) { - throw MappingException::duplicateFieldMapping($this->name, $fieldName); - } - } - - /** - * Gets the sequence name based on class metadata. - * - * @return string - * - * @todo Sequence names should be computed in DBAL depending on the platform - */ - public function getSequenceName(AbstractPlatform $platform) - { - $sequencePrefix = $this->getSequencePrefix($platform); - $columnName = $this->getSingleIdentifierColumnName(); - - return $sequencePrefix . '_' . $columnName . '_seq'; - } - - /** - * Gets the sequence name prefix based on class metadata. - * - * @return string - * - * @todo Sequence names should be computed in DBAL depending on the platform - */ - public function getSequencePrefix(AbstractPlatform $platform) - { - $tableName = $this->getTableName(); - $sequencePrefix = $tableName; - - // Prepend the schema name to the table name if there is one - $schemaName = $this->getSchemaName(); - if ($schemaName) { - $sequencePrefix = $schemaName . '.' . $tableName; - - // @phpstan-ignore method.deprecated - if (! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { - $sequencePrefix = $schemaName . '__' . $tableName; - } - } - - return $sequencePrefix; - } - - /** @phpstan-param AssociationMapping $mapping */ - private function assertMappingOrderBy(array $mapping): void - { - if (isset($mapping['orderBy']) && ! is_array($mapping['orderBy'])) { - throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy'])); - } - } - - /** @param class-string $class */ - private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ?ReflectionProperty - { - $reflectionProperty = $reflService->getAccessibleProperty($class, $field); - if ($reflectionProperty !== null && PHP_VERSION_ID >= 80100 && $reflectionProperty->isReadOnly()) { - $declaringClass = $reflectionProperty->class; - if ($declaringClass !== $class) { - $reflectionProperty = $reflService->getAccessibleProperty($declaringClass, $field); - } - - if ($reflectionProperty !== null) { - $reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty); - } - } - - if (PHP_VERSION_ID >= 80400 && $reflectionProperty !== null && count($reflectionProperty->getHooks()) > 0) { - throw new LogicException('Doctrine ORM does not support property hooks in this version. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.'); - } - - return $reflectionProperty; - } -} diff --git a/src/Mapping/Column.php b/src/Mapping/Column.php index 9d38d5ac043..e044f5e3144 100644 --- a/src/Mapping/Column.php +++ b/src/Mapping/Column.php @@ -6,132 +6,31 @@ use Attribute; use BackedEnum; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor - * @Target({"PROPERTY","ANNOTATION"}) - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class Column implements MappingAttribute { /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var mixed - * @readonly - */ - public $type; - - /** - * @var int|null - * @readonly - */ - public $length; - - /** - * The precision for a decimal (exact numeric) column (Applies only for decimal column). - * - * @var int|null - * @readonly - */ - public $precision = 0; - - /** - * The scale for a decimal (exact numeric) column (Applies only for decimal column). - * - * @var int|null - * @readonly - */ - public $scale = 0; - - /** - * @var bool - * @readonly - */ - public $unique = false; - - /** - * @var bool - * @readonly - */ - public $nullable = false; - - /** - * @var bool - * @readonly - */ - public $insertable = true; - - /** - * @var bool - * @readonly - */ - public $updatable = true; - - /** - * @var class-string|null - * @readonly - */ - public $enumType = null; - - /** - * @var array - * @readonly - */ - public $options = []; - - /** - * @var string|null - * @readonly - */ - public $columnDefinition; - - /** - * @var string|null - * @readonly - * @phpstan-var 'NEVER'|'INSERT'|'ALWAYS'|null - * @Enum({"NEVER", "INSERT", "ALWAYS"}) - */ - public $generated; - - /** + * @param int|null $precision The precision for a decimal (exact numeric) column (Applies only for decimal column). + * @param int|null $scale The scale for a decimal (exact numeric) column (Applies only for decimal column). * @param class-string|null $enumType * @param array $options * @phpstan-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated */ public function __construct( - ?string $name = null, - ?string $type = null, - ?int $length = null, - ?int $precision = null, - ?int $scale = null, - bool $unique = false, - bool $nullable = false, - bool $insertable = true, - bool $updatable = true, - ?string $enumType = null, - array $options = [], - ?string $columnDefinition = null, - ?string $generated = null + public readonly string|null $name = null, + public readonly string|null $type = null, + public readonly int|null $length = null, + public readonly int|null $precision = null, + public readonly int|null $scale = null, + public readonly bool $unique = false, + public readonly bool $nullable = false, + public readonly bool $insertable = true, + public readonly bool $updatable = true, + public readonly string|null $enumType = null, + public readonly array $options = [], + public readonly string|null $columnDefinition = null, + public readonly string|null $generated = null, ) { - $this->name = $name; - $this->type = $type; - $this->length = $length; - $this->precision = $precision; - $this->scale = $scale; - $this->unique = $unique; - $this->nullable = $nullable; - $this->insertable = $insertable; - $this->updatable = $updatable; - $this->enumType = $enumType; - $this->options = $options; - $this->columnDefinition = $columnDefinition; - $this->generated = $generated; } } diff --git a/src/Mapping/ColumnResult.php b/src/Mapping/ColumnResult.php deleted file mode 100644 index 20c90d1e08e..00000000000 --- a/src/Mapping/ColumnResult.php +++ /dev/null @@ -1,22 +0,0 @@ -class = $class; + public function __construct( + public readonly string|null $class = null, + ) { } } diff --git a/src/Mapping/DefaultEntityListenerResolver.php b/src/Mapping/DefaultEntityListenerResolver.php index 2658637a535..4878fbdd429 100644 --- a/src/Mapping/DefaultEntityListenerResolver.php +++ b/src/Mapping/DefaultEntityListenerResolver.php @@ -4,12 +4,6 @@ namespace Doctrine\ORM\Mapping; -use InvalidArgumentException; - -use function get_class; -use function gettype; -use function is_object; -use function sprintf; use function trim; /** @@ -18,12 +12,9 @@ class DefaultEntityListenerResolver implements EntityListenerResolver { /** @var array Map to store entity listener instances. */ - private $instances = []; + private array $instances = []; - /** - * {@inheritDoc} - */ - public function clear($className = null) + public function clear(string|null $className = null): void { if ($className === null) { $this->instances = []; @@ -32,33 +23,18 @@ public function clear($className = null) } $className = trim($className, '\\'); - if (isset($this->instances[$className])) { - unset($this->instances[$className]); - } + unset($this->instances[$className]); } - /** - * {@inheritDoc} - */ - public function register($object) + public function register(object $object): void { - if (! is_object($object)) { - throw new InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object))); - } - - $this->instances[get_class($object)] = $object; + $this->instances[$object::class] = $object; } - /** - * {@inheritDoc} - */ - public function resolve($className) + public function resolve(string $className): object { $className = trim($className, '\\'); - if (isset($this->instances[$className])) { - return $this->instances[$className]; - } - return $this->instances[$className] = new $className(); + return $this->instances[$className] ??= new $className(); } } diff --git a/src/Mapping/DefaultNamingStrategy.php b/src/Mapping/DefaultNamingStrategy.php index 350fdc3edef..15218f9cd7c 100644 --- a/src/Mapping/DefaultNamingStrategy.php +++ b/src/Mapping/DefaultNamingStrategy.php @@ -16,10 +16,7 @@ */ class DefaultNamingStrategy implements NamingStrategy { - /** - * {@inheritDoc} - */ - public function classToTableName($className) + public function classToTableName(string $className): string { if (str_contains($className, '\\')) { return substr($className, strrpos($className, '\\') + 1); @@ -28,55 +25,43 @@ public function classToTableName($className) return $className; } - /** - * {@inheritDoc} - */ - public function propertyToColumnName($propertyName, $className = null) + public function propertyToColumnName(string $propertyName, string $className): string { return $propertyName; } - /** - * {@inheritDoc} - */ - public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) - { + public function embeddedFieldToColumnName( + string $propertyName, + string $embeddedColumnName, + string $className, + string $embeddedClassName, + ): string { return $propertyName . '_' . $embeddedColumnName; } - /** - * {@inheritDoc} - */ - public function referenceColumnName() + public function referenceColumnName(): string { return 'id'; } - /** - * {@inheritDoc} - * - * @param string $propertyName - * @param class-string $className - */ - public function joinColumnName($propertyName, $className = null) + public function joinColumnName(string $propertyName, string $className): string { return $propertyName . '_' . $this->referenceColumnName(); } - /** - * {@inheritDoc} - */ - public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) - { + public function joinTableName( + string $sourceEntity, + string $targetEntity, + string $propertyName, + ): string { return strtolower($this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity)); } - /** - * {@inheritDoc} - */ - public function joinKeyColumnName($entityName, $referencedColumnName = null) - { + public function joinKeyColumnName( + string $entityName, + string|null $referencedColumnName, + ): string { return strtolower($this->classToTableName($entityName) . '_' . ($referencedColumnName ?: $this->referenceColumnName())); } diff --git a/src/Mapping/DefaultQuoteStrategy.php b/src/Mapping/DefaultQuoteStrategy.php index 78404e88dd1..b42de4e862b 100644 --- a/src/Mapping/DefaultQuoteStrategy.php +++ b/src/Mapping/DefaultQuoteStrategy.php @@ -9,6 +9,7 @@ use function array_map; use function array_merge; +use function assert; use function is_numeric; use function preg_replace; use function substr; @@ -20,14 +21,11 @@ class DefaultQuoteStrategy implements QuoteStrategy { use SQLResultCasing; - /** - * {@inheritDoc} - */ - public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string { - return isset($class->fieldMappings[$fieldName]['quoted']) - ? $platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName']) - : $class->fieldMappings[$fieldName]['columnName']; + return isset($class->fieldMappings[$fieldName]->quoted) + ? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName) + : $class->fieldMappings[$fieldName]->columnName; } /** @@ -35,71 +33,61 @@ public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform * * @todo Table names should be computed in DBAL depending on the platform */ - public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string { $tableName = $class->table['name']; if (! empty($class->table['schema'])) { $tableName = $class->table['schema'] . '.' . $class->table['name']; - - // @phpstan-ignore method.deprecated - if (! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { - $tableName = $class->table['schema'] . '__' . $class->table['name']; - } } return isset($class->table['quoted']) - ? $platform->quoteIdentifier($tableName) + ? $platform->quoteSingleIdentifier($tableName) : $tableName; } /** * {@inheritDoc} */ - public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string { return isset($definition['quoted']) - ? $platform->quoteIdentifier($definition['sequenceName']) + ? $platform->quoteSingleIdentifier($definition['sequenceName']) : $definition['sequenceName']; } - /** - * {@inheritDoc} - */ - public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string { - return isset($joinColumn['quoted']) - ? $platform->quoteIdentifier($joinColumn['name']) - : $joinColumn['name']; + return isset($joinColumn->quoted) + ? $platform->quoteSingleIdentifier($joinColumn->name) + : $joinColumn->name; } - /** - * {@inheritDoc} - */ - public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) - { - return isset($joinColumn['quoted']) - ? $platform->quoteIdentifier($joinColumn['referencedColumnName']) - : $joinColumn['referencedColumnName']; + public function getReferencedJoinColumnName( + JoinColumnMapping $joinColumn, + ClassMetadata $class, + AbstractPlatform $platform, + ): string { + return isset($joinColumn->quoted) + ? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName) + : $joinColumn->referencedColumnName; } - /** - * {@inheritDoc} - */ - public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) - { + public function getJoinTableName( + ManyToManyOwningSideMapping $association, + ClassMetadata $class, + AbstractPlatform $platform, + ): string { $schema = ''; - if (isset($association['joinTable']['schema'])) { - $schema = $association['joinTable']['schema']; - // @phpstan-ignore method.deprecated - $schema .= ! $platform->supportsSchemas() && $platform->canEmulateSchemas() ? '__' : '.'; + if (isset($association->joinTable->schema)) { + $schema = $association->joinTable->schema . '.'; } - $tableName = $association['joinTable']['name']; + $tableName = $association->joinTable->name; - if (isset($association['joinTable']['quoted'])) { - $tableName = $platform->quoteIdentifier($tableName); + if (isset($association->joinTable->quoted)) { + $tableName = $platform->quoteSingleIdentifier($tableName); } return $schema . $tableName; @@ -108,7 +96,7 @@ public function getJoinTableName(array $association, ClassMetadata $class, Abstr /** * {@inheritDoc} */ - public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array { $quotedColumnNames = []; @@ -120,14 +108,14 @@ public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform } // Association defined as Id field - $joinColumns = $class->associationMappings[$fieldName]['joinColumns']; + $assoc = $class->associationMappings[$fieldName]; + assert($assoc->isToOneOwningSide()); + $joinColumns = $assoc->joinColumns; $assocQuotedColumnNames = array_map( - static function ($joinColumn) use ($platform) { - return isset($joinColumn['quoted']) - ? $platform->quoteIdentifier($joinColumn['name']) - : $joinColumn['name']; - }, - $joinColumns + static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted) + ? $platform->quoteSingleIdentifier($joinColumn->name) + : $joinColumn->name, + $joinColumns, ); $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); @@ -136,11 +124,12 @@ static function ($joinColumn) use ($platform) { return $quotedColumnNames; } - /** - * {@inheritDoc} - */ - public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ?ClassMetadata $class = null) - { + public function getColumnAlias( + string $columnName, + int $counter, + AbstractPlatform $platform, + ClassMetadata|null $class = null, + ): string { // 1 ) Concatenate column name and counter // 2 ) Trim the column alias to the maximum identifier length of the platform. // If the alias is to long, characters are cut off from the beginning. diff --git a/src/Mapping/DefaultTypedFieldMapper.php b/src/Mapping/DefaultTypedFieldMapper.php index a62a347135a..47b842cf496 100644 --- a/src/Mapping/DefaultTypedFieldMapper.php +++ b/src/Mapping/DefaultTypedFieldMapper.php @@ -16,16 +16,15 @@ use function array_merge; use function assert; +use function defined; use function enum_exists; use function is_a; -use const PHP_VERSION_ID; - /** @phpstan-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */ final class DefaultTypedFieldMapper implements TypedFieldMapper { /** @var array|string> $typedFieldMappings */ - private $typedFieldMappings; + private array $typedFieldMappings; private const DEFAULT_TYPED_FIELD_MAPPINGS = [ DateInterval::class => Types::DATEINTERVAL, @@ -51,28 +50,38 @@ public function validateAndComplete(array $mapping, ReflectionProperty $field): { $type = $field->getType(); + if (! $type instanceof ReflectionNamedType) { + return $mapping; + } + if ( - ! isset($mapping['type']) - && ($type instanceof ReflectionNamedType) + ! $type->isBuiltin() + && enum_exists($type->getName()) + && (! isset($mapping['type']) || ( + defined('Doctrine\DBAL\Types\Types::ENUM') + && $mapping['type'] === Types::ENUM + )) ) { - if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) { - $reflection = new ReflectionEnum($type->getName()); - if (! $reflection->isBacked()) { - throw MappingException::backedEnumTypeRequired( - $field->class, - $mapping['fieldName'], - $type->getName() - ); - } - - assert(is_a($type->getName(), BackedEnum::class, true)); - $mapping['enumType'] = $type->getName(); - $type = $reflection->getBackingType(); + $reflection = new ReflectionEnum($type->getName()); + if (! $reflection->isBacked()) { + throw MappingException::backedEnumTypeRequired( + $field->class, + $mapping['fieldName'], + $type->getName(), + ); } - if (isset($this->typedFieldMappings[$type->getName()])) { - $mapping['type'] = $this->typedFieldMappings[$type->getName()]; - } + assert(is_a($type->getName(), BackedEnum::class, true)); + $mapping['enumType'] = $type->getName(); + $type = $reflection->getBackingType(); + } + + if (isset($mapping['type'])) { + return $mapping; + } + + if (isset($this->typedFieldMappings[$type->getName()])) { + $mapping['type'] = $this->typedFieldMappings[$type->getName()]; } return $mapping; diff --git a/src/Mapping/DiscriminatorColumn.php b/src/Mapping/DiscriminatorColumn.php index 9cec7ad75ef..fb9c7d34a9b 100644 --- a/src/Mapping/DiscriminatorColumn.php +++ b/src/Mapping/DiscriminatorColumn.php @@ -5,69 +5,20 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; +use BackedEnum; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class DiscriminatorColumn implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var string|null - * @readonly - */ - public $type; - - /** - * @var int|null - * @readonly - */ - public $length; - - /** - * @var string|null - * @readonly - */ - public $columnDefinition; - - /** - * @var class-string<\BackedEnum>|null - * @readonly - */ - public $enumType = null; - - /** - * @var array - * @readonly - */ - public $options = []; - - /** - * @param class-string<\BackedEnum>|null $enumType - * @param array $options - */ public function __construct( - ?string $name = null, - ?string $type = null, - ?int $length = null, - ?string $columnDefinition = null, - ?string $enumType = null, - array $options = [] + public readonly string|null $name = null, + public readonly string|null $type = null, + public readonly int|null $length = null, + public readonly string|null $columnDefinition = null, + /** @var class-string|null */ + public readonly string|null $enumType = null, + /** @var array */ + public readonly array $options = [], ) { - $this->name = $name; - $this->type = $type; - $this->length = $length; - $this->columnDefinition = $columnDefinition; - $this->enumType = $enumType; - $this->options = $options; } } diff --git a/src/Mapping/DiscriminatorColumnMapping.php b/src/Mapping/DiscriminatorColumnMapping.php new file mode 100644 index 00000000000..a96d22dbcbb --- /dev/null +++ b/src/Mapping/DiscriminatorColumnMapping.php @@ -0,0 +1,83 @@ + */ +final class DiscriminatorColumnMapping implements ArrayAccess +{ + use ArrayAccessImplementation; + + /** The database length of the column. Optional. Default value taken from the type. */ + public int|null $length = null; + + public string|null $columnDefinition = null; + + /** @var class-string|null */ + public string|null $enumType = null; + + /** @var array */ + public array $options = []; + + public function __construct( + public string $type, + public string $fieldName, + public string $name, + ) { + } + + /** + * @phpstan-param array{ + * type: string, + * fieldName: string, + * name: string, + * length?: int|null, + * columnDefinition?: string|null, + * enumType?: class-string|null, + * options?: array|null, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): self + { + $mapping = new self( + $mappingArray['type'], + $mappingArray['fieldName'], + $mappingArray['name'], + ); + foreach ($mappingArray as $key => $value) { + if (in_array($key, ['type', 'fieldName', 'name'])) { + continue; + } + + if (property_exists($mapping, $key)) { + $mapping->$key = $value ?? $mapping->$key; + } else { + throw new Exception('Unknown property ' . $key . ' on class ' . static::class); + } + } + + return $mapping; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = ['type', 'fieldName', 'name']; + + foreach (['length', 'columnDefinition', 'enumType', 'options'] as $stringOrArrayKey) { + if ($this->$stringOrArrayKey !== null) { + $serialized[] = $stringOrArrayKey; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/DiscriminatorMap.php b/src/Mapping/DiscriminatorMap.php index 6c12a5fdcad..2b204a9ea58 100644 --- a/src/Mapping/DiscriminatorMap.php +++ b/src/Mapping/DiscriminatorMap.php @@ -5,25 +5,13 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class DiscriminatorMap implements MappingAttribute { - /** - * @var array - * @readonly - */ - public $value; - /** @param array $value */ - public function __construct(array $value) - { - $this->value = $value; + public function __construct( + public readonly array $value, + ) { } } diff --git a/src/Mapping/Driver/AnnotationDriver.php b/src/Mapping/Driver/AnnotationDriver.php deleted file mode 100644 index 07c50692815..00000000000 --- a/src/Mapping/Driver/AnnotationDriver.php +++ /dev/null @@ -1,895 +0,0 @@ - */ - protected $entityAnnotationClasses = [ - Mapping\Entity::class => 1, - Mapping\MappedSuperclass::class => 2, - ]; - - /** - * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading - * docblock annotations. - * - * @param Reader $reader The AnnotationReader to use - * @param string|string[]|null $paths One or multiple paths where mapping classes can be found. - */ - public function __construct($reader, $paths = null, bool $reportFieldsWhereDeclared = false) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/10098', - 'The annotation mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to the attribute or XML driver.' - ); - $this->reader = $reader; - - $this->addPaths((array) $paths); - - if (! $reportFieldsWhereDeclared) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10455', - 'In ORM 3.0, the AttributeDriver will report fields for the classes where they are declared. This may uncover invalid mapping configurations. To opt into the new mode also with the AnnotationDriver today, set the "reportFieldsWhereDeclared" constructor parameter to true.', - self::class - ); - } - - $this->reportFieldsWhereDeclared = $reportFieldsWhereDeclared; - } - - /** - * {@inheritDoc} - * - * @param class-string $className - * @param ClassMetadata $metadata - * - * @template T of object - */ - public function loadMetadataForClass($className, PersistenceClassMetadata $metadata) - { - $class = $metadata->getReflectionClass() - // this happens when running annotation driver in combination with - // static reflection services. This is not the nicest fix - ?? new ReflectionClass($metadata->name); - - $classAnnotations = $this->reader->getClassAnnotations($class); - foreach ($classAnnotations as $key => $annot) { - if (! is_numeric($key)) { - continue; - } - - $classAnnotations[get_class($annot)] = $annot; - } - - // Evaluate Entity annotation - if (isset($classAnnotations[Mapping\Entity::class])) { - $entityAnnot = $classAnnotations[Mapping\Entity::class]; - assert($entityAnnot instanceof Mapping\Entity); - if ($entityAnnot->repositoryClass !== null) { - $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); - } - - if ($entityAnnot->readOnly) { - $metadata->markReadOnly(); - } - } elseif (isset($classAnnotations[Mapping\MappedSuperclass::class])) { - $mappedSuperclassAnnot = $classAnnotations[Mapping\MappedSuperclass::class]; - assert($mappedSuperclassAnnot instanceof Mapping\MappedSuperclass); - - $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); - $metadata->isMappedSuperclass = true; - } elseif (isset($classAnnotations[Mapping\Embeddable::class])) { - $metadata->isEmbeddedClass = true; - } else { - throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); - } - - // Evaluate Table annotation - if (isset($classAnnotations[Mapping\Table::class])) { - $tableAnnot = $classAnnotations[Mapping\Table::class]; - assert($tableAnnot instanceof Mapping\Table); - $primaryTable = [ - 'name' => $tableAnnot->name, - 'schema' => $tableAnnot->schema, - ]; - - foreach ($tableAnnot->indexes ?? [] as $indexAnnot) { - $index = []; - - if (! empty($indexAnnot->columns)) { - $index['columns'] = $indexAnnot->columns; - } - - if (! empty($indexAnnot->fields)) { - $index['fields'] = $indexAnnot->fields; - } - - if ( - isset($index['columns'], $index['fields']) - || ( - ! isset($index['columns']) - && ! isset($index['fields']) - ) - ) { - throw MappingException::invalidIndexConfiguration( - $className, - (string) ($indexAnnot->name ?? count($primaryTable['indexes'])) - ); - } - - if (! empty($indexAnnot->flags)) { - $index['flags'] = $indexAnnot->flags; - } - - if (! empty($indexAnnot->options)) { - $index['options'] = $indexAnnot->options; - } - - if (! empty($indexAnnot->name)) { - $primaryTable['indexes'][$indexAnnot->name] = $index; - } else { - $primaryTable['indexes'][] = $index; - } - } - - foreach ($tableAnnot->uniqueConstraints ?? [] as $uniqueConstraintAnnot) { - $uniqueConstraint = []; - - if (! empty($uniqueConstraintAnnot->columns)) { - $uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns; - } - - if (! empty($uniqueConstraintAnnot->fields)) { - $uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields; - } - - if ( - isset($uniqueConstraint['columns'], $uniqueConstraint['fields']) - || ( - ! isset($uniqueConstraint['columns']) - && ! isset($uniqueConstraint['fields']) - ) - ) { - throw MappingException::invalidUniqueConstraintConfiguration( - $className, - (string) ($uniqueConstraintAnnot->name ?? count($primaryTable['uniqueConstraints'])) - ); - } - - if (! empty($uniqueConstraintAnnot->options)) { - $uniqueConstraint['options'] = $uniqueConstraintAnnot->options; - } - - if (! empty($uniqueConstraintAnnot->name)) { - $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; - } else { - $primaryTable['uniqueConstraints'][] = $uniqueConstraint; - } - } - - if ($tableAnnot->options) { - $primaryTable['options'] = $tableAnnot->options; - } - - $metadata->setPrimaryTable($primaryTable); - } - - // Evaluate @Cache annotation - if (isset($classAnnotations[Mapping\Cache::class])) { - $cacheAnnot = $classAnnotations[Mapping\Cache::class]; - $cacheMap = [ - 'region' => $cacheAnnot->region, - 'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), - ]; - - $metadata->enableCache($cacheMap); - } - - // Evaluate NamedNativeQueries annotation - if (isset($classAnnotations[Mapping\NamedNativeQueries::class])) { - $namedNativeQueriesAnnot = $classAnnotations[Mapping\NamedNativeQueries::class]; - - foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { - $metadata->addNamedNativeQuery( - [ - 'name' => $namedNativeQuery->name, - 'query' => $namedNativeQuery->query, - 'resultClass' => $namedNativeQuery->resultClass, - 'resultSetMapping' => $namedNativeQuery->resultSetMapping, - ] - ); - } - } - - // Evaluate SqlResultSetMappings annotation - if (isset($classAnnotations[Mapping\SqlResultSetMappings::class])) { - $sqlResultSetMappingsAnnot = $classAnnotations[Mapping\SqlResultSetMappings::class]; - - foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { - $entities = []; - $columns = []; - foreach ($resultSetMapping->entities as $entityResultAnnot) { - $entityResult = [ - 'fields' => [], - 'entityClass' => $entityResultAnnot->entityClass, - 'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, - ]; - - foreach ($entityResultAnnot->fields as $fieldResultAnnot) { - $entityResult['fields'][] = [ - 'name' => $fieldResultAnnot->name, - 'column' => $fieldResultAnnot->column, - ]; - } - - $entities[] = $entityResult; - } - - foreach ($resultSetMapping->columns as $columnResultAnnot) { - $columns[] = [ - 'name' => $columnResultAnnot->name, - ]; - } - - $metadata->addSqlResultSetMapping( - [ - 'name' => $resultSetMapping->name, - 'entities' => $entities, - 'columns' => $columns, - ] - ); - } - } - - // Evaluate NamedQueries annotation - if (isset($classAnnotations[Mapping\NamedQueries::class])) { - $namedQueriesAnnot = $classAnnotations[Mapping\NamedQueries::class]; - - if (! is_array($namedQueriesAnnot->value)) { - throw new UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.'); - } - - foreach ($namedQueriesAnnot->value as $namedQuery) { - if (! ($namedQuery instanceof Mapping\NamedQuery)) { - throw new UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.'); - } - - $metadata->addNamedQuery( - [ - 'name' => $namedQuery->name, - 'query' => $namedQuery->query, - ] - ); - } - } - - // Evaluate InheritanceType annotation - if (isset($classAnnotations[Mapping\InheritanceType::class])) { - $inheritanceTypeAnnot = $classAnnotations[Mapping\InheritanceType::class]; - assert($inheritanceTypeAnnot instanceof Mapping\InheritanceType); - - $metadata->setInheritanceType( - constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value) - ); - - if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { - // Evaluate DiscriminatorColumn annotation - if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) { - $discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class]; - assert($discrColumnAnnot instanceof Mapping\DiscriminatorColumn); - - $columnDef = [ - 'name' => $discrColumnAnnot->name, - 'type' => $discrColumnAnnot->type ?: 'string', - 'length' => $discrColumnAnnot->length ?? 255, - 'columnDefinition' => $discrColumnAnnot->columnDefinition, - 'enumType' => $discrColumnAnnot->enumType, - ]; - - if ($discrColumnAnnot->options) { - $columnDef['options'] = $discrColumnAnnot->options; - } - - $metadata->setDiscriminatorColumn($columnDef); - } else { - $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]); - } - - // Evaluate DiscriminatorMap annotation - if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) { - $discrMapAnnot = $classAnnotations[Mapping\DiscriminatorMap::class]; - assert($discrMapAnnot instanceof Mapping\DiscriminatorMap); - $metadata->setDiscriminatorMap($discrMapAnnot->value); - } - } - } - - // Evaluate DoctrineChangeTrackingPolicy annotation - if (isset($classAnnotations[Mapping\ChangeTrackingPolicy::class])) { - $changeTrackingAnnot = $classAnnotations[Mapping\ChangeTrackingPolicy::class]; - assert($changeTrackingAnnot instanceof Mapping\ChangeTrackingPolicy); - $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value)); - } - - // Evaluate annotations on properties/fields - foreach ($class->getProperties() as $property) { - if ($this->isRepeatedPropertyDeclaration($property, $metadata)) { - continue; - } - - $mapping = []; - $mapping['fieldName'] = $property->name; - - // Evaluate @Cache annotation - $cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class); - if ($cacheAnnot !== null) { - $mapping['cache'] = $metadata->getAssociationCacheDefaults( - $mapping['fieldName'], - [ - 'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), - 'region' => $cacheAnnot->region, - ] - ); - } - - // Check for JoinColumn/JoinColumns annotations - $joinColumns = []; - - $joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class); - if ($joinColumnAnnot) { - $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); - } else { - $joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class); - if ($joinColumnsAnnot) { - foreach ($joinColumnsAnnot->value as $joinColumn) { - $joinColumns[] = $this->joinColumnToArray($joinColumn); - } - } - } - - // Field can only be annotated with one of: - // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany - $columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class); - if ($columnAnnot) { - $mapping = $this->columnToArray($property->name, $columnAnnot); - - $idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class); - if ($idAnnot) { - $mapping['id'] = true; - } - - $generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class); - if ($generatedValueAnnot) { - $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); - } - - if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) { - $metadata->setVersionMapping($mapping); - } - - $metadata->mapField($mapping); - - // Check for SequenceGenerator/TableGenerator definition - $seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class); - if ($seqGeneratorAnnot) { - $metadata->setSequenceGeneratorDefinition( - [ - 'sequenceName' => $seqGeneratorAnnot->sequenceName, - 'allocationSize' => $seqGeneratorAnnot->allocationSize, - 'initialValue' => $seqGeneratorAnnot->initialValue, - ] - ); - } else { - $customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class); - if ($customGeneratorAnnot) { - $metadata->setCustomGeneratorDefinition( - [ - 'class' => $customGeneratorAnnot->class, - ] - ); - } - } - } else { - $this->loadRelationShipMapping( - $property, - $mapping, - $metadata, - $joinColumns, - $className - ); - } - } - - // Evaluate AssociationOverrides annotation - if (isset($classAnnotations[Mapping\AssociationOverrides::class])) { - $associationOverridesAnnot = $classAnnotations[Mapping\AssociationOverrides::class]; - assert($associationOverridesAnnot instanceof Mapping\AssociationOverrides); - - foreach ($associationOverridesAnnot->overrides as $associationOverride) { - $override = []; - $fieldName = $associationOverride->name; - - // Check for JoinColumn/JoinColumns annotations - if ($associationOverride->joinColumns) { - $joinColumns = []; - - foreach ($associationOverride->joinColumns as $joinColumn) { - $joinColumns[] = $this->joinColumnToArray($joinColumn); - } - - $override['joinColumns'] = $joinColumns; - } - - // Check for JoinTable annotations - if ($associationOverride->joinTable) { - $joinTableAnnot = $associationOverride->joinTable; - $joinTable = [ - 'name' => $joinTableAnnot->name, - 'schema' => $joinTableAnnot->schema, - ]; - - foreach ($joinTableAnnot->joinColumns as $joinColumn) { - $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); - } - - foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { - $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); - } - - $override['joinTable'] = $joinTable; - } - - // Check for inversedBy - if ($associationOverride->inversedBy) { - $override['inversedBy'] = $associationOverride->inversedBy; - } - - // Check for `fetch` - if ($associationOverride->fetch) { - $override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch); - } - - $metadata->setAssociationOverride($fieldName, $override); - } - } - - // Evaluate AttributeOverrides annotation - if (isset($classAnnotations[Mapping\AttributeOverrides::class])) { - $attributeOverridesAnnot = $classAnnotations[Mapping\AttributeOverrides::class]; - assert($attributeOverridesAnnot instanceof Mapping\AttributeOverrides); - - foreach ($attributeOverridesAnnot->overrides as $attributeOverrideAnnot) { - $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); - - $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); - } - } - - // Evaluate EntityListeners annotation - if (isset($classAnnotations[Mapping\EntityListeners::class])) { - $entityListenersAnnot = $classAnnotations[Mapping\EntityListeners::class]; - assert($entityListenersAnnot instanceof Mapping\EntityListeners); - - foreach ($entityListenersAnnot->value as $item) { - $listenerClassName = $metadata->fullyQualifiedClassName($item); - - if (! class_exists($listenerClassName)) { - throw MappingException::entityListenerClassNotFound($listenerClassName, $className); - } - - $hasMapping = false; - $listenerClass = new ReflectionClass($listenerClassName); - - foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - // find method callbacks. - $callbacks = $this->getMethodCallbacks($method); - $hasMapping = $hasMapping ?: ! empty($callbacks); - - foreach ($callbacks as $value) { - $metadata->addEntityListener($value[1], $listenerClassName, $value[0]); - } - } - - // Evaluate the listener using naming convention. - if (! $hasMapping) { - EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName); - } - } - } - - // Evaluate @HasLifecycleCallbacks annotation - if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) { - foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - foreach ($this->getMethodCallbacks($method) as $value) { - $metadata->addLifecycleCallback($value[0], $value[1]); - } - } - } - } - - /** - * @param mixed[] $joinColumns - * @param class-string $className - * @param array $mapping - */ - private function loadRelationShipMapping( - ReflectionProperty $property, - array &$mapping, - PersistenceClassMetadata $metadata, - array $joinColumns, - string $className - ): void { - $oneToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class); - if ($oneToOneAnnot) { - $idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class); - if ($idAnnot) { - $mapping['id'] = true; - } - - $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; - $mapping['joinColumns'] = $joinColumns; - $mapping['mappedBy'] = $oneToOneAnnot->mappedBy; - $mapping['inversedBy'] = $oneToOneAnnot->inversedBy; - $mapping['cascade'] = $oneToOneAnnot->cascade; - $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; - $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch); - $metadata->mapOneToOne($mapping); - - return; - } - - $oneToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class); - if ($oneToManyAnnot) { - $mapping['mappedBy'] = $oneToManyAnnot->mappedBy; - $mapping['targetEntity'] = $oneToManyAnnot->targetEntity; - $mapping['cascade'] = $oneToManyAnnot->cascade; - $mapping['indexBy'] = $oneToManyAnnot->indexBy; - $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; - $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch); - - $orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class); - if ($orderByAnnot) { - $mapping['orderBy'] = $orderByAnnot->value; - } - - $metadata->mapOneToMany($mapping); - } - - $manyToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class); - if ($manyToOneAnnot) { - $idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class); - if ($idAnnot) { - $mapping['id'] = true; - } - - $mapping['joinColumns'] = $joinColumns; - $mapping['cascade'] = $manyToOneAnnot->cascade; - $mapping['inversedBy'] = $manyToOneAnnot->inversedBy; - $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; - $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch); - $metadata->mapManyToOne($mapping); - } - - $manyToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class); - if ($manyToManyAnnot) { - $joinTable = []; - - $joinTableAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class); - if ($joinTableAnnot) { - $joinTable = [ - 'name' => $joinTableAnnot->name, - 'schema' => $joinTableAnnot->schema, - ]; - - if ($joinTableAnnot->options) { - $joinTable['options'] = $joinTableAnnot->options; - } - - foreach ($joinTableAnnot->joinColumns as $joinColumn) { - $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); - } - - foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { - $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); - } - } - - $mapping['joinTable'] = $joinTable; - $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; - $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; - $mapping['inversedBy'] = $manyToManyAnnot->inversedBy; - $mapping['cascade'] = $manyToManyAnnot->cascade; - $mapping['indexBy'] = $manyToManyAnnot->indexBy; - $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval; - $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch); - - $orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class); - if ($orderByAnnot) { - $mapping['orderBy'] = $orderByAnnot->value; - } - - $metadata->mapManyToMany($mapping); - } - - $embeddedAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class); - if ($embeddedAnnot) { - $mapping['class'] = $embeddedAnnot->class; - $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix; - - $metadata->mapEmbedded($mapping); - } - } - - /** - * Attempts to resolve the fetch mode. - * - * @param class-string $className - * - * @phpstan-return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata. - * - * @throws MappingException If the fetch mode is not valid. - */ - private function getFetchMode(string $className, string $fetchMode): int - { - if (! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { - throw MappingException::invalidFetchMode($className, $fetchMode); - } - - return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); - } - - /** - * Attempts to resolve the generated mode. - * - * @phpstan-return ClassMetadata::GENERATED_* - * - * @throws MappingException If the fetch mode is not valid. - */ - private function getGeneratedMode(string $generatedMode): int - { - if (! defined('Doctrine\ORM\Mapping\ClassMetadata::GENERATED_' . $generatedMode)) { - throw MappingException::invalidGeneratedMode($generatedMode); - } - - return constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATED_' . $generatedMode); - } - - /** - * Parses the given method. - * - * @return list - * @phpstan-return list - */ - private function getMethodCallbacks(ReflectionMethod $method): array - { - $callbacks = []; - $annotations = $this->reader->getMethodAnnotations($method); - - foreach ($annotations as $annot) { - if ($annot instanceof Mapping\PrePersist) { - $callbacks[] = [$method->name, Events::prePersist]; - } - - if ($annot instanceof Mapping\PostPersist) { - $callbacks[] = [$method->name, Events::postPersist]; - } - - if ($annot instanceof Mapping\PreUpdate) { - $callbacks[] = [$method->name, Events::preUpdate]; - } - - if ($annot instanceof Mapping\PostUpdate) { - $callbacks[] = [$method->name, Events::postUpdate]; - } - - if ($annot instanceof Mapping\PreRemove) { - $callbacks[] = [$method->name, Events::preRemove]; - } - - if ($annot instanceof Mapping\PostRemove) { - $callbacks[] = [$method->name, Events::postRemove]; - } - - if ($annot instanceof Mapping\PostLoad) { - $callbacks[] = [$method->name, Events::postLoad]; - } - - if ($annot instanceof Mapping\PreFlush) { - $callbacks[] = [$method->name, Events::preFlush]; - } - } - - return $callbacks; - } - - /** - * Parse the given JoinColumn as array - * - * @return mixed[] - * @phpstan-return array{ - * name: string|null, - * unique: bool, - * nullable: bool, - * onDelete: mixed, - * columnDefinition: string|null, - * referencedColumnName: string, - * options?: array - * } - */ - private function joinColumnToArray(Mapping\JoinColumn $joinColumn): array - { - $mapping = [ - 'name' => $joinColumn->name, - 'unique' => $joinColumn->unique, - 'nullable' => $joinColumn->nullable, - 'onDelete' => $joinColumn->onDelete, - 'columnDefinition' => $joinColumn->columnDefinition, - 'referencedColumnName' => $joinColumn->referencedColumnName, - ]; - - if ($joinColumn->options) { - $mapping['options'] = $joinColumn->options; - } - - return $mapping; - } - - /** - * Parse the given Column as array - * - * @return mixed[] - * @phpstan-return array{ - * fieldName: string, - * type: mixed, - * scale: int, - * length: int, - * unique: bool, - * nullable: bool, - * precision: int, - * notInsertable?: bool, - * notUpdateble?: bool, - * generated?: ClassMetadata::GENERATED_*, - * enumType?: class-string, - * options?: mixed[], - * columnName?: string, - * columnDefinition?: string - * } - */ - private function columnToArray(string $fieldName, Mapping\Column $column): array - { - $mapping = [ - 'fieldName' => $fieldName, - 'type' => $column->type, - 'scale' => $column->scale, - 'length' => $column->length, - 'unique' => $column->unique, - 'nullable' => $column->nullable, - 'precision' => $column->precision, - ]; - - if (! $column->insertable) { - $mapping['notInsertable'] = true; - } - - if (! $column->updatable) { - $mapping['notUpdatable'] = true; - } - - if ($column->generated) { - $mapping['generated'] = $this->getGeneratedMode($column->generated); - } - - if ($column->options) { - $mapping['options'] = $column->options; - } - - if (isset($column->name)) { - $mapping['columnName'] = $column->name; - } - - if (isset($column->columnDefinition)) { - $mapping['columnDefinition'] = $column->columnDefinition; - } - - if ($column->enumType !== null) { - $mapping['enumType'] = $column->enumType; - } - - return $mapping; - } - - /** - * Retrieve the current annotation reader - * - * @return Reader - */ - public function getReader() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9587', - '%s is deprecated with no replacement', - __METHOD__ - ); - - return $this->reader; - } - - /** - * {@inheritDoc} - */ - public function isTransient($className) - { - $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className)); - - foreach ($classAnnotations as $annot) { - if (isset($this->entityAnnotationClasses[get_class($annot)])) { - return false; - } - } - - return true; - } - - /** - * Factory method for the Annotation Driver. - * - * @param mixed[]|string $paths - * - * @return AnnotationDriver - */ - public static function create($paths = [], ?AnnotationReader $reader = null) - { - if ($reader === null) { - $reader = new AnnotationReader(); - } - - return new self($reader, $paths); - } -} diff --git a/src/Mapping/Driver/AttributeDriver.php b/src/Mapping/Driver/AttributeDriver.php index 4664e4ccc27..e337d60ea55 100644 --- a/src/Mapping/Driver/AttributeDriver.php +++ b/src/Mapping/Driver/AttributeDriver.php @@ -4,27 +4,25 @@ namespace Doctrine\ORM\Mapping\Driver; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Events; use Doctrine\ORM\Mapping; use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\MappingAttribute; use Doctrine\ORM\Mapping\MappingException; use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata; use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver; -use LogicException; +use Doctrine\Persistence\Mapping\Driver\MappingDriver; +use InvalidArgumentException; use ReflectionClass; use ReflectionMethod; +use function assert; use function class_exists; use function constant; use function defined; -use function get_class; +use function sprintf; -use const PHP_VERSION_ID; - -class AttributeDriver extends CompatibilityAnnotationDriver +class AttributeDriver implements MappingDriver { use ColocatedMappingDriver; use ReflectionBasedDriver; @@ -34,87 +32,32 @@ class AttributeDriver extends CompatibilityAnnotationDriver Mapping\MappedSuperclass::class => 2, ]; - /** - * @deprecated override isTransient() instead of overriding this property - * - * @var array, int> - */ - protected $entityAnnotationClasses = self::ENTITY_ATTRIBUTE_CLASSES; + private readonly AttributeReader $reader; /** - * The attribute reader. - * - * @internal this property will be private in 3.0 - * - * @var AttributeReader + * @param array $paths + * @param true $reportFieldsWhereDeclared no-op, to be removed in 4.0 */ - protected $reader; - - /** @param array $paths */ - public function __construct(array $paths, bool $reportFieldsWhereDeclared = false) + public function __construct(array $paths, bool $reportFieldsWhereDeclared = true) { - if (PHP_VERSION_ID < 80000) { - throw new LogicException( - 'The attribute metadata driver cannot be enabled on PHP 7. Please upgrade to PHP 8 or choose a different' - . ' metadata driver.' - ); + if (! $reportFieldsWhereDeclared) { + throw new InvalidArgumentException(sprintf( + 'The $reportFieldsWhereDeclared argument is no longer supported, make sure to omit it when calling %s.', + __METHOD__, + )); } $this->reader = new AttributeReader(); $this->addPaths($paths); - - // @phpstan-ignore property.deprecated - if ($this->entityAnnotationClasses !== self::ENTITY_ATTRIBUTE_CLASSES) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10204', - 'Changing the value of %s::$entityAnnotationClasses is deprecated and will have no effect in Doctrine ORM 3.0.', - self::class - ); - } - - if (! $reportFieldsWhereDeclared) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10455', - 'In ORM 3.0, the AttributeDriver will report fields for the classes where they are declared. This may uncover invalid mapping configurations. To opt into the new mode today, set the "reportFieldsWhereDeclared" constructor parameter to true.', - self::class - ); - } - - $this->reportFieldsWhereDeclared = $reportFieldsWhereDeclared; } - /** - * Retrieve the current annotation reader - * - * @deprecated no replacement planned. - * - * @return AttributeReader - */ - public function getReader() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9587', - '%s is deprecated with no replacement', - __METHOD__ - ); - - return $this->reader; - } - - /** - * {@inheritDoc} - */ - public function isTransient($className) + public function isTransient(string $className): bool { $classAttributes = $this->reader->getClassAttributes(new ReflectionClass($className)); foreach ($classAttributes as $a) { $attr = $a instanceof RepeatableAttributeCollection ? $a[0] : $a; - // @phpstan-ignore property.deprecated - if (isset($this->entityAnnotationClasses[get_class($attr)])) { + if (isset(self::ENTITY_ATTRIBUTE_CLASSES[$attr::class])) { return false; } } @@ -130,7 +73,7 @@ public function isTransient($className) * * @template T of object */ - public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void + public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void { $reflectionClass = $metadata->getReflectionClass() // this happens when running attribute driver in combination with @@ -173,6 +116,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad } if (isset($classAttributes[Mapping\Index::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\Index::class); + } + foreach ($classAttributes[Mapping\Index::class] as $idx => $indexAnnot) { $index = []; @@ -193,7 +140,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad ) { throw MappingException::invalidIndexConfiguration( $className, - (string) ($indexAnnot->name ?? $idx) + (string) ($indexAnnot->name ?? $idx), ); } @@ -214,6 +161,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad } if (isset($classAttributes[Mapping\UniqueConstraint::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\UniqueConstraint::class); + } + foreach ($classAttributes[Mapping\UniqueConstraint::class] as $idx => $uniqueConstraintAnnot) { $uniqueConstraint = []; @@ -234,7 +185,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad ) { throw MappingException::invalidUniqueConstraintConfiguration( $className, - (string) ($uniqueConstraintAnnot->name ?? $idx) + (string) ($uniqueConstraintAnnot->name ?? $idx), ); } @@ -254,6 +205,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate #[Cache] attribute if (isset($classAttributes[Mapping\Cache::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\Cache::class); + } + $cacheAttribute = $classAttributes[Mapping\Cache::class]; $cacheMap = [ 'region' => $cacheAttribute->region, @@ -265,27 +220,32 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate InheritanceType attribute if (isset($classAttributes[Mapping\InheritanceType::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\InheritanceType::class); + } + $inheritanceTypeAttribute = $classAttributes[Mapping\InheritanceType::class]; $metadata->setInheritanceType( - constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value) + constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value), ); if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { // Evaluate DiscriminatorColumn attribute if (isset($classAttributes[Mapping\DiscriminatorColumn::class])) { $discrColumnAttribute = $classAttributes[Mapping\DiscriminatorColumn::class]; + assert($discrColumnAttribute instanceof Mapping\DiscriminatorColumn); $columnDef = [ - 'name' => isset($discrColumnAttribute->name) ? (string) $discrColumnAttribute->name : null, - 'type' => isset($discrColumnAttribute->type) ? (string) $discrColumnAttribute->type : 'string', - 'length' => isset($discrColumnAttribute->length) ? (int) $discrColumnAttribute->length : 255, - 'columnDefinition' => isset($discrColumnAttribute->columnDefinition) ? (string) $discrColumnAttribute->columnDefinition : null, - 'enumType' => isset($discrColumnAttribute->enumType) ? (string) $discrColumnAttribute->enumType : null, + 'name' => $discrColumnAttribute->name, + 'type' => $discrColumnAttribute->type ?? 'string', + 'length' => $discrColumnAttribute->length ?? 255, + 'columnDefinition' => $discrColumnAttribute->columnDefinition, + 'enumType' => $discrColumnAttribute->enumType, ]; if ($discrColumnAttribute->options) { - $columnDef['options'] = (array) $discrColumnAttribute->options; + $columnDef['options'] = $discrColumnAttribute->options; } $metadata->setDiscriminatorColumn($columnDef); @@ -303,6 +263,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate DoctrineChangeTrackingPolicy attribute if (isset($classAttributes[Mapping\ChangeTrackingPolicy::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\ChangeTrackingPolicy::class); + } + $changeTrackingAttribute = $classAttributes[Mapping\ChangeTrackingPolicy::class]; $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAttribute->value)); } @@ -323,7 +287,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad [ 'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAttribute->usage), 'region' => $cacheAttribute->region, - ] + ], ); } @@ -374,16 +338,20 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad 'sequenceName' => $seqGeneratorAttribute->sequenceName, 'allocationSize' => $seqGeneratorAttribute->allocationSize, 'initialValue' => $seqGeneratorAttribute->initialValue, - ] + ], ); } elseif ($customGeneratorAttribute !== null) { $metadata->setCustomGeneratorDefinition( [ 'class' => $customGeneratorAttribute->class, - ] + ], ); } } elseif ($oneToOneAttribute !== null) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\OneToOne::class); + } + if ($this->reader->getPropertyAttribute($property, Mapping\Id::class)) { $mapping['id'] = true; } @@ -397,6 +365,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAttribute->fetch); $metadata->mapOneToOne($mapping); } elseif ($oneToManyAttribute !== null) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\OneToMany::class); + } + $mapping['mappedBy'] = $oneToManyAttribute->mappedBy; $mapping['targetEntity'] = $oneToManyAttribute->targetEntity; $mapping['cascade'] = $oneToManyAttribute->cascade; @@ -412,6 +384,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $metadata->mapOneToMany($mapping); } elseif ($manyToOneAttribute !== null) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\ManyToOne::class); + } + $idAttribute = $this->reader->getPropertyAttribute($property, Mapping\Id::class); if ($idAttribute !== null) { @@ -425,6 +401,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAttribute->fetch); $metadata->mapManyToOne($mapping); } elseif ($manyToManyAttribute !== null) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\ManyToMany::class); + } + $joinTable = []; $joinTableAttribute = $this->reader->getPropertyAttribute($property, Mapping\JoinTable::class); @@ -481,6 +461,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate AssociationOverrides attribute if (isset($classAttributes[Mapping\AssociationOverrides::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\AssociationOverride::class); + } + $associationOverride = $classAttributes[Mapping\AssociationOverrides::class]; foreach ($associationOverride->overrides as $associationOverride) { @@ -539,6 +523,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate AttributeOverrides attribute if (isset($classAttributes[Mapping\AttributeOverrides::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\AttributeOverrides::class); + } + $attributeOverridesAnnot = $classAttributes[Mapping\AttributeOverrides::class]; foreach ($attributeOverridesAnnot->overrides as $attributeOverride) { @@ -550,6 +538,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate EntityListeners attribute if (isset($classAttributes[Mapping\EntityListeners::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\EntityListeners::class); + } + $entityListenersAttribute = $classAttributes[Mapping\EntityListeners::class]; foreach ($entityListenersAttribute->value as $item) { @@ -581,6 +573,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // Evaluate #[HasLifecycleCallbacks] attribute if (isset($classAttributes[Mapping\HasLifecycleCallbacks::class])) { + if ($metadata->isEmbeddedClass) { + throw MappingException::invalidAttributeOnEmbeddable($metadata->name, Mapping\HasLifecycleCallbacks::class); + } + foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { foreach ($this->getMethodCallbacks($method) as $value) { $metadata->addLifecycleCallback($value[0], $value[1]); @@ -673,8 +669,6 @@ private function getMethodCallbacks(ReflectionMethod $method): array /** * Parse the given JoinColumn as array * - * @param Mapping\JoinColumn|Mapping\InverseJoinColumn $joinColumn - * * @return mixed[] * @phpstan-return array{ * name: string|null, @@ -686,7 +680,7 @@ private function getMethodCallbacks(ReflectionMethod $method): array * options?: array * } */ - private function joinColumnToArray($joinColumn): array + private function joinColumnToArray(Mapping\JoinColumn|Mapping\InverseJoinColumn $joinColumn): array { $mapping = [ 'name' => $joinColumn->name, diff --git a/src/Mapping/Driver/AttributeReader.php b/src/Mapping/Driver/AttributeReader.php index 94b96663dff..fb8a4002b19 100644 --- a/src/Mapping/Driver/AttributeReader.php +++ b/src/Mapping/Driver/AttributeReader.php @@ -5,7 +5,7 @@ namespace Doctrine\ORM\Mapping\Driver; use Attribute; -use Doctrine\ORM\Mapping\Annotation; +use Doctrine\ORM\Mapping\MappingAttribute; use LogicException; use ReflectionAttribute; use ReflectionClass; @@ -20,13 +20,13 @@ /** @internal */ final class AttributeReader { - /** @var array,bool> */ + /** @var array, bool> */ private array $isRepeatableAttribute = []; /** * @phpstan-return class-string-map> * - * @template T of Annotation + * @template T of MappingAttribute */ public function getClassAttributes(ReflectionClass $class): array { @@ -36,7 +36,7 @@ public function getClassAttributes(ReflectionClass $class): array /** * @return class-string-map> * - * @template T of Annotation + * @template T of MappingAttribute */ public function getMethodAttributes(ReflectionMethod $method): array { @@ -46,7 +46,7 @@ public function getMethodAttributes(ReflectionMethod $method): array /** * @return class-string-map> * - * @template T of Annotation + * @template T of MappingAttribute */ public function getPropertyAttributes(ReflectionProperty $property): array { @@ -58,14 +58,14 @@ public function getPropertyAttributes(ReflectionProperty $property): array * * @return T|null * - * @template T of Annotation + * @template T of MappingAttribute */ - public function getPropertyAttribute(ReflectionProperty $property, $attributeName) + public function getPropertyAttribute(ReflectionProperty $property, string $attributeName) { if ($this->isRepeatable($attributeName)) { throw new LogicException(sprintf( 'The attribute "%s" is repeatable. Call getPropertyAttributeCollection() instead.', - $attributeName + $attributeName, )); } @@ -77,16 +77,16 @@ public function getPropertyAttribute(ReflectionProperty $property, $attributeNam * * @return RepeatableAttributeCollection * - * @template T of Annotation + * @template T of MappingAttribute */ public function getPropertyAttributeCollection( ReflectionProperty $property, - string $attributeName + string $attributeName, ): RepeatableAttributeCollection { if (! $this->isRepeatable($attributeName)) { throw new LogicException(sprintf( 'The attribute "%s" is not repeatable. Call getPropertyAttribute() instead.', - $attributeName + $attributeName, )); } @@ -98,7 +98,7 @@ public function getPropertyAttributeCollection( * * @return class-string-map> * - * @template T of Annotation + * @template T of MappingAttribute */ private function convertToAttributeInstances(array $attributes): array { @@ -108,12 +108,12 @@ private function convertToAttributeInstances(array $attributes): array $attributeName = $attribute->getName(); assert(is_string($attributeName)); // Make sure we only get Doctrine Attributes - if (! is_subclass_of($attributeName, Annotation::class)) { + if (! is_subclass_of($attributeName, MappingAttribute::class)) { continue; } $instance = $attribute->newInstance(); - assert($instance instanceof Annotation); + assert($instance instanceof MappingAttribute); if ($this->isRepeatable($attributeName)) { if (! isset($instances[$attributeName])) { @@ -131,7 +131,7 @@ private function convertToAttributeInstances(array $attributes): array return $instances; } - /** @param class-string $attributeClassName */ + /** @param class-string $attributeClassName */ private function isRepeatable(string $attributeClassName): bool { if (isset($this->isRepeatableAttribute[$attributeClassName])) { diff --git a/src/Mapping/Driver/CompatibilityAnnotationDriver.php b/src/Mapping/Driver/CompatibilityAnnotationDriver.php deleted file mode 100644 index 52b93b71339..00000000000 --- a/src/Mapping/Driver/CompatibilityAnnotationDriver.php +++ /dev/null @@ -1,22 +0,0 @@ -|null */ - private $tables = null; + private array|null $tables = null; /** @var array */ - private $classToTableNames = []; + private array $classToTableNames = []; /** @phpstan-var array */ - private $manyToManyTables = []; + private array $manyToManyTables = []; /** @var mixed[] */ - private $classNamesForTables = []; + private array $classNamesForTables = []; /** @var mixed[] */ - private $fieldNamesForColumns = []; + private array $fieldNamesForColumns = []; /** * The namespace for the generated entities. - * - * @var string|null */ - private $namespace; + private string|null $namespace = null; - /** @var Inflector */ - private $inflector; + private Inflector $inflector; - public function __construct(AbstractSchemaManager $schemaManager) + public function __construct(private readonly AbstractSchemaManager $sm) { - $this->sm = $schemaManager; $this->inflector = InflectorFactory::create()->build(); } /** * Set the namespace for the generated entities. - * - * @param string $namespace - * - * @return void */ - public function setNamespace($namespace) + public function setNamespace(string $namespace): void { $this->namespace = $namespace; } - /** - * {@inheritDoc} - */ - public function isTransient($className) + public function isTransient(string $className): bool { return true; } @@ -117,7 +98,7 @@ public function isTransient($className) /** * {@inheritDoc} */ - public function getAllClassNames() + public function getAllClassNames(): array { $this->reverseEngineerMappingFromDatabase(); @@ -126,27 +107,16 @@ public function getAllClassNames() /** * Sets class name for a table. - * - * @param string $tableName - * @param string $className - * - * @return void */ - public function setClassNameForTable($tableName, $className) + public function setClassNameForTable(string $tableName, string $className): void { $this->classNamesForTables[$tableName] = $className; } /** * Sets field name for a column on a specific table. - * - * @param string $tableName - * @param string $columnName - * @param string $fieldName - * - * @return void */ - public function setFieldNameForColumn($tableName, $columnName, $fieldName) + public function setFieldNameForColumn(string $tableName, string $columnName, string $fieldName): void { $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName; } @@ -158,10 +128,8 @@ public function setFieldNameForColumn($tableName, $columnName, $fieldName) * @param Table[] $manyToManyTables * @phpstan-param list $entityTables * @phpstan-param list
$manyToManyTables - * - * @return void */ - public function setTables($entityTables, $manyToManyTables) + public function setTables(array $entityTables, array $manyToManyTables): void { $this->tables = $this->manyToManyTables = $this->classToTableNames = []; @@ -190,17 +158,15 @@ public function setInflector(Inflector $inflector): void * * @template T of object */ - public function loadMetadataForClass($className, PersistenceClassMetadata $metadata) + public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void { if (! $metadata instanceof ClassMetadata) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/249', - 'Passing an instance of %s to %s is deprecated, please pass a %s instance instead.', - get_class($metadata), + throw new TypeError(sprintf( + 'Argument #2 passed to %s() must be an instance of %s, %s given.', __METHOD__, - ClassMetadata::class - ); + ClassMetadata::class, + get_debug_type($metadata), + )); } $this->reverseEngineerMappingFromDatabase(); @@ -308,7 +274,7 @@ private function reverseEngineerMappingFromDatabase(): void if ($primaryKey === null) { throw new MappingException( 'Table ' . $tableName . ' has no primary key. Doctrine does not ' . - "support reverse engineering from tables that don't have a primary key." + "support reverse engineering from tables that don't have a primary key.", ); } @@ -333,7 +299,7 @@ private function reverseEngineerMappingFromDatabase(): void /** * Build indexes from a class metadata. */ - private function buildIndexes(ClassMetadataInfo $metadata): void + private function buildIndexes(ClassMetadata $metadata): void { $tableName = $metadata->table['name']; $indexes = $this->tables[$tableName]->getIndexes(); @@ -356,7 +322,7 @@ private function buildIndexes(ClassMetadataInfo $metadata): void /** * Build field mapping from class metadata. */ - private function buildFieldMappings(ClassMetadataInfo $metadata): void + private function buildFieldMappings(ClassMetadata $metadata): void { $tableName = $metadata->table['name']; $columns = $this->tables[$tableName]->getColumns(); @@ -405,11 +371,11 @@ private function buildFieldMappings(ClassMetadataInfo $metadata): void * columnName: string, * type: string, * nullable: bool, - * options?: array{ + * options: array{ * unsigned?: bool, * fixed?: bool, - * comment?: string, - * default?: string + * comment: string|null, + * default?: mixed * }, * precision?: int, * scale?: int, @@ -423,6 +389,9 @@ private function buildFieldMapping(string $tableName, Column $column): array 'columnName' => $column->getName(), 'type' => Type::getTypeRegistry()->lookupName($column->getType()), 'nullable' => ! $column->getNotnull(), + 'options' => [ + 'comment' => $column->getComment(), + ], ]; // Type specific elements @@ -430,7 +399,6 @@ private function buildFieldMapping(string $tableName, Column $column): array case self::ARRAY: case Types::BLOB: case Types::GUID: - case self::JSON_ARRAY: case self::OBJECT: case Types::SIMPLE_ARRAY: case Types::STRING: @@ -452,12 +420,6 @@ private function buildFieldMapping(string $tableName, Column $column): array break; } - // Comment - $comment = $column->getComment(); - if ($comment !== null) { - $fieldMapping['options']['comment'] = $comment; - } - // Default $default = $column->getDefault(); if ($default !== null) { @@ -469,10 +431,8 @@ private function buildFieldMapping(string $tableName, Column $column): array /** * Build to one (one to one, many to one) association mapping from class metadata. - * - * @return void */ - private function buildToOneAssociationMappings(ClassMetadataInfo $metadata) + private function buildToOneAssociationMappings(ClassMetadata $metadata): void { assert($this->tables !== null); @@ -523,7 +483,7 @@ private function getTablePrimaryKeys(Table $table): array { try { return $table->getPrimaryKey()->getColumns(); - } catch (SchemaException $e) { + } catch (SchemaException) { // Do nothing } @@ -552,7 +512,7 @@ private function getClassNameForTable(string $tableName): string private function getFieldNameForColumn( string $tableName, string $columnName, - bool $fk = false + bool $fk = false, ): string { if (isset($this->fieldNamesForColumns[$tableName], $this->fieldNamesForColumns[$tableName][$columnName])) { return $this->fieldNamesForColumns[$tableName][$columnName]; diff --git a/src/Mapping/Driver/DriverChain.php b/src/Mapping/Driver/DriverChain.php deleted file mode 100644 index 1aeb6b064d6..00000000000 --- a/src/Mapping/Driver/DriverChain.php +++ /dev/null @@ -1,16 +0,0 @@ -doLoadMappingFile($file); + } + } +} else { + /** @internal */ + trait LoadMappingFileImplementation + { + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return $this->doLoadMappingFile($file); + } + } +} diff --git a/src/Mapping/Driver/PHPDriver.php b/src/Mapping/Driver/PHPDriver.php deleted file mode 100644 index b6a812565cb..00000000000 --- a/src/Mapping/Driver/PHPDriver.php +++ /dev/null @@ -1,29 +0,0 @@ -reportFieldsWhereDeclared) { - return $metadata->isMappedSuperclass && ! $property->isPrivate() - || $metadata->isInheritedField($property->name) - || $metadata->isInheritedAssociation($property->name) - || $metadata->isInheritedEmbeddedClass($property->name); - } - /** @var class-string $declaringClass */ $declaringClass = $property->class; @@ -40,20 +30,20 @@ private function isRepeatedPropertyDeclaration(ReflectionProperty $property, Cla } if ( - isset($metadata->fieldMappings[$property->name]['declared']) - && $metadata->fieldMappings[$property->name]['declared'] === $declaringClass + isset($metadata->fieldMappings[$property->name]->declared) + && $metadata->fieldMappings[$property->name]->declared === $declaringClass ) { return true; } if ( - isset($metadata->associationMappings[$property->name]['declared']) - && $metadata->associationMappings[$property->name]['declared'] === $declaringClass + isset($metadata->associationMappings[$property->name]->declared) + && $metadata->associationMappings[$property->name]->declared === $declaringClass ) { return true; } - return isset($metadata->embeddedClasses[$property->name]['declared']) - && $metadata->embeddedClasses[$property->name]['declared'] === $declaringClass; + return isset($metadata->embeddedClasses[$property->name]->declared) + && $metadata->embeddedClasses[$property->name]->declared === $declaringClass; } } diff --git a/src/Mapping/Driver/RepeatableAttributeCollection.php b/src/Mapping/Driver/RepeatableAttributeCollection.php index 995d293f82e..2f6ae93f4fb 100644 --- a/src/Mapping/Driver/RepeatableAttributeCollection.php +++ b/src/Mapping/Driver/RepeatableAttributeCollection.php @@ -5,11 +5,11 @@ namespace Doctrine\ORM\Mapping\Driver; use ArrayObject; -use Doctrine\ORM\Mapping\Annotation; +use Doctrine\ORM\Mapping\MappingAttribute; /** * @template-extends ArrayObject - * @template T of Annotation + * @template T of MappingAttribute */ final class RepeatableAttributeCollection extends ArrayObject { diff --git a/src/Mapping/Driver/SimplifiedXmlDriver.php b/src/Mapping/Driver/SimplifiedXmlDriver.php index 4ea9e556aa8..486185fead9 100644 --- a/src/Mapping/Driver/SimplifiedXmlDriver.php +++ b/src/Mapping/Driver/SimplifiedXmlDriver.php @@ -16,7 +16,7 @@ class SimplifiedXmlDriver extends XmlDriver /** * {@inheritDoc} */ - public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION, bool $isXsdValidationEnabled = false) + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION, bool $isXsdValidationEnabled = true) { $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); diff --git a/src/Mapping/Driver/SimplifiedYamlDriver.php b/src/Mapping/Driver/SimplifiedYamlDriver.php deleted file mode 100644 index c1fb4bd538b..00000000000 --- a/src/Mapping/Driver/SimplifiedYamlDriver.php +++ /dev/null @@ -1,29 +0,0 @@ -isXsdValidationEnabled = $isXsdValidationEnabled; - parent::__construct($locator, $fileExtension); } @@ -78,7 +79,7 @@ public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENS * * @template T of object */ - public function loadMetadataForClass($className, PersistenceClassMetadata $metadata) + public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void { $xmlRoot = $this->getElement($className); @@ -92,7 +93,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad } } elseif ($xmlRoot->getName() === 'mapped-superclass') { $metadata->setCustomRepositoryClass( - isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null + isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null, ); $metadata->isMappedSuperclass = true; } elseif ($xmlRoot->getName() === 'embeddable') { @@ -119,76 +120,6 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $metadata->enableCache($this->cacheToArray($xmlRoot->cache)); } - // Evaluate named queries - if (isset($xmlRoot->{'named-queries'})) { - foreach ($xmlRoot->{'named-queries'}->{'named-query'} ?? [] as $namedQueryElement) { - // @phpstan-ignore method.deprecated - $metadata->addNamedQuery( - [ - 'name' => (string) $namedQueryElement['name'], - 'query' => (string) $namedQueryElement['query'], - ] - ); - } - } - - // Evaluate native named queries - if (isset($xmlRoot->{'named-native-queries'})) { - foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} ?? [] as $nativeQueryElement) { - // @phpstan-ignore method.deprecated - $metadata->addNamedNativeQuery( - [ - 'name' => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null, - 'query' => isset($nativeQueryElement->query) ? (string) $nativeQueryElement->query : null, - 'resultClass' => isset($nativeQueryElement['result-class']) ? (string) $nativeQueryElement['result-class'] : null, - 'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string) $nativeQueryElement['result-set-mapping'] : null, - ] - ); - } - } - - // Evaluate sql result set mapping - if (isset($xmlRoot->{'sql-result-set-mappings'})) { - foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} ?? [] as $rsmElement) { - $entities = []; - $columns = []; - foreach ($rsmElement as $entityElement) { - // - if (isset($entityElement['entity-class'])) { - $entityResult = [ - 'fields' => [], - 'entityClass' => (string) $entityElement['entity-class'], - 'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string) $entityElement['discriminator-column'] : null, - ]; - - foreach ($entityElement as $fieldElement) { - $entityResult['fields'][] = [ - 'name' => isset($fieldElement['name']) ? (string) $fieldElement['name'] : null, - 'column' => isset($fieldElement['column']) ? (string) $fieldElement['column'] : null, - ]; - } - - $entities[] = $entityResult; - } - - // - if (isset($entityElement['name'])) { - $columns[] = [ - 'name' => (string) $entityElement['name'], - ]; - } - } - - $metadata->addSqlResultSetMapping( - [ - 'name' => (string) $rsmElement['name'], - 'entities' => $entities, - 'columns' => $columns, - ] - ); - } - } - if (isset($xmlRoot['inheritance-type'])) { $inheritanceType = (string) $xmlRoot['inheritance-type']; $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType)); @@ -257,7 +188,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad ) { throw MappingException::invalidIndexConfiguration( $className, - (string) ($indexXml['name'] ?? count($metadata->table['indexes'])) + (string) ($indexXml['name'] ?? count($metadata->table['indexes'])), ); } @@ -300,7 +231,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad ) { throw MappingException::invalidUniqueConstraintConfiguration( $className, - (string) ($uniqueXml['name'] ?? count($metadata->table['uniqueConstraints'])) + (string) ($uniqueXml['name'] ?? count($metadata->table['uniqueConstraints'])), ); } @@ -322,7 +253,6 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception - $mappings = []; // Evaluate mappings if (isset($xmlRoot->field)) { foreach ($xmlRoot->field as $fieldMapping) { @@ -357,14 +287,6 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad } } - foreach ($mappings as $mapping) { - if (isset($mapping['version'])) { - $metadata->setVersionMapping($mapping); - } - - $metadata->mapField($mapping); - } - // Evaluate mappings $associationIds = []; foreach ($xmlRoot->id ?? [] as $idElement) { @@ -393,14 +315,14 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad 'sequenceName' => (string) $seqGenerator['sequence-name'], 'allocationSize' => (string) $seqGenerator['allocation-size'], 'initialValue' => (string) $seqGenerator['initial-value'], - ] + ], ); } elseif (isset($idElement->{'custom-id-generator'})) { $customGenerator = $idElement->{'custom-id-generator'}; $metadata->setCustomGeneratorDefinition( [ 'class' => (string) $customGenerator['class'], - ] + ], ); } } @@ -491,7 +413,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $orderBy[(string) $orderByField['name']] = isset($orderByField['direction']) ? (string) $orderByField['direction'] // @phpstan-ignore classConstant.deprecated - : (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC); + : (enum_exists(Order::class) ? Order::Ascending->value : Criteria::ASC); } $mapping['orderBy'] = $orderBy; @@ -620,7 +542,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $orderBy[(string) $orderByField['name']] = isset($orderByField['direction']) ? (string) $orderByField['direction'] // @phpstan-ignore classConstant.deprecated - : (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC); + : (enum_exists(Order::class) ? Order::Ascending->value : Criteria::ASC); } $mapping['orderBy'] = $orderBy; @@ -746,7 +668,7 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad * @return mixed[] The options array. * @phpstan-return array|bool|string> */ - private function parseOptions(?SimpleXMLElement $options): array + private function parseOptions(SimpleXMLElement|null $options): array { $array = []; @@ -947,7 +869,7 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement): array foreach ($children as $action) { // According to the JPA specifications, XML uses "cascade-persist" // instead of "persist". Here, both variations - // are supported because YAML, Annotation and Attribute use "persist" + // are supported because Attribute uses "persist" // and we want to make sure that this driver doesn't need to know // anything about the supported cascading actions $cascades[] = str_replace('cascade-', '', $action->getName()); @@ -956,10 +878,8 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement): array return $cascades; } - /** - * {@inheritDoc} - */ - protected function loadMappingFile($file) + /** @return array */ + private function doLoadMappingFile(string $file): array { $this->validateMapping($file); $result = []; @@ -1011,12 +931,7 @@ private function validateMapping(string $file): void } } - /** - * @param mixed $element - * - * @return bool - */ - protected function evaluateBoolean($element) + protected function evaluateBoolean(mixed $element): bool { $flag = (string) $element; diff --git a/src/Mapping/Driver/YamlDriver.php b/src/Mapping/Driver/YamlDriver.php deleted file mode 100644 index 8757f79c0c6..00000000000 --- a/src/Mapping/Driver/YamlDriver.php +++ /dev/null @@ -1,939 +0,0 @@ -> - */ -class YamlDriver extends FileDriver -{ - public const DEFAULT_FILE_EXTENSION = '.dcm.yml'; - - /** - * {@inheritDoc} - */ - public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8465', - 'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to attribute or XML driver.' - ); - - if (! class_exists(Yaml::class)) { - throw new LogicException( - 'The YAML metadata driver cannot be enabled because the "symfony/yaml" library' - . ' is not installed. Please run "composer require symfony/yaml" or choose a different' - . ' metadata driver.' - ); - } - - parent::__construct($locator, $fileExtension); - } - - /** - * {@inheritDoc} - * - * @param class-string $className - * @param ClassMetadata $metadata - * - * @template T of object - */ - public function loadMetadataForClass($className, PersistenceClassMetadata $metadata) - { - $element = $this->getElement($className); - - if ($element['type'] === 'entity') { - if (isset($element['repositoryClass'])) { - $metadata->setCustomRepositoryClass($element['repositoryClass']); - } - - if (isset($element['readOnly']) && $element['readOnly'] === true) { - $metadata->markReadOnly(); - } - } elseif ($element['type'] === 'mappedSuperclass') { - $metadata->setCustomRepositoryClass( - $element['repositoryClass'] ?? null - ); - $metadata->isMappedSuperclass = true; - } elseif ($element['type'] === 'embeddable') { - $metadata->isEmbeddedClass = true; - } else { - throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); - } - - // Evaluate root level properties - $primaryTable = []; - - if (isset($element['table'])) { - $primaryTable['name'] = $element['table']; - } - - if (isset($element['schema'])) { - $primaryTable['schema'] = $element['schema']; - } - - // Evaluate second level cache - if (isset($element['cache'])) { - $metadata->enableCache($this->cacheToArray($element['cache'])); - } - - $metadata->setPrimaryTable($primaryTable); - - // Evaluate named queries - if (isset($element['namedQueries'])) { - foreach ($element['namedQueries'] as $name => $queryMapping) { - if (is_string($queryMapping)) { - $queryMapping = ['query' => $queryMapping]; - } - - if (! isset($queryMapping['name'])) { - $queryMapping['name'] = $name; - } - - $metadata->addNamedQuery($queryMapping); - } - } - - // Evaluate named native queries - if (isset($element['namedNativeQueries'])) { - foreach ($element['namedNativeQueries'] as $name => $mappingElement) { - if (! isset($mappingElement['name'])) { - $mappingElement['name'] = $name; - } - - $metadata->addNamedNativeQuery( - [ - 'name' => $mappingElement['name'], - 'query' => $mappingElement['query'] ?? null, - 'resultClass' => $mappingElement['resultClass'] ?? null, - 'resultSetMapping' => $mappingElement['resultSetMapping'] ?? null, - ] - ); - } - } - - // Evaluate sql result set mappings - if (isset($element['sqlResultSetMappings'])) { - foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) { - if (! isset($resultSetMapping['name'])) { - $resultSetMapping['name'] = $name; - } - - $entities = []; - $columns = []; - if (isset($resultSetMapping['entityResult'])) { - foreach ($resultSetMapping['entityResult'] as $entityResultElement) { - $entityResult = [ - 'fields' => [], - 'entityClass' => $entityResultElement['entityClass'] ?? null, - 'discriminatorColumn' => $entityResultElement['discriminatorColumn'] ?? null, - ]; - - if (isset($entityResultElement['fieldResult'])) { - foreach ($entityResultElement['fieldResult'] as $fieldResultElement) { - $entityResult['fields'][] = [ - 'name' => $fieldResultElement['name'] ?? null, - 'column' => $fieldResultElement['column'] ?? null, - ]; - } - } - - $entities[] = $entityResult; - } - } - - if (isset($resultSetMapping['columnResult'])) { - foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) { - $columns[] = [ - 'name' => $columnResultAnnot['name'] ?? null, - ]; - } - } - - $metadata->addSqlResultSetMapping( - [ - 'name' => $resultSetMapping['name'], - 'entities' => $entities, - 'columns' => $columns, - ] - ); - } - } - - if (isset($element['inheritanceType'])) { - $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType']))); - - if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { - // Evaluate discriminatorColumn - if (isset($element['discriminatorColumn'])) { - $discrColumn = $element['discriminatorColumn']; - $metadata->setDiscriminatorColumn( - [ - 'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null, - 'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string', - 'length' => isset($discrColumn['length']) ? (int) $discrColumn['length'] : 255, - 'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string) $discrColumn['columnDefinition'] : null, - 'enumType' => isset($discrColumn['enumType']) ? (string) $discrColumn['enumType'] : null, - ] - ); - } else { - $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]); - } - - // Evaluate discriminatorMap - if (isset($element['discriminatorMap'])) { - $metadata->setDiscriminatorMap($element['discriminatorMap']); - } - } - } - - // Evaluate changeTrackingPolicy - if (isset($element['changeTrackingPolicy'])) { - $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' - . strtoupper($element['changeTrackingPolicy']))); - } - - // Evaluate indexes - if (isset($element['indexes'])) { - foreach ($element['indexes'] as $name => $indexYml) { - if (! isset($indexYml['name'])) { - $indexYml['name'] = $name; - } - - $index = []; - - if (isset($indexYml['columns'])) { - if (is_string($indexYml['columns'])) { - $index['columns'] = array_map('trim', explode(',', $indexYml['columns'])); - } else { - $index['columns'] = $indexYml['columns']; - } - } - - if (isset($indexYml['fields'])) { - if (is_string($indexYml['fields'])) { - $index['fields'] = array_map('trim', explode(',', $indexYml['fields'])); - } else { - $index['fields'] = $indexYml['fields']; - } - } - - if ( - isset($index['columns'], $index['fields']) - || ( - ! isset($index['columns']) - && ! isset($index['fields']) - ) - ) { - throw MappingException::invalidIndexConfiguration( - $className, - $indexYml['name'] - ); - } - - if (isset($indexYml['flags'])) { - if (is_string($indexYml['flags'])) { - $index['flags'] = array_map('trim', explode(',', $indexYml['flags'])); - } else { - $index['flags'] = $indexYml['flags']; - } - } - - if (isset($indexYml['options'])) { - $index['options'] = $indexYml['options']; - } - - $metadata->table['indexes'][$indexYml['name']] = $index; - } - } - - // Evaluate uniqueConstraints - if (isset($element['uniqueConstraints'])) { - foreach ($element['uniqueConstraints'] as $name => $uniqueYml) { - if (! isset($uniqueYml['name'])) { - $uniqueYml['name'] = $name; - } - - $unique = []; - - if (isset($uniqueYml['columns'])) { - if (is_string($uniqueYml['columns'])) { - $unique['columns'] = array_map('trim', explode(',', $uniqueYml['columns'])); - } else { - $unique['columns'] = $uniqueYml['columns']; - } - } - - if (isset($uniqueYml['fields'])) { - if (is_string($uniqueYml['fields'])) { - $unique['fields'] = array_map('trim', explode(',', $uniqueYml['fields'])); - } else { - $unique['fields'] = $uniqueYml['fields']; - } - } - - if ( - isset($unique['columns'], $unique['fields']) - || ( - ! isset($unique['columns']) - && ! isset($unique['fields']) - ) - ) { - throw MappingException::invalidUniqueConstraintConfiguration( - $className, - $uniqueYml['name'] - ); - } - - if (isset($uniqueYml['options'])) { - $unique['options'] = $uniqueYml['options']; - } - - $metadata->table['uniqueConstraints'][$uniqueYml['name']] = $unique; - } - } - - if (isset($element['options'])) { - $metadata->table['options'] = $element['options']; - } - - $associationIds = []; - if (isset($element['id'])) { - // Evaluate identifier settings - foreach ($element['id'] as $name => $idElement) { - if (isset($idElement['associationKey']) && $idElement['associationKey'] === true) { - $associationIds[$name] = true; - continue; - } - - $mapping = [ - 'id' => true, - 'fieldName' => $name, - ]; - - if (isset($idElement['type'])) { - $mapping['type'] = $idElement['type']; - } - - if (isset($idElement['column'])) { - $mapping['columnName'] = $idElement['column']; - } - - if (isset($idElement['length'])) { - $mapping['length'] = $idElement['length']; - } - - if (isset($idElement['columnDefinition'])) { - $mapping['columnDefinition'] = $idElement['columnDefinition']; - } - - if (isset($idElement['options'])) { - $mapping['options'] = $idElement['options']; - } - - $metadata->mapField($mapping); - - if (isset($idElement['generator'])) { - $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' - . strtoupper($idElement['generator']['strategy']))); - } - - // Check for SequenceGenerator definition - if (isset($idElement['sequenceGenerator'])) { - $metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']); - } elseif (isset($idElement['customIdGenerator'])) { - $customGenerator = $idElement['customIdGenerator']; - $metadata->setCustomGeneratorDefinition( - [ - 'class' => (string) $customGenerator['class'], - ] - ); - } - } - } - - // Evaluate fields - if (isset($element['fields'])) { - foreach ($element['fields'] as $name => $fieldMapping) { - $mapping = $this->columnToArray($name, $fieldMapping); - - if (isset($fieldMapping['id'])) { - $mapping['id'] = true; - if (isset($fieldMapping['generator']['strategy'])) { - $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' - . strtoupper($fieldMapping['generator']['strategy']))); - } - } - - if (isset($mapping['version'])) { - $metadata->setVersionMapping($mapping); - unset($mapping['version']); - } - - $metadata->mapField($mapping); - } - } - - if (isset($element['embedded'])) { - foreach ($element['embedded'] as $name => $embeddedMapping) { - $mapping = [ - 'fieldName' => $name, - 'class' => $embeddedMapping['class'] ?? null, - 'columnPrefix' => $embeddedMapping['columnPrefix'] ?? null, - ]; - $metadata->mapEmbedded($mapping); - } - } - - // Evaluate oneToOne relationships - if (isset($element['oneToOne'])) { - foreach ($element['oneToOne'] as $name => $oneToOneElement) { - $mapping = [ - 'fieldName' => $name, - 'targetEntity' => $oneToOneElement['targetEntity'] ?? null, - ]; - - if (isset($associationIds[$mapping['fieldName']])) { - $mapping['id'] = true; - } - - if (isset($oneToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']); - } - - if (isset($oneToOneElement['mappedBy'])) { - $mapping['mappedBy'] = $oneToOneElement['mappedBy']; - } else { - if (isset($oneToOneElement['inversedBy'])) { - $mapping['inversedBy'] = $oneToOneElement['inversedBy']; - } - - $joinColumns = []; - - if (isset($oneToOneElement['joinColumn'])) { - $joinColumns[] = $this->joinColumnToArray($oneToOneElement['joinColumn']); - } elseif (isset($oneToOneElement['joinColumns'])) { - foreach ($oneToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $joinColumnName; - } - - $joinColumns[] = $this->joinColumnToArray($joinColumnElement); - } - } - - $mapping['joinColumns'] = $joinColumns; - } - - if (isset($oneToOneElement['cascade'])) { - $mapping['cascade'] = $oneToOneElement['cascade']; - } - - if (isset($oneToOneElement['orphanRemoval'])) { - $mapping['orphanRemoval'] = (bool) $oneToOneElement['orphanRemoval']; - } - - // Evaluate second level cache - if (isset($oneToOneElement['cache'])) { - $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement['cache'])); - } - - $metadata->mapOneToOne($mapping); - } - } - - // Evaluate oneToMany relationships - if (isset($element['oneToMany'])) { - foreach ($element['oneToMany'] as $name => $oneToManyElement) { - $mapping = [ - 'fieldName' => $name, - 'targetEntity' => $oneToManyElement['targetEntity'], - 'mappedBy' => $oneToManyElement['mappedBy'], - ]; - - if (isset($oneToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']); - } - - if (isset($oneToManyElement['cascade'])) { - $mapping['cascade'] = $oneToManyElement['cascade']; - } - - if (isset($oneToManyElement['orphanRemoval'])) { - $mapping['orphanRemoval'] = (bool) $oneToManyElement['orphanRemoval']; - } - - if (isset($oneToManyElement['orderBy'])) { - $mapping['orderBy'] = $oneToManyElement['orderBy']; - } - - if (isset($oneToManyElement['indexBy'])) { - $mapping['indexBy'] = $oneToManyElement['indexBy']; - } - - // Evaluate second level cache - if (isset($oneToManyElement['cache'])) { - $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement['cache'])); - } - - $metadata->mapOneToMany($mapping); - } - } - - // Evaluate manyToOne relationships - if (isset($element['manyToOne'])) { - foreach ($element['manyToOne'] as $name => $manyToOneElement) { - $mapping = [ - 'fieldName' => $name, - 'targetEntity' => $manyToOneElement['targetEntity'] ?? null, - ]; - - if (isset($associationIds[$mapping['fieldName']])) { - $mapping['id'] = true; - } - - if (isset($manyToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']); - } - - if (isset($manyToOneElement['inversedBy'])) { - $mapping['inversedBy'] = $manyToOneElement['inversedBy']; - } - - $joinColumns = []; - - if (isset($manyToOneElement['joinColumn'])) { - $joinColumns[] = $this->joinColumnToArray($manyToOneElement['joinColumn']); - } elseif (isset($manyToOneElement['joinColumns'])) { - foreach ($manyToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $joinColumnName; - } - - $joinColumns[] = $this->joinColumnToArray($joinColumnElement); - } - } - - $mapping['joinColumns'] = $joinColumns; - - if (isset($manyToOneElement['cascade'])) { - $mapping['cascade'] = $manyToOneElement['cascade']; - } - - // Evaluate second level cache - if (isset($manyToOneElement['cache'])) { - $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement['cache'])); - } - - $metadata->mapManyToOne($mapping); - } - } - - // Evaluate manyToMany relationships - if (isset($element['manyToMany'])) { - foreach ($element['manyToMany'] as $name => $manyToManyElement) { - $mapping = [ - 'fieldName' => $name, - 'targetEntity' => $manyToManyElement['targetEntity'], - ]; - - if (isset($manyToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']); - } - - if (isset($manyToManyElement['mappedBy'])) { - $mapping['mappedBy'] = $manyToManyElement['mappedBy']; - } elseif (isset($manyToManyElement['joinTable'])) { - $joinTableElement = $manyToManyElement['joinTable']; - $joinTable = [ - 'name' => $joinTableElement['name'], - ]; - - if (isset($joinTableElement['schema'])) { - $joinTable['schema'] = $joinTableElement['schema']; - } - - if (isset($joinTableElement['joinColumns'])) { - foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $joinColumnName; - } - - $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); - } - } - - if (isset($joinTableElement['inverseJoinColumns'])) { - foreach ($joinTableElement['inverseJoinColumns'] as $joinColumnName => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $joinColumnName; - } - - $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); - } - } - - $mapping['joinTable'] = $joinTable; - } - - if (isset($manyToManyElement['inversedBy'])) { - $mapping['inversedBy'] = $manyToManyElement['inversedBy']; - } - - if (isset($manyToManyElement['cascade'])) { - $mapping['cascade'] = $manyToManyElement['cascade']; - } - - if (isset($manyToManyElement['orderBy'])) { - $mapping['orderBy'] = $manyToManyElement['orderBy']; - } - - if (isset($manyToManyElement['indexBy'])) { - $mapping['indexBy'] = $manyToManyElement['indexBy']; - } - - if (isset($manyToManyElement['orphanRemoval'])) { - $mapping['orphanRemoval'] = (bool) $manyToManyElement['orphanRemoval']; - } - - // Evaluate second level cache - if (isset($manyToManyElement['cache'])) { - $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement['cache'])); - } - - $metadata->mapManyToMany($mapping); - } - } - - // Evaluate associationOverride - if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { - foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { - $override = []; - - // Check for joinColumn - if (isset($associationOverrideElement['joinColumn'])) { - $joinColumns = []; - foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $name; - } - - $joinColumns[] = $this->joinColumnToArray($joinColumnElement); - } - - $override['joinColumns'] = $joinColumns; - } - - // Check for joinTable - if (isset($associationOverrideElement['joinTable'])) { - $joinTableElement = $associationOverrideElement['joinTable']; - $joinTable = [ - 'name' => $joinTableElement['name'], - ]; - - if (isset($joinTableElement['schema'])) { - $joinTable['schema'] = $joinTableElement['schema']; - } - - foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $name; - } - - $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); - } - - foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { - if (! isset($joinColumnElement['name'])) { - $joinColumnElement['name'] = $name; - } - - $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); - } - - $override['joinTable'] = $joinTable; - } - - // Check for inversedBy - if (isset($associationOverrideElement['inversedBy'])) { - $override['inversedBy'] = (string) $associationOverrideElement['inversedBy']; - } - - // Check for `fetch` - if (isset($associationOverrideElement['fetch'])) { - $override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . $associationOverrideElement['fetch']); - } - - $metadata->setAssociationOverride($fieldName, $override); - } - } - - // Evaluate associationOverride - if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) { - foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) { - $mapping = $this->columnToArray($fieldName, $attributeOverrideElement); - $metadata->setAttributeOverride($fieldName, $mapping); - } - } - - // Evaluate lifeCycleCallbacks - if (isset($element['lifecycleCallbacks'])) { - foreach ($element['lifecycleCallbacks'] as $type => $methods) { - foreach ($methods as $method) { - $metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type)); - } - } - } - - // Evaluate entityListeners - if (isset($element['entityListeners'])) { - foreach ($element['entityListeners'] as $className => $entityListener) { - // Evaluate the listener using naming convention. - if (empty($entityListener)) { - EntityListenerBuilder::bindEntityListener($metadata, $className); - - continue; - } - - foreach ($entityListener as $eventName => $callbackElement) { - foreach ($callbackElement as $methodName) { - $metadata->addEntityListener($eventName, $className, $methodName); - } - } - } - } - } - - /** - * Constructs a joinColumn mapping array based on the information - * found in the given join column element. - * - * @phpstan-param array{ - * referencedColumnName?: mixed, - * name?: mixed, - * fieldName?: mixed, - * unique?: mixed, - * nullable?: mixed, - * onDelete?: mixed, - * columnDefinition?: mixed - * } $joinColumnElement The array join column element. - * - * @return mixed[] The mapping array. - * @phpstan-return array{ - * referencedColumnName?: string, - * name?: string, - * fieldName?: string, - * unique?: bool, - * nullable?: bool, - * onDelete?: mixed, - * columnDefinition?: mixed - * } - */ - private function joinColumnToArray(array $joinColumnElement): array - { - $joinColumn = []; - if (isset($joinColumnElement['referencedColumnName'])) { - $joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName']; - } - - if (isset($joinColumnElement['name'])) { - $joinColumn['name'] = (string) $joinColumnElement['name']; - } - - if (isset($joinColumnElement['fieldName'])) { - $joinColumn['fieldName'] = (string) $joinColumnElement['fieldName']; - } - - if (isset($joinColumnElement['unique'])) { - $joinColumn['unique'] = (bool) $joinColumnElement['unique']; - } - - if (isset($joinColumnElement['nullable'])) { - $joinColumn['nullable'] = (bool) $joinColumnElement['nullable']; - } - - if (isset($joinColumnElement['onDelete'])) { - $joinColumn['onDelete'] = $joinColumnElement['onDelete']; - } - - if (isset($joinColumnElement['columnDefinition'])) { - $joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition']; - } - - return $joinColumn; - } - - /** - * Parses the given column as array. - * - * @phpstan-param array{ - * type?: string, - * column?: string, - * precision?: mixed, - * scale?: mixed, - * unique?: mixed, - * options?: mixed, - * nullable?: mixed, - * insertable?: mixed, - * updatable?: mixed, - * generated?: mixed, - * enumType?: class-string, - * version?: mixed, - * columnDefinition?: mixed - * }|null $column - * - * @return mixed[] - * @phpstan-return array{ - * fieldName: string, - * type?: string, - * columnName?: string, - * length?: int, - * precision?: mixed, - * scale?: mixed, - * unique?: bool, - * options?: mixed, - * nullable?: mixed, - * notInsertable?: mixed, - * notUpdatable?: mixed, - * generated?: mixed, - * enumType?: class-string, - * version?: mixed, - * columnDefinition?: mixed - * } - */ - private function columnToArray(string $fieldName, ?array $column): array - { - $mapping = ['fieldName' => $fieldName]; - - if (isset($column['type'])) { - $params = explode('(', $column['type']); - - $column['type'] = $params[0]; - $mapping['type'] = $column['type']; - - if (isset($params[1])) { - $column['length'] = (int) substr($params[1], 0, strlen($params[1]) - 1); - } - } - - if (isset($column['column'])) { - $mapping['columnName'] = $column['column']; - } - - if (isset($column['length'])) { - $mapping['length'] = $column['length']; - } - - if (isset($column['precision'])) { - $mapping['precision'] = $column['precision']; - } - - if (isset($column['scale'])) { - $mapping['scale'] = $column['scale']; - } - - if (isset($column['unique'])) { - $mapping['unique'] = (bool) $column['unique']; - } - - if (isset($column['options'])) { - $mapping['options'] = $column['options']; - } - - if (isset($column['nullable'])) { - $mapping['nullable'] = $column['nullable']; - } - - if (isset($column['insertable']) && ! (bool) $column['insertable']) { - $mapping['notInsertable'] = true; - } - - if (isset($column['updatable']) && ! (bool) $column['updatable']) { - $mapping['notUpdatable'] = true; - } - - if (isset($column['generated'])) { - $mapping['generated'] = constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATED_' . $column['generated']); - } - - if (isset($column['version']) && $column['version']) { - $mapping['version'] = $column['version']; - } - - if (isset($column['columnDefinition'])) { - $mapping['columnDefinition'] = $column['columnDefinition']; - } - - if (isset($column['enumType'])) { - $mapping['enumType'] = $column['enumType']; - } - - return $mapping; - } - - /** - * Parse / Normalize the cache configuration - * - * @param mixed[] $cacheMapping - * @phpstan-param array{usage: string|null, region?: mixed} $cacheMapping - * - * @return mixed[] - * @phpstan-return array{usage: int|null, region: string|null} - */ - private function cacheToArray(array $cacheMapping): array - { - $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null; - $usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null; - - if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) { - throw new InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage)); - } - - if ($usage) { - $usage = (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage); - } - - return [ - 'usage' => $usage, - 'region' => $region, - ]; - } - - /** - * {@inheritDoc} - */ - protected function loadMappingFile($file) - { - return Yaml::parse(file_get_contents($file)); - } -} diff --git a/src/Mapping/Embeddable.php b/src/Mapping/Embeddable.php index a950da6b04a..b8dfea05a84 100644 --- a/src/Mapping/Embeddable.php +++ b/src/Mapping/Embeddable.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class Embeddable implements MappingAttribute { diff --git a/src/Mapping/Embedded.php b/src/Mapping/Embedded.php index e090e033e1b..be69b4f5cab 100644 --- a/src/Mapping/Embedded.php +++ b/src/Mapping/Embedded.php @@ -5,31 +5,13 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class Embedded implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $class; - - /** - * @var string|bool|null - * @readonly - */ - public $columnPrefix; - - public function __construct(?string $class = null, $columnPrefix = null) - { - $this->class = $class; - $this->columnPrefix = $columnPrefix; + public function __construct( + public readonly string|null $class = null, + public readonly string|bool|null $columnPrefix = null, + ) { } } diff --git a/src/Mapping/EmbeddedClassMapping.php b/src/Mapping/EmbeddedClassMapping.php new file mode 100644 index 00000000000..85e2e6e94a0 --- /dev/null +++ b/src/Mapping/EmbeddedClassMapping.php @@ -0,0 +1,93 @@ + */ +final class EmbeddedClassMapping implements ArrayAccess +{ + use ArrayAccessImplementation; + + public string|false|null $columnPrefix = null; + public string|null $declaredField = null; + public string|null $originalField = null; + + /** + * This is set when this embedded-class field is inherited by this class + * from another (inheritance) parent entity class. The value is + * the FQCN of the topmost entity class that contains mapping information + * for this field. (If there are transient classes in the class hierarchy, + * these are ignored, so the class property may in fact come from a class + * further up in the PHP class hierarchy.) Fields initially declared in + * mapped superclasses are not considered 'inherited' in the + * nearest entity subclasses. + * + * @var class-string|null + */ + public string|null $inherited = null; + + /** + * This is set when the embedded-class field does not appear for the first + * time in this class, but is originally declared in another parent + * entity or mapped superclass. The value is the FQCN of the + * topmost non-transient class that contains mapping information for this + * field. + * + * @var class-string|null + */ + public string|null $declared = null; + + /** @param class-string $class */ + public function __construct(public string $class) + { + } + + /** + * @phpstan-param array{ + * class: class-string, + * columnPrefix?: false|string|null, + * declaredField?: string|null, + * originalField?: string|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): self + { + $mapping = new self($mappingArray['class']); + foreach ($mappingArray as $key => $value) { + if ($key === 'class') { + continue; + } + + if (property_exists($mapping, $key)) { + $mapping->$key = $value; + } + } + + return $mapping; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = ['class']; + + if ($this->columnPrefix) { + $serialized[] = 'columnPrefix'; + } + + foreach (['declaredField', 'originalField', 'inherited', 'declared'] as $property) { + if ($this->$property !== null) { + $serialized[] = $property; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/Entity.php b/src/Mapping/Entity.php index bf39472d723..6466aa065d0 100644 --- a/src/Mapping/Entity.php +++ b/src/Mapping/Entity.php @@ -5,34 +5,16 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; use Doctrine\ORM\EntityRepository; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - * @template T of object - */ +/** @template T of object */ #[Attribute(Attribute::TARGET_CLASS)] final class Entity implements MappingAttribute { - /** - * @var class-string>|null - * @readonly - */ - public $repositoryClass; - - /** - * @var bool - * @readonly - */ - public $readOnly = false; - /** @phpstan-param class-string>|null $repositoryClass */ - public function __construct(?string $repositoryClass = null, bool $readOnly = false) - { - $this->repositoryClass = $repositoryClass; - $this->readOnly = $readOnly; + public function __construct( + public readonly string|null $repositoryClass = null, + public readonly bool $readOnly = false, + ) { } } diff --git a/src/Mapping/EntityListenerResolver.php b/src/Mapping/EntityListenerResolver.php index 2a10a2f9fa2..eabc217314d 100644 --- a/src/Mapping/EntityListenerResolver.php +++ b/src/Mapping/EntityListenerResolver.php @@ -13,26 +13,18 @@ interface EntityListenerResolver * Clear all instances from the set, or a specific instance when given its identifier. * * @param string|null $className May be any arbitrary string. Name kept for BC only. - * - * @return void */ - public function clear($className = null); + public function clear(string|null $className = null): void; /** * Returns a entity listener instance for the given identifier. * * @param string $className May be any arbitrary string. Name kept for BC only. - * - * @return object An entity listener */ - public function resolve($className); + public function resolve(string $className): object; /** * Register a entity listener instance. - * - * @param object $object An entity listener - * - * @return void */ - public function register($object); + public function register(object $object): void; } diff --git a/src/Mapping/EntityListeners.php b/src/Mapping/EntityListeners.php index 1b46812535f..8f822eaa039 100644 --- a/src/Mapping/EntityListeners.php +++ b/src/Mapping/EntityListeners.php @@ -5,30 +5,17 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; /** * The EntityListeners attribute specifies the callback listener classes to be used for an entity or mapped superclass. * The EntityListeners attribute may be applied to an entity class or mapped superclass. - * - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") */ #[Attribute(Attribute::TARGET_CLASS)] final class EntityListeners implements MappingAttribute { - /** - * Specifies the names of the entity listeners. - * - * @var array - * @readonly - */ - public $value = []; - /** @param array $value */ - public function __construct(array $value = []) - { - $this->value = $value; + public function __construct( + public readonly array $value = [], + ) { } } diff --git a/src/Mapping/EntityResult.php b/src/Mapping/EntityResult.php deleted file mode 100644 index 2ffcce52be3..00000000000 --- a/src/Mapping/EntityResult.php +++ /dev/null @@ -1,38 +0,0 @@ - - */ - public $fields = []; - - /** - * Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance. - * - * @var string - */ - public $discriminatorColumn; -} diff --git a/src/Mapping/Exception/CannotGenerateIds.php b/src/Mapping/Exception/CannotGenerateIds.php deleted file mode 100644 index e244e449d3c..00000000000 --- a/src/Mapping/Exception/CannotGenerateIds.php +++ /dev/null @@ -1,22 +0,0 @@ - */ +final class FieldMapping implements ArrayAccess +{ + use ArrayAccessImplementation; + + /** The database length of the column. Optional. Default value taken from the type. */ + public int|null $length = null; + /** + * Marks the field as the primary key of the entity. Multiple + * fields of an entity can have the id attribute, forming a composite key. + */ + public bool|null $id = null; + public bool|null $nullable = null; + public bool|null $notInsertable = null; + public bool|null $notUpdatable = null; + public string|null $columnDefinition = null; + /** @phpstan-var ClassMetadata::GENERATED_*|null */ + public int|null $generated = null; + /** @var class-string|null */ + public string|null $enumType = null; + /** + * The precision of a decimal column. + * Only valid if the column type is decimal + */ + public int|null $precision = null; + /** + * The scale of a decimal column. + * Only valid if the column type is decimal + */ + public int|null $scale = null; + /** Whether a unique constraint should be generated for the column. */ + public bool|null $unique = null; + /** + * @var class-string|null This is set when the field is inherited by this + * class from another (inheritance) parent entity class. The value + * is the FQCN of the topmost entity class that contains mapping information + * for this field. (If there are transient classes in the class hierarchy, + * these are ignored, so the class property may in fact come from a class + * further up in the PHP class hierarchy.) + * Fields initially declared in mapped superclasses are + * not considered 'inherited' in the nearest entity subclasses. + */ + public string|null $inherited = null; + + public string|null $originalClass = null; + public string|null $originalField = null; + public bool|null $quoted = null; + /** + * @var class-string|null This is set when the field does not appear for + * the first time in this class, but is originally declared in another + * parent entity or mapped superclass. The value is the FQCN of + * the topmost non-transient class that contains mapping information for + * this field. + */ + public string|null $declared = null; + public string|null $declaredField = null; + public array|null $options = null; + public bool|null $version = null; + public string|int|null $default = null; + + /** + * @param string $type The type name of the mapped field. Can be one of + * Doctrine's mapping types or a custom mapping type. + * @param string $fieldName The name of the field in the Entity. + * @param string $columnName The column name. Optional. Defaults to the field name. + */ + public function __construct( + public string $type, + public string $fieldName, + public string $columnName, + ) { + } + + /** + * @param array $mappingArray + * @phpstan-param array{ + * type: string, + * fieldName: string, + * columnName: string, + * length?: int|null, + * id?: bool|null, + * nullable?: bool|null, + * notInsertable?: bool|null, + * notUpdatable?: bool|null, + * columnDefinition?: string|null, + * generated?: ClassMetadata::GENERATED_*|null, + * enumType?: string|null, + * precision?: int|null, + * scale?: int|null, + * unique?: bool|null, + * inherited?: string|null, + * originalClass?: string|null, + * originalField?: string|null, + * quoted?: bool|null, + * declared?: string|null, + * declaredField?: string|null, + * options?: array|null, + * version?: bool|null, + * default?: string|int|null, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): self + { + $mapping = new self( + $mappingArray['type'], + $mappingArray['fieldName'], + $mappingArray['columnName'], + ); + foreach ($mappingArray as $key => $value) { + if (in_array($key, ['type', 'fieldName', 'columnName'])) { + continue; + } + + if (property_exists($mapping, $key)) { + $mapping->$key = $value; + } + } + + return $mapping; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = ['type', 'fieldName', 'columnName']; + + foreach (['nullable', 'notInsertable', 'notUpdatable', 'id', 'unique', 'version', 'quoted'] as $boolKey) { + if ($this->$boolKey) { + $serialized[] = $boolKey; + } + } + + foreach ( + [ + 'length', + 'columnDefinition', + 'generated', + 'enumType', + 'precision', + 'scale', + 'inherited', + 'originalClass', + 'originalField', + 'declared', + 'declaredField', + 'options', + 'default', + ] as $key + ) { + if ($this->$key !== null) { + $serialized[] = $key; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/FieldResult.php b/src/Mapping/FieldResult.php deleted file mode 100644 index e26065d6481..00000000000 --- a/src/Mapping/FieldResult.php +++ /dev/null @@ -1,28 +0,0 @@ -strategy = $strategy; + public function __construct( + public readonly string $strategy = 'AUTO', + ) { } } diff --git a/src/Mapping/GetReflectionClassImplementation.php b/src/Mapping/GetReflectionClassImplementation.php new file mode 100644 index 00000000000..780015c3680 --- /dev/null +++ b/src/Mapping/GetReflectionClassImplementation.php @@ -0,0 +1,33 @@ +reflClass; + } + } +} else { + trait GetReflectionClassImplementation + { + /** + * {@inheritDoc} + * + * Can return null when using static reflection, in violation of the LSP + */ + public function getReflectionClass(): ReflectionClass|null + { + return $this->reflClass; + } + } +} diff --git a/src/Mapping/HasLifecycleCallbacks.php b/src/Mapping/HasLifecycleCallbacks.php index b4700e9c18e..d41a696d33d 100644 --- a/src/Mapping/HasLifecycleCallbacks.php +++ b/src/Mapping/HasLifecycleCallbacks.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class HasLifecycleCallbacks implements MappingAttribute { diff --git a/src/Mapping/Id.php b/src/Mapping/Id.php index c09a7d49138..e85dd2fe5c6 100644 --- a/src/Mapping/Id.php +++ b/src/Mapping/Id.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class Id implements MappingAttribute { diff --git a/src/Mapping/Index.php b/src/Mapping/Index.php index 39ae78e9f17..1de939e506e 100644 --- a/src/Mapping/Index.php +++ b/src/Mapping/Index.php @@ -5,46 +5,10 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("ANNOTATION") - */ #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] final class Index implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var array|null - * @readonly - */ - public $columns; - - /** - * @var array|null - * @readonly - */ - public $fields; - - /** - * @var array|null - * @readonly - */ - public $flags; - - /** - * @var array|null - * @readonly - */ - public $options; - /** * @param array|null $columns * @param array|null $fields @@ -52,16 +16,11 @@ final class Index implements MappingAttribute * @param array|null $options */ public function __construct( - ?array $columns = null, - ?array $fields = null, - ?string $name = null, - ?array $flags = null, - ?array $options = null + public readonly string|null $name = null, + public readonly array|null $columns = null, + public readonly array|null $fields = null, + public readonly array|null $flags = null, + public readonly array|null $options = null, ) { - $this->columns = $columns; - $this->fields = $fields; - $this->name = $name; - $this->flags = $flags; - $this->options = $options; } } diff --git a/src/Mapping/InheritanceType.php b/src/Mapping/InheritanceType.php index 0c4c8ee8431..b33afd8608e 100644 --- a/src/Mapping/InheritanceType.php +++ b/src/Mapping/InheritanceType.php @@ -5,38 +5,13 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Deprecations\Deprecation; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class InheritanceType implements MappingAttribute { - /** - * The inheritance type used by the class and its subclasses. - * - * @var string - * @phpstan-var 'NONE'|'JOINED'|'SINGLE_TABLE'|'TABLE_PER_CLASS' - * @readonly - * @Enum({"NONE", "JOINED", "SINGLE_TABLE", "TABLE_PER_CLASS"}) - */ - public $value; - - /** @phpstan-param 'NONE'|'JOINED'|'SINGLE_TABLE'|'TABLE_PER_CLASS' $value */ - public function __construct(string $value) - { - if ($value === 'TABLE_PER_CLASS') { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10414/', - 'Concrete table inheritance has never been implemented, and its stubs will be removed in Doctrine ORM 3.0 with no replacement' - ); - } - - $this->value = $value; + /** @phpstan-param 'NONE'|'JOINED'|'SINGLE_TABLE' $value */ + public function __construct( + public readonly string $value, + ) { } } diff --git a/src/Mapping/InverseSideMapping.php b/src/Mapping/InverseSideMapping.php new file mode 100644 index 00000000000..56dce9f27ec --- /dev/null +++ b/src/Mapping/InverseSideMapping.php @@ -0,0 +1,30 @@ +mappedBy; + } + + /** @return list */ + public function __sleep(): array + { + return [ + ...parent::__sleep(), + 'mappedBy', + ]; + } +} diff --git a/src/Mapping/JoinColumn.php b/src/Mapping/JoinColumn.php index 6a048e8bf66..9a049fbfac8 100644 --- a/src/Mapping/JoinColumn.php +++ b/src/Mapping/JoinColumn.php @@ -5,13 +5,7 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target({"PROPERTY","ANNOTATION"}) - */ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] final class JoinColumn implements MappingAttribute { diff --git a/src/Mapping/JoinColumnMapping.php b/src/Mapping/JoinColumnMapping.php new file mode 100644 index 00000000000..cb54b196a99 --- /dev/null +++ b/src/Mapping/JoinColumnMapping.php @@ -0,0 +1,77 @@ + */ +final class JoinColumnMapping implements ArrayAccess +{ + use ArrayAccessImplementation; + + public bool|null $unique = null; + public bool|null $quoted = null; + public string|null $fieldName = null; + public string|null $onDelete = null; + public string|null $columnDefinition = null; + public bool|null $nullable = null; + + /** @var array|null */ + public array|null $options = null; + + public function __construct( + public string $name, + public string $referencedColumnName, + ) { + } + + /** + * @param array $mappingArray + * @phpstan-param array{ + * name: string, + * referencedColumnName: string, + * unique?: bool|null, + * quoted?: bool|null, + * fieldName?: string|null, + * onDelete?: string|null, + * columnDefinition?: string|null, + * nullable?: bool|null, + * options?: array|null, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): self + { + $mapping = new self($mappingArray['name'], $mappingArray['referencedColumnName']); + foreach ($mappingArray as $key => $value) { + if (property_exists($mapping, $key) && $value !== null) { + $mapping->$key = $value; + } + } + + return $mapping; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = []; + + foreach (['name', 'fieldName', 'onDelete', 'columnDefinition', 'referencedColumnName', 'options'] as $stringOrArrayKey) { + if ($this->$stringOrArrayKey !== null) { + $serialized[] = $stringOrArrayKey; + } + } + + foreach (['unique', 'quoted', 'nullable'] as $boolKey) { + if ($this->$boolKey !== null) { + $serialized[] = $boolKey; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/JoinColumnProperties.php b/src/Mapping/JoinColumnProperties.php index e092f8f8713..7d132952b31 100644 --- a/src/Mapping/JoinColumnProperties.php +++ b/src/Mapping/JoinColumnProperties.php @@ -6,77 +6,16 @@ trait JoinColumnProperties { - /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var string - * @readonly - */ - public $referencedColumnName = 'id'; - - /** - * @var bool - * @readonly - */ - public $unique = false; - - /** - * @var bool - * @readonly - */ - public $nullable = true; - - /** - * @var mixed - * @readonly - */ - public $onDelete; - - /** - * @var string|null - * @readonly - */ - public $columnDefinition; - - /** - * Field name used in non-object hydration (array/scalar). - * - * @var string|null - * @readonly - */ - public $fieldName; - - /** - * @var array - * @readonly - */ - public $options = []; - - /** - * @param mixed $onDelete - * @param array $options - */ + /** @param array $options */ public function __construct( - ?string $name = null, - string $referencedColumnName = 'id', - bool $unique = false, - bool $nullable = true, - $onDelete = null, - ?string $columnDefinition = null, - ?string $fieldName = null, - array $options = [] + public readonly string|null $name = null, + public readonly string $referencedColumnName = 'id', + public readonly bool $unique = false, + public readonly bool $nullable = true, + public readonly mixed $onDelete = null, + public readonly string|null $columnDefinition = null, + public readonly string|null $fieldName = null, + public readonly array $options = [], ) { - $this->name = $name; - $this->referencedColumnName = $referencedColumnName; - $this->unique = $unique; - $this->nullable = $nullable; - $this->onDelete = $onDelete; - $this->columnDefinition = $columnDefinition; - $this->fieldName = $fieldName; - $this->options = $options; } } diff --git a/src/Mapping/JoinColumns.php b/src/Mapping/JoinColumns.php index 49a1c2aadf3..f166b76fd53 100644 --- a/src/Mapping/JoinColumns.php +++ b/src/Mapping/JoinColumns.php @@ -4,12 +4,11 @@ namespace Doctrine\ORM\Mapping; -/** - * @Annotation - * @Target("PROPERTY") - */ final class JoinColumns implements MappingAttribute { - /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */ - public $value; + /** @param array $value */ + public function __construct( + public readonly array $value, + ) { + } } diff --git a/src/Mapping/JoinTable.php b/src/Mapping/JoinTable.php index a28ac433598..0558761185d 100644 --- a/src/Mapping/JoinTable.php +++ b/src/Mapping/JoinTable.php @@ -5,60 +5,31 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target({"PROPERTY","ANNOTATION"}) - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class JoinTable implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $name; + /** @var array */ + public readonly array $joinColumns; - /** - * @var string|null - * @readonly - */ - public $schema; + /** @var array */ + public readonly array $inverseJoinColumns; /** - * @var array - * @readonly + * @param array|JoinColumn $joinColumns + * @param array|JoinColumn $inverseJoinColumns + * @param array $options */ - public $joinColumns = []; - - /** - * @var array - * @readonly - */ - public $inverseJoinColumns = []; - - /** - * @var array - * @readonly - */ - public $options = []; - - /** @param array $options */ public function __construct( - ?string $name = null, - ?string $schema = null, - $joinColumns = [], - $inverseJoinColumns = [], - array $options = [] + public readonly string|null $name = null, + public readonly string|null $schema = null, + array|JoinColumn $joinColumns = [], + array|JoinColumn $inverseJoinColumns = [], + public readonly array $options = [], ) { - $this->name = $name; - $this->schema = $schema; $this->joinColumns = $joinColumns instanceof JoinColumn ? [$joinColumns] : $joinColumns; $this->inverseJoinColumns = $inverseJoinColumns instanceof JoinColumn ? [$inverseJoinColumns] : $inverseJoinColumns; - $this->options = $options; } } diff --git a/src/Mapping/JoinTableMapping.php b/src/Mapping/JoinTableMapping.php new file mode 100644 index 00000000000..29d3a698b98 --- /dev/null +++ b/src/Mapping/JoinTableMapping.php @@ -0,0 +1,115 @@ + */ +final class JoinTableMapping implements ArrayAccess +{ + use ArrayAccessImplementation; + + public bool|null $quoted = null; + + /** @var list */ + public array $joinColumns = []; + + /** @var list */ + public array $inverseJoinColumns = []; + + /** @var array */ + public array $options = []; + + public string|null $schema = null; + + public function __construct(public string $name) + { + } + + /** + * @param mixed[] $mappingArray + * @phpstan-param array{ + * name: string, + * quoted?: bool|null, + * joinColumns?: mixed[], + * inverseJoinColumns?: mixed[], + * schema?: string|null, + * options?: array + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): self + { + $mapping = new self($mappingArray['name']); + + foreach (['quoted', 'schema', 'options'] as $key) { + if (isset($mappingArray[$key])) { + $mapping->$key = $mappingArray[$key]; + } + } + + if (isset($mappingArray['joinColumns'])) { + foreach ($mappingArray['joinColumns'] as $column) { + $mapping->joinColumns[] = JoinColumnMapping::fromMappingArray($column); + } + } + + if (isset($mappingArray['inverseJoinColumns'])) { + foreach ($mappingArray['inverseJoinColumns'] as $column) { + $mapping->inverseJoinColumns[] = JoinColumnMapping::fromMappingArray($column); + } + } + + return $mapping; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + if (in_array($offset, ['joinColumns', 'inverseJoinColumns'], true)) { + $joinColumns = []; + foreach ($value as $column) { + $joinColumns[] = JoinColumnMapping::fromMappingArray($column); + } + + $value = $joinColumns; + } + + $this->$offset = $value; + } + + /** @return mixed[] */ + public function toArray(): array + { + $array = (array) $this; + + $toArray = static fn (JoinColumnMapping $column): array => (array) $column; + $array['joinColumns'] = array_map($toArray, $array['joinColumns']); + $array['inverseJoinColumns'] = array_map($toArray, $array['inverseJoinColumns']); + + return $array; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = []; + + foreach (['joinColumns', 'inverseJoinColumns', 'name', 'schema', 'options'] as $stringOrArrayKey) { + if ($this->$stringOrArrayKey !== null) { + $serialized[] = $stringOrArrayKey; + } + } + + foreach (['quoted'] as $boolKey) { + if ($this->$boolKey) { + $serialized[] = $boolKey; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/ManyToMany.php b/src/Mapping/ManyToMany.php index 851e7f89dc0..60d2e761a7e 100644 --- a/src/Mapping/ManyToMany.php +++ b/src/Mapping/ManyToMany.php @@ -5,91 +5,23 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Doctrine\Deprecations\Deprecation; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class ManyToMany implements MappingAttribute { /** - * @var class-string|null - * @readonly - */ - public $targetEntity; - - /** - * @var string|null - * @readonly - */ - public $mappedBy; - - /** - * @var string|null - * @readonly - */ - public $inversedBy; - - /** - * @var string[]|null - * @readonly - */ - public $cascade; - - /** - * The fetching strategy to use for the association. - * - * @var string - * @phpstan-var 'LAZY'|'EAGER'|'EXTRA_LAZY' - * @readonly - * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) - */ - public $fetch = 'LAZY'; - - /** - * @var bool - * @readonly - */ - public $orphanRemoval = false; - - /** - * @var string|null - * @readonly - */ - public $indexBy; - - /** - * @param class-string|null $targetEntity - * @param string[]|null $cascade + * @param class-string $targetEntity + * @param string[]|null $cascade * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch */ public function __construct( - ?string $targetEntity = null, - ?string $mappedBy = null, - ?string $inversedBy = null, - ?array $cascade = null, - string $fetch = 'LAZY', - bool $orphanRemoval = false, - ?string $indexBy = null + public readonly string $targetEntity, + public readonly string|null $mappedBy = null, + public readonly string|null $inversedBy = null, + public readonly array|null $cascade = null, + public readonly string $fetch = 'LAZY', + public readonly bool $orphanRemoval = false, + public readonly string|null $indexBy = null, ) { - if ($targetEntity === null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8753', - 'Passing no target entity is deprecated.' - ); - } - - $this->targetEntity = $targetEntity; - $this->mappedBy = $mappedBy; - $this->inversedBy = $inversedBy; - $this->cascade = $cascade; - $this->fetch = $fetch; - $this->orphanRemoval = $orphanRemoval; - $this->indexBy = $indexBy; } } diff --git a/src/Mapping/ManyToManyAssociationMapping.php b/src/Mapping/ManyToManyAssociationMapping.php new file mode 100644 index 00000000000..8d963c29469 --- /dev/null +++ b/src/Mapping/ManyToManyAssociationMapping.php @@ -0,0 +1,9 @@ + */ + public array $joinTableColumns = []; + + /** @var array */ + public array $relationToSourceKeyColumns = []; + /** @var array */ + public array $relationToTargetKeyColumns = []; + + /** @return array */ + public function toArray(): array + { + $array = parent::toArray(); + + $array['joinTable'] = $this->joinTable->toArray(); + + return $array; + } + + /** + * @param mixed[] $mappingArray + * @phpstan-param array{ + * fieldName: string, + * sourceEntity: class-string, + * targetEntity: class-string, + * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * } $mappingArray + */ + public static function fromMappingArrayAndNamingStrategy(array $mappingArray, NamingStrategy $namingStrategy): self + { + if (isset($mappingArray['joinTable']['joinColumns'])) { + foreach ($mappingArray['joinTable']['joinColumns'] as $key => $joinColumn) { + if (empty($joinColumn['name'])) { + $mappingArray['joinTable']['joinColumns'][$key]['name'] = $namingStrategy->joinKeyColumnName( + $mappingArray['sourceEntity'], + $joinColumn['referencedColumnName'] ?? null, + ); + } + } + } + + if (isset($mappingArray['joinTable']['inverseJoinColumns'])) { + foreach ($mappingArray['joinTable']['inverseJoinColumns'] as $key => $joinColumn) { + if (empty($joinColumn['name'])) { + $mappingArray['joinTable']['inverseJoinColumns'][$key]['name'] = $namingStrategy->joinKeyColumnName( + $mappingArray['targetEntity'], + $joinColumn['referencedColumnName'] ?? null, + ); + } + } + } + + // owning side MUST have a join table + if (! isset($mappingArray['joinTable']) || ! isset($mappingArray['joinTable']['name'])) { + $mappingArray['joinTable']['name'] = $namingStrategy->joinTableName( + $mappingArray['sourceEntity'], + $mappingArray['targetEntity'], + $mappingArray['fieldName'], + ); + } + + $mapping = parent::fromMappingArray($mappingArray); + + $selfReferencingEntityWithoutJoinColumns = $mapping->sourceEntity === $mapping->targetEntity + && $mapping->joinTable->joinColumns === [] + && $mapping->joinTable->inverseJoinColumns === []; + + if ($mapping->joinTable->joinColumns === []) { + $mapping->joinTable->joinColumns = [ + JoinColumnMapping::fromMappingArray([ + 'name' => $namingStrategy->joinKeyColumnName($mapping->sourceEntity, $selfReferencingEntityWithoutJoinColumns ? 'source' : null), + 'referencedColumnName' => $namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE', + ]), + ]; + } + + if ($mapping->joinTable->inverseJoinColumns === []) { + $mapping->joinTable->inverseJoinColumns = [ + JoinColumnMapping::fromMappingArray([ + 'name' => $namingStrategy->joinKeyColumnName($mapping->targetEntity, $selfReferencingEntityWithoutJoinColumns ? 'target' : null), + 'referencedColumnName' => $namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE', + ]), + ]; + } + + $mapping->joinTableColumns = []; + + foreach ($mapping->joinTable->joinColumns as $joinColumn) { + if (empty($joinColumn->referencedColumnName)) { + $joinColumn->referencedColumnName = $namingStrategy->referenceColumnName(); + } + + if ($joinColumn->name[0] === '`') { + $joinColumn->name = trim($joinColumn->name, '`'); + $joinColumn->quoted = true; + } + + if ($joinColumn->referencedColumnName[0] === '`') { + $joinColumn->referencedColumnName = trim($joinColumn->referencedColumnName, '`'); + $joinColumn->quoted = true; + } + + if (isset($joinColumn->onDelete) && strtolower($joinColumn->onDelete) === 'cascade') { + $mapping->isOnDeleteCascade = true; + } + + $mapping->relationToSourceKeyColumns[$joinColumn->name] = $joinColumn->referencedColumnName; + $mapping->joinTableColumns[] = $joinColumn->name; + } + + foreach ($mapping->joinTable->inverseJoinColumns as $inverseJoinColumn) { + if (empty($inverseJoinColumn->referencedColumnName)) { + $inverseJoinColumn->referencedColumnName = $namingStrategy->referenceColumnName(); + } + + if ($inverseJoinColumn->name[0] === '`') { + $inverseJoinColumn->name = trim($inverseJoinColumn->name, '`'); + $inverseJoinColumn->quoted = true; + } + + if ($inverseJoinColumn->referencedColumnName[0] === '`') { + $inverseJoinColumn->referencedColumnName = trim($inverseJoinColumn->referencedColumnName, '`'); + $inverseJoinColumn->quoted = true; + } + + if (isset($inverseJoinColumn->onDelete) && strtolower($inverseJoinColumn->onDelete) === 'cascade') { + $mapping->isOnDeleteCascade = true; + } + + $mapping->relationToTargetKeyColumns[$inverseJoinColumn->name] = $inverseJoinColumn->referencedColumnName; + $mapping->joinTableColumns[] = $inverseJoinColumn->name; + } + + return $mapping; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = parent::__sleep(); + $serialized[] = 'joinTable'; + $serialized[] = 'joinTableColumns'; + + foreach (['relationToSourceKeyColumns', 'relationToTargetKeyColumns'] as $arrayKey) { + if ($this->$arrayKey !== null) { + $serialized[] = $arrayKey; + } + } + + return $serialized; + } +} diff --git a/src/Mapping/ManyToOne.php b/src/Mapping/ManyToOne.php index 1ae62642e4a..b02d9333ff3 100644 --- a/src/Mapping/ManyToOne.php +++ b/src/Mapping/ManyToOne.php @@ -5,58 +5,20 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class ManyToOne implements MappingAttribute { - /** - * @var class-string|null - * @readonly - */ - public $targetEntity; - - /** - * @var string[]|null - * @readonly - */ - public $cascade; - - /** - * The fetching strategy to use for the association. - * - * @var string - * @phpstan-var 'LAZY'|'EAGER'|'EXTRA_LAZY' - * @readonly - * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) - */ - public $fetch = 'LAZY'; - - /** - * @var string|null - * @readonly - */ - public $inversedBy; - /** * @param class-string|null $targetEntity * @param string[]|null $cascade * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch */ public function __construct( - ?string $targetEntity = null, - ?array $cascade = null, - string $fetch = 'LAZY', - ?string $inversedBy = null + public readonly string|null $targetEntity = null, + public readonly array|null $cascade = null, + public readonly string $fetch = 'LAZY', + public readonly string|null $inversedBy = null, ) { - $this->targetEntity = $targetEntity; - $this->cascade = $cascade; - $this->fetch = $fetch; - $this->inversedBy = $inversedBy; } } diff --git a/src/Mapping/ManyToOneAssociationMapping.php b/src/Mapping/ManyToOneAssociationMapping.php new file mode 100644 index 00000000000..3d1bf90b178 --- /dev/null +++ b/src/Mapping/ManyToOneAssociationMapping.php @@ -0,0 +1,12 @@ +|null - * @readonly - */ - public $repositoryClass; - /** @param class-string|null $repositoryClass */ - public function __construct(?string $repositoryClass = null) - { - $this->repositoryClass = $repositoryClass; + public function __construct( + public readonly string|null $repositoryClass = null, + ) { } } diff --git a/src/Mapping/MappingAttribute.php b/src/Mapping/MappingAttribute.php index 48b491c0bdc..61091a90d0d 100644 --- a/src/Mapping/MappingAttribute.php +++ b/src/Mapping/MappingAttribute.php @@ -4,11 +4,7 @@ namespace Doctrine\ORM\Mapping; -/** - * A marker interface for mapping attributes. - * - * @phpstan-ignore interface.extendsDeprecatedInterface - */ -interface MappingAttribute extends Annotation +/** A marker interface for mapping attributes. */ +interface MappingAttribute { } diff --git a/src/Mapping/MappingException.php b/src/Mapping/MappingException.php index 864da952763..9b732427642 100644 --- a/src/Mapping/MappingException.php +++ b/src/Mapping/MappingException.php @@ -6,6 +6,7 @@ use BackedEnum; use Doctrine\ORM\Exception\ORMException; +use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException; use LibXMLError; use ReflectionException; use ValueError; @@ -23,104 +24,70 @@ /** * A MappingException indicates that something is wrong with the mapping setup. */ -class MappingException extends ORMException +class MappingException extends PersistenceMappingException implements ORMException { - /** @return MappingException */ - public static function pathRequired() - { - return new self('Specifying the paths to your entities is required ' . - 'in the AnnotationDriver to retrieve all class names.'); - } - - /** - * @param class-string $entityName - * - * @return MappingException - */ - public static function identifierRequired($entityName) + /** @param class-string $entityName */ + public static function identifierRequired(string $entityName): self { $parent = get_parent_class($entityName); if ($parent !== false) { return new self(sprintf( 'No identifier/primary key specified for Entity "%s" sub class of "%s". Every Entity must have an identifier/primary key.', $entityName, - $parent + $parent, )); } return new self(sprintf( 'No identifier/primary key specified for Entity "%s". Every Entity must have an identifier/primary key.', - $entityName + $entityName, )); } - /** - * @param string $entityName - * @param int $type - * - * @return MappingException - */ - public static function invalidInheritanceType($entityName, $type) + public static function invalidAssociationType(string $entityName, string $fieldName, int $type): self + { + return new self(sprintf( + 'The association "%s#%s" must be of type "ClassMetadata::ONE_TO_MANY", "ClassMetadata::MANY_TO_MANY" or "ClassMetadata::MANY_TO_ONE", "%d" given.', + $entityName, + $fieldName, + $type, + )); + } + + public static function invalidInheritanceType(string $entityName, int $type): self { return new self(sprintf("The inheritance type '%s' specified for '%s' does not exist.", $type, $entityName)); } - /** @return MappingException */ - public static function generatorNotAllowedWithCompositeId() + public static function generatorNotAllowedWithCompositeId(): self { return new self("Id generators can't be used with a composite id."); } - /** - * @param string $entity - * - * @return MappingException - */ - public static function missingFieldName($entity) + public static function missingFieldName(string $entity): self { return new self(sprintf( "The field or association mapping misses the 'fieldName' attribute in entity '%s'.", - $entity + $entity, )); } - /** - * @param string $fieldName - * - * @return MappingException - */ - public static function missingTargetEntity($fieldName) + public static function missingTargetEntity(string $fieldName): self { return new self(sprintf("The association mapping '%s' misses the 'targetEntity' attribute.", $fieldName)); } - /** - * @param string $fieldName - * - * @return MappingException - */ - public static function missingSourceEntity($fieldName) + public static function missingSourceEntity(string $fieldName): self { return new self(sprintf("The association mapping '%s' misses the 'sourceEntity' attribute.", $fieldName)); } - /** - * @param string $fieldName - * - * @return MappingException - */ - public static function missingEmbeddedClass($fieldName) + public static function missingEmbeddedClass(string $fieldName): self { return new self(sprintf("The embed mapping '%s' misses the 'class' attribute.", $fieldName)); } - /** - * @param string $entityName - * @param string $fileName - * - * @return MappingException - */ - public static function mappingFileNotFound($entityName, $fileName) + public static function mappingFileNotFound(string $entityName, string $fileName): self { return new self(sprintf("No mapping file found named '%s' for class '%s'.", $fileName, $entityName)); } @@ -129,11 +96,8 @@ public static function mappingFileNotFound($entityName, $fileName) * Exception for invalid property name override. * * @param string $className The entity's name. - * @param string $fieldName - * - * @return MappingException */ - public static function invalidOverrideFieldName($className, $fieldName) + public static function invalidOverrideFieldName(string $className, string $fieldName): self { return new self(sprintf("Invalid field override named '%s' for class '%s'.", $fieldName, $className)); } @@ -142,143 +106,78 @@ public static function invalidOverrideFieldName($className, $fieldName) * Exception for invalid property type override. * * @param string $className The entity's name. - * @param string $fieldName - * - * @return MappingException */ - public static function invalidOverrideFieldType($className, $fieldName) + public static function invalidOverrideFieldType(string $className, string $fieldName): self { return new self(sprintf( "The column type of attribute '%s' on class '%s' could not be changed.", $fieldName, - $className + $className, )); } - /** - * @param string $className - * @param string $fieldName - * - * @return MappingException - */ - public static function mappingNotFound($className, $fieldName) + public static function mappingNotFound(string $className, string $fieldName): self { return new self(sprintf("No mapping found for field '%s' on class '%s'.", $fieldName, $className)); } - /** - * @param string $className - * @param string $queryName - * - * @return MappingException - */ - public static function queryNotFound($className, $queryName) + public static function queryNotFound(string $className, string $queryName): self { return new self(sprintf("No query found named '%s' on class '%s'.", $queryName, $className)); } - /** - * @param string $className - * @param string $resultName - * - * @return MappingException - */ - public static function resultMappingNotFound($className, $resultName) + public static function resultMappingNotFound(string $className, string $resultName): self { return new self(sprintf("No result set mapping found named '%s' on class '%s'.", $resultName, $className)); } - /** - * @param string $entity - * @param string $queryName - * - * @return MappingException - */ - public static function emptyQueryMapping($entity, $queryName) + public static function emptyQueryMapping(string $entity, string $queryName): self { return new self(sprintf('Query named "%s" in "%s" could not be empty.', $queryName, $entity)); } - /** - * @param string $className - * - * @return MappingException - */ - public static function nameIsMandatoryForQueryMapping($className) + public static function nameIsMandatoryForQueryMapping(string $className): self { return new self(sprintf("Query name on entity class '%s' is not defined.", $className)); } - /** - * @param string $entity - * @param string $queryName - * - * @return MappingException - */ - public static function missingQueryMapping($entity, $queryName) + public static function missingQueryMapping(string $entity, string $queryName): self { return new self(sprintf( 'Query named "%s" in "%s requires a result class or result set mapping.', $queryName, - $entity + $entity, )); } - /** - * @param string $entity - * @param string $resultName - * - * @return MappingException - */ - public static function missingResultSetMappingEntity($entity, $resultName) + public static function missingResultSetMappingEntity(string $entity, string $resultName): self { return new self(sprintf( 'Result set mapping named "%s" in "%s requires a entity class name.', $resultName, - $entity + $entity, )); } - /** - * @param string $entity - * @param string $resultName - * - * @return MappingException - */ - public static function missingResultSetMappingFieldName($entity, $resultName) + public static function missingResultSetMappingFieldName(string $entity, string $resultName): self { return new self(sprintf( 'Result set mapping named "%s" in "%s requires a field name.', $resultName, - $entity + $entity, )); } - /** - * @param string $className - * - * @return MappingException - */ - public static function nameIsMandatoryForSqlResultSetMapping($className) - { - return new self(sprintf("Result set mapping name on entity class '%s' is not defined.", $className)); - } - public static function oneToManyRequiresMappedBy(string $entityName, string $fieldName): MappingException { return new self(sprintf( "OneToMany mapping on entity '%s' field '%s' requires the 'mappedBy' attribute.", $entityName, - $fieldName + $fieldName, )); } - /** - * @param string $fieldName - * - * @return MappingException - */ - public static function joinTableRequired($fieldName) + public static function joinTableRequired(string $fieldName): self { return new self(sprintf("The mapping of field '%s' requires an the 'joinTable' attribute.", $fieldName)); } @@ -290,10 +189,8 @@ public static function joinTableRequired($fieldName) * @param string $expectedOption Which option is required * @param string $hint Can optionally be used to supply a tip for common mistakes, * e.g. "Did you think of the plural s?" - * - * @return MappingException */ - public static function missingRequiredOption($field, $expectedOption, $hint = '') + public static function missingRequiredOption(string $field, string $expectedOption, string $hint = ''): self { $message = "The mapping of field '" . $field . "' is invalid: The option '" . $expectedOption . "' is required."; @@ -306,12 +203,8 @@ public static function missingRequiredOption($field, $expectedOption, $hint = '' /** * Generic exception for invalid mappings. - * - * @param string $fieldName - * - * @return MappingException */ - public static function invalidMapping($fieldName) + public static function invalidMapping(string $fieldName): self { return new self(sprintf("The mapping of field '%s' is invalid.", $fieldName)); } @@ -322,171 +215,109 @@ public static function invalidMapping($fieldName) * within the stacktrace * * @param string $entity The entity's name - * - * @return MappingException */ - public static function reflectionFailure($entity, ReflectionException $previousException) + public static function reflectionFailure(string $entity, ReflectionException $previousException): self { return new self(sprintf('An error occurred in %s', $entity), 0, $previousException); } - /** - * @param string $className - * @param string $joinColumn - * - * @return MappingException - */ - public static function joinColumnMustPointToMappedField($className, $joinColumn) + public static function joinColumnMustPointToMappedField(string $className, string $joinColumn): self { return new self(sprintf( 'The column %s must be mapped to a field in class %s since it is referenced by a join column of another class.', $joinColumn, - $className + $className, )); } - /** - * @param class-string $className - * - * @return MappingException - */ - public static function classIsNotAValidEntityOrMappedSuperClass($className) + public static function joinColumnNotAllowedOnOneToOneInverseSide(string $className, string $fieldName): self + { + return new self(sprintf( + '%s#%s is a OneToOne inverse side, which does not allow join columns.', + $className, + $fieldName, + )); + } + + /** @param class-string $className */ + public static function classIsNotAValidEntityOrMappedSuperClass(string $className): self { $parent = get_parent_class($className); if ($parent !== false) { return new self(sprintf( 'Class "%s" sub class of "%s" is not a valid entity or mapped super class.', $className, - $parent + $parent, )); } return new self(sprintf( 'Class "%s" is not a valid entity or mapped super class.', - $className - )); - } - - /** - * @deprecated 2.9 no longer in use - * - * @param string $className - * @param string $propertyName - * - * @return MappingException - */ - public static function propertyTypeIsRequired($className, $propertyName) - { - return new self(sprintf( - "The attribute 'type' is required for the column description of property %s::\$%s.", $className, - $propertyName )); } /** * @param string $entity The entity's name. * @param string $fieldName The name of the field that was already declared. - * - * @return MappingException */ - public static function duplicateFieldMapping($entity, $fieldName) + public static function duplicateFieldMapping(string $entity, string $fieldName): self { return new self(sprintf( 'Property "%s" in "%s" was already declared, but it must be declared only once', $fieldName, - $entity + $entity, )); } - /** - * @param string $entity - * @param string $fieldName - * - * @return MappingException - */ - public static function duplicateAssociationMapping($entity, $fieldName) + public static function duplicateAssociationMapping(string $entity, string $fieldName): self { return new self(sprintf( 'Property "%s" in "%s" was already declared, but it must be declared only once', $fieldName, - $entity + $entity, )); } - /** - * @param string $entity - * @param string $queryName - * - * @return MappingException - */ - public static function duplicateQueryMapping($entity, $queryName) + public static function duplicateQueryMapping(string $entity, string $queryName): self { return new self(sprintf( 'Query named "%s" in "%s" was already declared, but it must be declared only once', $queryName, - $entity + $entity, )); } - /** - * @param string $entity - * @param string $resultName - * - * @return MappingException - */ - public static function duplicateResultSetMapping($entity, $resultName) + public static function duplicateResultSetMapping(string $entity, string $resultName): self { return new self(sprintf( 'Result set mapping named "%s" in "%s" was already declared, but it must be declared only once', $resultName, - $entity + $entity, )); } - /** - * @param string $entity - * - * @return MappingException - */ - public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + public static function singleIdNotAllowedOnCompositePrimaryKey(string $entity): self { return new self('Single id is not allowed on composite primary key in entity ' . $entity); } - /** - * @param string $entity - * - * @return MappingException - */ - public static function noIdDefined($entity) + public static function noIdDefined(string $entity): self { return new self('No ID defined for entity ' . $entity); } - /** - * @param string $entity - * @param string $fieldName - * @param string $unsupportedType - * - * @return MappingException - */ - public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + public static function unsupportedOptimisticLockingType(string $entity, string $fieldName, string $unsupportedType): self { return new self(sprintf( 'Locking type "%s" (specified in "%s", field "%s") is not supported by Doctrine.', $unsupportedType, $entity, - $fieldName + $fieldName, )); } - /** - * @param string|null $path - * - * @return MappingException - */ - public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + public static function fileMappingDriversRequireConfiguredDirectoryPath(string|null $path = null): self { if (! empty($path)) { $path = '[' . $path . ']'; @@ -494,7 +325,7 @@ public static function fileMappingDriversRequireConfiguredDirectoryPath($path = return new self( 'File mapping drivers must have a valid directory path, ' . - 'however the given path ' . $path . ' seems to be incorrect!' + 'however the given path ' . $path . ' seems to be incorrect!', ); } @@ -504,311 +335,193 @@ public static function fileMappingDriversRequireConfiguredDirectoryPath($path = * * @param string $className The class that could not be found * @param string $owningClass The class that declares the discriminator map. - * - * @return MappingException */ - public static function invalidClassInDiscriminatorMap($className, $owningClass) + public static function invalidClassInDiscriminatorMap(string $className, string $owningClass): self { return new self(sprintf( "Entity class '%s' used in the discriminator map of class '%s' " . 'does not exist.', $className, - $owningClass + $owningClass, )); } /** - * @param string $className * @param string[] $entries * @param array $map - * - * @return MappingException */ - public static function duplicateDiscriminatorEntry($className, array $entries, array $map) + public static function duplicateDiscriminatorEntry(string $className, array $entries, array $map): self { return new self( 'The entries ' . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " . 'If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. ' . 'The entries of the current map are: @DiscriminatorMap({' . implode(', ', array_map( - static function ($a, $b) { - return sprintf("'%s': '%s'", $a, $b); - }, + static fn ($a, $b) => sprintf("'%s': '%s'", $a, $b), array_keys($map), - array_values($map) - )) . '})' + array_values($map), + )) . '})', ); } /** - * @param string $className - * - * @return MappingException + * @param class-string $rootEntityClass + * @param class-string $childEntityClass */ - public static function missingDiscriminatorMap($className) + public static function missingInheritanceTypeDeclaration(string $rootEntityClass, string $childEntityClass): self + { + return new self(sprintf( + "Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared.", + $childEntityClass, + $rootEntityClass, + )); + } + + public static function missingDiscriminatorMap(string $className): self { return new self(sprintf( "Entity class '%s' is using inheritance but no discriminator map was defined.", - $className + $className, )); } - /** - * @param string $className - * - * @return MappingException - */ - public static function missingDiscriminatorColumn($className) + public static function missingDiscriminatorColumn(string $className): self { return new self(sprintf( "Entity class '%s' is using inheritance but no discriminator column was defined.", - $className + $className, )); } - /** - * @param string $className - * @param string $type - * - * @return MappingException - */ - public static function invalidDiscriminatorColumnType($className, $type) + public static function invalidDiscriminatorColumnType(string $className, string $type): self { return new self(sprintf( "Discriminator column type on entity class '%s' is not allowed to be '%s'. 'string' or 'integer' type variables are suggested!", $className, - $type + $type, )); } - /** - * @param string $className - * - * @return MappingException - */ - public static function nameIsMandatoryForDiscriminatorColumns($className) + public static function nameIsMandatoryForDiscriminatorColumns(string $className): self { return new self(sprintf("Discriminator column name on entity class '%s' is not defined.", $className)); } - /** - * @param string $className - * @param string $fieldName - * - * @return MappingException - */ - public static function cannotVersionIdField($className, $fieldName) + public static function cannotVersionIdField(string $className, string $fieldName): self { return new self(sprintf( "Setting Id field '%s' as versionable in entity class '%s' is not supported.", $fieldName, - $className - )); - } - - /** - * @param string $className - * @param string $fieldName - * @param string $type - * - * @return MappingException - */ - public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type) - { - return new self(sprintf( - "It is not possible to set id field '%s' to type '%s' in entity class '%s'. The type '%s' requires conversion SQL which is not allowed for identifiers.", - $fieldName, - $type, $className, - $type )); } - /** - * @param string $className - * @param string $columnName - * - * @return MappingException - */ - public static function duplicateColumnName($className, $columnName) + public static function duplicateColumnName(string $className, string $columnName): self { return new self("Duplicate definition of column '" . $columnName . "' on entity '" . $className . "' in a field or discriminator column mapping."); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function illegalToManyAssociationOnMappedSuperclass($className, $field) + public static function illegalToManyAssociationOnMappedSuperclass(string $className, string $field): self { return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '" . $className . '#' . $field . "'."); } - /** - * @param string $className - * @param string $targetEntity - * @param string $targetField - * - * @return MappingException - */ - public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField) + public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId(string $className, string $targetEntity, string $targetField): self { return new self("It is not possible to map entity '" . $className . "' with a composite primary key " . "as part of the primary key of another entity '" . $targetEntity . '#' . $targetField . "'."); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function noSingleAssociationJoinColumnFound($className, $field) + public static function noSingleAssociationJoinColumnFound(string $className, string $field): self { return new self(sprintf("'%s#%s' is not an association with a single join column.", $className, $field)); } - /** - * @param string $className - * @param string $column - * - * @return MappingException - */ - public static function noFieldNameFoundForColumn($className, $column) + public static function noFieldNameFoundForColumn(string $className, string $column): self { return new self(sprintf( "Cannot find a field on '%s' that is mapped to column '%s'. Either the " . 'field does not exist or an association exists but it has multiple join columns.', $className, - $column + $column, )); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field) + public static function illegalOrphanRemovalOnIdentifierAssociation(string $className, string $field): self { return new self(sprintf( "The orphan removal option is not allowed on an association that is part of the identifier in '%s#%s'.", $className, - $field + $field, )); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function illegalOrphanRemoval($className, $field) + public static function illegalOrphanRemoval(string $className, string $field): self { return new self('Orphan removal is only allowed on one-to-one and one-to-many ' . 'associations, but ' . $className . '#' . $field . ' is not.'); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function illegalInverseIdentifierAssociation($className, $field) + public static function illegalInverseIdentifierAssociation(string $className, string $field): self { return new self(sprintf( "An inverse association is not allowed to be identifier in '%s#%s'.", $className, - $field + $field, )); } - /** - * @param string $className - * @param string $field - * - * @return MappingException - */ - public static function illegalToManyIdentifierAssociation($className, $field) + public static function illegalToManyIdentifierAssociation(string $className, string $field): self { return new self(sprintf( "Many-to-many or one-to-many associations are not allowed to be identifier in '%s#%s'.", $className, - $field + $field, )); } - /** - * @param string $className - * - * @return MappingException - */ - public static function noInheritanceOnMappedSuperClass($className) + public static function noInheritanceOnMappedSuperClass(string $className): self { return new self("It is not supported to define inheritance information on a mapped superclass '" . $className . "'."); } - /** - * @param string $className - * @param string $rootClassName - * - * @return MappingException - */ - public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName) + public static function mappedClassNotPartOfDiscriminatorMap(string $className, string $rootClassName): self { return new self( "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " . "to be properly mapped in the inheritance hierarchy. Alternatively you can make '" . $className . "' an abstract class " . - 'to avoid this exception from occurring.' + 'to avoid this exception from occurring.', ); } - /** - * @param string $className - * @param string $methodName - * - * @return MappingException - */ - public static function lifecycleCallbackMethodNotFound($className, $methodName) + public static function lifecycleCallbackMethodNotFound(string $className, string $methodName): self { return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); } - /** - * @param string $listenerName - * @param string $className - * - * @return MappingException - */ - public static function entityListenerClassNotFound($listenerName, $className) + /** @param class-string $className */ + public static function illegalLifecycleCallbackOnEmbeddedClass(string $event, string $className): self + { + return new self(sprintf( + <<<'EXCEPTION' + Context: Attempt to register lifecycle callback "%s" on embedded class "%s". + Problem: Registering lifecycle callbacks on embedded classes is not allowed. + EXCEPTION, + $event, + $className, + )); + } + + public static function entityListenerClassNotFound(string $listenerName, string $className): self { return new self(sprintf('Entity Listener "%s" declared on "%s" not found.', $listenerName, $className)); } - /** - * @param string $listenerName - * @param string $methodName - * @param string $className - * - * @return MappingException - */ - public static function entityListenerMethodNotFound($listenerName, $methodName, $className) + public static function entityListenerMethodNotFound(string $listenerName, string $methodName, string $className): self { return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName)); } - /** - * @param string $listenerName - * @param string $methodName - * @param string $className - * - * @return MappingException - */ - public static function duplicateEntityListener($listenerName, $methodName, $className) + public static function duplicateEntityListener(string $listenerName, string $methodName, string $className): self { return new self(sprintf('Entity Listener "%s#%s()" in "%s" was already declared, but it must be declared only once.', $listenerName, $methodName, $className)); } @@ -819,148 +532,103 @@ public static function invalidFetchMode(string $className, string $fetchMode): s return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $fetchMode . "'"); } - /** @param int|string $generatedMode */ - public static function invalidGeneratedMode($generatedMode): self + public static function invalidGeneratedMode(int|string $generatedMode): self { return new self("Invalid generated mode '" . $generatedMode . "'"); } - /** - * @param string $className - * - * @return MappingException - */ - public static function compositeKeyAssignedIdGeneratorRequired($className) + public static function compositeKeyAssignedIdGeneratorRequired(string $className): self { return new self("Entity '" . $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported."); } - /** - * @param string $targetEntity - * @param string $sourceEntity - * @param string $associationName - * - * @return MappingException - */ - public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + public static function invalidTargetEntityClass(string $targetEntity, string $sourceEntity, string $associationName): self { return new self('The target-entity ' . $targetEntity . " cannot be found in '" . $sourceEntity . '#' . $associationName . "'."); } - /** - * @param string[] $cascades - * @param string $className - * @param string $propertyName - * - * @return MappingException - */ - public static function invalidCascadeOption(array $cascades, $className, $propertyName) + /** @param string[] $cascades */ + public static function invalidCascadeOption(array $cascades, string $className, string $propertyName): self { - $cascades = implode(', ', array_map(static function ($e) { - return "'" . $e . "'"; - }, $cascades)); + $cascades = implode(', ', array_map(static fn (string $e): string => "'" . $e . "'", $cascades)); return new self(sprintf( - "You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'", + "You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', and 'detach'", $className, $propertyName, - $cascades + $cascades, )); } - /** - * @param string $className - * - * @return MappingException - */ - public static function missingSequenceName($className) + public static function missingSequenceName(string $className): self { return new self( - sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className) + sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className), ); } - /** - * @param string $className - * @param string $propertyName - * - * @return MappingException - */ - public static function infiniteEmbeddableNesting($className, $propertyName) + public static function infiniteEmbeddableNesting(string $className, string $propertyName): self { return new self( sprintf( 'Infinite nesting detected for embedded property %s::%s. ' . 'You cannot embed an embeddable from the same type inside an embeddable.', $className, - $propertyName - ) + $propertyName, + ), ); } - /** - * @param string $className - * @param string $propertyName - * - * @return self - */ - public static function illegalOverrideOfInheritedProperty($className, $propertyName) + public static function illegalOverrideOfInheritedProperty(string $className, string $propertyName, string $inheritFromClass): self { return new self( sprintf( - 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits, which is not the case for %s::%s.', + 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s.', $className, - $propertyName - ) + $propertyName, + $inheritFromClass, + ), ); } - /** @return self */ - public static function invalidIndexConfiguration($className, $indexName) + public static function invalidIndexConfiguration(string $className, string $indexName): self { return new self( sprintf( 'Index %s for entity %s should contain columns or fields values, but not both.', $indexName, - $className - ) + $className, + ), ); } - /** @return self */ - public static function invalidUniqueConstraintConfiguration($className, $indexName) + public static function invalidUniqueConstraintConfiguration(string $className, string $indexName): self { return new self( sprintf( 'Unique constraint %s for entity %s should contain columns or fields values, but not both.', $indexName, - $className - ) + $className, + ), ); } - /** @param mixed $givenValue */ - public static function invalidOverrideType(string $expectdType, $givenValue): self + public static function invalidOverrideType(string $expectdType, mixed $givenValue): self { return new self(sprintf( 'Expected %s, but %s was given.', $expectdType, - get_debug_type($givenValue) + get_debug_type($givenValue), )); } - public static function enumsRequirePhp81(string $className, string $fieldName): self - { - return new self(sprintf('Enum types require PHP 8.1 in %s::$%s', $className, $fieldName)); - } - public static function backedEnumTypeRequired(string $className, string $fieldName, string $enumType): self { return new self(sprintf( 'Attempting to map a non-backed enum type %s in entity %s::$%s. Please use backed enums only', $enumType, $className, - $fieldName + $fieldName, )); } @@ -970,7 +638,7 @@ public static function nonEnumTypeMapped(string $className, string $fieldName, s 'Attempting to map non-enum type %s as enum in entity %s::$%s', $enumType, $className, - $fieldName + $fieldName, )); } @@ -983,7 +651,7 @@ public static function invalidEnumValue( string $fieldName, string $value, string $enumType, - ValueError $previous + ValueError $previous, ): self { return new self(sprintf( <<<'EXCEPTION' @@ -995,22 +663,29 @@ public static function invalidEnumValue( $className, $fieldName, $value, - $enumType + $enumType, ), 0, $previous); } /** @param LibXMLError[] $errors */ public static function fromLibXmlErrors(array $errors): self { - $formatter = static function (LibXMLError $error): string { - return sprintf( - 'libxml error: %s in %s at line %d', - $error->message, - $error->file, - $error->line - ); - }; + $formatter = static fn (LibXMLError $error): string => sprintf( + 'libxml error: %s in %s at line %d', + $error->message, + $error->file, + $error->line, + ); return new self(implode(PHP_EOL, array_map($formatter, $errors))); } + + public static function invalidAttributeOnEmbeddable(string $entityName, string $attributeName): self + { + return new self(sprintf( + 'Attribute "%s" on embeddable "%s" is not allowed.', + $attributeName, + $entityName, + )); + } } diff --git a/src/Mapping/NamedNativeQueries.php b/src/Mapping/NamedNativeQueries.php deleted file mode 100644 index 02ec6100b69..00000000000 --- a/src/Mapping/NamedNativeQueries.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - public $value = []; -} diff --git a/src/Mapping/NamedNativeQuery.php b/src/Mapping/NamedNativeQuery.php deleted file mode 100644 index 159b4962965..00000000000 --- a/src/Mapping/NamedNativeQuery.php +++ /dev/null @@ -1,45 +0,0 @@ - */ - public $value; -} diff --git a/src/Mapping/NamedQuery.php b/src/Mapping/NamedQuery.php deleted file mode 100644 index 104cd95c7f9..00000000000 --- a/src/Mapping/NamedQuery.php +++ /dev/null @@ -1,20 +0,0 @@ -|null - * @readonly - */ - public $cascade; - - /** - * The fetching strategy to use for the association. - * - * @var string - * @phpstan-var 'LAZY'|'EAGER'|'EXTRA_LAZY' - * @readonly - * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) - */ - public $fetch = 'LAZY'; - - /** - * @var bool - * @readonly - */ - public $orphanRemoval = false; - - /** - * @var string|null - * @readonly - */ - public $indexBy; - /** * @param class-string|null $targetEntity * @param string[]|null $cascade * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch */ public function __construct( - ?string $mappedBy = null, - ?string $targetEntity = null, - ?array $cascade = null, - string $fetch = 'LAZY', - bool $orphanRemoval = false, - ?string $indexBy = null + public readonly string|null $targetEntity = null, + public readonly string|null $mappedBy = null, + public readonly array|null $cascade = null, + public readonly string $fetch = 'LAZY', + public readonly bool $orphanRemoval = false, + public readonly string|null $indexBy = null, ) { - $this->mappedBy = $mappedBy; - $this->targetEntity = $targetEntity; - $this->cascade = $cascade; - $this->fetch = $fetch; - $this->orphanRemoval = $orphanRemoval; - $this->indexBy = $indexBy; } } diff --git a/src/Mapping/OneToManyAssociationMapping.php b/src/Mapping/OneToManyAssociationMapping.php new file mode 100644 index 00000000000..139c1d9ef05 --- /dev/null +++ b/src/Mapping/OneToManyAssociationMapping.php @@ -0,0 +1,75 @@ +, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): static + { + $mapping = parent::fromMappingArray($mappingArray); + + if ($mapping->orphanRemoval && ! $mapping->isCascadeRemove()) { + $mapping->cascade[] = 'remove'; + } + + return $mapping; + } + + /** + * @param mixed[] $mappingArray + * @phpstan-param array{ + * fieldName: string, + * sourceEntity: class-string, + * targetEntity: class-string, + * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * } $mappingArray + */ + public static function fromMappingArrayAndName(array $mappingArray, string $name): static + { + $mapping = self::fromMappingArray($mappingArray); + + // OneToMany-side MUST be inverse (must have mappedBy) + if (! isset($mapping->mappedBy)) { + throw MappingException::oneToManyRequiresMappedBy($name, $mapping->fieldName); + } + + return $mapping; + } +} diff --git a/src/Mapping/OneToOne.php b/src/Mapping/OneToOne.php index 6318ae53c32..80c2392acae 100644 --- a/src/Mapping/OneToOne.php +++ b/src/Mapping/OneToOne.php @@ -5,74 +5,22 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class OneToOne implements MappingAttribute { - /** - * @var class-string|null - * @readonly - */ - public $targetEntity; - - /** - * @var string|null - * @readonly - */ - public $mappedBy; - - /** - * @var string|null - * @readonly - */ - public $inversedBy; - - /** - * @var array|null - * @readonly - */ - public $cascade; - - /** - * The fetching strategy to use for the association. - * - * @var string - * @phpstan-var 'LAZY'|'EAGER'|'EXTRA_LAZY' - * @readonly - * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) - */ - public $fetch = 'LAZY'; - - /** - * @var bool - * @readonly - */ - public $orphanRemoval = false; - /** * @param class-string|null $targetEntity * @param array|null $cascade * @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY' $fetch */ public function __construct( - ?string $mappedBy = null, - ?string $inversedBy = null, - ?string $targetEntity = null, - ?array $cascade = null, - string $fetch = 'LAZY', - bool $orphanRemoval = false + public readonly string|null $targetEntity = null, + public readonly string|null $mappedBy = null, + public readonly string|null $inversedBy = null, + public readonly array|null $cascade = null, + public readonly string $fetch = 'LAZY', + public readonly bool $orphanRemoval = false, ) { - $this->mappedBy = $mappedBy; - $this->inversedBy = $inversedBy; - $this->targetEntity = $targetEntity; - $this->cascade = $cascade; - $this->fetch = $fetch; - $this->orphanRemoval = $orphanRemoval; } } diff --git a/src/Mapping/OneToOneAssociationMapping.php b/src/Mapping/OneToOneAssociationMapping.php new file mode 100644 index 00000000000..89c6483e504 --- /dev/null +++ b/src/Mapping/OneToOneAssociationMapping.php @@ -0,0 +1,9 @@ + - * @readonly - */ - public $value; - /** @param array $value */ - public function __construct(array $value) - { - $this->value = $value; + public function __construct( + public readonly array $value, + ) { } } diff --git a/src/Mapping/OwningSideMapping.php b/src/Mapping/OwningSideMapping.php new file mode 100644 index 00000000000..ab8b7b2d7f1 --- /dev/null +++ b/src/Mapping/OwningSideMapping.php @@ -0,0 +1,28 @@ + */ + public function __sleep(): array + { + $serialized = parent::__sleep(); + + if ($this->inversedBy !== null) { + $serialized[] = 'inversedBy'; + } + + return $serialized; + } +} diff --git a/src/Mapping/PostLoad.php b/src/Mapping/PostLoad.php index 0b2d063e896..9ce1e5c2b9c 100644 --- a/src/Mapping/PostLoad.php +++ b/src/Mapping/PostLoad.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PostLoad implements MappingAttribute { diff --git a/src/Mapping/PostPersist.php b/src/Mapping/PostPersist.php index d28ac86c797..f7bf2e18b96 100644 --- a/src/Mapping/PostPersist.php +++ b/src/Mapping/PostPersist.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PostPersist implements MappingAttribute { diff --git a/src/Mapping/PostRemove.php b/src/Mapping/PostRemove.php index 9b7e1c40ae4..394c175674f 100644 --- a/src/Mapping/PostRemove.php +++ b/src/Mapping/PostRemove.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PostRemove implements MappingAttribute { diff --git a/src/Mapping/PostUpdate.php b/src/Mapping/PostUpdate.php index 771c2db731d..7b956757412 100644 --- a/src/Mapping/PostUpdate.php +++ b/src/Mapping/PostUpdate.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PostUpdate implements MappingAttribute { diff --git a/src/Mapping/PreFlush.php b/src/Mapping/PreFlush.php index 50dfa703000..f2c09d71c9e 100644 --- a/src/Mapping/PreFlush.php +++ b/src/Mapping/PreFlush.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PreFlush implements MappingAttribute { diff --git a/src/Mapping/PrePersist.php b/src/Mapping/PrePersist.php index ab4e4d54a9d..1b88a7a1c4a 100644 --- a/src/Mapping/PrePersist.php +++ b/src/Mapping/PrePersist.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PrePersist implements MappingAttribute { diff --git a/src/Mapping/PreRemove.php b/src/Mapping/PreRemove.php index 68c69157b76..f63d4e0975d 100644 --- a/src/Mapping/PreRemove.php +++ b/src/Mapping/PreRemove.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PreRemove implements MappingAttribute { diff --git a/src/Mapping/PreUpdate.php b/src/Mapping/PreUpdate.php index b6f2fcda624..9b73bfd61fd 100644 --- a/src/Mapping/PreUpdate.php +++ b/src/Mapping/PreUpdate.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("METHOD") - */ #[Attribute(Attribute::TARGET_METHOD)] final class PreUpdate implements MappingAttribute { diff --git a/src/Mapping/QuoteStrategy.php b/src/Mapping/QuoteStrategy.php index 3fe20709bef..7b78d12a81b 100644 --- a/src/Mapping/QuoteStrategy.php +++ b/src/Mapping/QuoteStrategy.php @@ -8,78 +8,61 @@ /** * A set of rules for determining the column, alias and table quotes. - * - * @phpstan-import-type AssociationMapping from ClassMetadata - * @phpstan-import-type JoinColumnData from ClassMetadata */ interface QuoteStrategy { /** * Gets the (possibly quoted) column name for safe use in an SQL statement. - * - * @param string $fieldName - * - * @return string */ - public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform); + public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string; /** * Gets the (possibly quoted) primary table name for safe use in an SQL statement. - * - * @return string */ - public function getTableName(ClassMetadata $class, AbstractPlatform $platform); + public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string; /** * Gets the (possibly quoted) sequence name for safe use in an SQL statement. * * @param mixed[] $definition - * - * @return string */ - public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform); + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string; - /** - * Gets the (possibly quoted) name of the join table. - * - * @param AssociationMapping $association - * - * @return string - */ - public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform); + /** Gets the (possibly quoted) name of the join table. */ + public function getJoinTableName( + ManyToManyOwningSideMapping $association, + ClassMetadata $class, + AbstractPlatform $platform, + ): string; /** * Gets the (possibly quoted) join column name. - * - * @param JoinColumnData $joinColumn - * - * @return string */ - public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string; /** * Gets the (possibly quoted) join column name. - * - * @param JoinColumnData $joinColumn - * - * @return string */ - public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + public function getReferencedJoinColumnName( + JoinColumnMapping $joinColumn, + ClassMetadata $class, + AbstractPlatform $platform, + ): string; /** * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. * * @phpstan-return list */ - public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform); + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array; /** * Gets the column alias. - * - * @param string $columnName - * @param int $counter - * - * @return string */ - public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ?ClassMetadata $class = null); + public function getColumnAlias( + string $columnName, + int $counter, + AbstractPlatform $platform, + ClassMetadata|null $class = null, + ): string; } diff --git a/src/Mapping/Reflection/ReflectionPropertiesGetter.php b/src/Mapping/Reflection/ReflectionPropertiesGetter.php deleted file mode 100644 index 886c7c77478..00000000000 --- a/src/Mapping/Reflection/ReflectionPropertiesGetter.php +++ /dev/null @@ -1,132 +0,0 @@ -reflectionService = $reflectionService; - } - - /** - * @param class-string $className - * - * @return ReflectionProperty[] indexed by property internal name - */ - public function getProperties($className): array - { - if (isset($this->properties[$className])) { - return $this->properties[$className]; - } - - return $this->properties[$className] = array_merge( - // first merge because `array_merge` expects >= 1 params - ...array_merge( - [[]], - array_map( - [$this, 'getClassProperties'], - $this->getHierarchyClasses($className) - ) - ) - ); - } - - /** - * @param class-string $className - * - * @return ReflectionClass[] - * @phpstan-return list> - */ - private function getHierarchyClasses(string $className): array - { - $classes = []; - $parentClassName = $className; - - while ($parentClassName && $currentClass = $this->reflectionService->getClass($parentClassName)) { - $classes[] = $currentClass; - $parentClassName = null; - - $parentClass = $currentClass->getParentClass(); - if ($parentClass) { - $parentClassName = $parentClass->name; - } - } - - return $classes; - } - - // phpcs:disable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod - - /** - * @return ReflectionProperty[] - * @phpstan-return array - */ - private function getClassProperties(ReflectionClass $reflectionClass): array - { - // phpcs:enable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod - $properties = $reflectionClass->getProperties(); - - return array_filter( - array_filter(array_map( - [$this, 'getAccessibleProperty'], - array_combine( - array_map([$this, 'getLogicalName'], $properties), - $properties - ) - )), - [$this, 'isInstanceProperty'] - ); - } - - private function isInstanceProperty(ReflectionProperty $reflectionProperty): bool - { - return ! $reflectionProperty->isStatic(); - } - - private function getAccessibleProperty(ReflectionProperty $property): ?ReflectionProperty - { - return $this->reflectionService->getAccessibleProperty( - $property->class, - $property->name - ); - } - - private function getLogicalName(ReflectionProperty $property): string - { - $propertyName = $property->name; - - if ($property->isPublic()) { - return $propertyName; - } - - if ($property->isProtected()) { - return "\0*\0" . $propertyName; - } - - return "\0" . $property->class . "\0" . $propertyName; - } -} diff --git a/src/Mapping/ReflectionEmbeddedProperty.php b/src/Mapping/ReflectionEmbeddedProperty.php index db4c1f868a3..27aabadec25 100644 --- a/src/Mapping/ReflectionEmbeddedProperty.php +++ b/src/Mapping/ReflectionEmbeddedProperty.php @@ -6,7 +6,6 @@ use Doctrine\Instantiator\Instantiator; use ReflectionProperty; -use ReturnTypeWillChange; /** * Acts as a proxy to a nested Property structure, making it look like @@ -17,37 +16,24 @@ * * TODO: Move this class into Common\Reflection */ -class ReflectionEmbeddedProperty extends ReflectionProperty +final class ReflectionEmbeddedProperty extends ReflectionProperty { - /** @var ReflectionProperty reflection property of the class where the embedded object has to be put */ - private $parentProperty; - - /** @var ReflectionProperty reflection property of the embedded object */ - private $childProperty; - - /** @var string name of the embedded class to be eventually instantiated */ - private $embeddedClass; - - /** @var Instantiator|null */ - private $instantiator; - - /** @param string $embeddedClass */ - public function __construct(ReflectionProperty $parentProperty, ReflectionProperty $childProperty, $embeddedClass) - { - $this->parentProperty = $parentProperty; - $this->childProperty = $childProperty; - $this->embeddedClass = (string) $embeddedClass; - - parent::__construct($childProperty->class, $childProperty->name); - } + private Instantiator|null $instantiator = null; /** - * {@inheritDoc} - * - * @return mixed + * @param ReflectionProperty $parentProperty reflection property of the class where the embedded object has to be put + * @param ReflectionProperty $childProperty reflection property of the embedded object + * @phpstan-param class-string $embeddedClass */ - #[ReturnTypeWillChange] - public function getValue($object = null) + public function __construct( + private readonly ReflectionProperty $parentProperty, + private readonly ReflectionProperty $childProperty, + private readonly string $embeddedClass, + ) { + parent::__construct($childProperty->getDeclaringClass()->name, $childProperty->getName()); + } + + public function getValue(object|null $object = null): mixed { $embeddedObject = $this->parentProperty->getValue($object); @@ -58,18 +44,12 @@ public function getValue($object = null) return $this->childProperty->getValue($embeddedObject); } - /** - * {@inheritDoc} - * - * @return void - */ - #[ReturnTypeWillChange] - public function setValue($object, $value = null) + public function setValue(mixed $object, mixed $value = null): void { $embeddedObject = $this->parentProperty->getValue($object); if ($embeddedObject === null) { - $this->instantiator = $this->instantiator ?: new Instantiator(); + $this->instantiator ??= new Instantiator(); $embeddedObject = $this->instantiator->instantiate($this->embeddedClass); diff --git a/src/Mapping/ReflectionEnumProperty.php b/src/Mapping/ReflectionEnumProperty.php index 3283a54ab0d..0ebd978f412 100644 --- a/src/Mapping/ReflectionEnumProperty.php +++ b/src/Mapping/ReflectionEnumProperty.php @@ -6,42 +6,26 @@ use BackedEnum; use ReflectionProperty; -use ReturnTypeWillChange; use ValueError; use function array_map; -use function get_class; use function is_array; -class ReflectionEnumProperty extends ReflectionProperty +/** @deprecated use Doctrine\Persistence\Reflection\EnumReflectionProperty instead */ +final class ReflectionEnumProperty extends ReflectionProperty { - /** @var ReflectionProperty */ - private $originalReflectionProperty; - - /** @var class-string */ - private $enumType; - /** @param class-string $enumType */ - public function __construct(ReflectionProperty $originalReflectionProperty, string $enumType) - { - $this->originalReflectionProperty = $originalReflectionProperty; - $this->enumType = $enumType; - + public function __construct( + private readonly ReflectionProperty $originalReflectionProperty, + private readonly string $enumType, + ) { parent::__construct( $originalReflectionProperty->class, - $originalReflectionProperty->name + $originalReflectionProperty->name, ); } - /** - * {@inheritDoc} - * - * @param object|null $object - * - * @return int|string|int[]|string[]|null - */ - #[ReturnTypeWillChange] - public function getValue($object = null) + public function getValue(object|null $object = null): int|string|array|null { if ($object === null) { return null; @@ -54,9 +38,10 @@ public function getValue($object = null) } if (is_array($enum)) { - return array_map(static function (BackedEnum $item): mixed { - return $item->value; - }, $enum); + return array_map( + static fn (BackedEnum $item): int|string => $item->value, + $enum, + ); } return $enum->value; @@ -66,13 +51,11 @@ public function getValue($object = null) * @param object $object * @param int|string|int[]|string[]|BackedEnum|BackedEnum[]|null $value */ - public function setValue($object, $value = null): void + public function setValue(mixed $object, mixed $value = null): void { if ($value !== null) { if (is_array($value)) { - $value = array_map(function ($item) use ($object): BackedEnum { - return $this->initializeEnumValue($object, $item); - }, $value); + $value = array_map(fn (int|string|BackedEnum $item): BackedEnum => $this->initializeEnumValue($object, $item), $value); } else { $value = $this->initializeEnumValue($object, $value); } @@ -81,11 +64,7 @@ public function setValue($object, $value = null): void $this->originalReflectionProperty->setValue($object, $value); } - /** - * @param object $object - * @param int|string|BackedEnum $value - */ - private function initializeEnumValue($object, $value): BackedEnum + private function initializeEnumValue(object $object, int|string|BackedEnum $value): BackedEnum { if ($value instanceof BackedEnum) { return $value; @@ -97,11 +76,11 @@ private function initializeEnumValue($object, $value): BackedEnum return $enumType::from($value); } catch (ValueError $e) { throw MappingException::invalidEnumValue( - get_class($object), + $object::class, $this->originalReflectionProperty->name, (string) $value, $enumType, - $e + $e, ); } } diff --git a/src/Mapping/ReflectionReadonlyProperty.php b/src/Mapping/ReflectionReadonlyProperty.php index ed91cd6715d..13e9f6ddda3 100644 --- a/src/Mapping/ReflectionReadonlyProperty.php +++ b/src/Mapping/ReflectionReadonlyProperty.php @@ -18,7 +18,7 @@ final class ReflectionReadonlyProperty extends ReflectionProperty { public function __construct( - private ReflectionProperty $wrappedProperty + private readonly ReflectionProperty $wrappedProperty, ) { if (! $wrappedProperty->isReadOnly()) { throw new InvalidArgumentException('Given property is not readonly.'); @@ -27,7 +27,7 @@ public function __construct( parent::__construct($wrappedProperty->class, $wrappedProperty->name); } - public function getValue(?object $object = null): mixed + public function getValue(object|null $object = null): mixed { return $this->wrappedProperty->getValue(...func_get_args()); } diff --git a/src/Mapping/SequenceGenerator.php b/src/Mapping/SequenceGenerator.php index 22a6e2406f5..6c06e845827 100644 --- a/src/Mapping/SequenceGenerator.php +++ b/src/Mapping/SequenceGenerator.php @@ -5,41 +5,14 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class SequenceGenerator implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $sequenceName; - - /** - * @var int - * @readonly - */ - public $allocationSize = 1; - - /** - * @var int - * @readonly - */ - public $initialValue = 1; - public function __construct( - ?string $sequenceName = null, - int $allocationSize = 1, - int $initialValue = 1 + public readonly string|null $sequenceName = null, + public readonly int $allocationSize = 1, + public readonly int $initialValue = 1, ) { - $this->sequenceName = $sequenceName; - $this->allocationSize = $allocationSize; - $this->initialValue = $initialValue; } } diff --git a/src/Mapping/SqlResultSetMapping.php b/src/Mapping/SqlResultSetMapping.php deleted file mode 100644 index 9cc91d6213d..00000000000 --- a/src/Mapping/SqlResultSetMapping.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - public $entities = []; - - /** - * Specifies the result set mapping to scalar values. - * - * @var array<\Doctrine\ORM\Mapping\ColumnResult> - */ - public $columns = []; -} diff --git a/src/Mapping/SqlResultSetMappings.php b/src/Mapping/SqlResultSetMappings.php deleted file mode 100644 index 79ad0280674..00000000000 --- a/src/Mapping/SqlResultSetMappings.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - public $value = []; -} diff --git a/src/Mapping/Table.php b/src/Mapping/Table.php index e57c608e8b5..ac1e8edfa35 100644 --- a/src/Mapping/Table.php +++ b/src/Mapping/Table.php @@ -5,62 +5,41 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; +use Doctrine\Deprecations\Deprecation; -/** - * @Annotation - * @NamedArgumentConstructor - * @Target("CLASS") - */ #[Attribute(Attribute::TARGET_CLASS)] final class Table implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var string|null - * @readonly - */ - public $schema; - - /** - * @var array|null - * @readonly - */ - public $indexes; - - /** - * @var array|null - * @readonly - */ - public $uniqueConstraints; - - /** - * @var array - * @readonly - */ - public $options = []; - /** * @param array|null $indexes * @param array|null $uniqueConstraints * @param array $options */ public function __construct( - ?string $name = null, - ?string $schema = null, - ?array $indexes = null, - ?array $uniqueConstraints = null, - array $options = [] + public readonly string|null $name = null, + public readonly string|null $schema = null, + public readonly array|null $indexes = null, + public readonly array|null $uniqueConstraints = null, + public readonly array $options = [], ) { - $this->name = $name; - $this->schema = $schema; - $this->indexes = $indexes; - $this->uniqueConstraints = $uniqueConstraints; - $this->options = $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, + ); + } } } diff --git a/src/Mapping/ToManyAssociationMapping.php b/src/Mapping/ToManyAssociationMapping.php new file mode 100644 index 00000000000..fb1fdf3fe86 --- /dev/null +++ b/src/Mapping/ToManyAssociationMapping.php @@ -0,0 +1,16 @@ +indexBy() */ + public function isIndexed(): bool; + + public function indexBy(): string; + + /** @return array */ + public function orderBy(): array; +} diff --git a/src/Mapping/ToManyAssociationMappingImplementation.php b/src/Mapping/ToManyAssociationMappingImplementation.php new file mode 100644 index 00000000000..d2ad7595372 --- /dev/null +++ b/src/Mapping/ToManyAssociationMappingImplementation.php @@ -0,0 +1,69 @@ + + */ + public array $orderBy = []; + + /** @return array */ + final public function orderBy(): array + { + return $this->orderBy; + } + + /** @phpstan-assert-if-true !null $this->indexBy */ + final public function isIndexed(): bool + { + return $this->indexBy !== null; + } + + final public function indexBy(): string + { + if (! $this->isIndexed()) { + throw new LogicException(sprintf( + 'This mapping is not indexed. Use %s::isIndexed() to check that before calling %s.', + self::class, + __METHOD__, + )); + } + + return $this->indexBy; + } + + /** @return list */ + public function __sleep(): array + { + $serialized = parent::__sleep(); + + if ($this->indexBy !== null) { + $serialized[] = 'indexBy'; + } + + if ($this->orderBy !== []) { + $serialized[] = 'orderBy'; + } + + return $serialized; + } +} diff --git a/src/Mapping/ToManyInverseSideMapping.php b/src/Mapping/ToManyInverseSideMapping.php new file mode 100644 index 00000000000..a092ebe31aa --- /dev/null +++ b/src/Mapping/ToManyInverseSideMapping.php @@ -0,0 +1,10 @@ +, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * } $mappingArray + */ + public static function fromMappingArrayAndName( + array $mappingArray, + string $name, + ): static { + $mapping = static::fromMappingArray($mappingArray); + + if (isset($mapping->id) && $mapping->id === true) { + throw MappingException::illegalInverseIdentifierAssociation($name, $mapping->fieldName); + } + + if ($mapping->orphanRemoval) { + if (! $mapping->isCascadeRemove()) { + $mapping->cascade[] = 'remove'; + } + + $mapping->unique = null; + } + + return $mapping; + } +} diff --git a/src/Mapping/ToOneOwningSideMapping.php b/src/Mapping/ToOneOwningSideMapping.php new file mode 100644 index 00000000000..ed3d596f801 --- /dev/null +++ b/src/Mapping/ToOneOwningSideMapping.php @@ -0,0 +1,212 @@ + */ + public array $sourceToTargetKeyColumns = []; + + /** @var array */ + public array $targetToSourceKeyColumns = []; + + /** @var list */ + public array $joinColumns = []; + + /** @var array */ + public array $joinColumnFieldNames = []; + + /** + * @param array $mappingArray + * @phpstan-param array{ + * fieldName: string, + * sourceEntity: class-string, + * targetEntity: class-string, + * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * joinColumns?: mixed[]|null, + * } $mappingArray + */ + public static function fromMappingArray(array $mappingArray): static + { + $joinColumns = $mappingArray['joinColumns'] ?? []; + unset($mappingArray['joinColumns']); + + $instance = parent::fromMappingArray($mappingArray); + assert($instance->isToOneOwningSide()); + + foreach ($joinColumns as $column) { + $instance->joinColumns[] = JoinColumnMapping::fromMappingArray($column); + } + + if ($instance->orphanRemoval) { + if (! $instance->isCascadeRemove()) { + $instance->cascade[] = 'remove'; + } + + $instance->unique = null; + } + + return $instance; + } + + /** + * @param mixed[] $mappingArray + * @param class-string $name + * @phpstan-param array{ + * fieldName: string, + * sourceEntity: class-string, + * targetEntity: class-string, + * cascade?: list<'persist'|'remove'|'detach'|'refresh'|'all'>, + * fetch?: ClassMetadata::FETCH_*|null, + * inherited?: class-string|null, + * declared?: class-string|null, + * cache?: array|null, + * id?: bool|null, + * isOnDeleteCascade?: bool|null, + * originalClass?: class-string|null, + * originalField?: string|null, + * orphanRemoval?: bool, + * unique?: bool|null, + * joinTable?: mixed[]|null, + * type?: int, + * isOwningSide: bool, + * joinColumns?: mixed[]|null, + * } $mappingArray + */ + public static function fromMappingArrayAndName( + array $mappingArray, + NamingStrategy $namingStrategy, + string $name, + array|null $table, + bool $isInheritanceTypeSingleTable, + ): static { + if (isset($mappingArray['joinColumns'])) { + foreach ($mappingArray['joinColumns'] as $index => $joinColumn) { + if (empty($joinColumn['name'])) { + $mappingArray['joinColumns'][$index]['name'] = $namingStrategy->joinColumnName($mappingArray['fieldName'], $name); + } + } + } + + $mapping = static::fromMappingArray($mappingArray); + + assert($mapping->isToOneOwningSide()); + if (empty($mapping->joinColumns)) { + // Apply default join column + $mapping->joinColumns = [ + JoinColumnMapping::fromMappingArray([ + 'name' => $namingStrategy->joinColumnName($mapping->fieldName, $name), + 'referencedColumnName' => $namingStrategy->referenceColumnName(), + ]), + ]; + } + + $uniqueConstraintColumns = []; + + foreach ($mapping->joinColumns as $joinColumn) { + if ($mapping->isOneToOne() && ! $isInheritanceTypeSingleTable) { + if (count($mapping->joinColumns) === 1) { + if (empty($mapping->id)) { + $joinColumn->unique = true; + } + } else { + $uniqueConstraintColumns[] = $joinColumn->name; + } + } + + if (empty($joinColumn->referencedColumnName)) { + $joinColumn->referencedColumnName = $namingStrategy->referenceColumnName(); + } + + if ($joinColumn->name[0] === '`') { + $joinColumn->name = trim($joinColumn->name, '`'); + $joinColumn->quoted = true; + } + + if ($joinColumn->referencedColumnName[0] === '`') { + $joinColumn->referencedColumnName = trim($joinColumn->referencedColumnName, '`'); + $joinColumn->quoted = true; + } + + $mapping->sourceToTargetKeyColumns[$joinColumn->name] = $joinColumn->referencedColumnName; + $mapping->joinColumnFieldNames[$joinColumn->name] = $joinColumn->fieldName ?? $joinColumn->name; + } + + if ($uniqueConstraintColumns) { + if (! $table) { + throw new RuntimeException('ClassMetadata::setTable() has to be called before defining a one to one relationship.'); + } + + $table['uniqueConstraints'][$mapping->fieldName . '_uniq'] = ['columns' => $uniqueConstraintColumns]; + } + + $mapping->targetToSourceKeyColumns = array_flip($mapping->sourceToTargetKeyColumns); + + return $mapping; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + if ($offset === 'joinColumns') { + $joinColumns = []; + foreach ($value as $column) { + $joinColumns[] = JoinColumnMapping::fromMappingArray($column); + } + + $this->joinColumns = $joinColumns; + + return; + } + + parent::offsetSet($offset, $value); + } + + /** @return array */ + public function toArray(): array + { + $array = parent::toArray(); + + $joinColumns = []; + foreach ($array['joinColumns'] as $column) { + $joinColumns[] = (array) $column; + } + + $array['joinColumns'] = $joinColumns; + + return $array; + } + + /** @return list */ + public function __sleep(): array + { + return [ + ...parent::__sleep(), + 'joinColumns', + 'joinColumnFieldNames', + 'sourceToTargetKeyColumns', + 'targetToSourceKeyColumns', + ]; + } +} diff --git a/src/Mapping/UnderscoreNamingStrategy.php b/src/Mapping/UnderscoreNamingStrategy.php index c463597c3a9..cedc1502205 100644 --- a/src/Mapping/UnderscoreNamingStrategy.php +++ b/src/Mapping/UnderscoreNamingStrategy.php @@ -4,8 +4,6 @@ namespace Doctrine\ORM\Mapping; -use Doctrine\Deprecations\Deprecation; - use function preg_replace; use function str_contains; use function strrpos; @@ -24,40 +22,17 @@ */ class UnderscoreNamingStrategy implements NamingStrategy { - private const DEFAULT_PATTERN = '/(?<=[a-z])([A-Z])/'; - private const NUMBER_AWARE_PATTERN = '/(?<=[a-z0-9])([A-Z])/'; - - /** @var int */ - private $case; - - /** - * @var string - * @phpstan-var non-empty-string - */ - private $pattern; - /** * Underscore naming strategy construct. * * @param int $case CASE_LOWER | CASE_UPPER */ - public function __construct($case = CASE_LOWER, bool $numberAware = false) + public function __construct(private int $case = CASE_LOWER) { - if (! $numberAware) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/7908', - 'Creating %s without setting second argument $numberAware=true is deprecated and will be removed in Doctrine ORM 3.0.', - self::class - ); - } - - $this->case = $case; - $this->pattern = $numberAware ? self::NUMBER_AWARE_PATTERN : self::DEFAULT_PATTERN; } /** @return int CASE_LOWER | CASE_UPPER */ - public function getCase() + public function getCase(): int { return $this->case; } @@ -65,20 +40,13 @@ public function getCase() /** * Sets string case CASE_LOWER | CASE_UPPER. * Alphabetic characters converted to lowercase or uppercase. - * - * @param int $case - * - * @return void */ - public function setCase($case) + public function setCase(int $case): void { $this->case = $case; } - /** - * {@inheritDoc} - */ - public function classToTableName($className) + public function classToTableName(string $className): string { if (str_contains($className, '\\')) { $className = substr($className, strrpos($className, '\\') + 1); @@ -87,61 +55,49 @@ public function classToTableName($className) return $this->underscore($className); } - /** - * {@inheritDoc} - */ - public function propertyToColumnName($propertyName, $className = null) + public function propertyToColumnName(string $propertyName, string $className): string { return $this->underscore($propertyName); } - /** - * {@inheritDoc} - */ - public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) - { + public function embeddedFieldToColumnName( + string $propertyName, + string $embeddedColumnName, + string $className, + string $embeddedClassName, + ): string { return $this->underscore($propertyName) . '_' . $embeddedColumnName; } - /** - * {@inheritDoc} - */ - public function referenceColumnName() + public function referenceColumnName(): string { return $this->case === CASE_UPPER ? 'ID' : 'id'; } - /** - * {@inheritDoc} - * - * @param string $propertyName - * @param class-string $className - */ - public function joinColumnName($propertyName, $className = null) + public function joinColumnName(string $propertyName, string $className): string { return $this->underscore($propertyName) . '_' . $this->referenceColumnName(); } - /** - * {@inheritDoc} - */ - public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) - { + public function joinTableName( + string $sourceEntity, + string $targetEntity, + string $propertyName, + ): string { return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity); } - /** - * {@inheritDoc} - */ - public function joinKeyColumnName($entityName, $referencedColumnName = null) - { + public function joinKeyColumnName( + string $entityName, + string|null $referencedColumnName, + ): string { return $this->classToTableName($entityName) . '_' . ($referencedColumnName ?: $this->referenceColumnName()); } private function underscore(string $string): string { - $string = preg_replace($this->pattern, '_$1', $string); + $string = preg_replace('/(?<=[a-z0-9])([A-Z])/', '_$1', $string); if ($this->case === CASE_UPPER) { return strtoupper($string); diff --git a/src/Mapping/UniqueConstraint.php b/src/Mapping/UniqueConstraint.php index a7de3dc7652..3180be063c7 100644 --- a/src/Mapping/UniqueConstraint.php +++ b/src/Mapping/UniqueConstraint.php @@ -5,54 +5,20 @@ namespace Doctrine\ORM\Mapping; use Attribute; -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -/** - * @Annotation - * @NamedArgumentConstructor() - * @Target("ANNOTATION") - */ #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] final class UniqueConstraint implements MappingAttribute { - /** - * @var string|null - * @readonly - */ - public $name; - - /** - * @var array|null - * @readonly - */ - public $columns; - - /** - * @var array|null - * @readonly - */ - public $fields; - - /** - * @var array|null - * @readonly - */ - public $options; - /** * @param array|null $columns * @param array|null $fields * @param array|null $options */ public function __construct( - ?string $name = null, - ?array $columns = null, - ?array $fields = null, - ?array $options = null + public readonly string|null $name = null, + public readonly array|null $columns = null, + public readonly array|null $fields = null, + public readonly array|null $options = null, ) { - $this->name = $name; - $this->columns = $columns; - $this->fields = $fields; - $this->options = $options; } } diff --git a/src/Mapping/Version.php b/src/Mapping/Version.php index 7f87e1d7a7c..7252e055908 100644 --- a/src/Mapping/Version.php +++ b/src/Mapping/Version.php @@ -6,10 +6,6 @@ use Attribute; -/** - * @Annotation - * @Target("PROPERTY") - */ #[Attribute(Attribute::TARGET_PROPERTY)] final class Version implements MappingAttribute { diff --git a/src/NativeQuery.php b/src/NativeQuery.php index 782983d50ec..8f57be88def 100644 --- a/src/NativeQuery.php +++ b/src/NativeQuery.php @@ -4,6 +4,9 @@ namespace Doctrine\ORM; +use Doctrine\DBAL\Result; +use Doctrine\ORM\Query\ParameterTypeInferer; + use function array_values; use function is_int; use function key; @@ -16,35 +19,22 @@ */ class NativeQuery extends AbstractQuery { - /** @var string */ - private $sql; - - /** - * Sets the SQL of the query. - * - * @param string $sql - * - * @return $this - */ - public function setSQL($sql): self + private string $sql; + + /** @return $this */ + public function setSQL(string $sql): self { $this->sql = $sql; return $this; } - /** - * Gets the SQL query. - */ public function getSQL(): string { return $this->sql; } - /** - * {@inheritDoc} - */ - protected function _doExecute() + protected function _doExecute(): Result|int { $parameters = []; $types = []; @@ -62,7 +52,7 @@ protected function _doExecute() $value = $this->processParameterValue($parameter->getValue()); $type = $parameter->getValue() === $value ? $parameter->getType() - : Query\ParameterTypeInferer::inferType($value); + : ParameterTypeInferer::inferType($value); $parameters[$name] = $value; $types[$name] = $type; @@ -76,11 +66,11 @@ protected function _doExecute() $types = array_values($types); } - return $this->_em->getConnection()->executeQuery( + return $this->em->getConnection()->executeQuery( $this->sql, $parameters, $types, - $this->_queryCacheProfile + $this->queryCacheProfile, ); } } diff --git a/src/NonUniqueResultException.php b/src/NonUniqueResultException.php index 80caa7c20b3..8f56e44c5ca 100644 --- a/src/NonUniqueResultException.php +++ b/src/NonUniqueResultException.php @@ -11,7 +11,7 @@ class NonUniqueResultException extends UnexpectedResultException { public const DEFAULT_MESSAGE = 'More than one result was found for query although one row or none was expected.'; - public function __construct(?string $message = null) + public function __construct(string|null $message = null) { parent::__construct($message ?? self::DEFAULT_MESSAGE); } diff --git a/src/ORMException.php b/src/ORMException.php deleted file mode 100644 index 898c20ecb0c..00000000000 --- a/src/ORMException.php +++ /dev/null @@ -1,316 +0,0 @@ - $newEntitiesWithAssociations non-empty an array - * of [array $associationMapping, object $entity] pairs - * - * @return ORMInvalidArgumentException - */ - public static function newEntitiesFoundThroughRelationships($newEntitiesWithAssociations) + /** @param non-empty-list $newEntitiesWithAssociations */ + public static function newEntitiesFoundThroughRelationships(array $newEntitiesWithAssociations): self { $errorMessages = array_map( static function (array $newEntityWithAssociation): string { @@ -96,7 +61,7 @@ static function (array $newEntityWithAssociation): string { return self::newEntityFoundThroughRelationshipMessage($associationMapping, $entity); }, - $newEntitiesWithAssociations + $newEntitiesWithAssociations, ); if (count($errorMessages) === 1) { @@ -106,164 +71,81 @@ static function (array $newEntityWithAssociation): string { return new self( 'Multiple non-persisted new entities were found through the given association graph:' . "\n\n * " - . implode("\n * ", $errorMessages) + . implode("\n * ", $errorMessages), ); } - /** - * @param object $entry - * @phpstan-param AssociationMapping $associationMapping - * - * @return ORMInvalidArgumentException - */ - public static function newEntityFoundThroughRelationship(array $associationMapping, $entry) + public static function newEntityFoundThroughRelationship(AssociationMapping $associationMapping, object $entry): self { return new self(self::newEntityFoundThroughRelationshipMessage($associationMapping, $entry)); } - /** - * @param object $entry - * @phpstan-param AssociationMapping $assoc - * - * @return ORMInvalidArgumentException - */ - public static function detachedEntityFoundThroughRelationship(array $assoc, $entry) + public static function detachedEntityFoundThroughRelationship(AssociationMapping $assoc, object $entry): self { - return new self('A detached entity of type ' . $assoc['targetEntity'] . ' (' . self::objToStr($entry) . ') ' - . " was found through the relationship '" . $assoc['sourceEntity'] . '#' . $assoc['fieldName'] . "' " + return new self('A detached entity of type ' . $assoc->targetEntity . ' (' . self::objToStr($entry) . ') ' + . " was found through the relationship '" . $assoc->sourceEntity . '#' . $assoc->fieldName . "' " . 'during cascading a persist operation.'); } - /** - * @param object $entity - * - * @return ORMInvalidArgumentException - */ - public static function entityNotManaged($entity) + public static function entityNotManaged(object $entity): self { return new self('Entity ' . self::objToStr($entity) . ' is not managed. An entity is managed if its fetched ' . 'from the database or registered as new through EntityManager#persist'); } - /** - * @param object $entity - * @param string $operation - * - * @return ORMInvalidArgumentException - */ - public static function entityHasNoIdentity($entity, $operation) + public static function entityHasNoIdentity(object $entity, string $operation): self { return new self('Entity has no identity, therefore ' . $operation . ' cannot be performed. ' . self::objToStr($entity)); } - /** - * @param object $entity - * @param string $operation - * - * @return ORMInvalidArgumentException - */ - public static function entityIsRemoved($entity, $operation) + public static function entityIsRemoved(object $entity, string $operation): self { return new self('Entity is removed, therefore ' . $operation . ' cannot be performed. ' . self::objToStr($entity)); } - /** - * @param object $entity - * @param string $operation - * - * @return ORMInvalidArgumentException - */ - public static function detachedEntityCannot($entity, $operation) + public static function detachedEntityCannot(object $entity, string $operation): self { return new self('Detached entity ' . self::objToStr($entity) . ' cannot be ' . $operation); } - /** - * @param string $context - * @param mixed $given - * @param int $parameterIndex - * - * @return ORMInvalidArgumentException - */ - public static function invalidObject($context, $given, $parameterIndex = 1) + public static function invalidObject(string $context, mixed $given, int $parameterIndex = 1): self { return new self($context . ' expects parameter ' . $parameterIndex . ' to be an entity object, ' . gettype($given) . ' given.'); } - /** @return ORMInvalidArgumentException */ - public static function invalidCompositeIdentifier() + public static function invalidCompositeIdentifier(): self { return new self('Binding an entity with a composite primary key to a query is not supported. ' . 'You should split the parameter into the explicit fields and bind them separately.'); } - /** @return ORMInvalidArgumentException */ - public static function invalidIdentifierBindingEntity(/* string $class */) + public static function invalidIdentifierBindingEntity(string $class): self { - if (func_num_args() === 0) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9642', - 'Omitting the class name in the exception method %s is deprecated.', - __METHOD__ - ); - - return new self('Binding entities to query parameters only allowed for entities that have an identifier.'); - } - return new self(sprintf( <<<'EXCEPTION' Binding entities to query parameters only allowed for entities that have an identifier. Class "%s" does not have an identifier. EXCEPTION , - func_get_arg(0) + $class, )); } - /** - * @param AssociationMapping $assoc - * @param mixed $actualValue - * - * @return self - */ - public static function invalidAssociation(ClassMetadata $targetClass, $assoc, $actualValue) + public static function invalidAssociation(ClassMetadata $targetClass, AssociationMapping $assoc, mixed $actualValue): self { $expectedType = $targetClass->getName(); return new self(sprintf( 'Expected value of type "%s" for association field "%s#$%s", got "%s" instead.', $expectedType, - $assoc['sourceEntity'], - $assoc['fieldName'], - get_debug_type($actualValue) + $assoc->sourceEntity, + $assoc->fieldName, + get_debug_type($actualValue), )); } - /** - * Used when a given entityName hasn't the good type - * - * @deprecated This method will be removed in 3.0. - * - * @param mixed $entityName The given entity (which shouldn't be a string) - * - * @return self - */ - public static function invalidEntityName($entityName) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9471', - '%s() is deprecated', - __METHOD__ - ); - - return new self(sprintf('Entity name must be a string, %s given', get_debug_type($entityName))); - } - - /** @param mixed $value */ - public static function invalidAutoGenerateMode($value): self + public static function invalidAutoGenerateMode(mixed $value): self { return new self(sprintf('Invalid auto generate mode "%s" given.', is_scalar($value) ? (string) $value : get_debug_type($value))); } @@ -290,30 +172,24 @@ public static function proxyDirectoryNotWritable(string $proxyDirectory): self /** * Helper method to show an object as string. - * - * @param object $obj */ - private static function objToStr($obj): string + private static function objToStr(object $obj): string { - return method_exists($obj, '__toString') ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj); + return $obj instanceof Stringable ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj); } - /** - * @param object $entity - * @phpstan-param AssociationMapping $associationMapping - */ - private static function newEntityFoundThroughRelationshipMessage(array $associationMapping, $entity): string + private static function newEntityFoundThroughRelationshipMessage(AssociationMapping $associationMapping, object $entity): string { return 'A new entity was found through the relationship \'' - . $associationMapping['sourceEntity'] . '#' . $associationMapping['fieldName'] . '\' that was not' + . $associationMapping->sourceEntity . '#' . $associationMapping->fieldName . '\' that was not' . ' configured to cascade persist operations for entity: ' . self::objToStr($entity) . '.' . ' To solve this issue: Either explicitly call EntityManager#persist()' . ' on this unknown entity or configure cascade persist' . ' this association in the mapping for example @ManyToOne(..,cascade={"persist"}).' - . (method_exists($entity, '__toString') + . ($entity instanceof Stringable ? '' : ' If you cannot find out which entity causes the problem implement \'' - . $associationMapping['targetEntity'] . '#__toString()\' to get a clue.' + . $associationMapping->targetEntity . '#__toString()\' to get a clue.' ); } } diff --git a/src/ORMSetup.php b/src/ORMSetup.php index 7210209727d..7354c710fe9 100644 --- a/src/ORMSetup.php +++ b/src/ORMSetup.php @@ -4,14 +4,8 @@ namespace Doctrine\ORM; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Deprecations\Deprecation; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; -use Doctrine\ORM\Mapping\Driver\YamlDriver; -use LogicException; use Psr\Cache\CacheItemPoolInterface; use Redis; use RuntimeException; @@ -28,70 +22,6 @@ final class ORMSetup { - /** - * Creates a configuration with an annotation metadata driver. - * - * @deprecated Use another mapping driver. - * - * @param string[] $paths - */ - public static function createAnnotationMetadataConfiguration( - array $paths, - bool $isDevMode = false, - ?string $proxyDir = null, - ?CacheItemPoolInterface $cache = null - ): Configuration { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/10098', - '%s is deprecated and will be removed in Doctrine ORM 3.0', - __METHOD__ - ); - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(self::createDefaultAnnotationDriver($paths)); - - return $config; - } - - /** - * Adds a new default annotation driver with a correctly configured annotation reader. - * - * @deprecated Use another mapping driver. - * - * @param string[] $paths - */ - public static function createDefaultAnnotationDriver( - array $paths = [], - ?CacheItemPoolInterface $cache = null, - bool $reportFieldsWhereDeclared = false - ): AnnotationDriver { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/10098', - '%s is deprecated and will be removed in Doctrine ORM 3.0', - __METHOD__ - ); - if (! class_exists(AnnotationReader::class)) { - throw new LogicException( - 'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library' - . ' is not installed. Please run "composer require doctrine/annotations" or choose a different' - . ' metadata driver.' - ); - } - - $reader = new AnnotationReader(); - - if ($cache === null && class_exists(ArrayAdapter::class)) { - $cache = new ArrayAdapter(); - } - - if ($cache !== null) { - $reader = new PsrCachedReader($reader, $cache); - } - - return new AnnotationDriver($reader, $paths, $reportFieldsWhereDeclared); - } - /** * Creates a configuration with an attribute metadata driver. * @@ -100,12 +30,11 @@ public static function createDefaultAnnotationDriver( public static function createAttributeMetadataConfiguration( array $paths, bool $isDevMode = false, - ?string $proxyDir = null, - ?CacheItemPoolInterface $cache = null, - bool $reportFieldsWhereDeclared = false + string|null $proxyDir = null, + CacheItemPoolInterface|null $cache = null, ): Configuration { $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(new AttributeDriver($paths, $reportFieldsWhereDeclared)); + $config->setMetadataDriverImpl(new AttributeDriver($paths)); return $config; } @@ -118,9 +47,9 @@ public static function createAttributeMetadataConfiguration( public static function createXMLMetadataConfiguration( array $paths, bool $isDevMode = false, - ?string $proxyDir = null, - ?CacheItemPoolInterface $cache = null, - bool $isXsdValidationEnabled = false + string|null $proxyDir = null, + CacheItemPoolInterface|null $cache = null, + bool $isXsdValidationEnabled = true, ): Configuration { $config = self::createConfiguration($isDevMode, $proxyDir, $cache); $config->setMetadataDriverImpl(new XmlDriver($paths, XmlDriver::DEFAULT_FILE_EXTENSION, $isXsdValidationEnabled)); @@ -128,38 +57,13 @@ public static function createXMLMetadataConfiguration( return $config; } - /** - * Creates a configuration with a YAML metadata driver. - * - * @deprecated YAML metadata mapping is deprecated and will be removed in 3.0 - * - * @param string[] $paths - */ - public static function createYAMLMetadataConfiguration( - array $paths, - bool $isDevMode = false, - ?string $proxyDir = null, - ?CacheItemPoolInterface $cache = null - ): Configuration { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8465', - 'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to attribute or XML driver.' - ); - - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(new YamlDriver($paths)); - - return $config; - } - /** * Creates a configuration without a metadata driver. */ public static function createConfiguration( bool $isDevMode = false, - ?string $proxyDir = null, - ?CacheItemPoolInterface $cache = null + string|null $proxyDir = null, + CacheItemPoolInterface|null $cache = null, ): Configuration { $proxyDir = $proxyDir ?: sys_get_temp_dir(); @@ -180,7 +84,7 @@ public static function createConfiguration( private static function createCacheInstance( bool $isDevMode, string $proxyDir, - ?CacheItemPoolInterface $cache + CacheItemPoolInterface|null $cache, ): CacheItemPoolInterface { if ($cache !== null) { return $cache; @@ -189,7 +93,7 @@ private static function createCacheInstance( if (! class_exists(ArrayAdapter::class)) { throw new RuntimeException( 'The Doctrine setup tool cannot configure caches without symfony/cache.' - . ' Please add symfony/cache as explicit dependency or pass your own cache implementation.' + . ' Please add symfony/cache as explicit dependency or pass your own cache implementation.', ); } diff --git a/src/OptimisticLockException.php b/src/OptimisticLockException.php index 1b350bde400..f84e134b05e 100644 --- a/src/OptimisticLockException.php +++ b/src/OptimisticLockException.php @@ -6,68 +6,49 @@ use DateTimeInterface; use Doctrine\ORM\Exception\ORMException; +use Exception; +use Throwable; /** * An OptimisticLockException is thrown when a version check on an object * that uses optimistic locking through a version field fails. */ -class OptimisticLockException extends ORMException +class OptimisticLockException extends Exception implements ORMException { - /** @var object|string|null */ - private $entity; - - /** - * @param string $msg - * @param object|string|null $entity - */ - public function __construct($msg, $entity) - { - parent::__construct($msg); - - $this->entity = $entity; + public function __construct( + string $msg, + private readonly object|string|null $entity, + Throwable|null $previous = null, + ) { + parent::__construct($msg, 0, $previous); } /** * Gets the entity that caused the exception. - * - * @return object|string|null */ - public function getEntity() + public function getEntity(): object|string|null { return $this->entity; } - /** - * @param object|class-string $entity - * - * @return OptimisticLockException - */ - public static function lockFailed($entity) + /** @param object|class-string $entity */ + public static function lockFailed(object|string $entity): self { return new self('The optimistic lock on an entity failed.', $entity); } - /** - * @param object $entity - * @param int|string|DateTimeInterface $expectedLockVersion - * @param int|string|DateTimeInterface $actualLockVersion - * - * @return OptimisticLockException - */ - public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) - { + public static function lockFailedVersionMismatch( + object $entity, + int|string|DateTimeInterface $expectedLockVersion, + int|string|DateTimeInterface $actualLockVersion, + ): self { $expectedLockVersion = $expectedLockVersion instanceof DateTimeInterface ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; $actualLockVersion = $actualLockVersion instanceof DateTimeInterface ? $actualLockVersion->getTimestamp() : $actualLockVersion; return new self('The optimistic lock failed, version ' . $expectedLockVersion . ' was expected, but is actually ' . $actualLockVersion, $entity); } - /** - * @param string $entityName - * - * @return OptimisticLockException - */ - public static function notVersioned($entityName) + public static function notVersioned(string $entityName): self { return new self('Cannot obtain optimistic lock on unversioned entity ' . $entityName, null); } diff --git a/src/PersistentCollection.php b/src/PersistentCollection.php index 20d98b5f455..e83e246d7de 100644 --- a/src/PersistentCollection.php +++ b/src/PersistentCollection.php @@ -8,10 +8,11 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Order; use Doctrine\Common\Collections\Selectable; -use Doctrine\ORM\Internal\CriteriaOrderings; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; -use ReturnTypeWillChange; +use Doctrine\ORM\Mapping\ToManyAssociationMapping; use RuntimeException; use UnexpectedValueException; @@ -21,9 +22,9 @@ use function array_values; use function array_walk; use function assert; -use function get_class; use function is_object; use function spl_object_id; +use function strtoupper; /** * A PersistentCollection represents a collection of elements that have persistent state. @@ -38,77 +39,55 @@ * @phpstan-template T * @template-extends AbstractLazyCollection * @template-implements Selectable - * @phpstan-import-type AssociationMapping from ClassMetadata */ final class PersistentCollection extends AbstractLazyCollection implements Selectable { - use CriteriaOrderings; - /** * A snapshot of the collection at the moment it was fetched from the database. * This is used to create a diff of the collection at commit time. * * @phpstan-var array */ - private $snapshot = []; + private array $snapshot = []; /** * The entity that owns this collection. - * - * @var object|null */ - private $owner; + private object|null $owner = null; /** * The association mapping the collection belongs to. * This is currently either a OneToManyMapping or a ManyToManyMapping. * - * @phpstan-var AssociationMapping|null - */ - private $association; - - /** - * The EntityManager that manages the persistence of the collection. - * - * @var EntityManagerInterface|null + * @var (AssociationMapping&ToManyAssociationMapping)|null */ - private $em; + private AssociationMapping|null $association = null; /** * The name of the field on the target entities that points to the owner * of the collection. This is only set if the association is bi-directional. - * - * @var string|null - */ - private $backRefFieldName; - - /** - * The class descriptor of the collection's entity type. - * - * @var ClassMetadata|null */ - private $typeClass; + private string|null $backRefFieldName = null; /** * Whether the collection is dirty and needs to be synchronized with the database * when the UnitOfWork that manages its persistent state commits. - * - * @var bool */ - private $isDirty = false; + private bool $isDirty = false; /** * Creates a new persistent collection. * - * @param EntityManagerInterface $em The EntityManager the collection will be associated with. - * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param EntityManagerInterface $em The EntityManager the collection will be associated with. + * @param ClassMetadata $typeClass The class descriptor of the entity type of this collection. * @phpstan-param Collection&Selectable $collection The collection elements. */ - public function __construct(EntityManagerInterface $em, $class, Collection $collection) - { + public function __construct( + private EntityManagerInterface|null $em, + private readonly ClassMetadata|null $typeClass, + Collection $collection, + ) { $this->collection = $collection; - $this->em = $em; - $this->typeClass = $class; $this->initialized = true; } @@ -116,30 +95,24 @@ public function __construct(EntityManagerInterface $em, $class, Collection $coll * INTERNAL: * Sets the collection's owning entity together with the AssociationMapping that * describes the association between the owner and the elements of the collection. - * - * @param object $entity - * @phpstan-param AssociationMapping $assoc */ - public function setOwner($entity, array $assoc): void + public function setOwner(object $entity, AssociationMapping&ToManyAssociationMapping $assoc): void { $this->owner = $entity; $this->association = $assoc; - $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; + $this->backRefFieldName = $assoc->isOwningSide() ? $assoc->inversedBy : $assoc->mappedBy; } /** * INTERNAL: * Gets the collection owner. - * - * @return object|null */ - public function getOwner() + public function getOwner(): object|null { return $this->owner; } - /** @return Mapping\ClassMetadata */ - public function getTypeClass(): Mapping\ClassMetadataInfo + public function getTypeClass(): ClassMetadata { assert($this->typeClass !== null); @@ -157,27 +130,25 @@ private function getUnitOfWork(): UnitOfWork * INTERNAL: * Adds an element to a collection during hydration. This will automatically * complete bidirectional associations in the case of a one-to-many association. - * - * @param mixed $element The element to add. */ - public function hydrateAdd($element): void + public function hydrateAdd(mixed $element): void { $this->unwrap()->add($element); // If _backRefFieldName is set and its a one-to-many association, // we need to set the back reference. - if ($this->backRefFieldName && $this->getMapping()['type'] === ClassMetadata::ONE_TO_MANY) { + if ($this->backRefFieldName && $this->getMapping()->isOneToMany()) { assert($this->typeClass !== null); // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName]->setValue( $element, - $this->owner + $this->owner, ); $this->getUnitOfWork()->setOriginalEntityProperty( spl_object_id($element), $this->backRefFieldName, - $this->owner + $this->owner, ); } } @@ -185,22 +156,19 @@ public function hydrateAdd($element): void /** * INTERNAL: * Sets a keyed element in the collection during hydration. - * - * @param mixed $key The key to set. - * @param mixed $element The element to set. */ - public function hydrateSet($key, $element): void + public function hydrateSet(mixed $key, mixed $element): void { $this->unwrap()->set($key, $element); // If _backRefFieldName is set, then the association is bidirectional // and we need to set the back reference. - if ($this->backRefFieldName && $this->getMapping()['type'] === ClassMetadata::ONE_TO_MANY) { + if ($this->backRefFieldName && $this->getMapping()->isOneToMany()) { assert($this->typeClass !== null); // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName]->setValue( $element, - $this->owner + $this->owner, ); } } @@ -253,7 +221,7 @@ public function getDeleteDiff(): array return array_values(array_diff_key( array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot), - array_combine(array_map('spl_object_id', $collectionItems), $collectionItems) + array_combine(array_map('spl_object_id', $collectionItems), $collectionItems), )); } @@ -269,16 +237,12 @@ public function getInsertDiff(): array return array_values(array_diff_key( array_combine(array_map('spl_object_id', $collectionItems), $collectionItems), - array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot) + array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot), )); } - /** - * INTERNAL: Gets the association mapping of the collection. - * - * @phpstan-return AssociationMapping - */ - public function getMapping(): array + /** INTERNAL: Gets the association mapping of the collection. */ + public function getMapping(): AssociationMapping&ToManyAssociationMapping { if ($this->association === null) { throw new UnexpectedValueException('The underlying association mapping is null although it should not be'); @@ -297,24 +261,11 @@ private function changed(): void } $this->isDirty = true; - - if ( - $this->association !== null && - $this->getMapping()['isOwningSide'] && - $this->getMapping()['type'] === ClassMetadata::MANY_TO_MANY && - $this->owner && - $this->em !== null && - $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify() - ) { - $this->getUnitOfWork()->scheduleForDirtyCheck($this->owner); - } } /** * Gets a boolean flag indicating whether this collection is dirty which means * its state needs to be synchronized with the database. - * - * @return bool TRUE if the collection is dirty, FALSE otherwise. */ public function isDirty(): bool { @@ -323,28 +274,21 @@ public function isDirty(): bool /** * Sets a boolean flag, indicating whether this collection is dirty. - * - * @param bool $dirty Whether the collection should be marked dirty or not. */ - public function setDirty($dirty): void + public function setDirty(bool $dirty): void { $this->isDirty = $dirty; } /** * Sets the initialized flag of the collection, forcing it into that state. - * - * @param bool $bool */ - public function setInitialized($bool): void + public function setInitialized(bool $bool): void { $this->initialized = $bool; } - /** - * {@inheritDoc} - */ - public function remove($key) + public function remove(string|int $key): mixed { // TODO: If the keys are persistent as well (not yet implemented) // and the collection is not initialized and orphanRemoval is @@ -360,9 +304,9 @@ public function remove($key) if ( $this->association !== null && - $this->getMapping()['type'] & ClassMetadata::TO_MANY && + $this->association->isToMany() && $this->owner && - $this->getMapping()['orphanRemoval'] + $this->getMapping()->orphanRemoval ) { $this->getUnitOfWork()->scheduleOrphanRemoval($removed); } @@ -370,10 +314,7 @@ public function remove($key) return $removed; } - /** - * {@inheritDoc} - */ - public function removeElement($element): bool + public function removeElement(mixed $element): bool { $removed = parent::removeElement($element); @@ -385,9 +326,9 @@ public function removeElement($element): bool if ( $this->association !== null && - $this->getMapping()['type'] & ClassMetadata::TO_MANY && + $this->association->isToMany() && $this->owner && - $this->getMapping()['orphanRemoval'] + $this->getMapping()->orphanRemoval ) { $this->getUnitOfWork()->scheduleOrphanRemoval($element); } @@ -395,14 +336,11 @@ public function removeElement($element): bool return $removed; } - /** - * {@inheritDoc} - */ - public function containsKey($key): bool + public function containsKey(mixed $key): bool { if ( - ! $this->initialized && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY - && isset($this->getMapping()['indexBy']) + ! $this->initialized && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY + && isset($this->getMapping()->indexBy) ) { $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping()); @@ -412,9 +350,9 @@ public function containsKey($key): bool return parent::containsKey($key); } - public function contains($element): bool + public function contains(mixed $element): bool { - if (! $this->initialized && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) { + if (! $this->initialized && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) { $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping()); return $this->unwrap()->contains($element) || $persister->contains($this, $element); @@ -423,19 +361,16 @@ public function contains($element): bool return parent::contains($element); } - /** - * {@inheritDoc} - */ - public function get($key) + public function get(string|int $key): mixed { if ( ! $this->initialized - && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY - && isset($this->getMapping()['indexBy']) + && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY + && isset($this->getMapping()->indexBy) ) { assert($this->em !== null); assert($this->typeClass !== null); - if (! $this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->getMapping()['indexBy'])) { + if (! $this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->getMapping()->indexBy)) { return $this->em->find($this->typeClass->name, $key); } @@ -447,7 +382,7 @@ public function get($key) public function count(): int { - if (! $this->initialized && $this->association !== null && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) { + if (! $this->initialized && $this->association !== null && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) { $persister = $this->getUnitOfWork()->getCollectionPersister($this->association); return $persister->count($this) + ($this->isDirty ? $this->unwrap()->count() : 0); @@ -456,10 +391,7 @@ public function count(): int return parent::count(); } - /** - * {@inheritDoc} - */ - public function set($key, $value): void + public function set(string|int $key, mixed $value): void { parent::set($key, $value); @@ -470,10 +402,7 @@ public function set($key, $value): void } } - /** - * {@inheritDoc} - */ - public function add($value): bool + public function add(mixed $value): bool { $this->unwrap()->add($value); @@ -486,29 +415,17 @@ public function add($value): bool return true; } - /* ArrayAccess implementation */ - - /** - * {@inheritDoc} - */ - public function offsetExists($offset): bool + public function offsetExists(mixed $offset): bool { return $this->containsKey($offset); } - /** - * {@inheritDoc} - */ - #[ReturnTypeWillChange] - public function offsetGet($offset) + public function offsetGet(mixed $offset): mixed { return $this->get($offset); } - /** - * {@inheritDoc} - */ - public function offsetSet($offset, $value): void + public function offsetSet(mixed $offset, mixed $value): void { if (! isset($offset)) { $this->add($value); @@ -519,15 +436,9 @@ public function offsetSet($offset, $value): void $this->set($offset, $value); } - /** - * {@inheritDoc} - * - * @return object|null - */ - #[ReturnTypeWillChange] - public function offsetUnset($offset) + public function offsetUnset(mixed $offset): void { - return $this->remove($offset); + $this->remove($offset); } public function isEmpty(): bool @@ -547,8 +458,8 @@ public function clear(): void $association = $this->getMapping(); if ( - $association['type'] & ClassMetadata::TO_MANY && - $association['orphanRemoval'] && + $association->isToMany() && + $association->orphanRemoval && $this->owner ) { // we need to initialize here, as orphan removal acts like implicit cascadeRemove, @@ -564,7 +475,7 @@ public function clear(): void $this->initialized = true; // direct call, {@link initialize()} is too expensive - if ($association['isOwningSide'] && $this->owner) { + if ($association->isOwningSide() && $this->owner) { $this->changed(); $uow->scheduleCollectionDeletion($this); @@ -588,6 +499,25 @@ public function __sleep(): array return ['collection', 'initialized']; } + public function __wakeup(): void + { + $this->em = null; + } + + /** + * {@inheritDoc} + */ + public function first() + { + if (! $this->initialized && ! $this->isDirty && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) { + $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping()); + + return array_values($persister->slice($this, 0, 1))[0] ?? false; + } + + return parent::first(); + } + /** * Extracts a slice of $length elements starting at position $offset from the Collection. * @@ -595,15 +525,12 @@ public function __sleep(): array * Keys have to be preserved by this method. Calling this method will only return the * selected slice and NOT change the elements contained in the collection slice is called on. * - * @param int $offset - * @param int|null $length - * * @return mixed[] * @phpstan-return array */ - public function slice($offset, $length = null): array + public function slice(int $offset, int|null $length = null): array { - if (! $this->initialized && ! $this->isDirty && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) { + if (! $this->initialized && ! $this->isDirty && $this->getMapping()->fetch === ClassMetadata::FETCH_EXTRA_LAZY) { $persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping()); return $persister->slice($this, $offset, $length); @@ -656,7 +583,7 @@ public function matching(Criteria $criteria): Collection } $association = $this->getMapping(); - if ($association['type'] === ClassMetadata::MANY_TO_MANY) { + if ($association->isManyToMany()) { $persister = $this->getUnitOfWork()->getCollectionPersister($association); return new ArrayCollection($persister->loadCriteria($this, $criteria)); @@ -669,13 +596,16 @@ public function matching(Criteria $criteria): Collection $criteria = clone $criteria; $criteria->where($expression); - $criteria->orderBy(self::mapToOrderEnumIfAvailable( - self::getCriteriaOrderings($criteria) ?: $association['orderBy'] ?? [] - )); + $criteria->orderBy( + $criteria->orderings() ?: array_map( + static fn (string $order): Order => Order::from(strtoupper($order)), + $association->orderBy(), + ), + ); - $persister = $this->getUnitOfWork()->getEntityPersister($association['targetEntity']); + $persister = $this->getUnitOfWork()->getEntityPersister($association->targetEntity); - return $association['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY + return $association->fetch === ClassMetadata::FETCH_EXTRA_LAZY ? new LazyCriteriaCollection($persister, $criteria) : new ArrayCollection($persister->loadCriteria($criteria)); } @@ -685,7 +615,7 @@ public function matching(Criteria $criteria): Collection * * @return Collection&Selectable */ - public function unwrap(): Collection + public function unwrap(): Selectable&Collection { assert($this->collection instanceof Collection); assert($this->collection instanceof Selectable); diff --git a/src/Persisters/Collection/AbstractCollectionPersister.php b/src/Persisters/Collection/AbstractCollectionPersister.php index 452310e3645..26f0b9ef901 100644 --- a/src/Persisters/Collection/AbstractCollectionPersister.php +++ b/src/Persisters/Collection/AbstractCollectionPersister.php @@ -15,31 +15,17 @@ */ abstract class AbstractCollectionPersister implements CollectionPersister { - /** @var EntityManagerInterface */ - protected $em; - - /** @var Connection */ - protected $conn; - - /** @var UnitOfWork */ - protected $uow; - - /** - * The database platform. - * - * @var AbstractPlatform - */ - protected $platform; - - /** @var QuoteStrategy */ - protected $quoteStrategy; + protected Connection $conn; + protected UnitOfWork $uow; + protected AbstractPlatform $platform; + protected QuoteStrategy $quoteStrategy; /** * Initializes a new instance of a class derived from AbstractCollectionPersister. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + protected EntityManagerInterface $em, + ) { $this->uow = $em->getUnitOfWork(); $this->conn = $em->getConnection(); $this->platform = $this->conn->getDatabasePlatform(); @@ -48,12 +34,8 @@ public function __construct(EntityManagerInterface $em) /** * Check if entity is in a valid state for operations. - * - * @param object $entity - * - * @return bool */ - protected function isValidEntityState($entity) + protected function isValidEntityState(object $entity): bool { $entityState = $this->uow->getEntityState($entity, UnitOfWork::STATE_NEW); diff --git a/src/Persisters/Collection/CollectionPersister.php b/src/Persisters/Collection/CollectionPersister.php index 5c27ad8b94b..07c4eaf9d42 100644 --- a/src/Persisters/Collection/CollectionPersister.php +++ b/src/Persisters/Collection/CollectionPersister.php @@ -14,67 +14,46 @@ interface CollectionPersister { /** * Deletes the persistent state represented by the given collection. - * - * @return void */ - public function delete(PersistentCollection $collection); + public function delete(PersistentCollection $collection): void; /** * Updates the given collection, synchronizing its state with the database * by inserting, updating and deleting individual elements. - * - * @return void */ - public function update(PersistentCollection $collection); + public function update(PersistentCollection $collection): void; /** * Counts the size of this persistent collection. - * - * @return int */ - public function count(PersistentCollection $collection); + public function count(PersistentCollection $collection): int; /** * Slices elements. * - * @param int $offset - * @param int|null $length - * * @return mixed[] */ - public function slice(PersistentCollection $collection, $offset, $length = null); + public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array; /** * Checks for existence of an element. - * - * @param object $element - * - * @return bool */ - public function contains(PersistentCollection $collection, $element); + public function contains(PersistentCollection $collection, object $element): bool; /** * Checks for existence of a key. - * - * @param mixed $key - * - * @return bool */ - public function containsKey(PersistentCollection $collection, $key); + public function containsKey(PersistentCollection $collection, mixed $key): bool; /** * Gets an element by key. - * - * @param mixed $index - * - * @return mixed */ - public function get(PersistentCollection $collection, $index); + public function get(PersistentCollection $collection, mixed $index): mixed; /** * Loads association entities matching the given Criteria object. * * @return mixed[] */ - public function loadCriteria(PersistentCollection $collection, Criteria $criteria); + public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array; } diff --git a/src/Persisters/Collection/ManyToManyPersister.php b/src/Persisters/Collection/ManyToManyPersister.php index 42bbb5c9410..893e0644e61 100644 --- a/src/Persisters/Collection/ManyToManyPersister.php +++ b/src/Persisters/Collection/ManyToManyPersister.php @@ -8,8 +8,11 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\DBAL\Exception as DBALException; -use Doctrine\ORM\Internal\CriteriaOrderings; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\InverseSideMapping; +use Doctrine\ORM\Mapping\ManyToManyAssociationMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Persisters\SqlValueVisitor; use Doctrine\ORM\Query; @@ -17,8 +20,8 @@ use function array_fill; use function array_pop; +use function assert; use function count; -use function get_class; use function implode; use function in_array; use function reset; @@ -26,42 +29,34 @@ /** * Persister for many-to-many collections. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ class ManyToManyPersister extends AbstractCollectionPersister { - use CriteriaOrderings; - - /** - * {@inheritDoc} - */ - public function delete(PersistentCollection $collection) + public function delete(PersistentCollection $collection): void { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! $mapping['isOwningSide']) { + if (! $mapping->isOwningSide()) { return; // ignore inverse side } + assert($mapping->isManyToManyOwningSide()); + $types = []; - $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $class = $this->em->getClassMetadata($mapping->sourceEntity); - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + foreach ($mapping->joinTable->joinColumns as $joinColumn) { + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em); } $this->conn->executeStatement($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types); } - /** - * {@inheritDoc} - */ - public function update(PersistentCollection $collection) + public function update(PersistentCollection $collection): void { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! $mapping['isOwningSide']) { + if (! $mapping->isOwningSide()) { return; // ignore inverse side } @@ -72,7 +67,7 @@ public function update(PersistentCollection $collection) $this->conn->executeStatement( $deleteSql, $this->getDeleteRowSQLParameters($collection, $element), - $deleteTypes + $deleteTypes, ); } @@ -80,54 +75,54 @@ public function update(PersistentCollection $collection) $this->conn->executeStatement( $insertSql, $this->getInsertRowSQLParameters($collection, $element), - $insertTypes + $insertTypes, ); } } - /** - * {@inheritDoc} - */ - public function get(PersistentCollection $collection, $index) + public function get(PersistentCollection $collection, mixed $index): object|null { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! isset($mapping['indexBy'])) { + if (! $mapping->isIndexed()) { throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.'); } - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); - $mappedKey = $mapping['isOwningSide'] - ? $mapping['inversedBy'] - : $mapping['mappedBy']; + $persister = $this->uow->getEntityPersister($mapping->targetEntity); + $mappedKey = $mapping->isOwningSide() + ? $mapping->inversedBy + : $mapping->mappedBy; + + assert($mappedKey !== null); - return $persister->load([$mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index], null, $mapping, [], 0, 1); + return $persister->load( + [$mappedKey => $collection->getOwner(), $mapping->indexBy() => $index], + null, + $mapping, + [], + LockMode::NONE, + 1, + ); } - /** - * {@inheritDoc} - */ - public function count(PersistentCollection $collection) + public function count(PersistentCollection $collection): int { $conditions = []; $params = []; $types = []; - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); $id = $this->uow->getEntityIdentifier($collection->getOwner()); - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - $association = ! $mapping['isOwningSide'] - ? $targetClass->associationMappings[$mapping['mappedBy']] - : $mapping; + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $association = $this->em->getMetadataFactory()->getOwningSide($mapping); $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform); - $joinColumns = ! $mapping['isOwningSide'] - ? $association['joinTable']['inverseJoinColumns'] - : $association['joinTable']['joinColumns']; + $joinColumns = ! $mapping->isOwningSide() + ? $association->joinTable->inverseJoinColumns + : $association->joinTable->joinColumns; foreach ($joinColumns as $joinColumn) { $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform); - $referencedName = $joinColumn['referencedColumnName']; + $referencedName = $joinColumn->referencedColumnName; $conditions[] = 't.' . $columnName . ' = ?'; $params[] = $id[$sourceClass->getFieldForColumn($referencedName)]; $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em); @@ -166,29 +161,26 @@ public function count(PersistentCollection $collection) /** * {@inheritDoc} */ - public function slice(PersistentCollection $collection, $offset, $length = null) + public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array { - $mapping = $collection->getMapping(); - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length); } - /** - * {@inheritDoc} - */ - public function containsKey(PersistentCollection $collection, $key) + public function containsKey(PersistentCollection $collection, mixed $key): bool { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! isset($mapping['indexBy'])) { + if (! $mapping->isIndexed()) { throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.'); } [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictionsWithKey( $collection, (string) $key, - true + true, ); $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); @@ -196,10 +188,7 @@ public function containsKey(PersistentCollection $collection, $key) return (bool) $this->conn->fetchOne($sql, $params, $types); } - /** - * {@inheritDoc} - */ - public function contains(PersistentCollection $collection, $element) + public function contains(PersistentCollection $collection, object $element): bool { if (! $this->isValidEntityState($element)) { return false; @@ -208,7 +197,7 @@ public function contains(PersistentCollection $collection, $element) [$quotedJoinTable, $whereClauses, $params, $types] = $this->getJoinTableRestrictions( $collection, $element, - true + true, ); $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); @@ -219,27 +208,29 @@ public function contains(PersistentCollection $collection, $element) /** * {@inheritDoc} */ - public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); $owner = $collection->getOwner(); - $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); + $ownerMetadata = $this->em->getClassMetadata($owner::class); $id = $this->uow->getEntityIdentifier($owner); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $onConditions = $this->getOnConditionSQL($mapping); $whereClauses = $params = []; $paramTypes = []; - if (! $mapping['isOwningSide']) { + if (! $mapping->isOwningSide()) { + assert($mapping instanceof InverseSideMapping); $associationSourceClass = $targetClass; - $mapping = $targetClass->associationMappings[$mapping['mappedBy']]; $sourceRelationMode = 'relationToTargetKeyColumns'; } else { $associationSourceClass = $ownerMetadata; $sourceRelationMode = 'relationToSourceKeyColumns'; } - foreach ($mapping[$sourceRelationMode] as $key => $value) { + $mapping = $this->em->getMetadataFactory()->getOwningSide($mapping); + + foreach ($mapping->$sourceRelationMode as $key => $value) { $whereClauses[] = sprintf('t.%s = ?', $key); $params[] = $ownerMetadata->containsForeignIdentifier ? $id[$ownerMetadata->getFieldForColumn($value)] @@ -296,17 +287,16 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri * have to join in the actual entities table leading to additional * JOIN. * - * @param mixed[] $mapping Array containing mapping information. - * @phpstan-param AssociationMapping $mapping + * @param AssociationMapping $mapping Array containing mapping information. * * @return string[] ordered tuple: * - JOIN condition to add to the SQL * - WHERE condition to add to the SQL * @phpstan-return array{0: string, 1: string} */ - public function getFilterSql($mapping) + public function getFilterSql(AssociationMapping $mapping): array { - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); $filterSql = $this->generateFilterConditionSQL($rootClass, 'te'); @@ -330,7 +320,7 @@ public function getFilterSql($mapping) * * @return string The SQL query part to add to a query. */ - protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string { $filterClauses = []; @@ -349,25 +339,19 @@ protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targ /** * Generate ON condition * - * @param mixed[] $mapping - * @phpstan-param AssociationMapping $mapping - * * @return string[] * @phpstan-return list */ - protected function getOnConditionSQL($mapping) + protected function getOnConditionSQL(AssociationMapping $mapping): array { - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - $association = ! $mapping['isOwningSide'] - ? $targetClass->associationMappings[$mapping['mappedBy']] - : $mapping; - - $joinColumns = $mapping['isOwningSide'] - ? $association['joinTable']['inverseJoinColumns'] - : $association['joinTable']['joinColumns']; + $association = $this->em->getMetadataFactory()->getOwningSide($mapping); + $joinColumns = $mapping->isOwningSide() + ? $association->joinTable->inverseJoinColumns + : $association->joinTable->joinColumns; $conditions = []; + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); foreach ($joinColumns as $joinColumn) { $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); @@ -378,15 +362,15 @@ protected function getOnConditionSQL($mapping) return $conditions; } - /** @return string */ - protected function getDeleteSQL(PersistentCollection $collection) + protected function getDeleteSQL(PersistentCollection $collection): string { - $columns = []; - $mapping = $collection->getMapping(); - $class = $this->em->getClassMetadata(get_class($collection->getOwner())); + $columns = []; + $mapping = $this->getMapping($collection); + assert($mapping->isManyToManyOwningSide()); + $class = $this->em->getClassMetadata($collection->getOwner()::class); $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + foreach ($mapping->joinTable->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } @@ -399,21 +383,22 @@ protected function getDeleteSQL(PersistentCollection $collection) * * @return list */ - protected function getDeleteSQLParameters(PersistentCollection $collection) + protected function getDeleteSQLParameters(PersistentCollection $collection): array { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); + assert($mapping->isManyToManyOwningSide()); $identifier = $this->uow->getEntityIdentifier($collection->getOwner()); // Optimization for single column identifier - if (count($mapping['relationToSourceKeyColumns']) === 1) { + if (count($mapping->relationToSourceKeyColumns) === 1) { return [reset($identifier)]; } // Composite identifier - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); $params = []; - foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + foreach ($mapping->relationToSourceKeyColumns as $columnName => $refColumnName) { $params[] = isset($sourceClass->fieldNames[$refColumnName]) ? $identifier[$sourceClass->fieldNames[$refColumnName]] : $identifier[$sourceClass->getFieldForColumn($refColumnName)]; @@ -429,22 +414,23 @@ protected function getDeleteSQLParameters(PersistentCollection $collection) * of types for bound parameters * @phpstan-return array{0: string, 1: list} */ - protected function getDeleteRowSQL(PersistentCollection $collection) + protected function getDeleteRowSQL(PersistentCollection $collection): array { - $mapping = $collection->getMapping(); - $class = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + assert($mapping->isManyToManyOwningSide()); + $class = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $columns = []; $types = []; - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + foreach ($mapping->joinTable->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em); } - foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + foreach ($mapping->joinTable->inverseJoinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); } return [ @@ -460,12 +446,10 @@ protected function getDeleteRowSQL(PersistentCollection $collection) * * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql. * - * @param mixed $element - * * @return mixed[] * @phpstan-return list */ - protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element) + protected function getDeleteRowSQLParameters(PersistentCollection $collection, object $element): array { return $this->collectJoinTableColumnParameters($collection, $element); } @@ -477,22 +461,23 @@ protected function getDeleteRowSQLParameters(PersistentCollection $collection, $ * of types for bound parameters * @phpstan-return array{0: string, 1: list} */ - protected function getInsertRowSQL(PersistentCollection $collection) + protected function getInsertRowSQL(PersistentCollection $collection): array { - $columns = []; - $types = []; - $mapping = $collection->getMapping(); - $class = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns = []; + $types = []; + $mapping = $this->getMapping($collection); + assert($mapping->isManyToManyOwningSide()); + $class = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); + + foreach ($mapping->joinTable->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $class, $this->em); } - foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + foreach ($mapping->joinTable->inverseJoinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); } return [ @@ -510,12 +495,10 @@ protected function getInsertRowSQL(PersistentCollection $collection) * * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql. * - * @param object $element - * * @return mixed[] * @phpstan-return list */ - protected function getInsertRowSQLParameters(PersistentCollection $collection, $element) + protected function getInsertRowSQLParameters(PersistentCollection $collection, object $element): array { return $this->collectJoinTableColumnParameters($collection, $element); } @@ -524,30 +507,29 @@ protected function getInsertRowSQLParameters(PersistentCollection $collection, $ * Collects the parameters for inserting/deleting on the join table in the order * of the join table columns as specified in ManyToManyMapping#joinTableColumns. * - * @param object $element - * * @return mixed[] * @phpstan-return list */ private function collectJoinTableColumnParameters( PersistentCollection $collection, - $element + object $element, ): array { - $params = []; - $mapping = $collection->getMapping(); - $isComposite = count($mapping['joinTableColumns']) > 2; + $params = []; + $mapping = $this->getMapping($collection); + assert($mapping->isManyToManyOwningSide()); + $isComposite = count($mapping->joinTableColumns) > 2; $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner()); $identifier2 = $this->uow->getEntityIdentifier($element); $class1 = $class2 = null; if ($isComposite) { - $class1 = $this->em->getClassMetadata(get_class($collection->getOwner())); + $class1 = $this->em->getClassMetadata($collection->getOwner()::class); $class2 = $collection->getTypeClass(); } - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); + foreach ($mapping->joinTableColumns as $joinTableColumn) { + $isRelationToSource = isset($mapping->relationToSourceKeyColumns[$joinTableColumn]); if (! $isComposite) { $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); @@ -556,12 +538,12 @@ private function collectJoinTableColumnParameters( } if ($isRelationToSource) { - $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; + $params[] = $identifier1[$class1->getFieldForColumn($mapping->relationToSourceKeyColumns[$joinTableColumn])]; continue; } - $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; + $params[] = $identifier2[$class2->getFieldForColumn($mapping->relationToTargetKeyColumns[$joinTableColumn])]; } return $params; @@ -580,24 +562,27 @@ private function collectJoinTableColumnParameters( private function getJoinTableRestrictionsWithKey( PersistentCollection $collection, string $key, - bool $addFilters + bool $addFilters, ): array { - $filterMapping = $collection->getMapping(); + $filterMapping = $this->getMapping($collection); $mapping = $filterMapping; - $indexBy = $mapping['indexBy']; + $indexBy = $mapping->indexBy(); $id = $this->uow->getEntityIdentifier($collection->getOwner()); - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); - - if (! $mapping['isOwningSide']) { - $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']); - $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']]; - $joinColumns = $mapping['joinTable']['joinColumns']; - $sourceRelationMode = 'relationToTargetKeyColumns'; - $targetRelationMode = 'relationToSourceKeyColumns'; + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); + + if (! $mapping->isOwningSide()) { + assert($mapping instanceof InverseSideMapping); + $associationSourceClass = $this->em->getClassMetadata($mapping->targetEntity); + $mapping = $associationSourceClass->associationMappings[$mapping->mappedBy]; + assert($mapping->isManyToManyOwningSide()); + $joinColumns = $mapping->joinTable->joinColumns; + $sourceRelationMode = 'relationToTargetKeyColumns'; + $targetRelationMode = 'relationToSourceKeyColumns'; } else { - $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $joinColumns = $mapping['joinTable']['inverseJoinColumns']; + assert($mapping->isManyToManyOwningSide()); + $associationSourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $joinColumns = $mapping->joinTable->inverseJoinColumns; $sourceRelationMode = 'relationToSourceKeyColumns'; $targetRelationMode = 'relationToTargetKeyColumns'; } @@ -613,7 +598,7 @@ private function getJoinTableRestrictionsWithKey( $joinConditions = []; foreach ($joinColumns as $joinTableColumn) { - $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName']; + $joinConditions[] = 't.' . $joinTableColumn->name . ' = tr.' . $joinTableColumn->referencedColumnName; } $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); @@ -625,16 +610,16 @@ private function getJoinTableRestrictionsWithKey( $types[] = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em); } - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - if (isset($mapping[$sourceRelationMode][$joinTableColumn])) { - $column = $mapping[$sourceRelationMode][$joinTableColumn]; + foreach ($mapping->joinTableColumns as $joinTableColumn) { + if (isset($mapping->{$sourceRelationMode}[$joinTableColumn])) { + $column = $mapping->{$sourceRelationMode}[$joinTableColumn]; $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; $params[] = $sourceClass->containsForeignIdentifier ? $id[$sourceClass->getFieldForColumn($column)] : $id[$sourceClass->fieldNames[$column]]; $types[] = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em); } elseif (! $joinNeeded) { - $column = $mapping[$targetRelationMode][$joinTableColumn]; + $column = $mapping->{$targetRelationMode}[$joinTableColumn]; $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; $params[] = $key; @@ -655,8 +640,7 @@ private function getJoinTableRestrictionsWithKey( } /** - * @param bool $addFilters Whether the filter SQL should be included or not. - * @param object $element + * @param bool $addFilters Whether the filter SQL should be included or not. * * @return mixed[] ordered vector: * - quoted join table name @@ -667,36 +651,36 @@ private function getJoinTableRestrictionsWithKey( */ private function getJoinTableRestrictions( PersistentCollection $collection, - $element, - bool $addFilters + object $element, + bool $addFilters, ): array { - $filterMapping = $collection->getMapping(); + $filterMapping = $this->getMapping($collection); $mapping = $filterMapping; - if (! $mapping['isOwningSide']) { - $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); - $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); + if (! $mapping->isOwningSide()) { + $sourceClass = $this->em->getClassMetadata($mapping->targetEntity); + $targetClass = $this->em->getClassMetadata($mapping->sourceEntity); $sourceId = $this->uow->getEntityIdentifier($element); $targetId = $this->uow->getEntityIdentifier($collection->getOwner()); - - $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; } else { - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $sourceId = $this->uow->getEntityIdentifier($collection->getOwner()); $targetId = $this->uow->getEntityIdentifier($element); } + $mapping = $this->em->getMetadataFactory()->getOwningSide($mapping); + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); $whereClauses = []; $params = []; $types = []; - foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + foreach ($mapping->joinTableColumns as $joinTableColumn) { $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; - if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { - $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn]; + if (isset($mapping->relationToTargetKeyColumns[$joinTableColumn])) { + $targetColumn = $mapping->relationToTargetKeyColumns[$joinTableColumn]; $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); @@ -704,7 +688,7 @@ private function getJoinTableRestrictions( } // relationToSourceKeyColumns - $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn]; + $targetColumn = $mapping->relationToSourceKeyColumns[$joinTableColumn]; $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em); } @@ -748,16 +732,16 @@ private function expandCriteriaParameters(Criteria $criteria): array private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string { - $orderings = self::getCriteriaOrderings($criteria); + $orderings = $criteria->orderings(); if ($orderings) { $orderBy = []; foreach ($orderings as $name => $direction) { $field = $this->quoteStrategy->getColumnName( $name, $targetClass, - $this->platform + $this->platform, ); - $orderBy[] = $field . ' ' . $direction; + $orderBy[] = $field . ' ' . $direction->value; } return ' ORDER BY ' . implode(', ', $orderBy); @@ -774,4 +758,13 @@ private function getLimitSql(Criteria $criteria): string return $this->platform->modifyLimitQuery('', $limit, $offset ?? 0); } + + private function getMapping(PersistentCollection $collection): AssociationMapping&ManyToManyAssociationMapping + { + $mapping = $collection->getMapping(); + + assert($mapping instanceof ManyToManyAssociationMapping); + + return $mapping; + } } diff --git a/src/Persisters/Collection/OneToManyPersister.php b/src/Persisters/Collection/OneToManyPersister.php index 1e032e99b49..d96be8deea8 100644 --- a/src/Persisters/Collection/OneToManyPersister.php +++ b/src/Persisters/Collection/OneToManyPersister.php @@ -10,12 +10,12 @@ use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityNotFoundException; use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Utility\PersisterHelper; use function array_fill; use function array_keys; -use function array_merge; use function array_reverse; use function array_values; use function assert; @@ -29,36 +29,28 @@ */ class OneToManyPersister extends AbstractCollectionPersister { - /** - * {@inheritDoc} - * - * @return int|null - */ - public function delete(PersistentCollection $collection) + public function delete(PersistentCollection $collection): void { // The only valid case here is when you have weak entities. In this // scenario, you have @OneToMany with orphanRemoval=true, and replacing // the entire collection with a new would trigger this operation. - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! $mapping['orphanRemoval']) { + if (! $mapping->orphanRemoval) { // Handling non-orphan removal should never happen, as @OneToMany // can only be inverse side. For owning side one to many, it is // required to have a join table, which would classify as a ManyToManyPersister. return; } - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); - return $targetClass->isInheritanceTypeJoined() + $targetClass->isInheritanceTypeJoined() ? $this->deleteJoinedEntityCollection($collection) : $this->deleteEntityCollection($collection); } - /** - * {@inheritDoc} - */ - public function update(PersistentCollection $collection) + public function update(PersistentCollection $collection): void { // This can never happen. One to many can only be inverse side. // For owning side one to many, it is required to have a join table, @@ -66,44 +58,38 @@ public function update(PersistentCollection $collection) return; } - /** - * {@inheritDoc} - */ - public function get(PersistentCollection $collection, $index) + public function get(PersistentCollection $collection, mixed $index): object|null { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! isset($mapping['indexBy'])) { + if (! $mapping->isIndexed()) { throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.'); } - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); return $persister->load( [ - $mapping['mappedBy'] => $collection->getOwner(), - $mapping['indexBy'] => $index, + $mapping->mappedBy => $collection->getOwner(), + $mapping->indexBy() => $index, ], null, $mapping, [], null, - 1 + 1, ); } - /** - * {@inheritDoc} - */ - public function count(PersistentCollection $collection) + public function count(PersistentCollection $collection): int { - $mapping = $collection->getMapping(); - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); // only works with single id identifier entities. Will throw an // exception in Entity Persisters if that is not the case for the // 'mappedBy' field. - $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + $criteria = new Criteria(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner())); return $persister->count($criteria); } @@ -111,54 +97,48 @@ public function count(PersistentCollection $collection) /** * {@inheritDoc} */ - public function slice(PersistentCollection $collection, $offset, $length = null) + public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array { - $mapping = $collection->getMapping(); - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); return $persister->getOneToManyCollection($mapping, $collection->getOwner(), $offset, $length); } - /** - * {@inheritDoc} - */ - public function containsKey(PersistentCollection $collection, $key) + public function containsKey(PersistentCollection $collection, mixed $key): bool { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); - if (! isset($mapping['indexBy'])) { + if (! $mapping->isIndexed()) { throw new BadMethodCallException('Selecting a collection by index is only supported on indexed collections.'); } - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); // only works with single id identifier entities. Will throw an // exception in Entity Persisters if that is not the case for the // 'mappedBy' field. $criteria = new Criteria(); - $criteria->andWhere(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); - $criteria->andWhere(Criteria::expr()->eq($mapping['indexBy'], $key)); + $criteria->andWhere(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner())); + $criteria->andWhere(Criteria::expr()->eq($mapping->indexBy(), $key)); return (bool) $persister->count($criteria); } - /** - * {@inheritDoc} - */ - public function contains(PersistentCollection $collection, $element) + public function contains(PersistentCollection $collection, object $element): bool { if (! $this->isValidEntityState($element)) { return false; } - $mapping = $collection->getMapping(); - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + $persister = $this->uow->getEntityPersister($mapping->targetEntity); // only works with single id identifier entities. Will throw an // exception in Entity Persisters if that is not the case for the // 'mappedBy' field. - $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + $criteria = new Criteria(Criteria::expr()->eq($mapping->mappedBy, $collection->getOwner())); return $persister->exists($element, $criteria); } @@ -166,7 +146,7 @@ public function contains(PersistentCollection $collection, $element) /** * {@inheritDoc} */ - public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array { throw new BadMethodCallException('Filtering a collection by Criteria is not supported by this CollectionPersister.'); } @@ -178,18 +158,18 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri */ private function deleteEntityCollection(PersistentCollection $collection): int { - $mapping = $collection->getMapping(); + $mapping = $this->getMapping($collection); $identifier = $this->uow->getEntityIdentifier($collection->getOwner()); - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $columns = []; $parameters = []; $types = []; - foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] as $joinColumn) { + foreach ($this->em->getMetadataFactory()->getOwningSide($mapping)->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); - $parameters[] = $identifier[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]; - $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $sourceClass, $this->em); + $parameters[] = $identifier[$sourceClass->getFieldForColumn($joinColumn->referencedColumnName)]; + $types[] = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $sourceClass, $this->em); } $statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) @@ -198,10 +178,10 @@ private function deleteEntityCollection(PersistentCollection $collection): int if ($targetClass->isInheritanceTypeSingleTable()) { $discriminatorColumn = $targetClass->getDiscriminatorColumn(); $discriminatorValues = $targetClass->discriminatorValue ? [$targetClass->discriminatorValue] : array_keys($targetClass->discriminatorMap); - $statement .= ' AND ' . $discriminatorColumn['name'] . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')'; + $statement .= ' AND ' . $discriminatorColumn->name . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')'; foreach ($discriminatorValues as $discriminatorValue) { $parameters[] = $discriminatorValue; - $types[] = $discriminatorColumn['type']; + $types[] = $discriminatorColumn->type; } } @@ -222,9 +202,9 @@ private function deleteEntityCollection(PersistentCollection $collection): int */ private function deleteJoinedEntityCollection(PersistentCollection $collection): int { - $mapping = $collection->getMapping(); - $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $mapping = $this->getMapping($collection); + $sourceClass = $this->em->getClassMetadata($mapping->sourceEntity); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); // 1) Build temporary table DDL @@ -235,6 +215,7 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection): foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = [ + 'name' => $idColumnName, 'notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em)), ]; @@ -248,7 +229,7 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection): // 2) Build insert table records into temporary table $query = $this->em->createQuery( ' SELECT t0.' . implode(', t0.', $rootClass->getIdentifierFieldNames()) - . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping['mappedBy'] . ' = :owner' + . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping->mappedBy . ' = :owner', )->setParameter('owner', $collection->getOwner()); $sql = $query->getSQL(); @@ -258,7 +239,7 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection): $numDeleted = $this->conn->executeStatement($statement, $parameters); // 3) Delete records on each table in the hierarchy - $classNames = array_merge($targetClass->parentClasses, [$targetClass->name], $targetClass->subClasses); + $classNames = [...$targetClass->parentClasses, ...[$targetClass->name], ...$targetClass->subClasses]; foreach (array_reverse($classNames) as $className) { $tableName = $this->quoteStrategy->getTableName($this->em->getClassMetadata($className), $this->platform); @@ -277,4 +258,13 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection): return $numDeleted; } + + private function getMapping(PersistentCollection $collection): OneToManyAssociationMapping + { + $mapping = $collection->getMapping(); + + assert($mapping->isOneToMany()); + + return $mapping; + } } diff --git a/src/Persisters/Entity/AbstractEntityInheritancePersister.php b/src/Persisters/Entity/AbstractEntityInheritancePersister.php index b17b30dbbef..cf8a74eb619 100644 --- a/src/Persisters/Entity/AbstractEntityInheritancePersister.php +++ b/src/Persisters/Entity/AbstractEntityInheritancePersister.php @@ -19,58 +19,43 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister /** * {@inheritDoc} */ - protected function prepareInsertData($entity) + protected function prepareInsertData(object $entity): array { $data = parent::prepareInsertData($entity); // Populate the discriminator column - $discColumn = $this->class->getDiscriminatorColumn(); - $this->columnTypes[$discColumn['name']] = $discColumn['type']; - $data[$this->getDiscriminatorColumnTableName()][$discColumn['name']] = $this->class->discriminatorValue; + $discColumn = $this->class->getDiscriminatorColumn(); + $this->columnTypes[$discColumn->name] = $discColumn->type; + $data[$this->getDiscriminatorColumnTableName()][$discColumn->name] = $this->class->discriminatorValue; return $data; } /** * Gets the name of the table that contains the discriminator column. - * - * @return string The table name. */ - abstract protected function getDiscriminatorColumnTableName(); + abstract protected function getDiscriminatorColumnTableName(): string; - /** - * {@inheritDoc} - */ - protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + protected function getSelectColumnSQL(string $field, ClassMetadata $class, string $alias = 'r'): string { $tableAlias = $alias === 'r' ? '' : $alias; $fieldMapping = $class->fieldMappings[$field]; - $columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']); + $columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName); $sql = sprintf( '%s.%s', $this->getSQLTableAlias($class->name, $tableAlias), - $this->quoteStrategy->getColumnName($field, $class, $this->platform) + $this->quoteStrategy->getColumnName($field, $class, $this->platform), ); $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field, $class->name); - if (isset($fieldMapping['requireSQLConversion'])) { - $type = Type::getType($fieldMapping['type']); - $sql = $type->convertToPHPValueSQL($sql, $this->platform); - } + $type = Type::getType($fieldMapping->type); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); return $sql . ' AS ' . $columnAlias; } - /** - * @param string $tableAlias - * @param string $joinColumnName - * @param string $quotedColumnName - * @param string $type - * - * @return string - */ - protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $quotedColumnName, $type) + protected function getSelectJoinColumnSQL(string $tableAlias, string $joinColumnName, string $quotedColumnName, string $type): string { $columnAlias = $this->getSQLColumnAlias($joinColumnName); diff --git a/src/Persisters/Entity/BasicEntityPersister.php b/src/Persisters/Entity/BasicEntityPersister.php index 4073c606edc..46ca8c0129a 100644 --- a/src/Persisters/Entity/BasicEntityPersister.php +++ b/src/Persisters/Entity/BasicEntityPersister.php @@ -7,17 +7,22 @@ use BackedEnum; use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Order; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Internal\CriteriaOrderings; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\JoinColumnMapping; +use Doctrine\ORM\Mapping\ManyToManyAssociationMapping; use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\ORM\Mapping\QuoteStrategy; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\PersistentCollection; @@ -29,6 +34,7 @@ use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; use Doctrine\ORM\Query; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Repository\Exception\InvalidFindByCall; use Doctrine\ORM\UnitOfWork; use Doctrine\ORM\Utility\IdentifierFlattener; @@ -89,16 +95,13 @@ * * Subclasses can be created to provide custom persisting and querying strategies, * i.e. spanning multiple tables. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ class BasicEntityPersister implements EntityPersister { - use CriteriaOrderings; use LockSqlHelper; /** @var array */ - private static $comparisonMap = [ + private static array $comparisonMap = [ Comparison::EQ => '= %s', Comparison::NEQ => '!= %s', Comparison::GT => '> %s', @@ -112,40 +115,22 @@ class BasicEntityPersister implements EntityPersister Comparison::ENDS_WITH => 'LIKE %s', ]; - /** - * Metadata object that describes the mapping of the mapped entity class. - * - * @var ClassMetadata - */ - protected $class; - /** * The underlying DBAL Connection of the used EntityManager. - * - * @var Connection $conn */ - protected $conn; + protected Connection $conn; /** * The database platform. - * - * @var AbstractPlatform */ - protected $platform; - - /** - * The EntityManager instance. - * - * @var EntityManagerInterface - */ - protected $em; + protected AbstractPlatform $platform; /** * Queued inserts. * * @phpstan-var array */ - protected $queuedInserts = []; + protected array $queuedInserts = []; /** * The map of column names to DBAL mapping types of all prepared columns used @@ -156,7 +141,7 @@ class BasicEntityPersister implements EntityPersister * * @var mixed[] */ - protected $columnTypes = []; + protected array $columnTypes = []; /** * The map of quoted column names. @@ -166,50 +151,40 @@ class BasicEntityPersister implements EntityPersister * * @var mixed[] */ - protected $quotedColumns = []; + protected array $quotedColumns = []; /** * The INSERT SQL statement used for entities handled by this persister. * This SQL is only generated once per request, if at all. - * - * @var string|null */ - private $insertSql; + private string|null $insertSql = null; /** * The quote strategy. - * - * @var QuoteStrategy */ - protected $quoteStrategy; + protected QuoteStrategy $quoteStrategy; /** * The IdentifierFlattener used for manipulating identifiers - * - * @var IdentifierFlattener */ - protected $identifierFlattener; - - /** @var CachedPersisterContext */ - protected $currentPersisterContext; + protected readonly IdentifierFlattener $identifierFlattener; - /** @var CachedPersisterContext */ - private $limitsHandlingContext; + protected CachedPersisterContext $currentPersisterContext; + private readonly CachedPersisterContext $limitsHandlingContext; + private readonly CachedPersisterContext $noLimitsContext; - /** @var CachedPersisterContext */ - private $noLimitsContext; - - /** @var ?string */ - private $filterHash = null; + private string|null $filterHash = null; /** * Initializes a new BasicEntityPersister that uses the given EntityManager * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param ClassMetadata $class Metadata object that describes the mapping of the mapped entity class. */ - public function __construct(EntityManagerInterface $em, ClassMetadata $class) - { - $this->em = $em; - $this->class = $class; + public function __construct( + protected EntityManagerInterface $em, + protected ClassMetadata $class, + ) { $this->conn = $em->getConnection(); $this->platform = $this->conn->getDatabasePlatform(); $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); @@ -217,35 +192,26 @@ public function __construct(EntityManagerInterface $em, ClassMetadata $class) $this->noLimitsContext = $this->currentPersisterContext = new CachedPersisterContext( $class, new Query\ResultSetMapping(), - false + false, ); $this->limitsHandlingContext = new CachedPersisterContext( $class, new Query\ResultSetMapping(), - true + true, ); } - /** - * {@inheritDoc} - */ - public function getClassMetadata() + public function getClassMetadata(): ClassMetadata { return $this->class; } - /** - * {@inheritDoc} - */ - public function getResultSetMapping() + public function getResultSetMapping(): ResultSetMapping { return $this->currentPersisterContext->rsm; } - /** - * {@inheritDoc} - */ - public function addInsert($entity) + public function addInsert(object $entity): void { $this->queuedInserts[spl_object_id($entity)] = $entity; } @@ -253,15 +219,12 @@ public function addInsert($entity) /** * {@inheritDoc} */ - public function getInserts() + public function getInserts(): array { return $this->queuedInserts; } - /** - * {@inheritDoc} - */ - public function executeInserts() + public function executeInserts(): void { if (! $this->queuedInserts) { return; @@ -318,17 +281,14 @@ public function executeInserts() * Also retrieves values of columns marked as 'non insertable' and / or * 'not updatable' and assigns them back to the entities corresponding fields. * - * @param object $entity * @param mixed[] $id - * - * @return void */ - protected function assignDefaultVersionAndUpsertableValues($entity, array $id) + protected function assignDefaultVersionAndUpsertableValues(object $entity, array $id): void { $values = $this->fetchVersionAndNotUpsertableValues($this->class, $id); foreach ($values as $field => $value) { - $value = Type::getType($this->class->fieldMappings[$field]['type'])->convertToPHPValue($value, $this->platform); + $value = Type::getType($this->class->fieldMappings[$field]->type)->convertToPHPValue($value, $this->platform); $this->class->setFieldValue($entity, $field, $value); } @@ -338,16 +298,13 @@ protected function assignDefaultVersionAndUpsertableValues($entity, array $id) * Fetches the current version value of a versioned entity and / or the values of fields * marked as 'not insertable' and / or 'not updatable'. * - * @param ClassMetadata $versionedClass - * @param mixed[] $id - * - * @return mixed + * @param mixed[] $id */ - protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id) + protected function fetchVersionAndNotUpsertableValues(ClassMetadata $versionedClass, array $id): mixed { $columnNames = []; foreach ($this->class->fieldMappings as $key => $column) { - if (isset($column['generated']) || ($this->class->isVersioned && $key === $versionedClass->versionField)) { + if (isset($column->generated) || ($this->class->isVersioned && $key === $versionedClass->versionField)) { $columnNames[$key] = $this->quoteStrategy->getColumnName($key, $versionedClass, $this->platform); } } @@ -365,7 +322,7 @@ protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id $values = $this->conn->fetchNumeric( $sql, array_values($flatId), - $this->extractIdentifierTypes($id, $versionedClass) + $this->extractIdentifierTypes($id, $versionedClass), ); if ($values === false) { @@ -384,24 +341,21 @@ protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id /** * @param mixed[] $id * - * @return int[]|null[]|string[] - * @phpstan-return list + * @return list + * @phpstan-return list */ final protected function extractIdentifierTypes(array $id, ClassMetadata $versionedClass): array { $types = []; foreach ($id as $field => $value) { - $types = array_merge($types, $this->getTypes($field, $value, $versionedClass)); + $types = [...$types, ...$this->getTypes($field, $value, $versionedClass)]; } return $types; } - /** - * {@inheritDoc} - */ - public function update($entity) + public function update(object $entity): void { $tableName = $this->class->getTableName(); $updateData = $this->prepareUpdateData($entity); @@ -441,10 +395,10 @@ public function update($entity) * @throws OptimisticLockException */ final protected function updateTable( - $entity, - $quotedTableName, + object $entity, + string $quotedTableName, array $updateData, - $versioned = false + bool $versioned = false, ): void { $set = []; $types = []; @@ -459,7 +413,7 @@ final protected function updateTable( $fieldName = $this->class->fieldNames[$columnName]; $column = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); - if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) { + if (isset($this->class->fieldMappings[$fieldName])) { $type = Type::getType($this->columnTypes[$columnName]); $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); } @@ -483,20 +437,22 @@ final protected function updateTable( foreach ($this->class->identifier as $idField) { if (! isset($this->class->associationMappings[$idField])) { $params[] = $identifier[$idField]; - $types[] = $this->class->fieldMappings[$idField]['type']; + $types[] = $this->class->fieldMappings[$idField]->type; $where[] = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform); continue; } + assert($this->class->associationMappings[$idField]->isToOneOwningSide()); + $params[] = $identifier[$idField]; $where[] = $this->quoteStrategy->getJoinColumnName( - $this->class->associationMappings[$idField]['joinColumns'][0], + $this->class->associationMappings[$idField]->joinColumns[0], $this->class, - $this->platform + $this->platform, ); - $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']); + $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]->targetEntity); $targetType = PersisterHelper::getTypeOfField($targetMapping->identifier[0], $targetMapping, $this->em); if ($targetType === []) { @@ -509,11 +465,11 @@ final protected function updateTable( if ($versioned) { $versionField = $this->class->versionField; assert($versionField !== null); - $versionFieldType = $this->class->fieldMappings[$versionField]['type']; + $versionFieldType = $this->class->fieldMappings[$versionField]->type; $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform); $where[] = $versionColumn; - $types[] = $this->class->fieldMappings[$versionField]['type']; + $types[] = $this->class->fieldMappings[$versionField]->type; $params[] = $this->class->reflFields[$versionField]->getValue($entity); switch ($versionFieldType) { @@ -549,32 +505,32 @@ final protected function updateTable( protected function deleteJoinTableRecords(array $identifier, array $types): void { foreach ($this->class->associationMappings as $mapping) { - if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY || isset($mapping['isOnDeleteCascade'])) { + if (! $mapping->isManyToMany() || $mapping->isOnDeleteCascade) { continue; } // @Todo this only covers scenarios with no inheritance or of the same level. Is there something // like self-referential relationship between different levels of an inheritance hierarchy? I hope not! - $selfReferential = ($mapping['targetEntity'] === $mapping['sourceEntity']); + $selfReferential = ($mapping->targetEntity === $mapping->sourceEntity); $class = $this->class; $association = $mapping; $otherColumns = []; $otherKeys = []; $keys = []; - if (! $mapping['isOwningSide']) { - $class = $this->em->getClassMetadata($mapping['targetEntity']); - $association = $class->associationMappings[$mapping['mappedBy']]; + if (! $mapping->isOwningSide()) { + $class = $this->em->getClassMetadata($mapping->targetEntity); } - $joinColumns = $mapping['isOwningSide'] - ? $association['joinTable']['joinColumns'] - : $association['joinTable']['inverseJoinColumns']; + $association = $this->em->getMetadataFactory()->getOwningSide($association); + $joinColumns = $mapping->isOwningSide() + ? $association->joinTable->joinColumns + : $association->joinTable->inverseJoinColumns; if ($selfReferential) { - $otherColumns = ! $mapping['isOwningSide'] - ? $association['joinTable']['joinColumns'] - : $association['joinTable']['inverseJoinColumns']; + $otherColumns = ! $mapping->isOwningSide() + ? $association->joinTable->joinColumns + : $association->joinTable->inverseJoinColumns; } foreach ($joinColumns as $joinColumn) { @@ -595,10 +551,7 @@ protected function deleteJoinTableRecords(array $identifier, array $types): void } } - /** - * {@inheritDoc} - */ - public function delete($entity) + public function delete(object $entity): bool { $class = $this->class; $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); @@ -635,7 +588,7 @@ public function delete($entity) * @return mixed[][] The prepared data. * @phpstan-return array> */ - protected function prepareUpdateData($entity, bool $isInsert = false) + protected function prepareUpdateData(object $entity, bool $isInsert = false): array { $versionField = null; $result = []; @@ -659,17 +612,17 @@ protected function prepareUpdateData($entity, bool $isInsert = false) if (! isset($this->class->associationMappings[$field])) { $fieldMapping = $this->class->fieldMappings[$field]; - $columnName = $fieldMapping['columnName']; + $columnName = $fieldMapping->columnName; - if (! $isInsert && isset($fieldMapping['notUpdatable'])) { + if (! $isInsert && isset($fieldMapping->notUpdatable)) { continue; } - if ($isInsert && isset($fieldMapping['notInsertable'])) { + if ($isInsert && isset($fieldMapping->notInsertable)) { continue; } - $this->columnTypes[$columnName] = $fieldMapping['type']; + $this->columnTypes[$columnName] = $fieldMapping->type; $result[$this->getOwningTable($field)][$columnName] = $newVal; @@ -679,7 +632,7 @@ protected function prepareUpdateData($entity, bool $isInsert = false) $assoc = $this->class->associationMappings[$field]; // Only owning side of x-1 associations can have a FK column. - if (! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { + if (! $assoc->isToOneOwningSide()) { continue; } @@ -722,12 +675,12 @@ protected function prepareUpdateData($entity, bool $isInsert = false) $newValId = $uow->getEntityIdentifier($newVal); } - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); $owningTable = $this->getOwningTable($field); - foreach ($assoc['joinColumns'] as $joinColumn) { - $sourceColumn = $joinColumn['name']; - $targetColumn = $joinColumn['referencedColumnName']; + foreach ($assoc->joinColumns as $joinColumn) { + $sourceColumn = $joinColumn->name; + $targetColumn = $joinColumn->referencedColumnName; $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); $this->quotedColumns[$sourceColumn] = $quotedColumn; @@ -754,15 +707,12 @@ protected function prepareUpdateData($entity, bool $isInsert = false) * @return mixed[][] The prepared data for the tables to update. * @phpstan-return array */ - protected function prepareInsertData($entity) + protected function prepareInsertData(object $entity): array { return $this->prepareUpdateData($entity, true); } - /** - * {@inheritDoc} - */ - public function getOwningTable($fieldName) + public function getOwningTable(string $fieldName): string { return $this->class->getTableName(); } @@ -770,8 +720,15 @@ public function getOwningTable($fieldName) /** * {@inheritDoc} */ - public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, ?array $orderBy = null) - { + public function load( + array $criteria, + object|null $entity = null, + AssociationMapping|null $assoc = null, + array $hints = [], + LockMode|int|null $lockMode = null, + int|null $limit = null, + array|null $orderBy = null, + ): object|null { $this->switchPersisterContext(null, $limit); $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy); @@ -792,7 +749,7 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint /** * {@inheritDoc} */ - public function loadById(array $identifier, $entity = null) + public function loadById(array $identifier, object|null $entity = null): object|null { return $this->load($identifier, $entity); } @@ -800,38 +757,40 @@ public function loadById(array $identifier, $entity = null) /** * {@inheritDoc} */ - public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = []) + public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null { - $foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity']); + $foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc->targetEntity); if ($foundEntity !== false) { return $foundEntity; } - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); - if ($assoc['isOwningSide']) { - $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); + if ($assoc->isOwningSide()) { + $isInverseSingleValued = $assoc->inversedBy !== null && ! $targetClass->isCollectionValuedAssociation($assoc->inversedBy); // Mark inverse side as fetched in the hints, otherwise the UoW would // try to load it in a separate query (remember: to-one inverse sides can not be lazy). $hints = []; if ($isInverseSingleValued) { - $hints['fetched']['r'][$assoc['inversedBy']] = true; + $hints['fetched']['r'][$assoc->inversedBy] = true; } $targetEntity = $this->load($identifier, null, $assoc, $hints); // Complete bidirectional association, if necessary if ($targetEntity !== null && $isInverseSingleValued) { - $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); + $targetClass->reflFields[$assoc->inversedBy]->setValue($targetEntity, $sourceEntity); } return $targetEntity; } - $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); - $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); + assert(isset($assoc->mappedBy)); + $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity); + $owningAssoc = $targetClass->getAssociationMapping($assoc->mappedBy); + assert($owningAssoc->isOneToOneOwningSide()); $computedIdentifier = []; @@ -839,7 +798,7 @@ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifie $sourceEntityData = null; // TRICKY: since the association is specular source and target are flipped - foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { if (! isset($sourceClass->fieldNames[$sourceKeyColumn])) { // The likely case here is that the column is a join column // in an association mapping. However, there is no guarantee @@ -864,7 +823,7 @@ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifie if (! $resolvedSourceData) { throw MappingException::joinColumnMustPointToMappedField( $sourceClass->name, - $sourceKeyColumn + $sourceKeyColumn, ); } } else { @@ -876,7 +835,7 @@ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifie $targetEntity = $this->load($computedIdentifier, null, $assoc); if ($targetEntity !== null) { - $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); + $targetClass->setFieldValue($targetEntity, $assoc->mappedBy, $sourceEntity); } return $targetEntity; @@ -885,7 +844,7 @@ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifie /** * {@inheritDoc} */ - public function refresh(array $id, $entity, $lockMode = null) + public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void { $sql = $this->getSelectSQL($id, null, $lockMode); [$params, $types] = $this->expandParameters($id); @@ -895,10 +854,7 @@ public function refresh(array $id, $entity, $lockMode = null) $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [Query::HINT_REFRESH => true]); } - /** - * {@inheritDoc} - */ - public function count($criteria = []) + public function count(array|Criteria $criteria = []): int { $sql = $this->getCountSQL($criteria); @@ -906,15 +862,21 @@ public function count($criteria = []) ? $this->expandCriteriaParameters($criteria) : $this->expandParameters($criteria); - return (int) $this->conn->executeQuery($sql, $params, $types)->fetchOne(); + $count = (int) $this->conn->executeQuery($sql, $params, $types)->fetchOne(); + assert($count >= 0); + + return $count; } /** * {@inheritDoc} */ - public function loadCriteria(Criteria $criteria) + public function loadCriteria(Criteria $criteria): array { - $orderBy = self::getCriteriaOrderings($criteria); + $orderBy = array_map( + static fn (Order $order): string => $order->value, + $criteria->orderings(), + ); $limit = $criteria->getMaxResults(); $offset = $criteria->getFirstResult(); $query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); @@ -930,7 +892,7 @@ public function loadCriteria(Criteria $criteria) /** * {@inheritDoc} */ - public function expandCriteriaParameters(Criteria $criteria) + public function expandCriteriaParameters(Criteria $criteria): array { $expression = $criteria->getWhereExpression(); $sqlParams = []; @@ -953,8 +915,8 @@ public function expandCriteriaParameters(Criteria $criteria) continue; } - $sqlParams = array_merge($sqlParams, $this->getValues($value)); - $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); + $sqlParams = [...$sqlParams, ...$this->getValues($value)]; + $sqlTypes = [...$sqlTypes, ...$this->getTypes($field, $value, $this->class)]; } return [$sqlParams, $sqlTypes]; @@ -963,8 +925,12 @@ public function expandCriteriaParameters(Criteria $criteria) /** * {@inheritDoc} */ - public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null) - { + public function loadAll( + array $criteria = [], + array|null $orderBy = null, + int|null $limit = null, + int|null $offset = null, + ): array { $this->switchPersisterContext($offset, $limit); $sql = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); @@ -979,8 +945,13 @@ public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = n /** * {@inheritDoc} */ - public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) - { + public function getManyToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array { + assert($assoc->isManyToMany()); $this->switchPersisterContext($offset, $limit); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); @@ -991,18 +962,16 @@ public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = n /** * Loads an array of entities from a given DBAL statement. * - * @param mixed[] $assoc - * * @return mixed[] */ - private function loadArrayFromResult(array $assoc, Result $stmt): array + private function loadArrayFromResult(AssociationMapping $assoc, Result $stmt): array { $rsm = $this->currentPersisterContext->rsm; $hints = [UnitOfWork::HINT_DEFEREAGERLOAD => true]; - if (isset($assoc['indexBy'])) { + if ($assoc->isIndexed()) { $rsm = clone $this->currentPersisterContext->rsm; // this is necessary because the "default rsm" should be changed. - $rsm->addIndexBy('r', $assoc['indexBy']); + $rsm->addIndexBy('r', $assoc->indexBy()); } return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); @@ -1011,14 +980,12 @@ private function loadArrayFromResult(array $assoc, Result $stmt): array /** * Hydrates a collection from a given DBAL statement. * - * @param mixed[] $assoc - * * @return mixed[] */ private function loadCollectionFromStatement( - array $assoc, + AssociationMapping $assoc, Result $stmt, - PersistentCollection $coll + PersistentCollection $coll, ): array { $rsm = $this->currentPersisterContext->rsm; $hints = [ @@ -1026,9 +993,9 @@ private function loadCollectionFromStatement( 'collection' => $coll, ]; - if (isset($assoc['indexBy'])) { + if ($assoc->isIndexed()) { $rsm = clone $this->currentPersisterContext->rsm; // this is necessary because the "default rsm" should be changed. - $rsm->addIndexBy('r', $assoc['indexBy']); + $rsm->addIndexBy('r', $assoc->indexBy()); } return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); @@ -1037,48 +1004,42 @@ private function loadCollectionFromStatement( /** * {@inheritDoc} */ - public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection) + public function loadManyToManyCollection(AssociationMapping $assoc, object $sourceEntity, PersistentCollection $collection): array { + assert($assoc->isManyToMany()); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); return $this->loadCollectionFromStatement($assoc, $stmt, $collection); } - /** - * @param object $sourceEntity - * @phpstan-param array $assoc - * - * @return Result - * - * @throws MappingException - */ + /** @throws MappingException */ private function getManyToManyStatement( - array $assoc, - $sourceEntity, - ?int $offset = null, - ?int $limit = null - ) { + AssociationMapping&ManyToManyAssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): Result { $this->switchPersisterContext($offset, $limit); - $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity); $class = $sourceClass; $association = $assoc; $criteria = []; $parameters = []; - if (! $assoc['isOwningSide']) { - $class = $this->em->getClassMetadata($assoc['targetEntity']); - $association = $class->associationMappings[$assoc['mappedBy']]; + if (! $assoc->isOwningSide()) { + $class = $this->em->getClassMetadata($assoc->targetEntity); } - $joinColumns = $assoc['isOwningSide'] - ? $association['joinTable']['joinColumns'] - : $association['joinTable']['inverseJoinColumns']; + $association = $this->em->getMetadataFactory()->getOwningSide($assoc); + $joinColumns = $assoc->isOwningSide() + ? $association->joinTable->joinColumns + : $association->joinTable->inverseJoinColumns; $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); foreach ($joinColumns as $joinColumn) { - $sourceKeyColumn = $joinColumn['referencedColumnName']; + $sourceKeyColumn = $joinColumn->referencedColumnName; $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); switch (true) { @@ -1088,7 +1049,7 @@ private function getManyToManyStatement( if (isset($sourceClass->associationMappings[$field])) { $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); - $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]->targetEntity)->identifier[0]]; } break; @@ -1102,7 +1063,7 @@ private function getManyToManyStatement( default: throw MappingException::joinColumnMustPointToMappedField( $sourceClass->name, - $sourceKeyColumn + $sourceKeyColumn, ); } @@ -1120,23 +1081,25 @@ private function getManyToManyStatement( return $this->conn->executeQuery($sql, $params, $types); } - /** - * {@inheritDoc} - */ - public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null) - { + public function getSelectSQL( + array|Criteria $criteria, + AssociationMapping|null $assoc = null, + LockMode|int|null $lockMode = null, + int|null $limit = null, + int|null $offset = null, + array|null $orderBy = null, + ): string { $this->switchPersisterContext($offset, $limit); - $lockSql = ''; $joinSql = ''; $orderBySql = ''; - if ($assoc !== null && $assoc['type'] === ClassMetadata::MANY_TO_MANY) { + if ($assoc !== null && $assoc->isManyToMany()) { $joinSql = $this->getSelectManyToManyJoinSQL($assoc); } - if (isset($assoc['orderBy'])) { - $orderBy = $assoc['orderBy']; + if ($assoc !== null && $assoc->isOrdered()) { + $orderBy = $assoc->orderBy(); } if ($orderBy) { @@ -1147,15 +1110,11 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit ? $this->getSelectConditionCriteriaSQL($criteria) : $this->getSelectConditionSQL($criteria, $assoc); - switch ($lockMode) { - case LockMode::PESSIMISTIC_READ: - $lockSql = ' ' . $this->getReadLockSQL($this->platform); - break; - - case LockMode::PESSIMISTIC_WRITE: - $lockSql = ' ' . $this->getWriteLockSQL($this->platform); - break; - } + $lockSql = match ($lockMode) { + LockMode::PESSIMISTIC_READ => ' ' . $this->getReadLockSQL($this->platform), + LockMode::PESSIMISTIC_WRITE => ' ' . $this->getWriteLockSQL($this->platform), + default => '', + }; $columnList = $this->getSelectColumnsSQL(); $tableAlias = $this->getSQLTableAlias($this->class->name); @@ -1182,10 +1141,7 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql; } - /** - * {@inheritDoc} - */ - public function getCountSQL($criteria = []) + public function getCountSQL(array|Criteria $criteria = []): string { $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); $tableAlias = $this->getSQLTableAlias($this->class->name); @@ -1228,8 +1184,8 @@ final protected function getOrderBySQL(array $orderBy, string $baseTableAlias): } if (isset($this->class->fieldMappings[$fieldName])) { - $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited']) - ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited']) + $tableAlias = isset($this->class->fieldMappings[$fieldName]->inherited) + ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]->inherited) : $baseTableAlias; $columnName = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); @@ -1239,15 +1195,18 @@ final protected function getOrderBySQL(array $orderBy, string $baseTableAlias): } if (isset($this->class->associationMappings[$fieldName])) { - if (! $this->class->associationMappings[$fieldName]['isOwningSide']) { + $association = $this->class->associationMappings[$fieldName]; + if (! $association->isOwningSide()) { throw InvalidFindByCall::fromInverseSideUsage($this->class->name, $fieldName); } - $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited']) - ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited']) + assert($association->isToOneOwningSide()); + + $tableAlias = isset($association->inherited) + ? $this->getSQLTableAlias($association->inherited) : $baseTableAlias; - foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) { + foreach ($association->joinColumns as $joinColumn) { $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; } @@ -1269,10 +1228,8 @@ final protected function getOrderBySQL(array $orderBy, string $baseTableAlias): * list SQL fragment. Note that in the implementation of BasicEntityPersister * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}. * Subclasses may or may not do the same. - * - * @return string The SQL fragment. */ - protected function getSelectColumnsSQL() + protected function getSelectColumnsSQL(): string { if ($this->currentPersisterContext->selectColumnListSql !== null && $this->filterHash === $this->em->getFilters()->getHash()) { return $this->currentPersisterContext->selectColumnListSql; @@ -1296,25 +1253,25 @@ protected function getSelectColumnsSQL() $columnList[] = $assocColumnSQL; } - $isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide']; - $isAssocFromOneEager = $assoc['type'] & ClassMetadata::TO_ONE && $assoc['fetch'] === ClassMetadata::FETCH_EAGER; + $isAssocToOneInverseSide = $assoc->isToOne() && ! $assoc->isOwningSide(); + $isAssocFromOneEager = $assoc->isToOne() && $assoc->fetch === ClassMetadata::FETCH_EAGER; if (! ($isAssocFromOneEager || $isAssocToOneInverseSide)) { continue; } - if ((($assoc['type'] & ClassMetadata::TO_MANY) > 0) && $this->currentPersisterContext->handlesLimits) { + if ($assoc->isToMany() && $this->currentPersisterContext->handlesLimits) { continue; } - $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $eagerEntity = $this->em->getClassMetadata($assoc->targetEntity); if ($eagerEntity->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { continue; // now this is why you shouldn't use inheritance } $assocAlias = 'e' . ($eagerAliasCounter++); - $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc->targetEntity, $assocAlias, 'r', $assocField); foreach ($eagerEntity->fieldNames as $field) { $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias); @@ -1325,7 +1282,7 @@ protected function getSelectColumnsSQL() $eagerAssocField, $eagerAssoc, $eagerEntity, - $assocAlias + $assocAlias, ); if ($eagerAssocColumnSQL) { @@ -1336,26 +1293,29 @@ protected function getSelectColumnsSQL() $association = $assoc; $joinCondition = []; - if (isset($assoc['indexBy'])) { - $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc['indexBy']); + if ($assoc->isIndexed()) { + assert($assoc->isToMany()); + $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc->indexBy()); } - if (! $assoc['isOwningSide']) { - $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); - $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + if (! $assoc->isOwningSide()) { + $eagerEntity = $this->em->getClassMetadata($assoc->targetEntity); + $association = $eagerEntity->getAssociationMapping($assoc->mappedBy); } + assert($association->isToOneOwningSide()); + $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias); $joinTableName = $this->quoteStrategy->getTableName($eagerEntity, $this->platform); - if ($assoc['isOwningSide']) { - $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias); - $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']); + if ($assoc->isOwningSide()) { + $tableAlias = $this->getSQLTableAlias($association->targetEntity, $assocAlias); + $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association->joinColumns); - foreach ($association['joinColumns'] as $joinColumn) { + foreach ($association->joinColumns as $joinColumn) { $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); - $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity']) + $joinCondition[] = $this->getSQLTableAlias($association->sourceEntity) . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol; } @@ -1367,12 +1327,18 @@ protected function getSelectColumnsSQL() } else { $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN'; - foreach ($association['joinColumns'] as $joinColumn) { + foreach ($association->joinColumns as $joinColumn) { $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); - $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' - . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol; + $joinCondition[] = $this->getSQLTableAlias($association->sourceEntity, $assocAlias) . '.' . $sourceCol . ' = ' + . $this->getSQLTableAlias($association->targetEntity) . '.' . $targetCol; + } + + // Add filter SQL + $filterSql = $this->generateFilterConditionSQL($eagerEntity, $joinTableAlias); + if ($filterSql) { + $joinCondition[] = $filterSql; } } @@ -1386,32 +1352,28 @@ protected function getSelectColumnsSQL() return $this->currentPersisterContext->selectColumnListSql; } - /** - * Gets the SQL join fragment used when selecting entities from an association. - * - * @param string $field - * @param AssociationMapping $assoc - * @param string $alias - * - * @return string - */ - protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') - { - if (! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + /** Gets the SQL join fragment used when selecting entities from an association. */ + protected function getSelectColumnAssociationSQL( + string $field, + AssociationMapping $assoc, + ClassMetadata $class, + string $alias = 'r', + ): string { + if (! $assoc->isToOneOwningSide()) { return ''; } $columnList = []; - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); - $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); + $isIdentifier = isset($assoc->id) && $assoc->id === true; $sqlTableAlias = $this->getSQLTableAlias($class->name, ($alias === 'r' ? '' : $alias)); - foreach ($assoc['joinColumns'] as $joinColumn) { + foreach ($assoc->joinColumns as $joinColumn) { $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); - $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); - $type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $resultColumnName = $this->getSQLColumnAlias($joinColumn->name); + $type = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); - $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn['name'], $isIdentifier, $type); + $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn->name, $isIdentifier, $type); $columnList[] = sprintf('%s.%s AS %s', $sqlTableAlias, $quotedColumn, $resultColumnName); } @@ -1422,26 +1384,18 @@ protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $ /** * Gets the SQL join fragment used when selecting entities from a * many-to-many association. - * - * @phpstan-param AssociationMapping $manyToMany - * - * @return string */ - protected function getSelectManyToManyJoinSQL(array $manyToMany) + protected function getSelectManyToManyJoinSQL(AssociationMapping&ManyToManyAssociationMapping $manyToMany): string { $conditions = []; $association = $manyToMany; $sourceTableAlias = $this->getSQLTableAlias($this->class->name); - if (! $manyToMany['isOwningSide']) { - $targetEntity = $this->em->getClassMetadata($manyToMany['targetEntity']); - $association = $targetEntity->associationMappings[$manyToMany['mappedBy']]; - } - + $association = $this->em->getMetadataFactory()->getOwningSide($manyToMany); $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); - $joinColumns = $manyToMany['isOwningSide'] - ? $association['joinTable']['inverseJoinColumns'] - : $association['joinTable']['joinColumns']; + $joinColumns = $manyToMany->isOwningSide() + ? $association->joinTable->inverseJoinColumns + : $association->joinTable->joinColumns; foreach ($joinColumns as $joinColumn) { $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); @@ -1452,10 +1406,7 @@ protected function getSelectManyToManyJoinSQL(array $manyToMany) return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions); } - /** - * {@inheritDoc} - */ - public function getInsertSQL() + public function getInsertSQL(): string { if ($this->insertSql !== null) { return $this->insertSql; @@ -1480,7 +1431,7 @@ public function getInsertSQL() if ( isset($this->class->fieldNames[$column]) && isset($this->columnTypes[$this->class->fieldNames[$column]]) - && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion']) + && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]) ) { $type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]); $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); @@ -1503,10 +1454,9 @@ public function getInsertSQL() * Subclasses should override this method to alter or change the list of * columns placed in the INSERT statements used by the persister. * - * @return string[] The list of columns. * @phpstan-return list */ - protected function getInsertColumnList() + protected function getInsertColumnList(): array { $columns = []; @@ -1522,8 +1472,8 @@ protected function getInsertColumnList() if (isset($this->class->associationMappings[$name])) { $assoc = $this->class->associationMappings[$name]; - if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { - foreach ($assoc['joinColumns'] as $joinColumn) { + if ($assoc->isToOneOwningSide()) { + foreach ($assoc->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); } } @@ -1532,12 +1482,12 @@ protected function getInsertColumnList() } if (! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] !== $name) { - if (isset($this->class->fieldMappings[$name]['notInsertable'])) { + if (isset($this->class->fieldMappings[$name]->notInsertable)) { continue; } $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); - $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + $this->columnTypes[$name] = $this->class->fieldMappings[$name]->type; } } @@ -1547,14 +1497,10 @@ protected function getInsertColumnList() /** * Gets the SQL snippet of a qualified column name for the given field name. * - * @param string $field The field name. * @param ClassMetadata $class The class that declares this field. The table this class is * mapped to must own the column for the given field. - * @param string $alias - * - * @return string */ - protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + protected function getSelectColumnSQL(string $field, ClassMetadata $class, string $alias = 'r'): string { $root = $alias === 'r' ? '' : $alias; $tableAlias = $this->getSQLTableAlias($class->name, $root); @@ -1567,18 +1513,16 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r' } if ($columnAlias === null) { - $columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']); + $columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName); } $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field); - if (! empty($fieldMapping['enumType'])) { - $this->currentPersisterContext->rsm->addEnumResult($columnAlias, $fieldMapping['enumType']); + if (! empty($fieldMapping->enumType)) { + $this->currentPersisterContext->rsm->addEnumResult($columnAlias, $fieldMapping->enumType); } - if (isset($fieldMapping['requireSQLConversion'])) { - $type = Type::getType($fieldMapping['type']); - $sql = $type->convertToPHPValueSQL($sql, $this->platform); - } + $type = Type::getType($fieldMapping->type); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); return $sql . ' AS ' . $columnAlias; } @@ -1586,14 +1530,9 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r' /** * Gets the SQL table alias for the given class name. * - * @param string $className - * @param string $assocName - * - * @return string The SQL table alias. - * * @todo Reconsider. Binding table aliases to class names is not such a good idea. */ - protected function getSQLTableAlias($className, $assocName = '') + protected function getSQLTableAlias(string $className, string $assocName = ''): string { if ($assocName) { $className .= '#' . $assocName; @@ -1613,20 +1552,15 @@ protected function getSQLTableAlias($className, $assocName = '') /** * {@inheritDoc} */ - public function lock(array $criteria, $lockMode) + public function lock(array $criteria, LockMode|int $lockMode): void { - $lockSql = ''; $conditionSql = $this->getSelectConditionSQL($criteria); - switch ($lockMode) { - case LockMode::PESSIMISTIC_READ: - $lockSql = $this->getReadLockSQL($this->platform); - - break; - case LockMode::PESSIMISTIC_WRITE: - $lockSql = $this->getWriteLockSQL($this->platform); - break; - } + $lockSql = match ($lockMode) { + LockMode::PESSIMISTIC_READ => $this->getReadLockSQL($this->platform), + LockMode::PESSIMISTIC_WRITE => $this->getWriteLockSQL($this->platform), + default => '', + }; $lock = $this->getLockTablesSql($lockMode); $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' '; @@ -1643,38 +1577,22 @@ public function lock(array $criteria, $lockMode) /** * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister. * - * @param int|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. - * @phpstan-param LockMode::*|null $lockMode - * - * @return string + * @phpstan-param LockMode::* $lockMode */ - protected function getLockTablesSql($lockMode) + protected function getLockTablesSql(LockMode|int $lockMode): string { - if ($lockMode === null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9466', - 'Passing null as argument to %s is deprecated, pass LockMode::NONE instead.', - __METHOD__ - ); - - $lockMode = LockMode::NONE; - } - return $this->platform->appendLockHint( 'FROM ' . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' ' . $this->getSQLTableAlias($this->class->name), - $lockMode + $lockMode, ); } /** * Gets the Select Where Condition from a Criteria object. - * - * @return string */ - protected function getSelectConditionCriteriaSQL(Criteria $criteria) + protected function getSelectConditionCriteriaSQL(Criteria $criteria): string { $expression = $criteria->getWhereExpression(); @@ -1687,11 +1605,12 @@ protected function getSelectConditionCriteriaSQL(Criteria $criteria) return $visitor->dispatch($expression); } - /** - * {@inheritDoc} - */ - public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) - { + public function getSelectConditionStatementSQL( + string $field, + mixed $value, + AssociationMapping|null $assoc = null, + string|null $comparison = null, + ): string { $selectedColumns = []; $columns = $this->getSelectConditionStatementColumnSQL($field, $assoc); @@ -1706,8 +1625,8 @@ public function getSelectConditionStatementSQL($field, $value, $assoc = null, $c foreach ($columns as $column) { $placeholder = '?'; - if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) { - $type = Type::getType($this->class->fieldMappings[$field]['type']); + if (isset($this->class->fieldMappings[$field])) { + $type = Type::getType($this->class->fieldMappings[$field]->type); $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->platform); } @@ -1759,8 +1678,6 @@ public function getSelectConditionStatementSQL($field, $value, $assoc = null, $c /** * Builds the left-hand-side of a where condition statement. * - * @phpstan-param AssociationMapping|null $assoc - * * @return string[] * @phpstan-return list * @@ -1769,10 +1686,10 @@ public function getSelectConditionStatementSQL($field, $value, $assoc = null, $c */ private function getSelectConditionStatementColumnSQL( string $field, - ?array $assoc = null + AssociationMapping|null $assoc = null, ): array { if (isset($this->class->fieldMappings[$field])) { - $className = $this->class->fieldMappings[$field]['inherited'] ?? $this->class->name; + $className = $this->class->fieldMappings[$field]->inherited ?? $this->class->name; return [$this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)]; } @@ -1783,30 +1700,35 @@ private function getSelectConditionStatementColumnSQL( $columns = []; $class = $this->class; - if ($association['type'] === ClassMetadata::MANY_TO_MANY) { - if (! $association['isOwningSide']) { + if ($association->isManyToMany()) { + assert($assoc !== null); + if (! $association->isOwningSide()) { $association = $assoc; } + assert($association->isManyToManyOwningSide()); + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); - $joinColumns = $assoc['isOwningSide'] - ? $association['joinTable']['joinColumns'] - : $association['joinTable']['inverseJoinColumns']; + $joinColumns = $assoc->isOwningSide() + ? $association->joinTable->joinColumns + : $association->joinTable->inverseJoinColumns; foreach ($joinColumns as $joinColumn) { $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } } else { - if (! $association['isOwningSide']) { + if (! $association->isOwningSide()) { throw InvalidFindByCall::fromInverseSideUsage( $this->class->name, - $field + $field, ); } - $className = $association['inherited'] ?? $this->class->name; + assert($association->isToOneOwningSide()); + + $className = $association->inherited ?? $this->class->name; - foreach ($association['joinColumns'] as $joinColumn) { + foreach ($association->joinColumns as $joinColumn) { $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); } } @@ -1832,13 +1754,9 @@ private function getSelectConditionStatementColumnSQL( * Subclasses are supposed to override this method if they intend to change * or alter the criteria by which entities are selected. * - * @param AssociationMapping|null $assoc * @phpstan-param array $criteria - * @phpstan-param array|null $assoc - * - * @return string */ - protected function getSelectConditionSQL(array $criteria, $assoc = null) + protected function getSelectConditionSQL(array $criteria, AssociationMapping|null $assoc = null): string { $conditions = []; @@ -1852,8 +1770,13 @@ protected function getSelectConditionSQL(array $criteria, $assoc = null) /** * {@inheritDoc} */ - public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) - { + public function getOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array { + assert($assoc instanceof OneToManyAssociationMapping); $this->switchPersisterContext($offset, $limit); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); @@ -1861,44 +1784,41 @@ public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = nu return $this->loadArrayFromResult($assoc, $stmt); } - /** - * {@inheritDoc} - */ - public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection) - { + public function loadOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + PersistentCollection $collection, + ): mixed { + assert($assoc instanceof OneToManyAssociationMapping); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); return $this->loadCollectionFromStatement($assoc, $stmt, $collection); } - /** - * Builds criteria and execute SQL statement to fetch the one to many entities from. - * - * @param object $sourceEntity - * @phpstan-param AssociationMapping $assoc - */ + /** Builds criteria and execute SQL statement to fetch the one to many entities from. */ private function getOneToManyStatement( - array $assoc, - $sourceEntity, - ?int $offset = null, - ?int $limit = null + OneToManyAssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, ): Result { $this->switchPersisterContext($offset, $limit); $criteria = []; $parameters = []; - $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']]; - $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); - $tableAlias = $this->getSQLTableAlias($owningAssoc['inherited'] ?? $this->class->name); + $owningAssoc = $this->class->associationMappings[$assoc->mappedBy]; + $sourceClass = $this->em->getClassMetadata($assoc->sourceEntity); + $tableAlias = $this->getSQLTableAlias($owningAssoc->inherited ?? $this->class->name); + assert($owningAssoc->isManyToOne()); - foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); if (isset($sourceClass->associationMappings[$field])) { $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); - $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]->targetEntity)->identifier[0]]; } $criteria[$tableAlias . '.' . $targetKeyColumn] = $value; @@ -1931,7 +1851,7 @@ private function getOneToManyStatement( /** * {@inheritDoc} */ - public function expandParameters($criteria) + public function expandParameters(array $criteria): array { $params = []; $types = []; @@ -1941,7 +1861,7 @@ public function expandParameters($criteria) continue; // skip null values. } - $types = array_merge($types, $this->getTypes($field, $value, $this->class)); + $types = [...$types, ...$this->getTypes($field, $value, $this->class)]; $params = array_merge($params, $this->getValues($value)); } @@ -1958,7 +1878,7 @@ public function expandParameters($criteria) * - class to which the field belongs to * * @return mixed[][] - * @phpstan-return array{0: array, 1: list} + * @phpstan-return array{0: array, 1: list} */ private function expandToManyParameters(array $criteria): array { @@ -1970,7 +1890,7 @@ private function expandToManyParameters(array $criteria): array continue; // skip null values. } - $types = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class'])); + $types = [...$types, ...$this->getTypes($criterion['field'], $criterion['value'], $criterion['class'])]; $params = array_merge($params, $this->getValues($criterion['value'])); } @@ -1980,35 +1900,31 @@ private function expandToManyParameters(array $criteria): array /** * Infers field types to be used by parameter type casting. * - * @param mixed $value - * - * @return int[]|null[]|string[] - * @phpstan-return list + * @return list + * @phpstan-return list * * @throws QueryException */ - private function getTypes(string $field, $value, ClassMetadata $class): array + private function getTypes(string $field, mixed $value, ClassMetadata $class): array { $types = []; switch (true) { case isset($class->fieldMappings[$field]): - $types = array_merge($types, [$class->fieldMappings[$field]['type']]); + $types = array_merge($types, [$class->fieldMappings[$field]->type]); break; case isset($class->associationMappings[$field]): - $assoc = $class->associationMappings[$field]; - $class = $this->em->getClassMetadata($assoc['targetEntity']); - - if (! $assoc['isOwningSide']) { - $assoc = $class->associationMappings[$assoc['mappedBy']]; - $class = $this->em->getClassMetadata($assoc['targetEntity']); + $assoc = $this->em->getMetadataFactory()->getOwningSide($class->associationMappings[$field]); + $class = $this->em->getClassMetadata($assoc->targetEntity); + + if ($assoc->isManyToManyOwningSide()) { + $columns = $assoc->relationToTargetKeyColumns; + } else { + assert($assoc->isToOneOwningSide()); + $columns = $assoc->sourceToTargetKeyColumns; } - $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY - ? $assoc['relationToTargetKeyColumns'] - : $assoc['sourceToTargetKeyColumns']; - foreach ($columns as $column) { $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em); } @@ -2016,29 +1932,37 @@ private function getTypes(string $field, $value, ClassMetadata $class): array break; default: - $types[] = null; + $types[] = ParameterType::STRING; break; } if (is_array($value)) { - return array_map(static function ($type) { - $type = Type::getType($type); - - return $type->getBindingType() + Connection::ARRAY_PARAM_OFFSET; - }, $types); + return array_map($this->getArrayBindingType(...), $types); } return $types; } + /** @phpstan-return ArrayParameterType::* */ + private function getArrayBindingType(ParameterType|int|string $type): ArrayParameterType|int + { + if (! $type instanceof ParameterType) { + $type = Type::getType((string) $type)->getBindingType(); + } + + return match ($type) { + ParameterType::STRING => ArrayParameterType::STRING, + ParameterType::INTEGER => ArrayParameterType::INTEGER, + ParameterType::ASCII => ArrayParameterType::ASCII, + }; + } + /** * Retrieves the parameters that identifies a value. * - * @param mixed $value - * * @return mixed[] */ - private function getValues($value): array + private function getValues(mixed $value): array { if (is_array($value)) { $newValue = []; @@ -2056,11 +1980,9 @@ private function getValues($value): array /** * Retrieves an individual parameter value. * - * @param mixed $value - * * @phpstan-return list */ - private function getIndividualValue($value): array + private function getIndividualValue(mixed $value): array { if (! is_object($value)) { return [$value]; @@ -2091,10 +2013,7 @@ private function getIndividualValue($value): array return [$this->em->getUnitOfWork()->getSingleIdentifierValue($value)]; } - /** - * {@inheritDoc} - */ - public function exists($entity, ?Criteria $extraConditions = null) + public function exists(object $entity, Criteria|null $extraConditions = null): bool { $criteria = $this->class->getIdentifierValues($entity); @@ -2114,8 +2033,8 @@ public function exists($entity, ?Criteria $extraConditions = null) $sql .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions); [$criteriaParams, $criteriaTypes] = $this->expandCriteriaParameters($extraConditions); - $params = array_merge($params, $criteriaParams); - $types = array_merge($types, $criteriaTypes); + $params = [...$params, ...$criteriaParams]; + $types = [...$types, ...$criteriaTypes]; } $filterSql = $this->generateFilterConditionSQL($this->class, $alias); @@ -2129,16 +2048,15 @@ public function exists($entity, ?Criteria $extraConditions = null) /** * Generates the appropriate join SQL for the given join column. * - * @param array[] $joinColumns The join columns definition of an association. - * @phpstan-param array> $joinColumns + * @param list $joinColumns The join columns definition of an association. * * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. */ - protected function getJoinSQLForJoinColumns($joinColumns) + protected function getJoinSQLForJoinColumns(array $joinColumns): string { // if one of the join columns is nullable, return left join foreach ($joinColumns as $joinColumn) { - if (! isset($joinColumn['nullable']) || $joinColumn['nullable']) { + if (! isset($joinColumn->nullable) || $joinColumn->nullable) { return 'LEFT JOIN'; } } @@ -2146,12 +2064,7 @@ protected function getJoinSQLForJoinColumns($joinColumns) return 'INNER JOIN'; } - /** - * @param string $columnName - * - * @return string - */ - public function getSQLColumnAlias($columnName) + public function getSQLColumnAlias(string $columnName): string { return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform); } @@ -2164,7 +2077,7 @@ public function getSQLColumnAlias($columnName) * * @return string The SQL query part to add to a query. */ - protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string { $filterClauses = []; @@ -2184,13 +2097,8 @@ protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targ * Switches persister context according to current query offset/limits * * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved - * - * @param int|null $offset - * @param int|null $limit - * - * @return void */ - protected function switchPersisterContext($offset, $limit) + protected function switchPersisterContext(int|null $offset, int|null $limit): void { if ($offset === null && $limit === null) { $this->currentPersisterContext = $this->noLimitsContext; @@ -2216,7 +2124,7 @@ static function ($fieldName) use ($class, $entityManager): string { return $types[0]; }, - $class->identifier + $class->identifier, ); } } diff --git a/src/Persisters/Entity/CachedPersisterContext.php b/src/Persisters/Entity/CachedPersisterContext.php index 000525bec0e..03d053b220c 100644 --- a/src/Persisters/Entity/CachedPersisterContext.php +++ b/src/Persisters/Entity/CachedPersisterContext.php @@ -18,65 +18,43 @@ */ class CachedPersisterContext { - /** - * Metadata object that describes the mapping of the mapped entity class. - * - * @var \Doctrine\ORM\Mapping\ClassMetadata - */ - public $class; - - /** - * ResultSetMapping that is used for all queries. Is generated lazily once per request. - * - * @var ResultSetMapping - */ - public $rsm; - /** * The SELECT column list SQL fragment used for querying entities by this persister. * This SQL fragment is only generated once per request, if at all. - * - * @var string|null */ - public $selectColumnListSql; + public string|null $selectColumnListSql = null; /** * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. - * - * @var string */ - public $selectJoinSql; + public string|null $selectJoinSql = null; /** * Counter for creating unique SQL table and column aliases. - * - * @var int */ - public $sqlAliasCounter = 0; + public int $sqlAliasCounter = 0; /** * Map from class names (FQCN) to the corresponding generated SQL table aliases. * * @var array */ - public $sqlTableAliases = []; - - /** - * Whether this persistent context is considering limit operations applied to the selection queries - * - * @var bool - */ - public $handlesLimits; + public array $sqlTableAliases = []; - /** @param bool $handlesLimits */ public function __construct( - ClassMetadata $class, - ResultSetMapping $rsm, - $handlesLimits + /** + * Metadata object that describes the mapping of the mapped entity class. + */ + public ClassMetadata $class, + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + */ + public ResultSetMapping $rsm, + /** + * Whether this persistent context is considering limit operations applied to the selection queries + */ + public bool $handlesLimits, ) { - $this->class = $class; - $this->rsm = $rsm; - $this->handlesLimits = (bool) $handlesLimits; } } diff --git a/src/Persisters/Entity/EntityPersister.php b/src/Persisters/Entity/EntityPersister.php index 4562feedf73..1c4da2f4c04 100644 --- a/src/Persisters/Entity/EntityPersister.php +++ b/src/Persisters/Entity/EntityPersister.php @@ -5,7 +5,10 @@ namespace Doctrine\ORM\Persisters\Entity; use Doctrine\Common\Collections\Criteria; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\ParameterType; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\PersistentCollection; @@ -14,122 +17,97 @@ /** * Entity persister interface * Define the behavior that should be implemented by all entity persisters. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ interface EntityPersister { - /** @return ClassMetadata */ - public function getClassMetadata(); + public function getClassMetadata(): ClassMetadata; /** * Gets the ResultSetMapping used for hydration. - * - * @return ResultSetMapping */ - public function getResultSetMapping(); + public function getResultSetMapping(): ResultSetMapping; /** * Get all queued inserts. * * @return object[] */ - public function getInserts(); + public function getInserts(): array; /** * Gets the INSERT SQL used by the persister to persist a new entity. * - * @return string - * * @TODO It should not be here. * But its necessary since JoinedSubclassPersister#executeInserts invoke the root persister. */ - public function getInsertSQL(); + public function getInsertSQL(): string; /** * Gets the SELECT SQL to select one or more entities by a set of field criteria. * * @param mixed[]|Criteria $criteria - * @param int|null $lockMode - * @param int|null $limit - * @param int|null $offset * @param mixed[]|null $orderBy * @phpstan-param AssociationMapping|null $assoc * @phpstan-param LockMode::*|null $lockMode - * - * @return string */ - public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null); + public function getSelectSQL( + array|Criteria $criteria, + AssociationMapping|null $assoc = null, + LockMode|int|null $lockMode = null, + int|null $limit = null, + int|null $offset = null, + array|null $orderBy = null, + ): string; /** * Get the COUNT SQL to count entities (optionally based on a criteria) * * @param mixed[]|Criteria $criteria - * - * @return string */ - public function getCountSQL($criteria = []); + public function getCountSQL(array|Criteria $criteria = []): string; /** * Expands the parameters from the given criteria and use the correct binding types if found. * * @param string[] $criteria * - * @phpstan-return array{list, list} + * @phpstan-return array{list, list} */ - public function expandParameters($criteria); + public function expandParameters(array $criteria): array; /** * Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it. * - * @phpstan-return array{list, list} + * @phpstan-return array{list, list} */ - public function expandCriteriaParameters(Criteria $criteria); + public function expandCriteriaParameters(Criteria $criteria): array; - /** - * Gets the SQL WHERE condition for matching a field with a given value. - * - * @param string $field - * @param mixed $value - * @param AssociationMapping|null $assoc - * @param string|null $comparison - * - * @return string - */ - public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null); + /** Gets the SQL WHERE condition for matching a field with a given value. */ + public function getSelectConditionStatementSQL( + string $field, + mixed $value, + AssociationMapping|null $assoc = null, + string|null $comparison = null, + ): string; /** * Adds an entity to the queued insertions. * The entity remains queued until {@link executeInserts} is invoked. - * - * @param object $entity The entity to queue for insertion. - * - * @return void */ - public function addInsert($entity); + public function addInsert(object $entity): void; /** * Executes all queued entity insertions. * * If no inserts are queued, invoking this method is a NOOP. - * - * @phpstan-return void|list Returning an array of generated post-insert IDs is deprecated, implementations - * should call UnitOfWork::assignPostInsertId() and return void. */ - public function executeInserts(); + public function executeInserts(): void; /** * Updates a managed entity. The entity is updated according to its current changeset * in the running UnitOfWork. If there is no changeset, nothing is updated. - * - * @param object $entity The entity to update. - * - * @return void */ - public function update($entity); + public function update(object $entity): void; /** * Deletes a managed entity. @@ -139,20 +117,18 @@ public function update($entity); * * Subclasses may override this method to customize the semantics of entity deletion. * - * @param object $entity The entity to delete. - * * @return bool TRUE if the entity got deleted in the database, FALSE otherwise. */ - public function delete($entity); + public function delete(object $entity): bool; /** * Count entities (optionally filtered by a criteria) * - * @param mixed[]|Criteria $criteria + * @param mixed[]|Criteria $criteria * - * @return int + * @phpstan-return 0|positive-int */ - public function count($criteria = []); + public function count(array|Criteria $criteria = []): int; /** * Gets the name of the table that owns the column the given field is mapped to. @@ -160,12 +136,8 @@ public function count($criteria = []); * The default implementation in BasicEntityPersister always returns the name * of the table the entity type of this persister is mapped to, since an entity * is always persisted to a single table with a BasicEntityPersister. - * - * @param string $fieldName The field name. - * - * @return string The table name. */ - public function getOwningTable($fieldName); + public function getOwningTable(string $fieldName): string; /** * Loads an entity by a list of field criteria. @@ -174,9 +146,9 @@ public function getOwningTable($fieldName); * @param object|null $entity The entity to load the data into. If not specified, * a new entity is created. * @param AssociationMapping|null $assoc The association that connects the entity - * to load to another entity, if any. + * to load to another entity, if any. * @param mixed[] $hints Hints for entity creation. - * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants * or NULL if no specific lock mode should be used * for loading the entity. * @param int|null $limit Limit number of results. @@ -192,13 +164,13 @@ public function getOwningTable($fieldName); */ public function load( array $criteria, - $entity = null, - $assoc = null, + object|null $entity = null, + AssociationMapping|null $assoc = null, array $hints = [], - $lockMode = null, - $limit = null, - ?array $orderBy = null - ); + LockMode|int|null $lockMode = null, + int|null $limit = null, + array|null $orderBy = null, + ): object|null; /** * Loads an entity by identifier. @@ -210,120 +182,119 @@ public function load( * * @todo Check parameters */ - public function loadById(array $identifier, $entity = null); + public function loadById(array $identifier, object|null $entity = null): object|null; /** * Loads an entity of this persister's mapped class as part of a single-valued * association from another entity. * - * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). + * @param AssociationMapping $assoc The association to load. + * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). * @phpstan-param array $identifier The identifier of the entity to load. Must be provided if * the association to load represents the owning side, otherwise * the identifier is derived from the $sourceEntity. - * @phpstan-param AssociationMapping $assoc The association to load. * - * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * @return object|null The loaded and managed entity instance or NULL if the entity can not be found. * * @throws MappingException */ - public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = []); + public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null; /** * Refreshes a managed entity. * - * @param object $entity The entity to refresh. - * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants - * or NULL if no specific lock mode should be used - * for refreshing the managed entity. + * @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * for refreshing the managed entity. * @phpstan-param array $id The identifier of the entity as an * associative array from column or * field names to values. * @phpstan-param LockMode::*|null $lockMode - * - * @return void */ - public function refresh(array $id, $entity, $lockMode = null); + public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void; /** * Loads Entities matching the given Criteria object. * * @return mixed[] */ - public function loadCriteria(Criteria $criteria); + public function loadCriteria(Criteria $criteria): array; /** * Loads a list of entities by a list of field criteria. * - * @param int|null $limit - * @param int|null $offset * @phpstan-param array|null $orderBy * @phpstan-param array $criteria + * + * @return mixed[] */ - public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null); + public function loadAll( + array $criteria = [], + array|null $orderBy = null, + int|null $limit = null, + int|null $offset = null, + ): array; /** * Gets (sliced or full) elements of the given collection. * - * @param object $sourceEntity - * @param int|null $offset - * @param int|null $limit - * @phpstan-param AssociationMapping $assoc - * * @return mixed[] */ - public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null); + public function getManyToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array; /** * Loads a collection of entities of a many-to-many association. * + * @param AssociationMapping $assoc The association mapping of the association being loaded. * @param object $sourceEntity The entity that owns the collection. * @param PersistentCollection $collection The collection to fill. - * @phpstan-param AssociationMapping $assoc The association mapping of the association being loaded. * * @return mixed[] */ - public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + public function loadManyToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + PersistentCollection $collection, + ): array; /** * Loads a collection of entities in a one-to-many association. * - * @param object $sourceEntity - * @param PersistentCollection $collection The collection to load/fill. - * @phpstan-param AssociationMapping $assoc - * - * @return mixed + * @param PersistentCollection $collection The collection to load/fill. */ - public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + public function loadOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + PersistentCollection $collection, + ): mixed; /** * Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode. * - * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants. * @phpstan-param array $criteria * @phpstan-param LockMode::* $lockMode - * - * @return void */ - public function lock(array $criteria, $lockMode); + public function lock(array $criteria, LockMode|int $lockMode): void; /** * Returns an array with (sliced or full list) of elements in the specified collection. * - * @param object $sourceEntity - * @param int|null $offset - * @param int|null $limit - * @phpstan-param AssociationMapping $assoc - * * @return mixed[] */ - public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null); + public function getOneToManyCollection( + AssociationMapping $assoc, + object $sourceEntity, + int|null $offset = null, + int|null $limit = null, + ): array; /** * Checks whether the given managed entity exists in the database. - * - * @param object $entity - * - * @return bool TRUE if the entity exists in the database, FALSE otherwise. */ - public function exists($entity, ?Criteria $extraConditions = null); + public function exists(object $entity, Criteria|null $extraConditions = null): bool; } diff --git a/src/Persisters/Entity/JoinedSubclassPersister.php b/src/Persisters/Entity/JoinedSubclassPersister.php index bf922e1248c..67c277be811 100644 --- a/src/Persisters/Entity/JoinedSubclassPersister.php +++ b/src/Persisters/Entity/JoinedSubclassPersister.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Internal\SQLResultCasing; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Utility\LockSqlHelper; use Doctrine\ORM\Utility\PersisterHelper; @@ -36,19 +37,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister * * @phpstan-var array */ - private $owningTableMap = []; + private array $owningTableMap = []; /** * Map of table to quoted table names. * * @phpstan-var array */ - private $quotedTableMap = []; + private array $quotedTableMap = []; - /** - * {@inheritDoc} - */ - protected function getDiscriminatorColumnTableName() + protected function getDiscriminatorColumnTableName(): string { $class = $this->class->name !== $this->class->rootEntityName ? $this->em->getClassMetadata($this->class->rootEntityName) @@ -63,8 +61,8 @@ protected function getDiscriminatorColumnTableName() */ private function getVersionedClassMetadata(): ClassMetadata { - if (isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) { - $definingClassName = $this->class->fieldMappings[$this->class->versionField]['inherited']; + if (isset($this->class->fieldMappings[$this->class->versionField]->inherited)) { + $definingClassName = $this->class->fieldMappings[$this->class->versionField]->inherited; return $this->em->getClassMetadata($definingClassName); } @@ -74,30 +72,20 @@ private function getVersionedClassMetadata(): ClassMetadata /** * Gets the name of the table that owns the column the given field is mapped to. - * - * @param string $fieldName - * - * @return string */ - public function getOwningTable($fieldName) + public function getOwningTable(string $fieldName): string { if (isset($this->owningTableMap[$fieldName])) { return $this->owningTableMap[$fieldName]; } - switch (true) { - case isset($this->class->associationMappings[$fieldName]['inherited']): - $cm = $this->em->getClassMetadata($this->class->associationMappings[$fieldName]['inherited']); - break; - - case isset($this->class->fieldMappings[$fieldName]['inherited']): - $cm = $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]['inherited']); - break; - - default: - $cm = $this->class; - break; - } + $cm = match (true) { + isset($this->class->associationMappings[$fieldName]->inherited) + => $this->em->getClassMetadata($this->class->associationMappings[$fieldName]->inherited), + isset($this->class->fieldMappings[$fieldName]->inherited) + => $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]->inherited), + default => $this->class, + }; $tableName = $cm->getTableName(); $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform); @@ -108,10 +96,7 @@ public function getOwningTable($fieldName) return $tableName; } - /** - * {@inheritDoc} - */ - public function executeInserts() + public function executeInserts(): void { if (! $this->queuedInserts) { return; @@ -199,10 +184,7 @@ public function executeInserts() $this->queuedInserts = []; } - /** - * {@inheritDoc} - */ - public function update($entity) + public function update(object $entity): void { $updateData = $this->prepareUpdateData($entity); @@ -237,10 +219,7 @@ public function update($entity) } } - /** - * {@inheritDoc} - */ - public function delete($entity) + public function delete(object $entity): bool { $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); $id = array_combine($this->class->getIdentifierColumnNames(), $identifier); @@ -248,45 +227,28 @@ public function delete($entity) $this->deleteJoinTableRecords($identifier, $types); - // If the database platform supports FKs, just - // delete the row from the root table. Cascades do the rest. - // @phpstan-ignore method.deprecated - if ($this->platform->supportsForeignKeyConstraints()) { - $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); - $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); - $rootTypes = $this->getClassIdentifiersTypes($rootClass); - - return (bool) $this->conn->delete($rootTable, $id, $rootTypes); - } - - // Delete from all tables individually, starting from this class' table up to the root table. - $rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform); - $rootTypes = $this->getClassIdentifiersTypes($this->class); - - $affectedRows = $this->conn->delete($rootTable, $id, $rootTypes); + // Delete the row from the root table. Cascades do the rest. + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); + $rootTypes = $this->getClassIdentifiersTypes($rootClass); - foreach ($this->class->parentClasses as $parentClass) { - $parentMetadata = $this->em->getClassMetadata($parentClass); - $parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform); - $parentTypes = $this->getClassIdentifiersTypes($parentMetadata); - - $this->conn->delete($parentTable, $id, $parentTypes); - } - - return (bool) $affectedRows; + return (bool) $this->conn->delete($rootTable, $id, $rootTypes); } - /** - * {@inheritDoc} - */ - public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null) - { + public function getSelectSQL( + array|Criteria $criteria, + AssociationMapping|null $assoc = null, + LockMode|int|null $lockMode = null, + int|null $limit = null, + int|null $offset = null, + array|null $orderBy = null, + ): string { $this->switchPersisterContext($offset, $limit); $baseTableAlias = $this->getSQLTableAlias($this->class->name); $joinSql = $this->getJoinSql($baseTableAlias); - if ($assoc !== null && $assoc['type'] === ClassMetadata::MANY_TO_MANY) { + if ($assoc !== null && $assoc->isManyToMany()) { $joinSql .= $this->getSelectManyToManyJoinSQL($assoc); } @@ -296,7 +258,7 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit $filterSql = $this->generateFilterConditionSQL( $this->em->getClassMetadata($this->class->rootEntityName), - $this->getSQLTableAlias($this->class->rootEntityName) + $this->getSQLTableAlias($this->class->rootEntityName), ); // If the current class in the root entity, add the filters if ($filterSql) { @@ -307,8 +269,8 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit $orderBySql = ''; - if ($assoc !== null && isset($assoc['orderBy'])) { - $orderBy = $assoc['orderBy']; + if ($assoc !== null && $assoc->isOrdered()) { + $orderBy = $assoc->orderBy(); } if ($orderBy) { @@ -343,10 +305,7 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql; } - /** - * {@inheritDoc} - */ - public function getCountSQL($criteria = []) + public function getCountSQL(array|Criteria $criteria = []): string { $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); $baseTableAlias = $this->getSQLTableAlias($this->class->name); @@ -370,10 +329,7 @@ public function getCountSQL($criteria = []) . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); } - /** - * {@inheritDoc} - */ - protected function getLockTablesSql($lockMode) + protected function getLockTablesSql(LockMode|int $lockMode): string { $joinSql = ''; $identifierColumns = $this->class->getIdentifierColumnNames(); @@ -398,10 +354,8 @@ protected function getLockTablesSql($lockMode) /** * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. - * - * @return string */ - protected function getSelectColumnsSQL() + protected function getSelectColumnsSQL(): string { // Create the column list fragment only once if ($this->currentPersisterContext->selectColumnListSql !== null) { @@ -410,8 +364,8 @@ protected function getSelectColumnsSQL() $columnList = []; $discrColumn = $this->class->getDiscriminatorColumn(); - $discrColumnName = $discrColumn['name']; - $discrColumnType = $discrColumn['type']; + $discrColumnName = $discrColumn->name; + $discrColumnType = $discrColumn->type; $baseTableAlias = $this->getSQLTableAlias($this->class->name); $resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName); @@ -421,8 +375,8 @@ protected function getSelectColumnsSQL() // Add regular columns foreach ($this->class->fieldMappings as $fieldName => $mapping) { - $class = isset($mapping['inherited']) - ? $this->em->getClassMetadata($mapping['inherited']) + $class = isset($mapping->inherited) + ? $this->em->getClassMetadata($mapping->inherited) : $this->class; $columnList[] = $this->getSelectColumnSQL($fieldName, $class); @@ -430,22 +384,22 @@ protected function getSelectColumnsSQL() // Add foreign key columns foreach ($this->class->associationMappings as $mapping) { - if (! $mapping['isOwningSide'] || ! ($mapping['type'] & ClassMetadata::TO_ONE)) { + if (! $mapping->isToOneOwningSide()) { continue; } - $tableAlias = isset($mapping['inherited']) - ? $this->getSQLTableAlias($mapping['inherited']) + $tableAlias = isset($mapping->inherited) + ? $this->getSQLTableAlias($mapping->inherited) : $baseTableAlias; - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); - foreach ($mapping['joinColumns'] as $joinColumn) { + foreach ($mapping->joinColumns as $joinColumn) { $columnList[] = $this->getSelectJoinColumnSQL( $tableAlias, - $joinColumn['name'], + $joinColumn->name, $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform), - PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em) + PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em), ); } } @@ -464,7 +418,7 @@ protected function getSelectColumnsSQL() // Add subclass columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited'])) { + if (isset($mapping->inherited)) { continue; } @@ -473,22 +427,18 @@ protected function getSelectColumnsSQL() // Add join columns (foreign keys) foreach ($subClass->associationMappings as $mapping) { - if ( - ! $mapping['isOwningSide'] - || ! ($mapping['type'] & ClassMetadata::TO_ONE) - || isset($mapping['inherited']) - ) { + if (! $mapping->isToOneOwningSide() || isset($mapping->inherited)) { continue; } - $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping->targetEntity); - foreach ($mapping['joinColumns'] as $joinColumn) { + foreach ($mapping->joinColumns as $joinColumn) { $columnList[] = $this->getSelectJoinColumnSQL( $tableAlias, - $joinColumn['name'], + $joinColumn->name, $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform), - PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em) + PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em), ); } } @@ -502,7 +452,7 @@ protected function getSelectColumnsSQL() /** * {@inheritDoc} */ - protected function getInsertColumnList() + protected function getInsertColumnList(): array { // Identifier columns must always come first in the column list of subclasses. $columns = $this->class->parentClasses @@ -511,20 +461,20 @@ protected function getInsertColumnList() foreach ($this->class->reflFields as $name => $field) { if ( - isset($this->class->fieldMappings[$name]['inherited']) - && ! isset($this->class->fieldMappings[$name]['id']) - || isset($this->class->associationMappings[$name]['inherited']) + isset($this->class->fieldMappings[$name]->inherited) + && ! isset($this->class->fieldMappings[$name]->id) + || isset($this->class->associationMappings[$name]->inherited) || ($this->class->isVersioned && $this->class->versionField === $name) || isset($this->class->embeddedClasses[$name]) - || isset($this->class->fieldMappings[$name]['notInsertable']) + || isset($this->class->fieldMappings[$name]->notInsertable) ) { continue; } if (isset($this->class->associationMappings[$name])) { $assoc = $this->class->associationMappings[$name]; - if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { - foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { + if ($assoc->isToOneOwningSide()) { + foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { $columns[] = $sourceCol; } } @@ -533,13 +483,13 @@ protected function getInsertColumnList() ! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] !== $name ) { $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); - $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + $this->columnTypes[$name] = $this->class->fieldMappings[$name]->type; } } // Add discriminator column if it is the topmost class. if ($this->class->name === $this->class->rootEntityName) { - $columns[] = $this->class->getDiscriminatorColumn()['name']; + $columns[] = $this->class->getDiscriminatorColumn()->name; } return $columns; @@ -548,12 +498,12 @@ protected function getInsertColumnList() /** * {@inheritDoc} */ - protected function assignDefaultVersionAndUpsertableValues($entity, array $id) + protected function assignDefaultVersionAndUpsertableValues(object $entity, array $id): void { $values = $this->fetchVersionAndNotUpsertableValues($this->getVersionedClassMetadata(), $id); foreach ($values as $field => $value) { - $value = Type::getType($this->class->fieldMappings[$field]['type'])->convertToPHPValue($value, $this->platform); + $value = Type::getType($this->class->fieldMappings[$field]->type)->convertToPHPValue($value, $this->platform); $this->class->setFieldValue($entity, $field, $value); } @@ -562,16 +512,16 @@ protected function assignDefaultVersionAndUpsertableValues($entity, array $id) /** * {@inheritDoc} */ - protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id) + protected function fetchVersionAndNotUpsertableValues(ClassMetadata $versionedClass, array $id): mixed { $columnNames = []; foreach ($this->class->fieldMappings as $key => $column) { $class = null; if ($this->class->isVersioned && $key === $versionedClass->versionField) { $class = $versionedClass; - } elseif (isset($column['generated'])) { - $class = isset($column['inherited']) - ? $this->em->getClassMetadata($column['inherited']) + } elseif (isset($column->generated)) { + $class = isset($column->inherited) + ? $this->em->getClassMetadata($column->inherited) : $this->class; } else { continue; @@ -597,7 +547,7 @@ protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id $values = $this->conn->fetchNumeric( $sql, array_values($flatId), - $this->extractIdentifierTypes($id, $versionedClass) + $this->extractIdentifierTypes($id, $versionedClass), ); if ($values === false) { diff --git a/src/Persisters/Entity/SingleTablePersister.php b/src/Persisters/Entity/SingleTablePersister.php index 2d5325e99a8..4a4d9998266 100644 --- a/src/Persisters/Entity/SingleTablePersister.php +++ b/src/Persisters/Entity/SingleTablePersister.php @@ -6,6 +6,7 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Internal\SQLResultCasing; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Utility\PersisterHelper; @@ -14,6 +15,7 @@ use function array_map; use function array_unshift; use function implode; +use function strval; /** * Persister for entities that participate in a hierarchy mapped with the @@ -25,19 +27,14 @@ class SingleTablePersister extends AbstractEntityInheritancePersister { use SQLResultCasing; - /** - * {@inheritDoc} - */ - protected function getDiscriminatorColumnTableName() + protected function getDiscriminatorColumnTableName(): string { return $this->class->getTableName(); } - /** - * {@inheritDoc} - */ - protected function getSelectColumnsSQL() + protected function getSelectColumnsSQL(): string { + $columnList = []; if ($this->currentPersisterContext->selectColumnListSql !== null) { return $this->currentPersisterContext->selectColumnListSql; } @@ -49,8 +46,8 @@ protected function getSelectColumnsSQL() // Append discriminator column $discrColumn = $this->class->getDiscriminatorColumn(); - $discrColumnName = $discrColumn['name']; - $discrColumnType = $discrColumn['type']; + $discrColumnName = $discrColumn->name; + $discrColumnType = $discrColumn->type; $columnList[] = $tableAlias . '.' . $discrColumnName; @@ -65,7 +62,7 @@ protected function getSelectColumnsSQL() // Regular columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited'])) { + if (isset($mapping->inherited)) { continue; } @@ -74,18 +71,18 @@ protected function getSelectColumnsSQL() // Foreign key columns foreach ($subClass->associationMappings as $assoc) { - if (! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE) || isset($assoc['inherited'])) { + if (! $assoc->isToOneOwningSide() || isset($assoc->inherited)) { continue; } - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); - foreach ($assoc['joinColumns'] as $joinColumn) { + foreach ($assoc->joinColumns as $joinColumn) { $columnList[] = $this->getSelectJoinColumnSQL( $tableAlias, - $joinColumn['name'], + $joinColumn->name, $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform), - PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em) + PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em), ); } } @@ -99,20 +96,17 @@ protected function getSelectColumnsSQL() /** * {@inheritDoc} */ - protected function getInsertColumnList() + protected function getInsertColumnList(): array { $columns = parent::getInsertColumnList(); // Add discriminator column to the INSERT SQL - $columns[] = $this->class->getDiscriminatorColumn()['name']; + $columns[] = $this->class->getDiscriminatorColumn()->name; return $columns; } - /** - * {@inheritDoc} - */ - protected function getSQLTableAlias($className, $assocName = '') + protected function getSQLTableAlias(string $className, string $assocName = ''): string { return parent::getSQLTableAlias($this->class->rootEntityName, $assocName); } @@ -120,7 +114,7 @@ protected function getSQLTableAlias($className, $assocName = '') /** * {@inheritDoc} */ - protected function getSelectConditionSQL(array $criteria, $assoc = null) + protected function getSelectConditionSQL(array $criteria, AssociationMapping|null $assoc = null): string { $conditionSql = parent::getSelectConditionSQL($criteria, $assoc); @@ -131,10 +125,7 @@ protected function getSelectConditionSQL(array $criteria, $assoc = null) return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); } - /** - * {@inheritDoc} - */ - protected function getSelectConditionCriteriaSQL(Criteria $criteria) + protected function getSelectConditionCriteriaSQL(Criteria $criteria): string { $conditionSql = parent::getSelectConditionCriteriaSQL($criteria); @@ -145,19 +136,18 @@ protected function getSelectConditionCriteriaSQL(Criteria $criteria) return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); } - /** @return string */ - protected function getSelectConditionDiscriminatorValueSQL() + protected function getSelectConditionDiscriminatorValueSQL(): string { - $values = array_map( - [$this->conn, 'quote'], - array_flip(array_intersect($this->class->discriminatorMap, $this->class->subClasses)) - ); + $values = array_map($this->conn->quote(...), array_map( + strval(...), + array_flip(array_intersect($this->class->discriminatorMap, $this->class->subClasses)), + )); if ($this->class->discriminatorValue !== null) { // discriminators can be 0 - array_unshift($values, $this->conn->quote($this->class->discriminatorValue)); + array_unshift($values, $this->conn->quote((string) $this->class->discriminatorValue)); } - $discColumnName = $this->class->getDiscriminatorColumn()['name']; + $discColumnName = $this->class->getDiscriminatorColumn()->name; $values = implode(', ', $values); $tableAlias = $this->getSQLTableAlias($this->class->name); @@ -165,10 +155,7 @@ protected function getSelectConditionDiscriminatorValueSQL() return $tableAlias . '.' . $discColumnName . ' IN (' . $values . ')'; } - /** - * {@inheritDoc} - */ - protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, string $targetTableAlias): string { // Ensure that the filters are applied to the root entity of the inheritance tree $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); diff --git a/src/Persisters/MatchingAssociationFieldRequiresObject.php b/src/Persisters/MatchingAssociationFieldRequiresObject.php index 210ff425b1d..4e7251ef228 100644 --- a/src/Persisters/MatchingAssociationFieldRequiresObject.php +++ b/src/Persisters/MatchingAssociationFieldRequiresObject.php @@ -16,7 +16,7 @@ public static function fromClassAndAssociation(string $class, string $associatio 'Cannot match on %s::%s with a non-object value. Matching objects by id is ' . 'not compatible with matching on an in-memory collection, which compares objects by reference.', $class, - $associationName + $associationName, )); } } diff --git a/src/Persisters/PersisterException.php b/src/Persisters/PersisterException.php index 3d4098f9d99..00164720d06 100644 --- a/src/Persisters/PersisterException.php +++ b/src/Persisters/PersisterException.php @@ -5,24 +5,19 @@ namespace Doctrine\ORM\Persisters; use Doctrine\ORM\Exception\ORMException; +use Exception; use function sprintf; -class PersisterException extends ORMException +class PersisterException extends Exception implements ORMException { - /** - * @param string $class - * @param string $associationName - * - * @return PersisterException - */ - public static function matchingAssocationFieldRequiresObject($class, $associationName) + public static function matchingAssocationFieldRequiresObject(string $class, string $associationName): PersisterException { return new self(sprintf( 'Cannot match on %s::%s with a non-object value. Matching objects by id is ' . 'not compatible with matching on an in-memory collection, which compares objects by reference.', $class, - $associationName + $associationName, )); } } diff --git a/src/Persisters/SqlExpressionVisitor.php b/src/Persisters/SqlExpressionVisitor.php index 4d1d935bf95..df60f6a01e2 100644 --- a/src/Persisters/SqlExpressionVisitor.php +++ b/src/Persisters/SqlExpressionVisitor.php @@ -12,7 +12,6 @@ use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; use RuntimeException; -use function defined; use function implode; use function in_array; use function is_object; @@ -22,24 +21,14 @@ */ class SqlExpressionVisitor extends ExpressionVisitor { - /** @var BasicEntityPersister */ - private $persister; - - /** @var ClassMetadata */ - private $classMetadata; - - public function __construct(BasicEntityPersister $persister, ClassMetadata $classMetadata) - { - $this->persister = $persister; - $this->classMetadata = $classMetadata; + public function __construct( + private readonly BasicEntityPersister $persister, + private readonly ClassMetadata $classMetadata, + ) { } - /** - * Converts a comparison expression into the target query language output. - * - * @return mixed - */ - public function walkComparison(Comparison $comparison) + /** Converts a comparison expression into the target query language output. */ + public function walkComparison(Comparison $comparison): string { $field = $comparison->getField(); $value = $comparison->getValue()->getValue(); // shortcut for walkValue() @@ -52,7 +41,7 @@ public function walkComparison(Comparison $comparison) ) { throw MatchingAssociationFieldRequiresObject::fromClassAndAssociation( $this->classMetadata->name, - $field + $field, ); } @@ -62,11 +51,9 @@ public function walkComparison(Comparison $comparison) /** * Converts a composite expression into the target query language output. * - * @return string - * * @throws RuntimeException */ - public function walkCompositeExpression(CompositeExpression $expr) + public function walkCompositeExpression(CompositeExpression $expr): string { $expressionList = []; @@ -74,29 +61,18 @@ public function walkCompositeExpression(CompositeExpression $expr) $expressionList[] = $this->dispatch($child); } - switch ($expr->getType()) { - case CompositeExpression::TYPE_AND: - return '(' . implode(' AND ', $expressionList) . ')'; - - case CompositeExpression::TYPE_OR: - return '(' . implode(' OR ', $expressionList) . ')'; - - default: - // Multiversion support for `doctrine/collections` before and after v2.1.0 - if (defined(CompositeExpression::class . '::TYPE_NOT') && $expr->getType() === CompositeExpression::TYPE_NOT) { - return 'NOT (' . $expressionList[0] . ')'; - } - - throw new RuntimeException('Unknown composite ' . $expr->getType()); - } + return match ($expr->getType()) { + CompositeExpression::TYPE_AND => '(' . implode(' AND ', $expressionList) . ')', + CompositeExpression::TYPE_OR => '(' . implode(' OR ', $expressionList) . ')', + CompositeExpression::TYPE_NOT => 'NOT (' . $expressionList[0] . ')', + default => throw new RuntimeException('Unknown composite ' . $expr->getType()), + }; } /** * Converts a value expression into the target query language part. - * - * @return string */ - public function walkValue(Value $value) + public function walkValue(Value $value): string { return '?'; } diff --git a/src/Persisters/SqlValueVisitor.php b/src/Persisters/SqlValueVisitor.php index b1814a21315..b346ec94472 100644 --- a/src/Persisters/SqlValueVisitor.php +++ b/src/Persisters/SqlValueVisitor.php @@ -15,10 +15,10 @@ class SqlValueVisitor extends ExpressionVisitor { /** @var mixed[] */ - private $values = []; + private array $values = []; /** @var mixed[][] */ - private $types = []; + private array $types = []; /** * Converts a comparison expression into the target query language output. @@ -65,7 +65,7 @@ public function walkValue(Value $value) * @return mixed[][] * @phpstan-return array{0: array, 1: array>} */ - public function getParamsAndTypes() + public function getParamsAndTypes(): array { return [$this->values, $this->types]; } @@ -73,25 +73,16 @@ public function getParamsAndTypes() /** * Returns the value from a Comparison. In case of a CONTAINS comparison, * the value is wrapped in %-signs, because it will be used in a LIKE clause. - * - * @return mixed */ - protected function getValueFromComparison(Comparison $comparison) + protected function getValueFromComparison(Comparison $comparison): mixed { $value = $comparison->getValue()->getValue(); - switch ($comparison->getOperator()) { - case Comparison::CONTAINS: - return '%' . $value . '%'; - - case Comparison::STARTS_WITH: - return $value . '%'; - - case Comparison::ENDS_WITH: - return '%' . $value; - - default: - return $value; - } + return match ($comparison->getOperator()) { + Comparison::CONTAINS => '%' . $value . '%', + Comparison::STARTS_WITH => $value . '%', + Comparison::ENDS_WITH => '%' . $value, + default => $value, + }; } } diff --git a/src/PessimisticLockException.php b/src/PessimisticLockException.php index 3ff3b9137cd..c71560f80bb 100644 --- a/src/PessimisticLockException.php +++ b/src/PessimisticLockException.php @@ -5,11 +5,11 @@ namespace Doctrine\ORM; use Doctrine\ORM\Exception\ORMException; +use RuntimeException; -class PessimisticLockException extends ORMException +class PessimisticLockException extends RuntimeException implements ORMException { - /** @return PessimisticLockException */ - public static function lockFailed() + public static function lockFailed(): self { return new self('The pessimistic lock failed.'); } diff --git a/src/Proxy/Autoloader.php b/src/Proxy/Autoloader.php index dd433d35fba..a16d6fb3600 100644 --- a/src/Proxy/Autoloader.php +++ b/src/Proxy/Autoloader.php @@ -4,8 +4,83 @@ namespace Doctrine\ORM\Proxy; -use Doctrine\Common\Proxy\Autoloader as BaseAutoloader; +use Closure; -class Autoloader extends BaseAutoloader +use function file_exists; +use function ltrim; +use function spl_autoload_register; +use function str_replace; +use function str_starts_with; +use function strlen; +use function substr; + +use const DIRECTORY_SEPARATOR; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + */ +final class Autoloader { + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @phpstan-param class-string $className + * + * @throws NotAProxyClass + */ + public static function resolveFile(string $proxyDir, string $proxyNamespace, string $className): string + { + if (! str_starts_with($className, $proxyNamespace)) { + throw new NotAProxyClass($className, $proxyNamespace); + } + + // remove proxy namespace from class name + $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace)); + + // remove namespace separators from remaining class name + $fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace); + + return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param Closure(string, string, class-string): void|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return Closure(string): void + */ + public static function register( + string $proxyDir, + string $proxyNamespace, + Closure|null $notFoundCallback = null, + ): Closure { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + $autoloader = /** @param class-string $className */ static function (string $className) use ($proxyDir, $proxyNamespace, $notFoundCallback): void { + if ($proxyNamespace === '') { + return; + } + + if (! str_starts_with($className, $proxyNamespace)) { + return; + } + + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + $notFoundCallback($proxyDir, $proxyNamespace, $className); + } + + require $file; + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } } diff --git a/src/Proxy/DefaultProxyClassNameResolver.php b/src/Proxy/DefaultProxyClassNameResolver.php index a9d0a3dba93..1345f2e3122 100644 --- a/src/Proxy/DefaultProxyClassNameResolver.php +++ b/src/Proxy/DefaultProxyClassNameResolver.php @@ -7,7 +7,6 @@ use Doctrine\Persistence\Mapping\ProxyClassNameResolver; use Doctrine\Persistence\Proxy; -use function get_class; use function strrpos; use function substr; @@ -28,13 +27,9 @@ public function resolveClassName(string $className): string return substr($className, $pos + Proxy::MARKER_LENGTH + 2); } - /** - * @param object $object - * - * @return class-string - */ - public static function getClass($object): string + /** @return class-string */ + public static function getClass(object $object): string { - return (new self())->resolveClassName(get_class($object)); + return (new self())->resolveClassName($object::class); } } diff --git a/src/Proxy/InternalProxy.php b/src/Proxy/InternalProxy.php index baa5703f2a3..7c1d8339bd3 100644 --- a/src/Proxy/InternalProxy.php +++ b/src/Proxy/InternalProxy.php @@ -11,9 +11,8 @@ * * @template T of object * @template-extends Proxy - * - * @method void __setInitialized(bool $initialized) */ interface InternalProxy extends Proxy { + public function __setInitialized(bool $initialized): void; } diff --git a/src/Proxy/NotAProxyClass.php b/src/Proxy/NotAProxyClass.php new file mode 100644 index 00000000000..689cc3e12dc --- /dev/null +++ b/src/Proxy/NotAProxyClass.php @@ -0,0 +1,22 @@ + - * @template-extends InternalProxy - */ -interface Proxy extends BaseProxy, InternalProxy -{ -} diff --git a/src/Proxy/ProxyFactory.php b/src/Proxy/ProxyFactory.php index 9abe921d381..b2d114a6698 100644 --- a/src/Proxy/ProxyFactory.php +++ b/src/Proxy/ProxyFactory.php @@ -5,27 +5,21 @@ namespace Doctrine\ORM\Proxy; use Closure; -use Doctrine\Common\Proxy\AbstractProxyFactory; -use Doctrine\Common\Proxy\Proxy as CommonProxy; -use Doctrine\Common\Proxy\ProxyDefinition; -use Doctrine\Common\Proxy\ProxyGenerator; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityNotFoundException; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\Persisters\Entity\EntityPersister; -use Doctrine\ORM\Proxy\Proxy as LegacyProxy; use Doctrine\ORM\UnitOfWork; use Doctrine\ORM\Utility\IdentifierFlattener; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Proxy; use ReflectionProperty; use Symfony\Component\VarExporter\ProxyHelper; -use Throwable; use function array_combine; use function array_flip; use function array_intersect_key; +use function assert; use function bin2hex; use function chmod; use function class_exists; @@ -51,12 +45,11 @@ use function ucfirst; use const DIRECTORY_SEPARATOR; -use const PHP_VERSION_ID; /** * This factory is used to create proxy objects for entities at runtime. */ -class ProxyFactory extends AbstractProxyFactory +class ProxyFactory { /** * Never autogenerate a proxy and rely that it was generated by some @@ -122,33 +115,17 @@ public function __serialize(): array EOPHP; - /** @var EntityManagerInterface The EntityManager this factory is bound to. */ - private $em; - - /** @var UnitOfWork The UnitOfWork this factory uses to retrieve persisters */ - private $uow; - - /** @var string */ - private $proxyDir; - - /** @var string */ - private $proxyNs; + /** The UnitOfWork this factory uses to retrieve persisters */ + private readonly UnitOfWork $uow; /** @var self::AUTOGENERATE_* */ private $autoGenerate; - /** - * The IdentifierFlattener used for manipulating identifiers - * - * @var IdentifierFlattener - */ - private $identifierFlattener; + /** The IdentifierFlattener used for manipulating identifiers */ + private readonly IdentifierFlattener $identifierFlattener; /** @var array */ - private $proxyFactories = []; - - /** @var bool */ - private $isLazyGhostObjectEnabled = true; + private array $proxyFactories = []; /** * Initializes a new instance of the ProxyFactory class that is @@ -159,26 +136,12 @@ public function __serialize(): array * @param string $proxyNs The namespace to use for the proxy classes. * @param bool|self::AUTOGENERATE_* $autoGenerate The strategy for automatically generating proxy classes. */ - public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = self::AUTOGENERATE_NEVER) - { - if (! $em->getConfiguration()->isLazyGhostObjectEnabled()) { - if (PHP_VERSION_ID >= 80100) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10837/', - 'Not enabling lazy ghost objects is deprecated and will not be supported in Doctrine ORM 3.0. Ensure Doctrine\ORM\Configuration::setLazyGhostObjectEnabled(true) is called to enable them.' - ); - } - - $this->isLazyGhostObjectEnabled = false; - - $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs); - // @phpstan-ignore classConstant.deprecatedInterface - $proxyGenerator->setPlaceholder('baseProxyInterface', LegacyProxy::class); - - parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate); - } - + public function __construct( + private readonly EntityManagerInterface $em, + private readonly string $proxyDir, + private readonly string $proxyNs, + bool|int $autoGenerate = self::AUTOGENERATE_NEVER, + ) { if (! $proxyDir) { throw ORMInvalidArgumentException::proxyDirectoryRequired(); } @@ -191,23 +154,17 @@ public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $au throw ORMInvalidArgumentException::invalidAutoGenerateMode($autoGenerate); } - $this->em = $em; $this->uow = $em->getUnitOfWork(); - $this->proxyDir = $proxyDir; - $this->proxyNs = $proxyNs; $this->autoGenerate = (int) $autoGenerate; $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory()); } /** - * {@inheritDoc} + * @param class-string $className + * @param array $identifier */ - public function getProxy($className, array $identifier) + public function getProxy(string $className, array $identifier): InternalProxy { - if (! $this->isLazyGhostObjectEnabled) { - return parent::getProxy($className, $identifier); - } - $proxyFactory = $this->proxyFactories[$className] ?? $this->getProxyFactory($className); return $proxyFactory($identifier); @@ -223,12 +180,8 @@ public function getProxy($className, array $identifier) * * @return int Number of generated proxies. */ - public function generateProxyClasses(array $classes, $proxyDir = null) + public function generateProxyClasses(array $classes, string|null $proxyDir = null): int { - if (! $this->isLazyGhostObjectEnabled) { - return parent::generateProxyClasses($classes, $proxyDir); - } - $generated = 0; foreach ($classes as $class) { @@ -247,111 +200,13 @@ public function generateProxyClasses(array $classes, $proxyDir = null) return $generated; } - /** - * {@inheritDoc} - * - * @deprecated ProxyFactory::resetUninitializedProxy() is deprecated and will be removed in version 3.0 of doctrine/orm. - */ - public function resetUninitializedProxy(CommonProxy $proxy) - { - return parent::resetUninitializedProxy($proxy); - } - - /** - * {@inheritDoc} - */ - protected function skipClass(ClassMetadata $metadata) + protected function skipClass(ClassMetadata $metadata): bool { return $metadata->isMappedSuperclass || $metadata->isEmbeddedClass || $metadata->getReflectionClass()->isAbstract(); } - /** - * {@inheritDoc} - * - * @deprecated ProxyFactory::createProxyDefinition() is deprecated and will be removed in version 3.0 of doctrine/orm. - */ - protected function createProxyDefinition($className) - { - $classMetadata = $this->em->getClassMetadata($className); - $entityPersister = $this->uow->getEntityPersister($className); - - $initializer = $this->createInitializer($classMetadata, $entityPersister); - $cloner = $this->createCloner($classMetadata, $entityPersister); - - return new ProxyDefinition( - self::generateProxyClassName($className, $this->proxyNs), - $classMetadata->getIdentifierFieldNames(), - $classMetadata->getReflectionProperties(), - $initializer, - $cloner - ); - } - - /** - * Creates a closure capable of initializing a proxy - * - * @deprecated ProxyFactory::createInitializer() is deprecated and will be removed in version 3.0 of doctrine/orm. - * - * @phpstan-return Closure(CommonProxy):void - * - * @throws EntityNotFoundException - */ - private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister): Closure - { - $wakeupProxy = $classMetadata->getReflectionClass()->hasMethod('__wakeup'); - - return function (CommonProxy $proxy) use ($entityPersister, $classMetadata, $wakeupProxy): void { - $initializer = $proxy->__getInitializer(); - $cloner = $proxy->__getCloner(); - - $proxy->__setInitializer(null); - $proxy->__setCloner(null); - - if ($proxy->__isInitialized()) { - return; - } - - $properties = $proxy->__getLazyProperties(); - - foreach ($properties as $propertyName => $property) { - if (! isset($proxy->$propertyName)) { - $proxy->$propertyName = $properties[$propertyName]; - } - } - - $proxy->__setInitialized(true); - - if ($wakeupProxy) { - $proxy->__wakeup(); - } - - $identifier = $classMetadata->getIdentifierValues($proxy); - - try { - $entity = $entityPersister->loadById($identifier, $proxy); - } catch (Throwable $exception) { - $proxy->__setInitializer($initializer); - $proxy->__setCloner($cloner); - $proxy->__setInitialized(false); - - throw $exception; - } - - if ($entity === null) { - $proxy->__setInitializer($initializer); - $proxy->__setCloner($cloner); - $proxy->__setInitialized(false); - - throw EntityNotFoundException::fromClassNameAndIdentifier( - $classMetadata->getName(), - $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) - ); - } - }; - } - /** * Creates a closure capable of initializing a proxy * @@ -367,7 +222,7 @@ private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersi if ($original === null) { throw EntityNotFoundException::fromClassNameAndIdentifier( $classMetadata->getName(), - $identifierFlattener->flattenIdentifier($classMetadata, $identifier) + $identifierFlattener->flattenIdentifier($classMetadata, $identifier), ); } @@ -378,51 +233,10 @@ private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersi $class = $entityPersister->getClassMetadata(); foreach ($class->getReflectionProperties() as $property) { - if (isset($identifier[$property->name]) || ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) { - continue; - } - - $property->setValue($proxy, $property->getValue($original)); - } - }; - } - - /** - * Creates a closure capable of finalizing state a cloned proxy - * - * @deprecated ProxyFactory::createCloner() is deprecated and will be removed in version 3.0 of doctrine/orm. - * - * @phpstan-return Closure(CommonProxy):void - * - * @throws EntityNotFoundException - */ - private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister): Closure - { - return function (CommonProxy $proxy) use ($entityPersister, $classMetadata): void { - if ($proxy->__isInitialized()) { - return; - } - - $proxy->__setInitialized(true); - $proxy->__setInitializer(null); - - $class = $entityPersister->getClassMetadata(); - $identifier = $classMetadata->getIdentifierValues($proxy); - $original = $entityPersister->loadById($identifier); - - if ($original === null) { - throw EntityNotFoundException::fromClassNameAndIdentifier( - $classMetadata->getName(), - $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) - ); - } - - foreach ($class->getReflectionProperties() as $property) { - if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) { + if (! $property || isset($identifier[$property->getName()]) || ! $class->hasField($property->getName()) && ! $class->hasAssociation($property->getName())) { continue; } - $property->setAccessible(true); $property->setValue($proxy, $property->getValue($original)); } }; @@ -477,6 +291,7 @@ private function getProxyFactory(string $className): Closure throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField); } + assert($reflector !== null); $reflector->setValue($proxy, $identifier[$idField]); } @@ -523,7 +338,7 @@ private function loadProxyClass(ClassMetadata $class): string return $proxyClassName; } - private function generateProxyClass(ClassMetadata $class, ?string $fileName, string $proxyClassName): void + private function generateProxyClass(ClassMetadata $class, string|null $fileName, string $proxyClassName): void { $i = strrpos($proxyClassName, '\\'); $placeholders = [ @@ -572,12 +387,18 @@ private function generateUseLazyGhostTrait(ClassMetadata $class): string $code = substr($code, 7 + (int) strpos($code, "\n{")); $code = substr($code, 0, (int) strpos($code, "\n}")); $code = str_replace('LazyGhostTrait;', str_replace("\n ", "\n", 'LazyGhostTrait { - initializeLazyObject as __load; + initializeLazyObject as private; setLazyObjectAsInitialized as public __setInitialized; isLazyObjectInitialized as private; createLazyGhost as private; resetLazyObject as private; - }'), $code); + } + + public function __load(): void + { + $this->initializeLazyObject(); + } + '), $code); return $code; } diff --git a/src/Query.php b/src/Query.php index 41471738e04..9365770159d 100644 --- a/src/Query.php +++ b/src/Query.php @@ -4,15 +4,10 @@ namespace Doctrine\ORM; -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; -use Doctrine\ORM\Internal\Hydration\IterableResult; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\AST\DeleteStatement; use Doctrine\ORM\Query\AST\SelectStatement; @@ -36,10 +31,8 @@ use function get_debug_type; use function in_array; use function is_a; -use function is_int; use function ksort; use function md5; -use function method_exists; use function reset; use function serialize; use function sha1; @@ -124,109 +117,85 @@ class Query extends AbstractQuery /** * The current state of this query. * - * @var int * @phpstan-var self::STATE_* */ - private $state = self::STATE_DIRTY; + private int $state = self::STATE_DIRTY; /** * A snapshot of the parameter types the query was parsed with. * * @var array */ - private $parsedTypes = []; + private array $parsedTypes = []; /** * Cached DQL query. - * - * @var string|null */ - private $dql = null; + private string|null $dql = null; /** * The parser result that holds DQL => SQL information. - * - * @var ParserResult */ - private $parserResult; + private ParserResult $parserResult; /** * The first result to return (the "offset"). - * - * @var int */ - private $firstResult = 0; + private int $firstResult = 0; /** * The maximum number of results to return (the "limit"). - * - * @var int|null */ - private $maxResults = null; + private int|null $maxResults = null; /** * The cache driver used for caching queries. - * - * @var CacheItemPoolInterface|null */ - private $queryCache; + private CacheItemPoolInterface|null $queryCache = null; /** * Whether or not expire the query cache. - * - * @var bool */ - private $expireQueryCache = false; + private bool $expireQueryCache = false; /** * The query cache lifetime. - * - * @var int|null */ - private $queryCacheTTL; + private int|null $queryCacheTTL = null; /** * Whether to use a query cache, if available. Defaults to TRUE. - * - * @var bool */ - private $useQueryCache = true; + private bool $useQueryCache = true; /** * Gets the SQL query/queries that correspond to this DQL query. * * @return list|string The built sql query or an array of all sql queries. */ - public function getSQL() + public function getSQL(): string|array { return $this->getSqlExecutor()->getSqlStatements(); } /** * Returns the corresponding AST for this DQL query. - * - * @return SelectStatement|UpdateStatement|DeleteStatement */ - public function getAST() + public function getAST(): SelectStatement|UpdateStatement|DeleteStatement { $parser = new Parser($this); return $parser->getAST(); } - /** - * {@inheritDoc} - * - * @return ResultSetMapping - */ - protected function getResultSetMapping() + protected function getResultSetMapping(): ResultSetMapping { // parse query or load from cache - if ($this->_resultSetMapping === null) { - $this->_resultSetMapping = $this->parse()->getResultSetMapping(); + if ($this->resultSetMapping === null) { + $this->resultSetMapping = $this->parse()->getResultSetMapping(); } - return $this->_resultSetMapping; + return $this->resultSetMapping; } /** @@ -244,14 +213,14 @@ private function parse(): ParserResult } // Return previous parser result if the query and the filter collection are both clean - if ($this->state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->_em->isFiltersStateClean()) { + if ($this->state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->em->isFiltersStateClean()) { return $this->parserResult; } $this->state = self::STATE_CLEAN; $this->parsedTypes = $types; - $queryCache = $this->queryCache ?? $this->_em->getConfiguration()->getQueryCache(); + $queryCache = $this->queryCache ?? $this->em->getConfiguration()->getQueryCache(); // Check query cache. if (! ($this->useQueryCache && $queryCache)) { $parser = new Parser($this); @@ -283,21 +252,18 @@ private function parse(): ParserResult return $this->parserResult; } - /** - * {@inheritDoc} - */ - protected function _doExecute() + protected function _doExecute(): Result|int { $executor = $this->getSqlExecutor(); - if ($this->_queryCacheProfile) { - $executor->setQueryCacheProfile($this->_queryCacheProfile); + if ($this->queryCacheProfile) { + $executor->setQueryCacheProfile($this->queryCacheProfile); } else { $executor->removeQueryCacheProfile(); } - if ($this->_resultSetMapping === null) { - $this->_resultSetMapping = $this->parserResult->getResultSetMapping(); + if ($this->resultSetMapping === null) { + $this->resultSetMapping = $this->parserResult->getResultSetMapping(); } // Prepare parameters @@ -314,7 +280,7 @@ protected function _doExecute() } // evict all cache for the entity region - if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) { + if ($this->hasCache && isset($this->hints[self::HINT_CACHE_EVICT]) && $this->hints[self::HINT_CACHE_EVICT]) { $this->evictEntityCacheRegion(); } @@ -324,10 +290,10 @@ protected function _doExecute() $executor, $sqlParams, $types, - $this->_em->getConnection()->getParams() + $this->em->getConnection()->getParams(), ); - return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + return $executor->execute($this->em->getConnection(), $sqlParams, $types); } /** @@ -339,27 +305,21 @@ private function evictResultSetCache( AbstractSqlExecutor $executor, array $sqlParams, array $types, - array $connectionParams + array $connectionParams, ): void { - if ($this->_queryCacheProfile === null || ! $this->getExpireResultCache()) { + if ($this->queryCacheProfile === null || ! $this->getExpireResultCache()) { return; } - $cache = method_exists(QueryCacheProfile::class, 'getResultCache') - ? $this->_queryCacheProfile->getResultCache() - // @phpstan-ignore method.deprecated - : $this->_queryCacheProfile->getResultCacheDriver(); + $cache = $this->queryCacheProfile->getResultCache(); assert($cache !== null); $statements = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array foreach ($statements as $statement) { - $cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams); - - $cache instanceof CacheItemPoolInterface - ? $cache->deleteItem(reset($cacheKeys)) - : $cache->delete(reset($cacheKeys)); + $cacheKeys = $this->queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams); + $cache->deleteItem(reset($cacheKeys)); } } @@ -378,7 +338,7 @@ private function evictEntityCacheRegion(): void ? $AST->deleteClause->abstractSchemaName : $AST->updateClause->abstractSchemaName; - $this->_em->getCache()->evictEntityRegion($className); + $this->em->getCache()->evictEntityRegion($className); } /** @@ -456,7 +416,7 @@ private function resolveParameterValue(Parameter $parameter): array } if ($value instanceof ClassMetadata && isset($rsm->discriminatorParameters[$key])) { - $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em)); + $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->em)); } $processedValue = $this->processParameterValue($value); @@ -472,32 +432,9 @@ private function resolveParameterValue(Parameter $parameter): array /** * Defines a cache driver to be used for caching queries. * - * @deprecated Call {@see setQueryCache()} instead. - * - * @param Cache|null $queryCache Cache driver. - * * @return $this */ - public function setQueryCacheDriver($queryCache): self - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9004', - '%s is deprecated and will be removed in Doctrine 3.0. Use setQueryCache() instead.', - __METHOD__ - ); - - $this->queryCache = $queryCache ? CacheAdapter::wrap($queryCache) : null; - - return $this; - } - - /** - * Defines a cache driver to be used for caching queries. - * - * @return $this - */ - public function setQueryCache(?CacheItemPoolInterface $queryCache): self + public function setQueryCache(CacheItemPoolInterface|null $queryCache): self { $this->queryCache = $queryCache; @@ -507,39 +444,15 @@ public function setQueryCache(?CacheItemPoolInterface $queryCache): self /** * Defines whether the query should make use of a query cache, if available. * - * @param bool $bool - * * @return $this */ - public function useQueryCache($bool): self + public function useQueryCache(bool $bool): self { $this->useQueryCache = $bool; return $this; } - /** - * Returns the cache driver used for query caching. - * - * @deprecated - * - * @return Cache|null The cache driver used for query caching or NULL, if - * this Query does not use query caching. - */ - public function getQueryCacheDriver(): ?Cache - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9004', - '%s is deprecated and will be removed in Doctrine 3.0 without replacement.', - __METHOD__ - ); - - $queryCache = $this->queryCache ?? $this->_em->getConfiguration()->getQueryCache(); - - return $queryCache ? DoctrineProvider::wrap($queryCache) : null; - } - /** * Defines how long the query cache will be active before expire. * @@ -547,12 +460,8 @@ public function getQueryCacheDriver(): ?Cache * * @return $this */ - public function setQueryCacheLifetime($timeToLive): self + public function setQueryCacheLifetime(int|null $timeToLive): self { - if ($timeToLive !== null) { - $timeToLive = (int) $timeToLive; - } - $this->queryCacheTTL = $timeToLive; return $this; @@ -561,7 +470,7 @@ public function setQueryCacheLifetime($timeToLive): self /** * Retrieves the lifetime of resultset cache. */ - public function getQueryCacheLifetime(): ?int + public function getQueryCacheLifetime(): int|null { return $this->queryCacheTTL; } @@ -569,11 +478,9 @@ public function getQueryCacheLifetime(): ?int /** * Defines if the query cache is active or not. * - * @param bool $expire Whether or not to force query cache expiration. - * * @return $this */ - public function expireQueryCache($expire = true): self + public function expireQueryCache(bool $expire = true): self { $this->expireQueryCache = $expire; @@ -598,22 +505,9 @@ public function free(): void /** * Sets a DQL query string. - * - * @param string|null $dqlQuery DQL Query. */ - public function setDQL($dqlQuery): self + public function setDQL(string $dqlQuery): self { - if ($dqlQuery === null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9784', - 'Calling %s with null is deprecated and will result in a TypeError in Doctrine 3.0', - __METHOD__ - ); - - return $this; - } - $this->dql = $dqlQuery; $this->state = self::STATE_DIRTY; @@ -623,7 +517,7 @@ public function setDQL($dqlQuery): self /** * Returns the DQL query that is represented by this query object. */ - public function getDQL(): ?string + public function getDQL(): string|null { return $this->dql; } @@ -649,7 +543,7 @@ public function getState(): int * * @param string $dql Arbitrary piece of DQL to check for. */ - public function contains($dql): bool + public function contains(string $dql): bool { return stripos($this->getDQL(), $dql) !== false; } @@ -657,24 +551,12 @@ public function contains($dql): bool /** * Sets the position of the first result to retrieve (the "offset"). * - * @param int|null $firstResult The first result to return. + * @param int $firstResult The first result to return. * * @return $this */ - public function setFirstResult($firstResult): self + public function setFirstResult(int $firstResult): self { - if (! is_int($firstResult)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9809', - 'Calling %s with %s is deprecated and will result in a TypeError in Doctrine 3.0. Pass an integer.', - __METHOD__, - get_debug_type($firstResult) - ); - - $firstResult = (int) $firstResult; - } - $this->firstResult = $firstResult; $this->state = self::STATE_DIRTY; @@ -695,16 +577,10 @@ public function getFirstResult(): int /** * Sets the maximum number of results to retrieve (the "limit"). * - * @param int|null $maxResults - * * @return $this */ - public function setMaxResults($maxResults): self + public function setMaxResults(int|null $maxResults): self { - if ($maxResults !== null) { - $maxResults = (int) $maxResults; - } - $this->maxResults = $maxResults; $this->state = self::STATE_DIRTY; @@ -717,29 +593,11 @@ public function setMaxResults($maxResults): self * * @return int|null Maximum number of results. */ - public function getMaxResults(): ?int + public function getMaxResults(): int|null { return $this->maxResults; } - /** - * Executes the query and returns an IterableResult that can be used to incrementally - * iterated over the result. - * - * @deprecated - * - * @param ArrayCollection|mixed[]|null $parameters The query parameters. - * @param string|int $hydrationMode The hydration mode to use. - * @phpstan-param ArrayCollection|array|null $parameters - * @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode - */ - public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT): IterableResult - { - $this->setHint(self::HINT_INTERNAL_ITERATION, true); - - return parent::iterate($parameters, $hydrationMode); - } - /** {@inheritDoc} */ public function toIterable(iterable $parameters = [], $hydrationMode = self::HYDRATE_OBJECT): iterable { @@ -748,20 +606,14 @@ public function toIterable(iterable $parameters = [], $hydrationMode = self::HYD return parent::toIterable($parameters, $hydrationMode); } - /** - * {@inheritDoc} - */ - public function setHint($name, $value): self + public function setHint(string $name, mixed $value): static { $this->state = self::STATE_DIRTY; return parent::setHint($name, $value); } - /** - * {@inheritDoc} - */ - public function setHydrationMode($hydrationMode): self + public function setHydrationMode(string|int $hydrationMode): static { $this->state = self::STATE_DIRTY; @@ -773,17 +625,16 @@ public function setHydrationMode($hydrationMode): self * * @see \Doctrine\DBAL\LockMode * - * @param int $lockMode * @phpstan-param LockMode::* $lockMode * * @return $this * * @throws TransactionRequiredException */ - public function setLockMode($lockMode): self + public function setLockMode(LockMode|int $lockMode): self { if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) { - if (! $this->_em->getConnection()->isTransactionActive()) { + if (! $this->em->getConnection()->isTransactionActive()) { throw TransactionRequiredException::transactionRequired(); } } @@ -796,9 +647,10 @@ public function setLockMode($lockMode): self /** * 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. + * @phpstan-return LockMode::*|null */ - public function getLockMode(): ?int + public function getLockMode(): LockMode|int|null { $lockMode = $this->getHint(self::HINT_LOCK_MODE); @@ -814,7 +666,7 @@ public function getLockMode(): ?int */ protected function getQueryCacheId(): string { - ksort($this->_hints); + ksort($this->hints); if (! $this->hasHint(self::HINT_CUSTOM_OUTPUT_WALKER)) { // Assume Parser will create the SqlOutputWalker; save is_a call, which might trigger a class load @@ -830,18 +682,18 @@ protected function getQueryCacheId(): string 'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.', $outputWalkerClass, OutputWalker::class, - SqlFinalizer::class + SqlFinalizer::class, ); $firstAndMaxResult = '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults; } } return md5( - $this->getDQL() . serialize($this->_hints) . + $this->getDQL() . serialize($this->hints) . '&platform=' . get_debug_type($this->getEntityManager()->getConnection()->getDatabasePlatform()) . - ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + ($this->em->hasFilters() ? $this->em->getFilters()->getHash() : '') . $firstAndMaxResult . - '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT' + '&hydrationMode=' . $this->hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT', ); } diff --git a/src/Query/AST/ASTException.php b/src/Query/AST/ASTException.php index de99519a367..1ef890acfc7 100644 --- a/src/Query/AST/ASTException.php +++ b/src/Query/AST/ASTException.php @@ -13,12 +13,7 @@ */ class ASTException extends QueryException { - /** - * @param Node $node - * - * @return ASTException - */ - public static function noDispatchForNode($node) + public static function noDispatchForNode(Node $node): self { return new self('Double-dispatch for node ' . get_debug_type($node) . ' is not supported.'); } diff --git a/src/Query/AST/AggregateExpression.php b/src/Query/AST/AggregateExpression.php index 85a12a4b367..468c65c2a70 100644 --- a/src/Query/AST/AggregateExpression.php +++ b/src/Query/AST/AggregateExpression.php @@ -4,37 +4,19 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + class AggregateExpression extends Node { - /** @var string */ - public $functionName; - - /** @var PathExpression|SimpleArithmeticExpression */ - public $pathExpression; - - /** - * Some aggregate expressions support distinct, eg COUNT. - * - * @var bool - */ - public $isDistinct = false; - - /** - * @param string $functionName - * @param PathExpression|SimpleArithmeticExpression $pathExpression - * @param bool $isDistinct - */ - public function __construct($functionName, $pathExpression, $isDistinct) - { - $this->functionName = $functionName; - $this->pathExpression = $pathExpression; - $this->isDistinct = $isDistinct; + /** @param bool $isDistinct Some aggregate expressions support distinct, eg COUNT. */ + public function __construct( + public string $functionName, + public Node|string $pathExpression, + public bool $isDistinct, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkAggregateExpression($this); } diff --git a/src/Query/AST/ArithmeticExpression.php b/src/Query/AST/ArithmeticExpression.php index 48d0c2b9ee2..a819e058282 100644 --- a/src/Query/AST/ArithmeticExpression.php +++ b/src/Query/AST/ArithmeticExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" * @@ -11,28 +13,21 @@ */ class ArithmeticExpression extends Node { - /** @var SimpleArithmeticExpression|null */ - public $simpleArithmeticExpression; + public Node|string|null $simpleArithmeticExpression = null; - /** @var Subselect|null */ - public $subselect; + public Subselect|null $subselect = null; - /** @return bool */ - public function isSimpleArithmeticExpression() + public function isSimpleArithmeticExpression(): bool { return (bool) $this->simpleArithmeticExpression; } - /** @return bool */ - public function isSubselect() + public function isSubselect(): bool { return (bool) $this->subselect; } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkArithmeticExpression($this); } diff --git a/src/Query/AST/ArithmeticFactor.php b/src/Query/AST/ArithmeticFactor.php index f6f4f5b8f98..278a921084b 100644 --- a/src/Query/AST/ArithmeticFactor.php +++ b/src/Query/AST/ArithmeticFactor.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary * @@ -11,43 +13,24 @@ */ class ArithmeticFactor extends Node { - /** @var mixed */ - public $arithmeticPrimary; - - /** - * NULL represents no sign, TRUE means positive and FALSE means negative sign. - * - * @var bool|null - */ - public $sign; - - /** - * @param mixed $arithmeticPrimary - * @param bool|null $sign - */ - public function __construct($arithmeticPrimary, $sign = null) - { - $this->arithmeticPrimary = $arithmeticPrimary; - $this->sign = $sign; + public function __construct( + public mixed $arithmeticPrimary, + public bool|null $sign = null, + ) { } - /** @return bool */ - public function isPositiveSigned() + public function isPositiveSigned(): bool { return $this->sign === true; } - /** @return bool */ - public function isNegativeSigned() + public function isNegativeSigned(): bool { return $this->sign === false; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkArithmeticFactor($this); + return $walker->walkArithmeticFactor($this); } } diff --git a/src/Query/AST/ArithmeticTerm.php b/src/Query/AST/ArithmeticTerm.php index ceb7bbb6a2c..b23361264bb 100644 --- a/src/Query/AST/ArithmeticTerm.php +++ b/src/Query/AST/ArithmeticTerm.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* * @@ -11,20 +13,13 @@ */ class ArithmeticTerm extends Node { - /** @var mixed[] */ - public $arithmeticFactors; - /** @param mixed[] $arithmeticFactors */ - public function __construct(array $arithmeticFactors) + public function __construct(public array $arithmeticFactors) { - $this->arithmeticFactors = $arithmeticFactors; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkArithmeticTerm($this); + return $walker->walkArithmeticTerm($this); } } diff --git a/src/Query/AST/BetweenExpression.php b/src/Query/AST/BetweenExpression.php index 6c699503ce0..c13292b741f 100644 --- a/src/Query/AST/BetweenExpression.php +++ b/src/Query/AST/BetweenExpression.php @@ -4,38 +4,20 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + class BetweenExpression extends Node { - /** @var ArithmeticExpression */ - public $expression; - - /** @var ArithmeticExpression */ - public $leftBetweenExpression; - - /** @var ArithmeticExpression */ - public $rightBetweenExpression; - - /** @var bool */ - public $not; - - /** - * @param ArithmeticExpression $expr - * @param ArithmeticExpression $leftExpr - * @param ArithmeticExpression $rightExpr - */ - public function __construct($expr, $leftExpr, $rightExpr, bool $not = false) - { - $this->expression = $expr; - $this->leftBetweenExpression = $leftExpr; - $this->rightBetweenExpression = $rightExpr; - $this->not = $not; + public function __construct( + public ArithmeticExpression $expression, + public ArithmeticExpression $leftBetweenExpression, + public ArithmeticExpression $rightBetweenExpression, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkBetweenExpression($this); + return $walker->walkBetweenExpression($this); } } diff --git a/src/Query/AST/CoalesceExpression.php b/src/Query/AST/CoalesceExpression.php index cd1f18142d6..89f025f6d13 100644 --- a/src/Query/AST/CoalesceExpression.php +++ b/src/Query/AST/CoalesceExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" * @@ -11,20 +13,13 @@ */ class CoalesceExpression extends Node { - /** @var mixed[] */ - public $scalarExpressions = []; - /** @param mixed[] $scalarExpressions */ - public function __construct(array $scalarExpressions) + public function __construct(public array $scalarExpressions) { - $this->scalarExpressions = $scalarExpressions; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkCoalesceExpression($this); + return $walker->walkCoalesceExpression($this); } } diff --git a/src/Query/AST/CollectionMemberExpression.php b/src/Query/AST/CollectionMemberExpression.php index 2b93c770f79..a62a191a712 100644 --- a/src/Query/AST/CollectionMemberExpression.php +++ b/src/Query/AST/CollectionMemberExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression * @@ -11,30 +13,14 @@ */ class CollectionMemberExpression extends Node { - /** @var mixed */ - public $entityExpression; - - /** @var PathExpression */ - public $collectionValuedPathExpression; - - /** @var bool */ - public $not; - - /** - * @param mixed $entityExpr - * @param PathExpression $collValuedPathExpr - */ - public function __construct($entityExpr, $collValuedPathExpr, bool $not = false) - { - $this->entityExpression = $entityExpr; - $this->collectionValuedPathExpression = $collValuedPathExpr; - $this->not = $not; + public function __construct( + public mixed $entityExpression, + public PathExpression $collectionValuedPathExpression, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkCollectionMemberExpression($this); } diff --git a/src/Query/AST/ComparisonExpression.php b/src/Query/AST/ComparisonExpression.php index c56005f4dc6..a7d91f94a85 100644 --- a/src/Query/AST/ComparisonExpression.php +++ b/src/Query/AST/ComparisonExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) | * StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) | @@ -16,32 +18,15 @@ */ class ComparisonExpression extends Node { - /** @var Node|string */ - public $leftExpression; - - /** @var Node|string */ - public $rightExpression; - - /** @var string */ - public $operator; - - /** - * @param Node|string $leftExpr - * @param string $operator - * @param Node|string $rightExpr - */ - public function __construct($leftExpr, $operator, $rightExpr) - { - $this->leftExpression = $leftExpr; - $this->rightExpression = $rightExpr; - $this->operator = $operator; + public function __construct( + public Node|string $leftExpression, + public string $operator, + public Node|string $rightExpression, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkComparisonExpression($this); + return $walker->walkComparisonExpression($this); } } diff --git a/src/Query/AST/ConditionalExpression.php b/src/Query/AST/ConditionalExpression.php index fd7182b5971..26a98e53300 100644 --- a/src/Query/AST/ConditionalExpression.php +++ b/src/Query/AST/ConditionalExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* * @@ -11,20 +13,13 @@ */ class ConditionalExpression extends Node { - /** @var mixed[] */ - public $conditionalTerms = []; - /** @param mixed[] $conditionalTerms */ - public function __construct(array $conditionalTerms) + public function __construct(public array $conditionalTerms) { - $this->conditionalTerms = $conditionalTerms; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkConditionalExpression($this); + return $walker->walkConditionalExpression($this); } } diff --git a/src/Query/AST/ConditionalFactor.php b/src/Query/AST/ConditionalFactor.php index a39a02c819a..788174340ab 100644 --- a/src/Query/AST/ConditionalFactor.php +++ b/src/Query/AST/ConditionalFactor.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ConditionalFactor ::= ["NOT"] ConditionalPrimary * @@ -11,24 +13,14 @@ */ class ConditionalFactor extends Node implements Phase2OptimizableConditional { - /** @var bool */ - public $not = false; - - /** @var ConditionalPrimary */ - public $conditionalPrimary; - - /** @param ConditionalPrimary $conditionalPrimary */ - public function __construct($conditionalPrimary, bool $not = false) - { - $this->conditionalPrimary = $conditionalPrimary; - $this->not = $not; + public function __construct( + public ConditionalPrimary $conditionalPrimary, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkConditionalFactor($this); + return $walker->walkConditionalFactor($this); } } diff --git a/src/Query/AST/ConditionalPrimary.php b/src/Query/AST/ConditionalPrimary.php index c0c7e917b20..9344cd9c6e9 100644 --- a/src/Query/AST/ConditionalPrimary.php +++ b/src/Query/AST/ConditionalPrimary.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" * @@ -11,29 +13,22 @@ */ class ConditionalPrimary extends Node implements Phase2OptimizableConditional { - /** @var Node|null */ - public $simpleConditionalExpression; + public Node|null $simpleConditionalExpression = null; - /** @var ConditionalExpression|Phase2OptimizableConditional|null */ - public $conditionalExpression; + public ConditionalExpression|Phase2OptimizableConditional|null $conditionalExpression = null; - /** @return bool */ - public function isSimpleConditionalExpression() + public function isSimpleConditionalExpression(): bool { return (bool) $this->simpleConditionalExpression; } - /** @return bool */ - public function isConditionalExpression() + public function isConditionalExpression(): bool { return (bool) $this->conditionalExpression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkConditionalPrimary($this); + return $walker->walkConditionalPrimary($this); } } diff --git a/src/Query/AST/ConditionalTerm.php b/src/Query/AST/ConditionalTerm.php index 37cd95e4e30..dcea50bc1c5 100644 --- a/src/Query/AST/ConditionalTerm.php +++ b/src/Query/AST/ConditionalTerm.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* * @@ -11,20 +13,13 @@ */ class ConditionalTerm extends Node implements Phase2OptimizableConditional { - /** @var mixed[] */ - public $conditionalFactors = []; - /** @param mixed[] $conditionalFactors */ - public function __construct(array $conditionalFactors) + public function __construct(public array $conditionalFactors) { - $this->conditionalFactors = $conditionalFactors; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkConditionalTerm($this); + return $walker->walkConditionalTerm($this); } } diff --git a/src/Query/AST/DeleteClause.php b/src/Query/AST/DeleteClause.php index 3431b095892..25e90854751 100644 --- a/src/Query/AST/DeleteClause.php +++ b/src/Query/AST/DeleteClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable] * @@ -11,23 +13,14 @@ */ class DeleteClause extends Node { - /** @var string */ - public $abstractSchemaName; - - /** @var string */ - public $aliasIdentificationVariable; + public string $aliasIdentificationVariable; - /** @param string $abstractSchemaName */ - public function __construct($abstractSchemaName) + public function __construct(public string $abstractSchemaName) { - $this->abstractSchemaName = $abstractSchemaName; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkDeleteClause($this); + return $walker->walkDeleteClause($this); } } diff --git a/src/Query/AST/DeleteStatement.php b/src/Query/AST/DeleteStatement.php index 4c37500b1aa..f367d097c7a 100644 --- a/src/Query/AST/DeleteStatement.php +++ b/src/Query/AST/DeleteStatement.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * DeleteStatement = DeleteClause [WhereClause] * @@ -11,23 +13,14 @@ */ class DeleteStatement extends Node { - /** @var DeleteClause */ - public $deleteClause; - - /** @var WhereClause|null */ - public $whereClause; + public WhereClause|null $whereClause = null; - /** @param DeleteClause $deleteClause */ - public function __construct($deleteClause) + public function __construct(public DeleteClause $deleteClause) { - $this->deleteClause = $deleteClause; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkDeleteStatement($this); + return $walker->walkDeleteStatement($this); } } diff --git a/src/Query/AST/EmptyCollectionComparisonExpression.php b/src/Query/AST/EmptyCollectionComparisonExpression.php index 2d374942a69..99788007e20 100644 --- a/src/Query/AST/EmptyCollectionComparisonExpression.php +++ b/src/Query/AST/EmptyCollectionComparisonExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" * @@ -11,24 +13,14 @@ */ class EmptyCollectionComparisonExpression extends Node { - /** @var PathExpression */ - public $expression; - - /** @var bool */ - public $not; - - /** @param PathExpression $expression */ - public function __construct($expression, bool $not = false) - { - $this->expression = $expression; - $this->not = $not; + public function __construct( + public PathExpression $expression, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkEmptyCollectionComparisonExpression($this); + return $walker->walkEmptyCollectionComparisonExpression($this); } } diff --git a/src/Query/AST/ExistsExpression.php b/src/Query/AST/ExistsExpression.php index 95c758a4715..72757f45263 100644 --- a/src/Query/AST/ExistsExpression.php +++ b/src/Query/AST/ExistsExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" * @@ -11,24 +13,14 @@ */ class ExistsExpression extends Node { - /** @var bool */ - public $not; - - /** @var Subselect */ - public $subselect; - - /** @param Subselect $subselect */ - public function __construct($subselect, bool $not = false) - { - $this->subselect = $subselect; - $this->not = $not; + public function __construct( + public Subselect $subselect, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkExistsExpression($this); + return $walker->walkExistsExpression($this); } } diff --git a/src/Query/AST/FromClause.php b/src/Query/AST/FromClause.php index 7001049c44c..0b74393b635 100644 --- a/src/Query/AST/FromClause.php +++ b/src/Query/AST/FromClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} * @@ -11,20 +13,13 @@ */ class FromClause extends Node { - /** @var mixed[] */ - public $identificationVariableDeclarations = []; - /** @param mixed[] $identificationVariableDeclarations */ - public function __construct(array $identificationVariableDeclarations) + public function __construct(public array $identificationVariableDeclarations) { - $this->identificationVariableDeclarations = $identificationVariableDeclarations; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkFromClause($this); + return $walker->walkFromClause($this); } } diff --git a/src/Query/AST/Functions/AbsFunction.php b/src/Query/AST/Functions/AbsFunction.php index 5c0fd7fac15..4edff060353 100644 --- a/src/Query/AST/Functions/AbsFunction.php +++ b/src/Query/AST/Functions/AbsFunction.php @@ -4,7 +4,7 @@ namespace Doctrine\ORM\Query\AST\Functions; -use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\AST\Node; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; @@ -16,19 +16,16 @@ */ class AbsFunction extends FunctionNode { - /** @var SimpleArithmeticExpression */ - public $simpleArithmeticExpression; + public Node|string $simpleArithmeticExpression; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression( - $this->simpleArithmeticExpression + $this->simpleArithmeticExpression, ) . ')'; } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/AvgFunction.php b/src/Query/AST/Functions/AvgFunction.php index a0b0ca5e527..ba7b7f35055 100644 --- a/src/Query/AST/Functions/AvgFunction.php +++ b/src/Query/AST/Functions/AvgFunction.php @@ -13,8 +13,7 @@ */ final class AvgFunction extends FunctionNode { - /** @var AggregateExpression */ - private $aggregateExpression; + private AggregateExpression $aggregateExpression; public function getSql(SqlWalker $sqlWalker): string { diff --git a/src/Query/AST/Functions/BitAndFunction.php b/src/Query/AST/Functions/BitAndFunction.php index c278de5e36e..f2d3146d21e 100644 --- a/src/Query/AST/Functions/BitAndFunction.php +++ b/src/Query/AST/Functions/BitAndFunction.php @@ -16,25 +16,20 @@ */ class BitAndFunction extends FunctionNode { - /** @var Node */ - public $firstArithmetic; + public Node $firstArithmetic; + public Node $secondArithmetic; - /** @var Node */ - public $secondArithmetic; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $platform = $sqlWalker->getConnection()->getDatabasePlatform(); return $platform->getBitAndComparisonExpression( $this->firstArithmetic->dispatch($sqlWalker), - $this->secondArithmetic->dispatch($sqlWalker) + $this->secondArithmetic->dispatch($sqlWalker), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/BitOrFunction.php b/src/Query/AST/Functions/BitOrFunction.php index a380009cceb..f3f84da56f6 100644 --- a/src/Query/AST/Functions/BitOrFunction.php +++ b/src/Query/AST/Functions/BitOrFunction.php @@ -16,25 +16,20 @@ */ class BitOrFunction extends FunctionNode { - /** @var Node */ - public $firstArithmetic; + public Node $firstArithmetic; + public Node $secondArithmetic; - /** @var Node */ - public $secondArithmetic; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $platform = $sqlWalker->getConnection()->getDatabasePlatform(); return $platform->getBitOrComparisonExpression( $this->firstArithmetic->dispatch($sqlWalker), - $this->secondArithmetic->dispatch($sqlWalker) + $this->secondArithmetic->dispatch($sqlWalker), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/ConcatFunction.php b/src/Query/AST/Functions/ConcatFunction.php index 9e6dd8a8ae2..7a7d04cd846 100644 --- a/src/Query/AST/Functions/ConcatFunction.php +++ b/src/Query/AST/Functions/ConcatFunction.php @@ -16,17 +16,13 @@ */ class ConcatFunction extends FunctionNode { - /** @var Node */ - public $firstStringPrimary; - - /** @var Node */ - public $secondStringPrimary; + public Node $firstStringPrimary; + public Node $secondStringPrimary; /** @phpstan-var list */ - public $concatExpressions = []; + public array $concatExpressions = []; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $platform = $sqlWalker->getConnection()->getDatabasePlatform(); @@ -39,8 +35,7 @@ public function getSql(SqlWalker $sqlWalker) return $platform->getConcatExpression(...$args); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/CountFunction.php b/src/Query/AST/Functions/CountFunction.php index a7e0c18c9d6..dc926a52a2b 100644 --- a/src/Query/AST/Functions/CountFunction.php +++ b/src/Query/AST/Functions/CountFunction.php @@ -16,8 +16,7 @@ */ final class CountFunction extends FunctionNode implements TypedExpression { - /** @var AggregateExpression */ - private $aggregateExpression; + private AggregateExpression $aggregateExpression; public function getSql(SqlWalker $sqlWalker): string { diff --git a/src/Query/AST/Functions/CurrentDateFunction.php b/src/Query/AST/Functions/CurrentDateFunction.php index c19c640c598..cec963269f0 100644 --- a/src/Query/AST/Functions/CurrentDateFunction.php +++ b/src/Query/AST/Functions/CurrentDateFunction.php @@ -15,14 +15,12 @@ */ class CurrentDateFunction extends FunctionNode { - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL(); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/CurrentTimeFunction.php b/src/Query/AST/Functions/CurrentTimeFunction.php index 3595a0385d9..6473fcea494 100644 --- a/src/Query/AST/Functions/CurrentTimeFunction.php +++ b/src/Query/AST/Functions/CurrentTimeFunction.php @@ -15,14 +15,12 @@ */ class CurrentTimeFunction extends FunctionNode { - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL(); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/CurrentTimestampFunction.php b/src/Query/AST/Functions/CurrentTimestampFunction.php index 349e32746df..edcd27cfa11 100644 --- a/src/Query/AST/Functions/CurrentTimestampFunction.php +++ b/src/Query/AST/Functions/CurrentTimestampFunction.php @@ -15,14 +15,12 @@ */ class CurrentTimestampFunction extends FunctionNode { - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL(); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/DateAddFunction.php b/src/Query/AST/Functions/DateAddFunction.php index e980e8e8155..12920dcbd0f 100644 --- a/src/Query/AST/Functions/DateAddFunction.php +++ b/src/Query/AST/Functions/DateAddFunction.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Query\AST\Functions; +use Doctrine\ORM\Query\AST\ASTException; use Doctrine\ORM\Query\AST\Node; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\QueryException; @@ -19,70 +20,54 @@ */ class DateAddFunction extends FunctionNode { - /** @var Node */ - public $firstDateExpression = null; + public Node $firstDateExpression; + public Node $intervalExpression; + public Node $unit; - /** @var Node */ - public $intervalExpression = null; - - /** @var Node */ - public $unit = null; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { - switch (strtolower($this->unit->value)) { - case 'second': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'minute': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMinutesExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'hour': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'day': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'week': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddWeeksExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'month': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'year': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddYearsExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); + return match (strtolower((string) $this->unit->value)) { + 'second' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'minute' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMinutesExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'hour' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'day' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'week' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddWeeksExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'month' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'year' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddYearsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + default => throw QueryException::semanticalError( + 'DATE_ADD() only supports units of type second, minute, hour, day, week, month and year.', + ), + }; + } - default: - throw QueryException::semanticalError( - 'DATE_ADD() only supports units of type second, minute, hour, day, week, month and year.' - ); - } + /** @throws ASTException */ + private function dispatchIntervalExpression(SqlWalker $sqlWalker): string + { + return $this->intervalExpression->dispatch($sqlWalker); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/DateDiffFunction.php b/src/Query/AST/Functions/DateDiffFunction.php index 209df00abd5..55598c09010 100644 --- a/src/Query/AST/Functions/DateDiffFunction.php +++ b/src/Query/AST/Functions/DateDiffFunction.php @@ -16,23 +16,18 @@ */ class DateDiffFunction extends FunctionNode { - /** @var Node */ - public $date1; + public Node $date1; + public Node $date2; - /** @var Node */ - public $date2; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( $this->date1->dispatch($sqlWalker), - $this->date2->dispatch($sqlWalker) + $this->date2->dispatch($sqlWalker), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/DateSubFunction.php b/src/Query/AST/Functions/DateSubFunction.php index 42f1b4a6b7c..5363680e2a4 100644 --- a/src/Query/AST/Functions/DateSubFunction.php +++ b/src/Query/AST/Functions/DateSubFunction.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Query\AST\Functions; +use Doctrine\ORM\Query\AST\ASTException; use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\Query\SqlWalker; @@ -16,56 +17,46 @@ */ class DateSubFunction extends DateAddFunction { - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { - switch (strtolower($this->unit->value)) { - case 'second': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubSecondsExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'minute': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMinutesExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'hour': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'day': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'week': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubWeeksExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'month': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); - - case 'year': - return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubYearsExpression( - $this->firstDateExpression->dispatch($sqlWalker), - $this->intervalExpression->dispatch($sqlWalker) - ); + return match (strtolower((string) $this->unit->value)) { + 'second' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubSecondsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'minute' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMinutesExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'hour' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'day' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'week' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubWeeksExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'month' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + 'year' => $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubYearsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->dispatchIntervalExpression($sqlWalker), + ), + default => throw QueryException::semanticalError( + 'DATE_SUB() only supports units of type second, minute, hour, day, week, month and year.', + ), + }; + } - default: - throw QueryException::semanticalError( - 'DATE_SUB() only supports units of type second, minute, hour, day, week, month and year.' - ); - } + /** @throws ASTException */ + private function dispatchIntervalExpression(SqlWalker $sqlWalker): string + { + return $this->intervalExpression->dispatch($sqlWalker); } } diff --git a/src/Query/AST/Functions/FunctionNode.php b/src/Query/AST/Functions/FunctionNode.php index d521bd26f76..e49ddba6b3b 100644 --- a/src/Query/AST/Functions/FunctionNode.php +++ b/src/Query/AST/Functions/FunctionNode.php @@ -17,28 +17,16 @@ */ abstract class FunctionNode extends Node { - /** @var string */ - public $name; - - /** @param string $name */ - public function __construct($name) + public function __construct(public string $name) { - $this->name = $name; } - /** @return string */ - abstract public function getSql(SqlWalker $sqlWalker); + abstract public function getSql(SqlWalker $sqlWalker): string; - /** - * @param SqlWalker $sqlWalker - * - * @return string - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $sqlWalker): string { return $sqlWalker->walkFunction($this); } - /** @return void */ - abstract public function parse(Parser $parser); + abstract public function parse(Parser $parser): void; } diff --git a/src/Query/AST/Functions/IdentityFunction.php b/src/Query/AST/Functions/IdentityFunction.php index e292c975afd..1dd1bf5e621 100644 --- a/src/Query/AST/Functions/IdentityFunction.php +++ b/src/Query/AST/Functions/IdentityFunction.php @@ -21,16 +21,11 @@ */ class IdentityFunction extends FunctionNode { - /** @var PathExpression */ - public $pathExpression; + public PathExpression $pathExpression; - /** @var string|null */ - public $fieldMapping; + public string|null $fieldMapping = null; - /** - * {@inheritDoc} - */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { assert($this->pathExpression->field !== null); $entityManager = $sqlWalker->getEntityManager(); @@ -39,8 +34,10 @@ public function getSql(SqlWalker $sqlWalker) $dqlAlias = $this->pathExpression->identificationVariable; $assocField = $this->pathExpression->field; $assoc = $sqlWalker->getMetadataForDqlAlias($dqlAlias)->associationMappings[$assocField]; - $targetEntity = $entityManager->getClassMetadata($assoc['targetEntity']); - $joinColumn = reset($assoc['joinColumns']); + $targetEntity = $entityManager->getClassMetadata($assoc->targetEntity); + + assert($assoc->isToOneOwningSide()); + $joinColumn = reset($assoc->joinColumns); if ($this->fieldMapping !== null) { if (! isset($targetEntity->fieldMappings[$this->fieldMapping])) { @@ -50,8 +47,8 @@ public function getSql(SqlWalker $sqlWalker) $field = $targetEntity->fieldMappings[$this->fieldMapping]; $joinColumn = null; - foreach ($assoc['joinColumns'] as $mapping) { - if ($mapping['referencedColumnName'] === $field['columnName']) { + foreach ($assoc->joinColumns as $mapping) { + if ($mapping->referencedColumnName === $field->columnName) { $joinColumn = $mapping; break; @@ -64,7 +61,7 @@ public function getSql(SqlWalker $sqlWalker) } // The table with the relation may be a subclass, so get the table name from the association definition - $tableName = $entityManager->getClassMetadata($assoc['sourceEntity'])->getTableName(); + $tableName = $entityManager->getClassMetadata($assoc->sourceEntity)->getTableName(); $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias); $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); @@ -72,10 +69,7 @@ public function getSql(SqlWalker $sqlWalker) return $tableAlias . '.' . $columnName; } - /** - * {@inheritDoc} - */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/LengthFunction.php b/src/Query/AST/Functions/LengthFunction.php index 42420af54f0..39949183bd2 100644 --- a/src/Query/AST/Functions/LengthFunction.php +++ b/src/Query/AST/Functions/LengthFunction.php @@ -19,19 +19,16 @@ */ class LengthFunction extends FunctionNode implements TypedExpression { - /** @var Node */ - public $stringPrimary; + public Node $stringPrimary; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression( - $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/LocateFunction.php b/src/Query/AST/Functions/LocateFunction.php index 2942f06107d..c0d3b4a21b5 100644 --- a/src/Query/AST/Functions/LocateFunction.php +++ b/src/Query/AST/Functions/LocateFunction.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Query\AST\Functions; use Doctrine\ORM\Query\AST\Node; -use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; @@ -17,17 +16,12 @@ */ class LocateFunction extends FunctionNode { - /** @var Node */ - public $firstStringPrimary; + public Node|string $firstStringPrimary; + public Node|string $secondStringPrimary; - /** @var Node */ - public $secondStringPrimary; + public Node|string|bool $simpleArithmeticExpression = false; - /** @var SimpleArithmeticExpression|bool */ - public $simpleArithmeticExpression = false; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $platform = $sqlWalker->getConnection()->getDatabasePlatform(); @@ -38,15 +32,14 @@ public function getSql(SqlWalker $sqlWalker) return $platform->getLocateExpression( $secondString, $firstString, - $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression), ); } return $platform->getLocateExpression($secondString, $firstString); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/LowerFunction.php b/src/Query/AST/Functions/LowerFunction.php index e6c73cde4d9..8ae337ac273 100644 --- a/src/Query/AST/Functions/LowerFunction.php +++ b/src/Query/AST/Functions/LowerFunction.php @@ -18,20 +18,17 @@ */ class LowerFunction extends FunctionNode { - /** @var Node */ - public $stringPrimary; + public Node $stringPrimary; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return sprintf( 'LOWER(%s)', - $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/MaxFunction.php b/src/Query/AST/Functions/MaxFunction.php index a08e33413e0..8a6eecf3031 100644 --- a/src/Query/AST/Functions/MaxFunction.php +++ b/src/Query/AST/Functions/MaxFunction.php @@ -13,8 +13,7 @@ */ final class MaxFunction extends FunctionNode { - /** @var AggregateExpression */ - private $aggregateExpression; + private AggregateExpression $aggregateExpression; public function getSql(SqlWalker $sqlWalker): string { diff --git a/src/Query/AST/Functions/MinFunction.php b/src/Query/AST/Functions/MinFunction.php index 83cc3ac27e9..98d73a2825e 100644 --- a/src/Query/AST/Functions/MinFunction.php +++ b/src/Query/AST/Functions/MinFunction.php @@ -13,8 +13,7 @@ */ final class MinFunction extends FunctionNode { - /** @var AggregateExpression */ - private $aggregateExpression; + private AggregateExpression $aggregateExpression; public function getSql(SqlWalker $sqlWalker): string { diff --git a/src/Query/AST/Functions/ModFunction.php b/src/Query/AST/Functions/ModFunction.php index ea6ee2e0d4e..7c1af0bd71b 100644 --- a/src/Query/AST/Functions/ModFunction.php +++ b/src/Query/AST/Functions/ModFunction.php @@ -4,7 +4,7 @@ namespace Doctrine\ORM\Query\AST\Functions; -use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\AST\Node; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; @@ -16,23 +16,18 @@ */ class ModFunction extends FunctionNode { - /** @var SimpleArithmeticExpression */ - public $firstSimpleArithmeticExpression; + public Node|string $firstSimpleArithmeticExpression; + public Node|string $secondSimpleArithmeticExpression; - /** @var SimpleArithmeticExpression */ - public $secondSimpleArithmeticExpression; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression( $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), - $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) + $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/SizeFunction.php b/src/Query/AST/Functions/SizeFunction.php index 4576f26d21f..87ee7133b4b 100644 --- a/src/Query/AST/Functions/SizeFunction.php +++ b/src/Query/AST/Functions/SizeFunction.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Query\AST\Functions; -use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\AST\PathExpression; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; @@ -19,14 +18,13 @@ */ class SizeFunction extends FunctionNode { - /** @var PathExpression */ - public $collectionPathExpression; + public PathExpression $collectionPathExpression; /** - * @inheritDoc + * @inheritdoc * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient). */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { assert($this->collectionPathExpression->field !== null); $entityManager = $sqlWalker->getEntityManager(); @@ -39,18 +37,19 @@ public function getSql(SqlWalker $sqlWalker) $assoc = $class->associationMappings[$assocField]; $sql = 'SELECT COUNT(*) FROM '; - if ($assoc['type'] === ClassMetadata::ONE_TO_MANY) { - $targetClass = $entityManager->getClassMetadata($assoc['targetEntity']); + if ($assoc->isOneToMany()) { + $targetClass = $entityManager->getClassMetadata($assoc->targetEntity); $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName()); $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); $sql .= $quoteStrategy->getTableName($targetClass, $platform) . ' ' . $targetTableAlias . ' WHERE '; - $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy]; + assert($owningAssoc->isManyToOne()); $first = true; - foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { if ($first) { $first = false; } else { @@ -62,21 +61,21 @@ public function getSql(SqlWalker $sqlWalker) . $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform); } } else { // many-to-many - $targetClass = $entityManager->getClassMetadata($assoc['targetEntity']); - - $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; - $joinTable = $owningAssoc['joinTable']; + assert($assoc->isManyToMany()); + $owningAssoc = $entityManager->getMetadataFactory()->getOwningSide($assoc); + $joinTable = $owningAssoc->joinTable; // SQL table aliases - $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); + $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable->name); $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); // join to target table - $sql .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE '; + $targetClass = $entityManager->getClassMetadata($assoc->targetEntity); + $sql .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE '; - $joinColumns = $assoc['isOwningSide'] - ? $joinTable['joinColumns'] - : $joinTable['inverseJoinColumns']; + $joinColumns = $assoc->isOwningSide() + ? $joinTable->joinColumns + : $joinTable->inverseJoinColumns; $first = true; @@ -88,12 +87,12 @@ public function getSql(SqlWalker $sqlWalker) } $sourceColumnName = $quoteStrategy->getColumnName( - $class->fieldNames[$joinColumn['referencedColumnName']], + $class->fieldNames[$joinColumn->referencedColumnName], $class, - $platform + $platform, ); - $sql .= $joinTableAlias . '.' . $joinColumn['name'] + $sql .= $joinTableAlias . '.' . $joinColumn->name . ' = ' . $sourceTableAlias . '.' . $sourceColumnName; } @@ -102,8 +101,7 @@ public function getSql(SqlWalker $sqlWalker) return '(' . $sql . ')'; } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/SqrtFunction.php b/src/Query/AST/Functions/SqrtFunction.php index a5cf3038768..e643663b627 100644 --- a/src/Query/AST/Functions/SqrtFunction.php +++ b/src/Query/AST/Functions/SqrtFunction.php @@ -4,7 +4,7 @@ namespace Doctrine\ORM\Query\AST\Functions; -use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\AST\Node; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; @@ -18,20 +18,17 @@ */ class SqrtFunction extends FunctionNode { - /** @var SimpleArithmeticExpression */ - public $simpleArithmeticExpression; + public Node|string $simpleArithmeticExpression; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return sprintf( 'SQRT(%s)', - $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/SubstringFunction.php b/src/Query/AST/Functions/SubstringFunction.php index 50031c7b361..5744f083ba4 100644 --- a/src/Query/AST/Functions/SubstringFunction.php +++ b/src/Query/AST/Functions/SubstringFunction.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Query\AST\Functions; use Doctrine\ORM\Query\AST\Node; -use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; @@ -17,17 +16,12 @@ */ class SubstringFunction extends FunctionNode { - /** @var Node */ - public $stringPrimary; + public Node $stringPrimary; - /** @var SimpleArithmeticExpression */ - public $firstSimpleArithmeticExpression; + public Node|string $firstSimpleArithmeticExpression; + public Node|string|null $secondSimpleArithmeticExpression = null; - /** @var SimpleArithmeticExpression|null */ - public $secondSimpleArithmeticExpression = null; - - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $optionalSecondSimpleArithmeticExpression = null; if ($this->secondSimpleArithmeticExpression !== null) { @@ -37,12 +31,11 @@ public function getSql(SqlWalker $sqlWalker) return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression( $sqlWalker->walkStringPrimary($this->stringPrimary), $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), - $optionalSecondSimpleArithmeticExpression + $optionalSecondSimpleArithmeticExpression, ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/Functions/SumFunction.php b/src/Query/AST/Functions/SumFunction.php index c317c018e90..588dce9775a 100644 --- a/src/Query/AST/Functions/SumFunction.php +++ b/src/Query/AST/Functions/SumFunction.php @@ -13,8 +13,7 @@ */ final class SumFunction extends FunctionNode { - /** @var AggregateExpression */ - private $aggregateExpression; + private AggregateExpression $aggregateExpression; public function getSql(SqlWalker $sqlWalker): string { diff --git a/src/Query/AST/Functions/TrimFunction.php b/src/Query/AST/Functions/TrimFunction.php index 509a6c7cedc..cae620075de 100644 --- a/src/Query/AST/Functions/TrimFunction.php +++ b/src/Query/AST/Functions/TrimFunction.php @@ -20,25 +20,13 @@ */ class TrimFunction extends FunctionNode { - /** @var bool */ - public $leading; + public bool $leading = false; + public bool $trailing = false; + public bool $both = false; + public string|false $trimChar = false; + public Node $stringPrimary; - /** @var bool */ - public $trailing; - - /** @var bool */ - public $both; - - /** @var string|false */ - public $trimChar = false; - - /** @var Node */ - public $stringPrimary; - - /** - * {@inheritDoc} - */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { $stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary); $platform = $sqlWalker->getConnection()->getDatabasePlatform(); @@ -48,17 +36,14 @@ public function getSql(SqlWalker $sqlWalker) return $platform->getTrimExpression( $stringPrimary, $trimMode, - $platform->quoteStringLiteral($this->trimChar) + $platform->quoteStringLiteral($this->trimChar), ); } return $platform->getTrimExpression($stringPrimary, $trimMode); } - /** - * {@inheritDoc} - */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $lexer = $parser->getLexer(); @@ -84,7 +69,7 @@ public function parse(Parser $parser) } /** @phpstan-return TrimMode::* */ - private function getTrimMode(): int + private function getTrimMode(): TrimMode|int { if ($this->leading) { return TrimMode::LEADING; diff --git a/src/Query/AST/Functions/UpperFunction.php b/src/Query/AST/Functions/UpperFunction.php index 15273973f98..1ecef66f8a5 100644 --- a/src/Query/AST/Functions/UpperFunction.php +++ b/src/Query/AST/Functions/UpperFunction.php @@ -18,20 +18,17 @@ */ class UpperFunction extends FunctionNode { - /** @var Node */ - public $stringPrimary; + public Node $stringPrimary; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return sprintf( 'UPPER(%s)', - $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary), ); } - /** @inheritDoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { $parser->match(TokenType::T_IDENTIFIER); $parser->match(TokenType::T_OPEN_PARENTHESIS); diff --git a/src/Query/AST/GeneralCaseExpression.php b/src/Query/AST/GeneralCaseExpression.php index b4f322766c7..39d760a26b9 100644 --- a/src/Query/AST/GeneralCaseExpression.php +++ b/src/Query/AST/GeneralCaseExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" * @@ -11,27 +13,15 @@ */ class GeneralCaseExpression extends Node { - /** @var mixed[] */ - public $whenClauses = []; - - /** @var mixed */ - public $elseScalarExpression = null; - - /** - * @param mixed[] $whenClauses - * @param mixed $elseScalarExpression - */ - public function __construct(array $whenClauses, $elseScalarExpression) - { - $this->whenClauses = $whenClauses; - $this->elseScalarExpression = $elseScalarExpression; + /** @param mixed[] $whenClauses */ + public function __construct( + public array $whenClauses, + public mixed $elseScalarExpression = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkGeneralCaseExpression($this); + return $walker->walkGeneralCaseExpression($this); } } diff --git a/src/Query/AST/GroupByClause.php b/src/Query/AST/GroupByClause.php index 1e9b98e7bbf..eb0f1b9f89c 100644 --- a/src/Query/AST/GroupByClause.php +++ b/src/Query/AST/GroupByClause.php @@ -4,22 +4,17 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + class GroupByClause extends Node { - /** @var mixed[] */ - public $groupByItems = []; - /** @param mixed[] $groupByItems */ - public function __construct(array $groupByItems) + public function __construct(public array $groupByItems) { - $this->groupByItems = $groupByItems; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkGroupByClause($this); + return $walker->walkGroupByClause($this); } } diff --git a/src/Query/AST/HavingClause.php b/src/Query/AST/HavingClause.php index f2891a4c745..0d4d821f87d 100644 --- a/src/Query/AST/HavingClause.php +++ b/src/Query/AST/HavingClause.php @@ -4,22 +4,16 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + class HavingClause extends Node { - /** @var ConditionalExpression|Phase2OptimizableConditional */ - public $conditionalExpression; - - /** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */ - public function __construct($conditionalExpression) + public function __construct(public ConditionalExpression|Phase2OptimizableConditional $conditionalExpression) { - $this->conditionalExpression = $conditionalExpression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkHavingClause($this); + return $walker->walkHavingClause($this); } } diff --git a/src/Query/AST/IdentificationVariableDeclaration.php b/src/Query/AST/IdentificationVariableDeclaration.php index 0676b0625f0..c4c7cca3b56 100644 --- a/src/Query/AST/IdentificationVariableDeclaration.php +++ b/src/Query/AST/IdentificationVariableDeclaration.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* * @@ -11,32 +13,16 @@ */ class IdentificationVariableDeclaration extends Node { - /** @var RangeVariableDeclaration|null */ - public $rangeVariableDeclaration = null; - - /** @var IndexBy|null */ - public $indexBy = null; - - /** @var mixed[] */ - public $joins = []; - - /** - * @param RangeVariableDeclaration|null $rangeVariableDecl - * @param IndexBy|null $indexBy - * @param mixed[] $joins - */ - public function __construct($rangeVariableDecl, $indexBy, array $joins) - { - $this->rangeVariableDeclaration = $rangeVariableDecl; - $this->indexBy = $indexBy; - $this->joins = $joins; + /** @param mixed[] $joins */ + public function __construct( + public RangeVariableDeclaration|null $rangeVariableDeclaration = null, + public IndexBy|null $indexBy = null, + public array $joins = [], + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkIdentificationVariableDeclaration($this); + return $walker->walkIdentificationVariableDeclaration($this); } } diff --git a/src/Query/AST/InExpression.php b/src/Query/AST/InExpression.php deleted file mode 100644 index 76f1ccad508..00000000000 --- a/src/Query/AST/InExpression.php +++ /dev/null @@ -1,53 +0,0 @@ -expression = $expression; - } - - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) - { - // We still call the deprecated method in order to not break existing custom SQL walkers. - return $sqlWalker->walkInExpression($this); - } -} diff --git a/src/Query/AST/InListExpression.php b/src/Query/AST/InListExpression.php index 5139fefba1a..dc0f32b52da 100644 --- a/src/Query/AST/InListExpression.php +++ b/src/Query/AST/InListExpression.php @@ -4,19 +4,20 @@ namespace Doctrine\ORM\Query\AST; -/** @phpstan-ignore class.extendsDeprecatedClass */ -class InListExpression extends InExpression -{ - /** @var non-empty-list */ - public $literals; +use Doctrine\ORM\Query\SqlWalker; +class InListExpression extends Node +{ /** @param non-empty-list $literals */ - public function __construct(ArithmeticExpression $expression, array $literals, bool $not = false) - { - $this->literals = $literals; - $this->not = $not; + public function __construct( + public ArithmeticExpression $expression, + public array $literals, + public bool $not = false, + ) { + } - // @phpstan-ignore staticMethod.deprecatedClass - parent::__construct($expression); + public function dispatch(SqlWalker $walker): string + { + return $walker->walkInListExpression($this); } } diff --git a/src/Query/AST/InSubselectExpression.php b/src/Query/AST/InSubselectExpression.php index 12d3aa61dfd..1128285a7f5 100644 --- a/src/Query/AST/InSubselectExpression.php +++ b/src/Query/AST/InSubselectExpression.php @@ -4,18 +4,19 @@ namespace Doctrine\ORM\Query\AST; -/** @phpstan-ignore class.extendsDeprecatedClass */ -class InSubselectExpression extends InExpression +use Doctrine\ORM\Query\SqlWalker; + +class InSubselectExpression extends Node { - /** @var Subselect */ - public $subselect; + public function __construct( + public ArithmeticExpression $expression, + public Subselect $subselect, + public bool $not = false, + ) { + } - public function __construct(ArithmeticExpression $expression, Subselect $subselect, bool $not = false) + public function dispatch(SqlWalker $walker): string { - $this->subselect = $subselect; - $this->not = $not; - - // @phpstan-ignore staticMethod.deprecatedClass - parent::__construct($expression); + return $walker->walkInSubselectExpression($this); } } diff --git a/src/Query/AST/IndexBy.php b/src/Query/AST/IndexBy.php index 0f0eb6a5316..3d90265bc58 100644 --- a/src/Query/AST/IndexBy.php +++ b/src/Query/AST/IndexBy.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * IndexBy ::= "INDEX" "BY" SingleValuedPathExpression * @@ -11,27 +13,14 @@ */ class IndexBy extends Node { - /** @var PathExpression */ - public $singleValuedPathExpression = null; - - /** - * @deprecated - * - * @var PathExpression - */ - public $simpleStateFieldPathExpression = null; - - public function __construct(PathExpression $singleValuedPathExpression) + public function __construct(public PathExpression $singleValuedPathExpression) { - // @phpstan-ignore property.deprecated - $this->singleValuedPathExpression = $this->simpleStateFieldPathExpression = $singleValuedPathExpression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkIndexBy($this); + $walker->walkIndexBy($this); + + return ''; } } diff --git a/src/Query/AST/InputParameter.php b/src/Query/AST/InputParameter.php index bd67d390877..a8e0a3b9f7f 100644 --- a/src/Query/AST/InputParameter.php +++ b/src/Query/AST/InputParameter.php @@ -5,6 +5,7 @@ namespace Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Query\SqlWalker; use function is_numeric; use function strlen; @@ -12,18 +13,11 @@ class InputParameter extends Node { - /** @var bool */ - public $isNamed; - - /** @var string */ - public $name; - - /** - * @param string $value - * - * @throws QueryException - */ - public function __construct($value) + public bool $isNamed; + public string $name; + + /** @throws QueryException */ + public function __construct(string $value) { if (strlen($value) === 1) { throw QueryException::invalidParameterFormat($value); @@ -34,10 +28,7 @@ public function __construct($value) $this->name = $param; } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkInputParameter($this); } diff --git a/src/Query/AST/InstanceOfExpression.php b/src/Query/AST/InstanceOfExpression.php index fcd8916e1a1..3a4e75f7cb8 100644 --- a/src/Query/AST/InstanceOfExpression.php +++ b/src/Query/AST/InstanceOfExpression.php @@ -4,9 +4,7 @@ namespace Doctrine\ORM\Query\AST; -use Doctrine\Deprecations\Deprecation; - -use function func_num_args; +use Doctrine\ORM\Query\SqlWalker; /** * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") @@ -16,40 +14,16 @@ */ class InstanceOfExpression extends Node { - /** @var bool */ - public $not; - - /** @var string */ - public $identificationVariable; - - /** @var non-empty-list */ - public $value; - - /** - * @param string $identVariable - * @param non-empty-list $value - */ - public function __construct($identVariable, array $value = [], bool $not = false) - { - if (func_num_args() < 2) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10267', - 'Not passing a value for $value to %s() is deprecated.', - __METHOD__ - ); - } - - $this->identificationVariable = $identVariable; - $this->value = $value; - $this->not = $not; + /** @param non-empty-list $value */ + public function __construct( + public string $identificationVariable, + public array $value, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkInstanceOfExpression($this); + return $walker->walkInstanceOfExpression($this); } } diff --git a/src/Query/AST/Join.php b/src/Query/AST/Join.php index cf52082a3fa..67833096f86 100644 --- a/src/Query/AST/Join.php +++ b/src/Query/AST/Join.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression * ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression] @@ -12,38 +14,21 @@ */ class Join extends Node { - public const JOIN_TYPE_LEFT = 1; - public const JOIN_TYPE_LEFTOUTER = 2; - public const JOIN_TYPE_INNER = 3; - - /** - * @var int - * @phpstan-var self::JOIN_TYPE_* - */ - public $joinType = self::JOIN_TYPE_INNER; + final public const JOIN_TYPE_LEFT = 1; + final public const JOIN_TYPE_LEFTOUTER = 2; + final public const JOIN_TYPE_INNER = 3; - /** @var Node|null */ - public $joinAssociationDeclaration = null; + public ConditionalExpression|Phase2OptimizableConditional|null $conditionalExpression = null; - /** @var ConditionalExpression|Phase2OptimizableConditional|null */ - public $conditionalExpression = null; - - /** - * @param int $joinType - * @param Node $joinAssociationDeclaration - * @phpstan-param self::JOIN_TYPE_* $joinType - */ - public function __construct($joinType, $joinAssociationDeclaration) - { - $this->joinType = $joinType; - $this->joinAssociationDeclaration = $joinAssociationDeclaration; + /** @phpstan-param self::JOIN_TYPE_* $joinType */ + public function __construct( + public int $joinType, + public Node|null $joinAssociationDeclaration = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkJoin($this); + return $walker->walkJoin($this); } } diff --git a/src/Query/AST/JoinAssociationDeclaration.php b/src/Query/AST/JoinAssociationDeclaration.php index 41d5b48e8c8..e08d7f51514 100644 --- a/src/Query/AST/JoinAssociationDeclaration.php +++ b/src/Query/AST/JoinAssociationDeclaration.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable * @@ -11,32 +13,15 @@ */ class JoinAssociationDeclaration extends Node { - /** @var JoinAssociationPathExpression */ - public $joinAssociationPathExpression; - - /** @var string */ - public $aliasIdentificationVariable; - - /** @var IndexBy|null */ - public $indexBy; - - /** - * @param JoinAssociationPathExpression $joinAssociationPathExpression - * @param string $aliasIdentificationVariable - * @param IndexBy|null $indexBy - */ - public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy) - { - $this->joinAssociationPathExpression = $joinAssociationPathExpression; - $this->aliasIdentificationVariable = $aliasIdentificationVariable; - $this->indexBy = $indexBy; + public function __construct( + public JoinAssociationPathExpression $joinAssociationPathExpression, + public string $aliasIdentificationVariable, + public IndexBy|null $indexBy, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkJoinAssociationDeclaration($this); + return $walker->walkJoinAssociationDeclaration($this); } } diff --git a/src/Query/AST/JoinAssociationPathExpression.php b/src/Query/AST/JoinAssociationPathExpression.php index da1283fb247..230be36a0fa 100644 --- a/src/Query/AST/JoinAssociationPathExpression.php +++ b/src/Query/AST/JoinAssociationPathExpression.php @@ -11,19 +11,9 @@ */ class JoinAssociationPathExpression extends Node { - /** @var string */ - public $identificationVariable; - - /** @var string */ - public $associationField; - - /** - * @param string $identificationVariable - * @param string $associationField - */ - public function __construct($identificationVariable, $associationField) - { - $this->identificationVariable = $identificationVariable; - $this->associationField = $associationField; + public function __construct( + public string $identificationVariable, + public string $associationField, + ) { } } diff --git a/src/Query/AST/JoinClassPathExpression.php b/src/Query/AST/JoinClassPathExpression.php index 8ceca0cccd6..cc9278278ed 100644 --- a/src/Query/AST/JoinClassPathExpression.php +++ b/src/Query/AST/JoinClassPathExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable * @@ -11,26 +13,13 @@ */ class JoinClassPathExpression extends Node { - /** @var mixed */ - public $abstractSchemaName; - - /** @var mixed */ - public $aliasIdentificationVariable; - - /** - * @param mixed $abstractSchemaName - * @param mixed $aliasIdentificationVar - */ - public function __construct($abstractSchemaName, $aliasIdentificationVar) - { - $this->abstractSchemaName = $abstractSchemaName; - $this->aliasIdentificationVariable = $aliasIdentificationVar; + public function __construct( + public mixed $abstractSchemaName, + public mixed $aliasIdentificationVariable, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkJoinPathExpression($this); } diff --git a/src/Query/AST/JoinVariableDeclaration.php b/src/Query/AST/JoinVariableDeclaration.php index f7d197cac22..bf766955c5e 100644 --- a/src/Query/AST/JoinVariableDeclaration.php +++ b/src/Query/AST/JoinVariableDeclaration.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * JoinVariableDeclaration ::= Join [IndexBy] * @@ -11,26 +13,11 @@ */ class JoinVariableDeclaration extends Node { - /** @var Join */ - public $join; - - /** @var IndexBy|null */ - public $indexBy; - - /** - * @param Join $join - * @param IndexBy|null $indexBy - */ - public function __construct($join, $indexBy) + public function __construct(public Join $join, public IndexBy|null $indexBy) { - $this->join = $join; - $this->indexBy = $indexBy; } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkJoinVariableDeclaration($this); } diff --git a/src/Query/AST/LikeExpression.php b/src/Query/AST/LikeExpression.php index 276011fd30a..e3f67f8c39c 100644 --- a/src/Query/AST/LikeExpression.php +++ b/src/Query/AST/LikeExpression.php @@ -5,6 +5,7 @@ namespace Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\SqlWalker; /** * LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char] @@ -13,36 +14,16 @@ */ class LikeExpression extends Node { - /** @var bool */ - public $not = false; - - /** @var Node|string */ - public $stringExpression; - - /** @var InputParameter|FunctionNode|PathExpression|Literal */ - public $stringPattern; - - /** @var Literal|null */ - public $escapeChar; - - /** - * @param Node|string $stringExpression - * @param InputParameter|FunctionNode|PathExpression|Literal $stringPattern - * @param Literal|null $escapeChar - */ - public function __construct($stringExpression, $stringPattern, $escapeChar = null, bool $not = false) - { - $this->stringExpression = $stringExpression; - $this->stringPattern = $stringPattern; - $this->escapeChar = $escapeChar; - $this->not = $not; + public function __construct( + public Node|string $stringExpression, + public InputParameter|FunctionNode|PathExpression|Literal $stringPattern, + public Literal|null $escapeChar = null, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkLikeExpression($this); + return $walker->walkLikeExpression($this); } } diff --git a/src/Query/AST/Literal.php b/src/Query/AST/Literal.php index 6931ddada84..a7b4aae0655 100644 --- a/src/Query/AST/Literal.php +++ b/src/Query/AST/Literal.php @@ -4,36 +4,22 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + class Literal extends Node { - public const STRING = 1; - public const BOOLEAN = 2; - public const NUMERIC = 3; - - /** - * @var int - * @phpstan-var self::* - */ - public $type; + final public const STRING = 1; + final public const BOOLEAN = 2; + final public const NUMERIC = 3; - /** @var mixed */ - public $value; - - /** - * @param int $type - * @param mixed $value - * @phpstan-param self::* $type - */ - public function __construct($type, $value) - { - $this->type = $type; - $this->value = $value; + /** @phpstan-param self::* $type */ + public function __construct( + public int $type, + public mixed $value, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkLiteral($this); } diff --git a/src/Query/AST/NewObjectExpression.php b/src/Query/AST/NewObjectExpression.php index 5068b56051b..7383c487234 100644 --- a/src/Query/AST/NewObjectExpression.php +++ b/src/Query/AST/NewObjectExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" * @@ -11,27 +13,13 @@ */ class NewObjectExpression extends Node { - /** @var string */ - public $className; - - /** @var mixed[] */ - public $args; - - /** - * @param string $className - * @param mixed[] $args - */ - public function __construct($className, array $args) + /** @param mixed[] $args */ + public function __construct(public string $className, public array $args) { - $this->className = $className; - $this->args = $args; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkNewObject($this); + return $walker->walkNewObject($this); } } diff --git a/src/Query/AST/Node.php b/src/Query/AST/Node.php index 507b41c915b..cdb58552c96 100644 --- a/src/Query/AST/Node.php +++ b/src/Query/AST/Node.php @@ -5,6 +5,7 @@ namespace Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\SqlWalker; +use Stringable; use function get_debug_type; use function get_object_vars; @@ -20,40 +21,29 @@ * * @link www.doctrine-project.org */ -abstract class Node +abstract class Node implements Stringable { /** * Double-dispatch method, supposed to dispatch back to the walker. * * Implementation is not mandatory for all nodes. * - * @param SqlWalker $walker - * - * @return string - * * @throws ASTException */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { throw ASTException::noDispatchForNode($this); } /** * Dumps the AST Node into a string representation for information purpose only. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->dump($this); } - /** - * @param mixed $value - * - * @return string - */ - public function dump($value) + public function dump(mixed $value): string { static $ident = 0; diff --git a/src/Query/AST/NullComparisonExpression.php b/src/Query/AST/NullComparisonExpression.php index 90dce6db6a1..e60cb04c5e5 100644 --- a/src/Query/AST/NullComparisonExpression.php +++ b/src/Query/AST/NullComparisonExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" * @@ -11,24 +13,14 @@ */ class NullComparisonExpression extends Node { - /** @var bool */ - public $not; - - /** @var Node */ - public $expression; - - /** @param Node $expression */ - public function __construct($expression, bool $not = false) - { - $this->expression = $expression; - $this->not = $not; + public function __construct( + public Node|string $expression, + public bool $not = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkNullComparisonExpression($this); + return $walker->walkNullComparisonExpression($this); } } diff --git a/src/Query/AST/NullIfExpression.php b/src/Query/AST/NullIfExpression.php index 31630802d99..6fffeeb082c 100644 --- a/src/Query/AST/NullIfExpression.php +++ b/src/Query/AST/NullIfExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" * @@ -11,27 +13,12 @@ */ class NullIfExpression extends Node { - /** @var mixed */ - public $firstExpression; - - /** @var mixed */ - public $secondExpression; - - /** - * @param mixed $firstExpression - * @param mixed $secondExpression - */ - public function __construct($firstExpression, $secondExpression) + public function __construct(public mixed $firstExpression, public mixed $secondExpression) { - $this->firstExpression = $firstExpression; - $this->secondExpression = $secondExpression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkNullIfExpression($this); + return $walker->walkNullIfExpression($this); } } diff --git a/src/Query/AST/OrderByClause.php b/src/Query/AST/OrderByClause.php index befc09bec12..f6d7a671a45 100644 --- a/src/Query/AST/OrderByClause.php +++ b/src/Query/AST/OrderByClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* * @@ -11,20 +13,13 @@ */ class OrderByClause extends Node { - /** @var OrderByItem[] */ - public $orderByItems = []; - /** @param OrderByItem[] $orderByItems */ - public function __construct(array $orderByItems) + public function __construct(public array $orderByItems) { - $this->orderByItems = $orderByItems; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkOrderByClause($this); + return $walker->walkOrderByClause($this); } } diff --git a/src/Query/AST/OrderByItem.php b/src/Query/AST/OrderByItem.php index 5a87c2cd99f..64b3f405adc 100644 --- a/src/Query/AST/OrderByItem.php +++ b/src/Query/AST/OrderByItem.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + use function strtoupper; /** @@ -13,35 +15,24 @@ */ class OrderByItem extends Node { - /** @var mixed */ - public $expression; - - /** @var string */ - public $type; + public string $type; - /** @param mixed $expression */ - public function __construct($expression) + public function __construct(public mixed $expression) { - $this->expression = $expression; } - /** @return bool */ - public function isAsc() + public function isAsc(): bool { return strtoupper($this->type) === 'ASC'; } - /** @return bool */ - public function isDesc() + public function isDesc(): bool { return strtoupper($this->type) === 'DESC'; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkOrderByItem($this); + return $walker->walkOrderByItem($this); } } diff --git a/src/Query/AST/ParenthesisExpression.php b/src/Query/AST/ParenthesisExpression.php index 463ced40d36..cda6d19aeea 100644 --- a/src/Query/AST/ParenthesisExpression.php +++ b/src/Query/AST/ParenthesisExpression.php @@ -4,23 +4,18 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * ParenthesisExpression ::= "(" ArithmeticPrimary ")" */ class ParenthesisExpression extends Node { - /** @var Node */ - public $expression; - - public function __construct(Node $expression) + public function __construct(public Node $expression) { - $this->expression = $expression; } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkParenthesisExpression($this); } diff --git a/src/Query/AST/PartialObjectExpression.php b/src/Query/AST/PartialObjectExpression.php index 020c0999a9b..875f4453fe1 100644 --- a/src/Query/AST/PartialObjectExpression.php +++ b/src/Query/AST/PartialObjectExpression.php @@ -6,19 +6,10 @@ class PartialObjectExpression extends Node { - /** @var string */ - public $identificationVariable; - - /** @var mixed[] */ - public $partialFieldSet; - - /** - * @param string $identificationVariable - * @param mixed[] $partialFieldSet - */ - public function __construct($identificationVariable, array $partialFieldSet) - { - $this->identificationVariable = $identificationVariable; - $this->partialFieldSet = $partialFieldSet; + /** @param mixed[] $partialFieldSet */ + public function __construct( + public string $identificationVariable, + public array $partialFieldSet, + ) { } } diff --git a/src/Query/AST/PathExpression.php b/src/Query/AST/PathExpression.php index 786fef45831..15a3abfd023 100644 --- a/src/Query/AST/PathExpression.php +++ b/src/Query/AST/PathExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression @@ -15,45 +17,22 @@ */ class PathExpression extends Node { - public const TYPE_COLLECTION_VALUED_ASSOCIATION = 2; - public const TYPE_SINGLE_VALUED_ASSOCIATION = 4; - public const TYPE_STATE_FIELD = 8; - - /** - * @var int|null - * @phpstan-var self::TYPE_*|null - */ - public $type; - - /** - * @var int - * @phpstan-var int-mask-of - */ - public $expectedType; - - /** @var string */ - public $identificationVariable; - - /** @var string|null */ - public $field; - - /** - * @param int $expectedType - * @param string $identificationVariable - * @param string|null $field - * @phpstan-param int-mask-of $expectedType - */ - public function __construct($expectedType, $identificationVariable, $field = null) - { - $this->expectedType = $expectedType; - $this->identificationVariable = $identificationVariable; - $this->field = $field; + final public const TYPE_COLLECTION_VALUED_ASSOCIATION = 2; + final public const TYPE_SINGLE_VALUED_ASSOCIATION = 4; + final public const TYPE_STATE_FIELD = 8; + + /** @phpstan-var self::TYPE_*|null */ + public int|null $type = null; + + /** @phpstan-param int-mask-of $expectedType */ + public function __construct( + public int $expectedType, + public string $identificationVariable, + public string|null $field = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkPathExpression($this); } diff --git a/src/Query/AST/QuantifiedExpression.php b/src/Query/AST/QuantifiedExpression.php index 200b729f1ad..90331cdd35f 100644 --- a/src/Query/AST/QuantifiedExpression.php +++ b/src/Query/AST/QuantifiedExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + use function strtoupper; /** @@ -13,41 +15,29 @@ */ class QuantifiedExpression extends Node { - /** @var string */ - public $type; - - /** @var Subselect */ - public $subselect; + public string $type; - /** @param Subselect $subselect */ - public function __construct($subselect) + public function __construct(public Subselect $subselect) { - $this->subselect = $subselect; } - /** @return bool */ - public function isAll() + public function isAll(): bool { return strtoupper($this->type) === 'ALL'; } - /** @return bool */ - public function isAny() + public function isAny(): bool { return strtoupper($this->type) === 'ANY'; } - /** @return bool */ - public function isSome() + public function isSome(): bool { return strtoupper($this->type) === 'SOME'; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkQuantifiedExpression($this); + return $walker->walkQuantifiedExpression($this); } } diff --git a/src/Query/AST/RangeVariableDeclaration.php b/src/Query/AST/RangeVariableDeclaration.php index b01c45310f0..59bd5c8d586 100644 --- a/src/Query/AST/RangeVariableDeclaration.php +++ b/src/Query/AST/RangeVariableDeclaration.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable * @@ -11,31 +13,14 @@ */ class RangeVariableDeclaration extends Node { - /** @var string */ - public $abstractSchemaName; - - /** @var string */ - public $aliasIdentificationVariable; - - /** @var bool */ - public $isRoot; - - /** - * @param string $abstractSchemaName - * @param string $aliasIdentificationVar - * @param bool $isRoot - */ - public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true) - { - $this->abstractSchemaName = $abstractSchemaName; - $this->aliasIdentificationVariable = $aliasIdentificationVar; - $this->isRoot = $isRoot; + public function __construct( + public string $abstractSchemaName, + public string $aliasIdentificationVariable, + public bool $isRoot = true, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($walker) + public function dispatch(SqlWalker $walker): string { return $walker->walkRangeVariableDeclaration($this); } diff --git a/src/Query/AST/SelectClause.php b/src/Query/AST/SelectClause.php index 6d2e2227600..ad50e672ee5 100644 --- a/src/Query/AST/SelectClause.php +++ b/src/Query/AST/SelectClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} * @@ -11,27 +13,15 @@ */ class SelectClause extends Node { - /** @var bool */ - public $isDistinct; - - /** @var mixed[] */ - public $selectExpressions = []; - - /** - * @param mixed[] $selectExpressions - * @param bool $isDistinct - */ - public function __construct(array $selectExpressions, $isDistinct) - { - $this->isDistinct = $isDistinct; - $this->selectExpressions = $selectExpressions; + /** @param mixed[] $selectExpressions */ + public function __construct( + public array $selectExpressions, + public bool $isDistinct, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSelectClause($this); + return $walker->walkSelectClause($this); } } diff --git a/src/Query/AST/SelectExpression.php b/src/Query/AST/SelectExpression.php index 74376a556d2..f09f3cda0ac 100644 --- a/src/Query/AST/SelectExpression.php +++ b/src/Query/AST/SelectExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable] @@ -12,32 +14,15 @@ */ class SelectExpression extends Node { - /** @var mixed */ - public $expression; - - /** @var string|null */ - public $fieldIdentificationVariable; - - /** @var bool */ - public $hiddenAliasResultVariable; - - /** - * @param mixed $expression - * @param string|null $fieldIdentificationVariable - * @param bool $hiddenAliasResultVariable - */ - public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) - { - $this->expression = $expression; - $this->fieldIdentificationVariable = $fieldIdentificationVariable; - $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + public function __construct( + public mixed $expression, + public string|null $fieldIdentificationVariable, + public bool $hiddenAliasResultVariable = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSelectExpression($this); + return $walker->walkSelectExpression($this); } } diff --git a/src/Query/AST/SelectStatement.php b/src/Query/AST/SelectStatement.php index f4602a122df..399462f2c25 100644 --- a/src/Query/AST/SelectStatement.php +++ b/src/Query/AST/SelectStatement.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] * @@ -11,39 +13,20 @@ */ class SelectStatement extends Node { - /** @var SelectClause */ - public $selectClause; - - /** @var FromClause */ - public $fromClause; - - /** @var WhereClause|null */ - public $whereClause; + public WhereClause|null $whereClause = null; - /** @var GroupByClause|null */ - public $groupByClause; + public GroupByClause|null $groupByClause = null; - /** @var HavingClause|null */ - public $havingClause; + public HavingClause|null $havingClause = null; - /** @var OrderByClause|null */ - public $orderByClause; + public OrderByClause|null $orderByClause = null; - /** - * @param SelectClause $selectClause - * @param FromClause $fromClause - */ - public function __construct($selectClause, $fromClause) + public function __construct(public SelectClause $selectClause, public FromClause $fromClause) { - $this->selectClause = $selectClause; - $this->fromClause = $fromClause; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSelectStatement($this); + return $walker->walkSelectStatement($this); } } diff --git a/src/Query/AST/SimpleArithmeticExpression.php b/src/Query/AST/SimpleArithmeticExpression.php index 701b0042749..ae7ca440ad5 100644 --- a/src/Query/AST/SimpleArithmeticExpression.php +++ b/src/Query/AST/SimpleArithmeticExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* * @@ -11,20 +13,13 @@ */ class SimpleArithmeticExpression extends Node { - /** @var mixed[] */ - public $arithmeticTerms = []; - /** @param mixed[] $arithmeticTerms */ - public function __construct(array $arithmeticTerms) + public function __construct(public array $arithmeticTerms) { - $this->arithmeticTerms = $arithmeticTerms; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSimpleArithmeticExpression($this); + return $walker->walkSimpleArithmeticExpression($this); } } diff --git a/src/Query/AST/SimpleCaseExpression.php b/src/Query/AST/SimpleCaseExpression.php index 39b743e4d12..b3764ba1545 100644 --- a/src/Query/AST/SimpleCaseExpression.php +++ b/src/Query/AST/SimpleCaseExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" * @@ -11,32 +13,16 @@ */ class SimpleCaseExpression extends Node { - /** @var PathExpression */ - public $caseOperand = null; - - /** @var mixed[] */ - public $simpleWhenClauses = []; - - /** @var mixed */ - public $elseScalarExpression = null; - - /** - * @param PathExpression $caseOperand - * @param mixed[] $simpleWhenClauses - * @param mixed $elseScalarExpression - */ - public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) - { - $this->caseOperand = $caseOperand; - $this->simpleWhenClauses = $simpleWhenClauses; - $this->elseScalarExpression = $elseScalarExpression; + /** @param mixed[] $simpleWhenClauses */ + public function __construct( + public PathExpression|null $caseOperand = null, + public array $simpleWhenClauses = [], + public mixed $elseScalarExpression = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSimpleCaseExpression($this); + return $walker->walkSimpleCaseExpression($this); } } diff --git a/src/Query/AST/SimpleSelectClause.php b/src/Query/AST/SimpleSelectClause.php index f15e1a0c1fb..0259e3bd162 100644 --- a/src/Query/AST/SimpleSelectClause.php +++ b/src/Query/AST/SimpleSelectClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression * @@ -11,27 +13,14 @@ */ class SimpleSelectClause extends Node { - /** @var bool */ - public $isDistinct = false; - - /** @var SimpleSelectExpression */ - public $simpleSelectExpression; - - /** - * @param SimpleSelectExpression $simpleSelectExpression - * @param bool $isDistinct - */ - public function __construct($simpleSelectExpression, $isDistinct) - { - $this->simpleSelectExpression = $simpleSelectExpression; - $this->isDistinct = $isDistinct; + public function __construct( + public SimpleSelectExpression $simpleSelectExpression, + public bool $isDistinct = false, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSimpleSelectClause($this); + return $walker->walkSimpleSelectClause($this); } } diff --git a/src/Query/AST/SimpleSelectExpression.php b/src/Query/AST/SimpleSelectExpression.php index 04033cf9b83..97e8f087e5c 100644 --- a/src/Query/AST/SimpleSelectExpression.php +++ b/src/Query/AST/SimpleSelectExpression.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable * | (AggregateExpression [["AS"] FieldAliasIdentificationVariable]) @@ -12,23 +14,14 @@ */ class SimpleSelectExpression extends Node { - /** @var Node|string */ - public $expression; - - /** @var string */ - public $fieldIdentificationVariable; + public string|null $fieldIdentificationVariable = null; - /** @param Node|string $expression */ - public function __construct($expression) + public function __construct(public Node|string $expression) { - $this->expression = $expression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSimpleSelectExpression($this); + return $walker->walkSimpleSelectExpression($this); } } diff --git a/src/Query/AST/SimpleWhenClause.php b/src/Query/AST/SimpleWhenClause.php index a4e624009b2..892165a9891 100644 --- a/src/Query/AST/SimpleWhenClause.php +++ b/src/Query/AST/SimpleWhenClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression * @@ -11,27 +13,14 @@ */ class SimpleWhenClause extends Node { - /** @var mixed */ - public $caseScalarExpression = null; - - /** @var mixed */ - public $thenScalarExpression = null; - - /** - * @param mixed $caseScalarExpression - * @param mixed $thenScalarExpression - */ - public function __construct($caseScalarExpression, $thenScalarExpression) - { - $this->caseScalarExpression = $caseScalarExpression; - $this->thenScalarExpression = $thenScalarExpression; + public function __construct( + public mixed $caseScalarExpression = null, + public mixed $thenScalarExpression = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkWhenClauseExpression($this); + return $walker->walkWhenClauseExpression($this); } } diff --git a/src/Query/AST/Subselect.php b/src/Query/AST/Subselect.php index 58e31e1fbf9..8ff85954fd7 100644 --- a/src/Query/AST/Subselect.php +++ b/src/Query/AST/Subselect.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] * @@ -11,39 +13,20 @@ */ class Subselect extends Node { - /** @var SimpleSelectClause */ - public $simpleSelectClause; - - /** @var SubselectFromClause */ - public $subselectFromClause; - - /** @var WhereClause|null */ - public $whereClause; + public WhereClause|null $whereClause = null; - /** @var GroupByClause|null */ - public $groupByClause; + public GroupByClause|null $groupByClause = null; - /** @var HavingClause|null */ - public $havingClause; + public HavingClause|null $havingClause = null; - /** @var OrderByClause|null */ - public $orderByClause; + public OrderByClause|null $orderByClause = null; - /** - * @param SimpleSelectClause $simpleSelectClause - * @param SubselectFromClause $subselectFromClause - */ - public function __construct($simpleSelectClause, $subselectFromClause) + public function __construct(public SimpleSelectClause $simpleSelectClause, public SubselectFromClause $subselectFromClause) { - $this->simpleSelectClause = $simpleSelectClause; - $this->subselectFromClause = $subselectFromClause; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSubselect($this); + return $walker->walkSubselect($this); } } diff --git a/src/Query/AST/SubselectFromClause.php b/src/Query/AST/SubselectFromClause.php index f160ae91042..7cf01e22636 100644 --- a/src/Query/AST/SubselectFromClause.php +++ b/src/Query/AST/SubselectFromClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* * @@ -11,20 +13,13 @@ */ class SubselectFromClause extends Node { - /** @var mixed[] */ - public $identificationVariableDeclarations = []; - /** @param mixed[] $identificationVariableDeclarations */ - public function __construct(array $identificationVariableDeclarations) + public function __construct(public array $identificationVariableDeclarations) { - $this->identificationVariableDeclarations = $identificationVariableDeclarations; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkSubselectFromClause($this); + return $walker->walkSubselectFromClause($this); } } diff --git a/src/Query/AST/SubselectIdentificationVariableDeclaration.php b/src/Query/AST/SubselectIdentificationVariableDeclaration.php index b0e881b0f47..eadf6bc41f7 100644 --- a/src/Query/AST/SubselectIdentificationVariableDeclaration.php +++ b/src/Query/AST/SubselectIdentificationVariableDeclaration.php @@ -11,19 +11,9 @@ */ class SubselectIdentificationVariableDeclaration { - /** @var PathExpression */ - public $associationPathExpression; - - /** @var string */ - public $aliasIdentificationVariable; - - /** - * @param PathExpression $associationPathExpression - * @param string $aliasIdentificationVariable - */ - public function __construct($associationPathExpression, $aliasIdentificationVariable) - { - $this->associationPathExpression = $associationPathExpression; - $this->aliasIdentificationVariable = $aliasIdentificationVariable; + public function __construct( + public PathExpression $associationPathExpression, + public string $aliasIdentificationVariable, + ) { } } diff --git a/src/Query/AST/UpdateClause.php b/src/Query/AST/UpdateClause.php index 1a8b0eec7e5..cafcff01b75 100644 --- a/src/Query/AST/UpdateClause.php +++ b/src/Query/AST/UpdateClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}* * @@ -11,30 +13,17 @@ */ class UpdateClause extends Node { - /** @var string */ - public $abstractSchemaName; - - /** @var string */ - public $aliasIdentificationVariable; + public string $aliasIdentificationVariable; - /** @var mixed[] */ - public $updateItems = []; - - /** - * @param string $abstractSchemaName - * @param mixed[] $updateItems - */ - public function __construct($abstractSchemaName, array $updateItems) - { - $this->abstractSchemaName = $abstractSchemaName; - $this->updateItems = $updateItems; + /** @param mixed[] $updateItems */ + public function __construct( + public string $abstractSchemaName, + public array $updateItems, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkUpdateClause($this); + return $walker->walkUpdateClause($this); } } diff --git a/src/Query/AST/UpdateItem.php b/src/Query/AST/UpdateItem.php index 65e2ba4c8c5..b540593c724 100644 --- a/src/Query/AST/UpdateItem.php +++ b/src/Query/AST/UpdateItem.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | @@ -13,27 +15,12 @@ */ class UpdateItem extends Node { - /** @var PathExpression */ - public $pathExpression; - - /** @var InputParameter|ArithmeticExpression|null */ - public $newValue; - - /** - * @param PathExpression $pathExpression - * @param InputParameter|ArithmeticExpression|null $newValue - */ - public function __construct($pathExpression, $newValue) + public function __construct(public PathExpression $pathExpression, public InputParameter|ArithmeticExpression|null $newValue) { - $this->pathExpression = $pathExpression; - $this->newValue = $newValue; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkUpdateItem($this); + return $walker->walkUpdateItem($this); } } diff --git a/src/Query/AST/UpdateStatement.php b/src/Query/AST/UpdateStatement.php index 8a770052e60..7ea50766b70 100644 --- a/src/Query/AST/UpdateStatement.php +++ b/src/Query/AST/UpdateStatement.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * UpdateStatement = UpdateClause [WhereClause] * @@ -11,23 +13,14 @@ */ class UpdateStatement extends Node { - /** @var UpdateClause */ - public $updateClause; - - /** @var WhereClause|null */ - public $whereClause; + public WhereClause|null $whereClause = null; - /** @param UpdateClause $updateClause */ - public function __construct($updateClause) + public function __construct(public UpdateClause $updateClause) { - $this->updateClause = $updateClause; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkUpdateStatement($this); + return $walker->walkUpdateStatement($this); } } diff --git a/src/Query/AST/WhenClause.php b/src/Query/AST/WhenClause.php index 0bfe4a43f95..9bf194e300d 100644 --- a/src/Query/AST/WhenClause.php +++ b/src/Query/AST/WhenClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression * @@ -11,27 +13,14 @@ */ class WhenClause extends Node { - /** @var ConditionalExpression|Phase2OptimizableConditional */ - public $caseConditionExpression; - - /** @var mixed */ - public $thenScalarExpression = null; - - /** - * @param ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression - * @param mixed $thenScalarExpression - */ - public function __construct($caseConditionExpression, $thenScalarExpression) - { - $this->caseConditionExpression = $caseConditionExpression; - $this->thenScalarExpression = $thenScalarExpression; + public function __construct( + public ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression, + public mixed $thenScalarExpression = null, + ) { } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkWhenClauseExpression($this); + return $walker->walkWhenClauseExpression($this); } } diff --git a/src/Query/AST/WhereClause.php b/src/Query/AST/WhereClause.php index d66ca1a33ad..e4d7b66cc20 100644 --- a/src/Query/AST/WhereClause.php +++ b/src/Query/AST/WhereClause.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\SqlWalker; + /** * WhereClause ::= "WHERE" ConditionalExpression * @@ -11,20 +13,12 @@ */ class WhereClause extends Node { - /** @var ConditionalExpression|Phase2OptimizableConditional */ - public $conditionalExpression; - - /** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */ - public function __construct($conditionalExpression) + public function __construct(public ConditionalExpression|Phase2OptimizableConditional $conditionalExpression) { - $this->conditionalExpression = $conditionalExpression; } - /** - * {@inheritDoc} - */ - public function dispatch($sqlWalker) + public function dispatch(SqlWalker $walker): string { - return $sqlWalker->walkWhereClause($this); + return $walker->walkWhereClause($this); } } diff --git a/src/Query/Exec/AbstractSqlExecutor.php b/src/Query/Exec/AbstractSqlExecutor.php index 45277413e2a..65c7592ad06 100644 --- a/src/Query/Exec/AbstractSqlExecutor.php +++ b/src/Query/Exec/AbstractSqlExecutor.php @@ -4,51 +4,35 @@ namespace Doctrine\ORM\Query\Exec; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; -use function array_diff; -use function array_keys; -use function array_map; -use function array_values; -use function str_replace; - /** * Base class for SQL statement executors. * * @link http://www.doctrine-project.org * * @todo Rename: AbstractSQLExecutor + * @phpstan-type WrapperParameterType = string|Type|ParameterType::*|ArrayParameterType::* + * @phpstan-type WrapperParameterTypeArray = array, WrapperParameterType>|array */ abstract class AbstractSqlExecutor { - /** - * @deprecated use $sqlStatements instead - * - * @var list|string - */ - protected $_sqlStatements; - /** @var list|string */ - protected $sqlStatements; - - /** @var QueryCacheProfile */ - protected $queryCacheProfile; + protected array|string $sqlStatements; - public function __construct() - { - // @phpstan-ignore property.deprecated - $this->_sqlStatements = &$this->sqlStatements; - } + protected QueryCacheProfile|null $queryCacheProfile = null; /** * Gets the SQL statements that are executed by the executor. * * @return list|string All the SQL update statements. */ - public function getSqlStatements() + public function getSqlStatements(): array|string { return $this->sqlStatements; } @@ -69,38 +53,9 @@ public function removeQueryCacheProfile(): void /** * Executes all sql statements. * - * @param Connection $conn The database connection that is used to execute the queries. - * @param list|array $params The parameters. - * @param array|array $types The parameter types. - * - * @return Result|int + * @param Connection $conn The database connection that is used to execute the queries. + * @param list|array $params The parameters. + * @phpstan-param WrapperParameterTypeArray $types The parameter types. */ - abstract public function execute(Connection $conn, array $params, array $types); - - /** @return list */ - public function __sleep(): array - { - /* Two reasons for this: - - we do not need to serialize the deprecated property, we can - rebuild the reference to the new property in __wakeup() - - not having the legacy property in the serialized data means the - serialized representation becomes compatible with 3.0.x, meaning - there will not be a deprecation warning about a missing property - when unserializing data */ - return array_values(array_diff(array_map(static function (string $prop): string { - return str_replace("\0*\0", '', $prop); - }, array_keys((array) $this)), ['_sqlStatements'])); - } - - public function __wakeup(): void - { - // @phpstan-ignore property.deprecated - if ($this->_sqlStatements !== null && $this->sqlStatements === null) { - // @phpstan-ignore property.deprecated - $this->sqlStatements = $this->_sqlStatements; - } - - // @phpstan-ignore property.deprecated - $this->_sqlStatements = &$this->sqlStatements; - } + abstract public function execute(Connection $conn, array $params, array $types): Result|int; } diff --git a/src/Query/Exec/FinalizedSelectExecutor.php b/src/Query/Exec/FinalizedSelectExecutor.php index a97e6f428c5..872d42cb6c4 100644 --- a/src/Query/Exec/FinalizedSelectExecutor.php +++ b/src/Query/Exec/FinalizedSelectExecutor.php @@ -6,7 +6,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Result; -use Doctrine\DBAL\Types\Type; /** * SQL executor for a given, final, single SELECT SQL query @@ -17,14 +16,11 @@ class FinalizedSelectExecutor extends AbstractSqlExecutor { public function __construct(string $sql) { - parent::__construct(); - $this->sqlStatements = $sql; } /** - * @param list|array $params - * @param array|array $types + * {@inheritDoc} */ public function execute(Connection $conn, array $params, array $types): Result { diff --git a/src/Query/Exec/MultiTableDeleteExecutor.php b/src/Query/Exec/MultiTableDeleteExecutor.php index bea246e2f5b..6096462c7d9 100644 --- a/src/Query/Exec/MultiTableDeleteExecutor.php +++ b/src/Query/Exec/MultiTableDeleteExecutor.php @@ -13,7 +13,6 @@ use Doctrine\ORM\Utility\PersisterHelper; use Throwable; -use function array_merge; use function array_reverse; use function implode; @@ -25,14 +24,9 @@ */ class MultiTableDeleteExecutor extends AbstractSqlExecutor { - /** @var string */ - private $createTempTableSql; - - /** @var string */ - private $dropTempTableSql; - - /** @var string */ - private $insertSql; + private readonly string $createTempTableSql; + private readonly string $dropTempTableSql; + private readonly string $insertSql; /** * Initializes a new MultiTableDeleteExecutor. @@ -43,10 +37,8 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor * @param DeleteStatement $AST The root AST node of the DQL query. * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. */ - public function __construct(AST\Node $AST, $sqlWalker) + public function __construct(AST\Node $AST, SqlWalker $sqlWalker) { - parent::__construct(); - $em = $sqlWalker->getEntityManager(); $conn = $em->getConnection(); $platform = $conn->getDatabasePlatform(); @@ -67,23 +59,25 @@ public function __construct(AST\Node $AST, $sqlWalker) // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); - $this->insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); - $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); - $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); - $this->insertSql .= $sqlWalker->walkFromClause($fromClause); + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); + $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); + $insertSql .= $sqlWalker->walkFromClause($fromClause); // Append WHERE clause, if there is one. if ($AST->whereClause) { - $this->insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); } + $this->insertSql = $insertSql; + // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; // 3. Create and store DELETE statements - $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses); + $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses]; foreach (array_reverse($classNames) as $className) { $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform); $this->sqlStatements[] = 'DELETE FROM ' . $tableName @@ -94,6 +88,7 @@ public function __construct(AST\Node $AST, $sqlWalker) $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = [ + 'name' => $idColumnName, 'notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)), ]; @@ -106,10 +101,8 @@ public function __construct(AST\Node $AST, $sqlWalker) /** * {@inheritDoc} - * - * @return int */ - public function execute(Connection $conn, array $params, array $types) + public function execute(Connection $conn, array $params, array $types): int { // Create temporary id table $conn->executeStatement($this->createTempTableSql); diff --git a/src/Query/Exec/MultiTableUpdateExecutor.php b/src/Query/Exec/MultiTableUpdateExecutor.php index eb3ac148020..dab1b6172f9 100644 --- a/src/Query/Exec/MultiTableUpdateExecutor.php +++ b/src/Query/Exec/MultiTableUpdateExecutor.php @@ -13,7 +13,6 @@ use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Utility\PersisterHelper; -use function array_merge; use function array_reverse; use function array_slice; use function implode; @@ -24,20 +23,13 @@ */ class MultiTableUpdateExecutor extends AbstractSqlExecutor { - /** @var string */ - private $createTempTableSql; - - /** @var string */ - private $dropTempTableSql; - - /** @var string */ - private $insertSql; + private readonly string $createTempTableSql; + private readonly string $dropTempTableSql; + private readonly string $insertSql; /** @var mixed[] */ - private $sqlParameters = []; - - /** @var int */ - private $numParametersInUpdateClause = 0; + private array $sqlParameters = []; + private int $numParametersInUpdateClause = 0; /** * Initializes a new MultiTableUpdateExecutor. @@ -48,14 +40,13 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor * @param UpdateStatement $AST The root AST node of the DQL query. * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. */ - public function __construct(AST\Node $AST, $sqlWalker) + public function __construct(AST\Node $AST, SqlWalker $sqlWalker) { - parent::__construct(); - - $em = $sqlWalker->getEntityManager(); - $conn = $em->getConnection(); - $platform = $conn->getDatabasePlatform(); - $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + $this->sqlStatements = []; if ($conn instanceof PrimaryReadReplicaConnection) { $conn->ensureConnectedToPrimary(); @@ -74,19 +65,19 @@ public function __construct(AST\Node $AST, $sqlWalker) // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); - $this->insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); - $this->insertSql .= $sqlWalker->walkFromClause($fromClause); + $insertSql .= $sqlWalker->walkFromClause($fromClause); // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; // 3. Create and store UPDATE statements - $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses); + $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses]; foreach (array_reverse($classNames) as $className) { $affected = false; @@ -98,8 +89,8 @@ public function __construct(AST\Node $AST, $sqlWalker) $field = $updateItem->pathExpression->field; if ( - (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited'])) || - (isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) + (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]->inherited)) || + (isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]->inherited)) ) { $newValue = $updateItem->newValue; @@ -127,14 +118,17 @@ public function __construct(AST\Node $AST, $sqlWalker) // Append WHERE clause to insertSql, if there is one. if ($AST->whereClause) { - $this->insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); } + $this->insertSql = $insertSql; + // 4. Store DDL for temporary identifier table. $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = [ + 'name' => $idColumnName, 'notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)), ]; @@ -148,10 +142,8 @@ public function __construct(AST\Node $AST, $sqlWalker) /** * {@inheritDoc} - * - * @return int */ - public function execute(Connection $conn, array $params, array $types) + public function execute(Connection $conn, array $params, array $types): int { // Create temporary id table $conn->executeStatement($this->createTempTableSql); @@ -161,7 +153,7 @@ public function execute(Connection $conn, array $params, array $types) $numUpdated = $conn->executeStatement( $this->insertSql, array_slice($params, $this->numParametersInUpdateClause), - array_slice($types, $this->numParametersInUpdateClause) + array_slice($types, $this->numParametersInUpdateClause), ); // Execute UPDATE statements diff --git a/src/Query/Exec/PreparedExecutorFinalizer.php b/src/Query/Exec/PreparedExecutorFinalizer.php index b89b124e352..26161dba782 100644 --- a/src/Query/Exec/PreparedExecutorFinalizer.php +++ b/src/Query/Exec/PreparedExecutorFinalizer.php @@ -13,8 +13,7 @@ */ final class PreparedExecutorFinalizer implements SqlFinalizer { - /** @var AbstractSqlExecutor */ - private $executor; + private AbstractSqlExecutor $executor; public function __construct(AbstractSqlExecutor $exeutor) { diff --git a/src/Query/Exec/SingleSelectExecutor.php b/src/Query/Exec/SingleSelectExecutor.php index fbcab590e09..5445edb9994 100644 --- a/src/Query/Exec/SingleSelectExecutor.php +++ b/src/Query/Exec/SingleSelectExecutor.php @@ -18,17 +18,13 @@ class SingleSelectExecutor extends AbstractSqlExecutor { public function __construct(SelectStatement $AST, SqlWalker $sqlWalker) { - parent::__construct(); - $this->sqlStatements = $sqlWalker->walkSelectStatement($AST); } /** * {@inheritDoc} - * - * @return Result */ - public function execute(Connection $conn, array $params, array $types) + public function execute(Connection $conn, array $params, array $types): Result { return $conn->executeQuery($this->sqlStatements, $params, $types, $this->queryCacheProfile); } diff --git a/src/Query/Exec/SingleSelectSqlFinalizer.php b/src/Query/Exec/SingleSelectSqlFinalizer.php index 73e9a0c1547..bbaab2b6360 100644 --- a/src/Query/Exec/SingleSelectSqlFinalizer.php +++ b/src/Query/Exec/SingleSelectSqlFinalizer.php @@ -20,12 +20,8 @@ class SingleSelectSqlFinalizer implements SqlFinalizer { use LockSqlHelper; - /** @var string */ - private $sql; - - public function __construct(string $sql) + public function __construct(private string $sql) { - $this->sql = $sql; } /** diff --git a/src/Query/Exec/SingleTableDeleteUpdateExecutor.php b/src/Query/Exec/SingleTableDeleteUpdateExecutor.php index 4a4a2986467..721bb40ad1f 100644 --- a/src/Query/Exec/SingleTableDeleteUpdateExecutor.php +++ b/src/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -17,11 +17,8 @@ */ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor { - /** @param SqlWalker $sqlWalker */ - public function __construct(AST\Node $AST, $sqlWalker) + public function __construct(AST\Node $AST, SqlWalker $sqlWalker) { - parent::__construct(); - if ($AST instanceof AST\UpdateStatement) { $this->sqlStatements = $sqlWalker->walkUpdateStatement($AST); } elseif ($AST instanceof AST\DeleteStatement) { @@ -31,10 +28,8 @@ public function __construct(AST\Node $AST, $sqlWalker) /** * {@inheritDoc} - * - * @return int */ - public function execute(Connection $conn, array $params, array $types) + public function execute(Connection $conn, array $params, array $types): int { if ($conn instanceof PrimaryReadReplicaConnection) { $conn->ensureConnectedToPrimary(); diff --git a/src/Query/Expr.php b/src/Query/Expr.php index 309e7bdb0d1..65f30827f24 100644 --- a/src/Query/Expr.php +++ b/src/Query/Expr.php @@ -4,9 +4,9 @@ namespace Doctrine\ORM\Query; +use Doctrine\ORM\Internal\NoUnknownNamedArguments; use Traversable; -use function func_get_args; use function implode; use function is_bool; use function is_float; @@ -24,6 +24,8 @@ */ class Expr { + use NoUnknownNamedArguments; + /** * Creates a conjunction of the given boolean expressions. * @@ -33,16 +35,15 @@ class Expr * // (u.type = ?1) AND (u.role = ?2) * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2')); * - * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null, - * but requires at least one defined - * when converting to string. - * @phpstan-param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x - * - * @return Expr\Andx + * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x Optional clause. Defaults to null, + * but requires at least one defined + * when converting to string. */ - public function andX($x = null) + public function andX(Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x): Expr\Andx { - return new Expr\Andx(func_get_args()); + self::validateVariadicParameter($x); + + return new Expr\Andx($x); } /** @@ -54,38 +55,29 @@ public function andX($x = null) * // (u.type = ?1) OR (u.role = ?2) * $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2')); * - * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null, - * but requires at least one defined - * when converting to string. - * @phpstan-param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x - * - * @return Expr\Orx + * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x Optional clause. Defaults to null, + * but requires at least one defined + * when converting to string. */ - public function orX($x = null) + public function orX(Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x): Expr\Orx { - return new Expr\Orx(func_get_args()); + self::validateVariadicParameter($x); + + return new Expr\Orx($x); } /** * Creates an ASCending order expression. - * - * @param mixed $expr - * - * @return Expr\OrderBy */ - public function asc($expr) + public function asc(mixed $expr): Expr\OrderBy { return new Expr\OrderBy($expr, 'ASC'); } /** * Creates a DESCending order expression. - * - * @param mixed $expr - * - * @return Expr\OrderBy */ - public function desc($expr) + public function desc(mixed $expr): Expr\OrderBy { return new Expr\OrderBy($expr, 'DESC'); } @@ -102,10 +94,8 @@ public function desc($expr) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function eq($x, $y) + public function eq(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::EQ, $y); } @@ -121,10 +111,8 @@ public function eq($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function neq($x, $y) + public function neq(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::NEQ, $y); } @@ -140,10 +128,8 @@ public function neq($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function lt($x, $y) + public function lt(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::LT, $y); } @@ -159,10 +145,8 @@ public function lt($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function lte($x, $y) + public function lte(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::LTE, $y); } @@ -178,10 +162,8 @@ public function lte($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function gt($x, $y) + public function gt(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::GT, $y); } @@ -197,10 +179,8 @@ public function gt($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Comparison */ - public function gte($x, $y) + public function gte(mixed $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, Expr\Comparison::GTE, $y); } @@ -209,10 +189,8 @@ public function gte($x, $y) * Creates an instance of AVG() function, with the given argument. * * @param mixed $x Argument to be used in AVG() function. - * - * @return Expr\Func */ - public function avg($x) + public function avg(mixed $x): Expr\Func { return new Expr\Func('AVG', [$x]); } @@ -221,10 +199,8 @@ public function avg($x) * Creates an instance of MAX() function, with the given argument. * * @param mixed $x Argument to be used in MAX() function. - * - * @return Expr\Func */ - public function max($x) + public function max(mixed $x): Expr\Func { return new Expr\Func('MAX', [$x]); } @@ -233,10 +209,8 @@ public function max($x) * Creates an instance of MIN() function, with the given argument. * * @param mixed $x Argument to be used in MIN() function. - * - * @return Expr\Func */ - public function min($x) + public function min(mixed $x): Expr\Func { return new Expr\Func('MIN', [$x]); } @@ -245,10 +219,8 @@ public function min($x) * Creates an instance of COUNT() function, with the given argument. * * @param mixed $x Argument to be used in COUNT() function. - * - * @return Expr\Func */ - public function count($x) + public function count(mixed $x): Expr\Func { return new Expr\Func('COUNT', [$x]); } @@ -256,23 +228,21 @@ public function count($x) /** * Creates an instance of COUNT(DISTINCT) function, with the given argument. * - * @param mixed $x Argument to be used in COUNT(DISTINCT) function. - * - * @return string + * @param mixed ...$x Argument to be used in COUNT(DISTINCT) function. */ - public function countDistinct($x) + public function countDistinct(mixed ...$x): string { - return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')'; + self::validateVariadicParameter($x); + + return 'COUNT(DISTINCT ' . implode(', ', $x) . ')'; } /** * Creates an instance of EXISTS() function, with the given DQL Subquery. * * @param mixed $subquery DQL Subquery to be used in EXISTS() function. - * - * @return Expr\Func */ - public function exists($subquery) + public function exists(mixed $subquery): Expr\Func { return new Expr\Func('EXISTS', [$subquery]); } @@ -281,10 +251,8 @@ public function exists($subquery) * Creates an instance of ALL() function, with the given DQL Subquery. * * @param mixed $subquery DQL Subquery to be used in ALL() function. - * - * @return Expr\Func */ - public function all($subquery) + public function all(mixed $subquery): Expr\Func { return new Expr\Func('ALL', [$subquery]); } @@ -293,10 +261,8 @@ public function all($subquery) * Creates a SOME() function expression with the given DQL subquery. * * @param mixed $subquery DQL Subquery to be used in SOME() function. - * - * @return Expr\Func */ - public function some($subquery) + public function some(mixed $subquery): Expr\Func { return new Expr\Func('SOME', [$subquery]); } @@ -305,10 +271,8 @@ public function some($subquery) * Creates an ANY() function expression with the given DQL subquery. * * @param mixed $subquery DQL Subquery to be used in ANY() function. - * - * @return Expr\Func */ - public function any($subquery) + public function any(mixed $subquery): Expr\Func { return new Expr\Func('ANY', [$subquery]); } @@ -317,10 +281,8 @@ public function any($subquery) * Creates a negation expression of the given restriction. * * @param mixed $restriction Restriction to be used in NOT() function. - * - * @return Expr\Func */ - public function not($restriction) + public function not(mixed $restriction): Expr\Func { return new Expr\Func('NOT', [$restriction]); } @@ -329,21 +291,16 @@ public function not($restriction) * Creates an ABS() function expression with the given argument. * * @param mixed $x Argument to be used in ABS() function. - * - * @return Expr\Func */ - public function abs($x) + public function abs(mixed $x): Expr\Func { return new Expr\Func('ABS', [$x]); } /** * Creates a MOD($x, $y) function expression to return the remainder of $x divided by $y. - * - * @param mixed $x - * @param mixed $y */ - public function mod($x, $y): Expr\Func + public function mod(mixed $x, mixed $y): Expr\Func { return new Expr\Func('MOD', [$x, $y]); } @@ -360,10 +317,8 @@ public function mod($x, $y): Expr\Func * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Math */ - public function prod($x, $y) + public function prod(mixed $x, mixed $y): Expr\Math { return new Expr\Math($x, '*', $y); } @@ -379,10 +334,8 @@ public function prod($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Math */ - public function diff($x, $y) + public function diff(mixed $x, mixed $y): Expr\Math { return new Expr\Math($x, '-', $y); } @@ -398,10 +351,8 @@ public function diff($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Math */ - public function sum($x, $y) + public function sum(mixed $x, mixed $y): Expr\Math { return new Expr\Math($x, '+', $y); } @@ -417,10 +368,8 @@ public function sum($x, $y) * * @param mixed $x Left expression. * @param mixed $y Right expression. - * - * @return Expr\Math */ - public function quot($x, $y) + public function quot(mixed $x, mixed $y): Expr\Math { return new Expr\Math($x, '/', $y); } @@ -429,10 +378,8 @@ public function quot($x, $y) * Creates a SQRT() function expression with the given argument. * * @param mixed $x Argument to be used in SQRT() function. - * - * @return Expr\Func */ - public function sqrt($x) + public function sqrt(mixed $x): Expr\Func { return new Expr\Func('SQRT', [$x]); } @@ -442,10 +389,8 @@ public function sqrt($x) * * @param string $x Field in string format to be restricted by IN() function. * @param mixed $y Argument to be used in IN() function. - * - * @return Expr\Func */ - public function in($x, $y) + public function in(string $x, mixed $y): Expr\Func { if (is_iterable($y)) { if ($y instanceof Traversable) { @@ -467,10 +412,8 @@ public function in($x, $y) * * @param string $x Field in string format to be restricted by NOT IN() function. * @param mixed $y Argument to be used in NOT IN() function. - * - * @return Expr\Func */ - public function notIn($x, $y) + public function notIn(string $x, mixed $y): Expr\Func { if (is_iterable($y)) { if ($y instanceof Traversable) { @@ -491,10 +434,8 @@ public function notIn($x, $y) * Creates an IS NULL expression with the given arguments. * * @param string $x Field in string format to be restricted by IS NULL. - * - * @return string */ - public function isNull($x) + public function isNull(string $x): string { return $x . ' IS NULL'; } @@ -503,10 +444,8 @@ public function isNull($x) * Creates an IS NOT NULL expression with the given arguments. * * @param string $x Field in string format to be restricted by IS NOT NULL. - * - * @return string */ - public function isNotNull($x) + public function isNotNull(string $x): string { return $x . ' IS NOT NULL'; } @@ -516,10 +455,8 @@ public function isNotNull($x) * * @param string $x Field in string format to be inspected by LIKE() comparison. * @param mixed $y Argument to be used in LIKE() comparison. - * - * @return Expr\Comparison */ - public function like($x, $y) + public function like(string $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, 'LIKE', $y); } @@ -529,10 +466,8 @@ public function like($x, $y) * * @param string $x Field in string format to be inspected by LIKE() comparison. * @param mixed $y Argument to be used in LIKE() comparison. - * - * @return Expr\Comparison */ - public function notLike($x, $y) + public function notLike(string $x, mixed $y): Expr\Comparison { return new Expr\Comparison($x, 'NOT LIKE', $y); } @@ -540,14 +475,13 @@ public function notLike($x, $y) /** * Creates a CONCAT() function expression with the given arguments. * - * @param mixed $x First argument to be used in CONCAT() function. - * @param mixed $y,... Other arguments to be used in CONCAT() function. - * - * @return Expr\Func + * @param mixed ...$x Arguments to be used in CONCAT() function. */ - public function concat($x, $y) + public function concat(mixed ...$x): Expr\Func { - return new Expr\Func('CONCAT', func_get_args()); + self::validateVariadicParameter($x); + + return new Expr\Func('CONCAT', $x); } /** @@ -556,10 +490,8 @@ public function concat($x, $y) * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function. * @param int $from Initial offset to start cropping string. May accept negative values. * @param int|null $len Length of crop. May accept negative values. - * - * @return Expr\Func */ - public function substring($x, $from, $len = null) + public function substring(mixed $x, int $from, int|null $len = null): Expr\Func { $args = [$x, $from]; if ($len !== null) { @@ -576,7 +508,7 @@ public function substring($x, $from, $len = null) * * @return Expr\Func A LOWER function expression. */ - public function lower($x) + public function lower(mixed $x): Expr\Func { return new Expr\Func('LOWER', [$x]); } @@ -588,7 +520,7 @@ public function lower($x) * * @return Expr\Func An UPPER function expression. */ - public function upper($x) + public function upper(mixed $x): Expr\Func { return new Expr\Func('UPPER', [$x]); } @@ -600,7 +532,7 @@ public function upper($x) * * @return Expr\Func A LENGTH function expression. */ - public function length($x) + public function length(mixed $x): Expr\Func { return new Expr\Func('LENGTH', [$x]); } @@ -609,10 +541,8 @@ public function length($x) * Creates a literal expression of the given argument. * * @param scalar $literal Argument to be converted to literal. - * - * @return Expr\Literal */ - public function literal($literal) + public function literal(bool|string|int|float $literal): Expr\Literal { return new Expr\Literal($this->quoteLiteral($literal)); } @@ -622,7 +552,7 @@ public function literal($literal) * * @param scalar $literal The literal value. */ - private function quoteLiteral($literal): string + private function quoteLiteral(bool|string|int|float $literal): string { if (is_int($literal) || is_float($literal)) { return (string) $literal; @@ -644,7 +574,7 @@ private function quoteLiteral($literal): string * * @return string A BETWEEN expression. */ - public function between($val, $x, $y) + public function between(mixed $val, int|string $x, int|string $y): string { return $val . ' BETWEEN ' . $x . ' AND ' . $y; } @@ -656,7 +586,7 @@ public function between($val, $x, $y) * * @return Expr\Func a TRIM expression. */ - public function trim($x) + public function trim(mixed $x): Expr\Func { return new Expr\Func('TRIM', $x); } @@ -666,10 +596,8 @@ public function trim($x) * * @param string $x Value to be checked * @param string $y Value to be checked against - * - * @return Expr\Comparison */ - public function isMemberOf($x, $y) + public function isMemberOf(string $x, string $y): Expr\Comparison { return new Expr\Comparison($x, 'MEMBER OF', $y); } @@ -679,10 +607,8 @@ public function isMemberOf($x, $y) * * @param string $x Value to be checked * @param string $y Value to be checked against - * - * @return Expr\Comparison */ - public function isInstanceOf($x, $y) + public function isInstanceOf(string $x, string $y): Expr\Comparison { return new Expr\Comparison($x, 'INSTANCE OF', $y); } diff --git a/src/Query/Expr/Andx.php b/src/Query/Expr/Andx.php index eddfa747948..a5dd03fef34 100644 --- a/src/Query/Expr/Andx.php +++ b/src/Query/Expr/Andx.php @@ -11,11 +11,10 @@ */ class Andx extends Composite { - /** @var string */ - protected $separator = ' AND '; + protected string $separator = ' AND '; /** @var string[] */ - protected $allowedClasses = [ + protected array $allowedClasses = [ Comparison::class, Func::class, Orx::class, @@ -23,10 +22,10 @@ class Andx extends Composite ]; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } diff --git a/src/Query/Expr/Base.php b/src/Query/Expr/Base.php index 3b2c49fa076..62a89737d48 100644 --- a/src/Query/Expr/Base.php +++ b/src/Query/Expr/Base.php @@ -7,11 +7,12 @@ use InvalidArgumentException; use Stringable; +use function array_key_exists; use function count; -use function get_class; use function get_debug_type; use function implode; use function in_array; +use function is_array; use function is_string; use function sprintf; @@ -20,26 +21,24 @@ * * @link www.doctrine-project.org */ -abstract class Base +abstract class Base implements Stringable { - /** @var string */ - protected $preSeparator = '('; - - /** @var string */ - protected $separator = ', '; - - /** @var string */ - protected $postSeparator = ')'; + protected string $preSeparator = '('; + protected string $separator = ', '; + protected string $postSeparator = ')'; /** @var list */ - protected $allowedClasses = []; + protected array $allowedClasses = []; /** @var list */ - protected $parts = []; + protected array $parts = []; - /** @param mixed $args */ - public function __construct($args = []) + public function __construct(mixed $args = []) { + if (is_array($args) && array_key_exists(0, $args) && is_array($args[0])) { + $args = $args[0]; + } + $this->addMultiple($args); } @@ -49,7 +48,7 @@ public function __construct($args = []) * * @return $this */ - public function addMultiple($args = []) + public function addMultiple(array|string|object $args = []): static { foreach ((array) $args as $arg) { $this->add($arg); @@ -59,20 +58,18 @@ public function addMultiple($args = []) } /** - * @param mixed $arg - * * @return $this * * @throws InvalidArgumentException */ - public function add($arg) + public function add(mixed $arg): static { if ($arg !== null && (! $arg instanceof self || $arg->count() > 0)) { // If we decide to keep Expr\Base instances, we can use this check - if (! is_string($arg) && ! in_array(get_class($arg), $this->allowedClasses, true)) { + if (! is_string($arg) && ! in_array($arg::class, $this->allowedClasses, true)) { throw new InvalidArgumentException(sprintf( "Expression of type '%s' not allowed in this context.", - get_debug_type($arg) + get_debug_type($arg), )); } @@ -82,17 +79,13 @@ public function add($arg) return $this; } - /** - * @return int - * @phpstan-return 0|positive-int - */ - public function count() + /** @phpstan-return 0|positive-int */ + public function count(): int { return count($this->parts); } - /** @return string */ - public function __toString() + public function __toString(): string { if ($this->count() === 1) { return (string) $this->parts[0]; diff --git a/src/Query/Expr/Comparison.php b/src/Query/Expr/Comparison.php index a3c533407ef..ec8ef21b2d9 100644 --- a/src/Query/Expr/Comparison.php +++ b/src/Query/Expr/Comparison.php @@ -4,63 +4,43 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + /** * Expression class for DQL comparison expressions. * * @link www.doctrine-project.org */ -class Comparison +class Comparison implements Stringable { - public const EQ = '='; - public const NEQ = '<>'; - public const LT = '<'; - public const LTE = '<='; - public const GT = '>'; - public const GTE = '>='; - - /** @var mixed */ - protected $leftExpr; - - /** @var string */ - protected $operator; - - /** @var mixed */ - protected $rightExpr; - - /** - * Creates a comparison expression with the given arguments. - * - * @param mixed $leftExpr - * @param string $operator - * @param mixed $rightExpr - */ - public function __construct($leftExpr, $operator, $rightExpr) + final public const EQ = '='; + final public const NEQ = '<>'; + final public const LT = '<'; + final public const LTE = '<='; + final public const GT = '>'; + final public const GTE = '>='; + + /** Creates a comparison expression with the given arguments. */ + public function __construct(protected mixed $leftExpr, protected string $operator, protected mixed $rightExpr) { - $this->leftExpr = $leftExpr; - $this->operator = $operator; - $this->rightExpr = $rightExpr; } - /** @return mixed */ - public function getLeftExpr() + public function getLeftExpr(): mixed { return $this->leftExpr; } - /** @return string */ - public function getOperator() + public function getOperator(): string { return $this->operator; } - /** @return mixed */ - public function getRightExpr() + public function getRightExpr(): mixed { return $this->rightExpr; } - /** @return string */ - public function __toString() + public function __toString(): string { return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr; } diff --git a/src/Query/Expr/Composite.php b/src/Query/Expr/Composite.php index 2270fbca34b..f3007a77534 100644 --- a/src/Query/Expr/Composite.php +++ b/src/Query/Expr/Composite.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + use function implode; use function is_object; use function preg_match; @@ -15,8 +17,7 @@ */ class Composite extends Base { - /** @return string */ - public function __toString() + public function __toString(): string { if ($this->count() === 1) { return (string) $this->parts[0]; @@ -31,8 +32,7 @@ public function __toString() return implode($this->separator, $components); } - /** @param string|object $part */ - private function processQueryPart($part): string + private function processQueryPart(string|Stringable $part): string { $queryPart = (string) $part; diff --git a/src/Query/Expr/From.php b/src/Query/Expr/From.php index 1f9af17a1d5..21af0786e27 100644 --- a/src/Query/Expr/From.php +++ b/src/Query/Expr/From.php @@ -4,54 +4,43 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + /** * Expression class for DQL from. * * @link www.doctrine-project.org */ -class From +class From implements Stringable { - /** @var string */ - protected $from; - - /** @var string */ - protected $alias; - - /** @var string|null */ - protected $indexBy; - /** - * @param string $from The class name. - * @param string $alias The alias of the class. - * @param string $indexBy The index for the from. + * @param class-string $from The class name. + * @param string $alias The alias of the class. */ - public function __construct($from, $alias, $indexBy = null) - { - $this->from = $from; - $this->alias = $alias; - $this->indexBy = $indexBy; + public function __construct( + protected string $from, + protected string $alias, + protected string|null $indexBy = null, + ) { } - /** @return string */ - public function getFrom() + /** @return class-string */ + public function getFrom(): string { return $this->from; } - /** @return string */ - public function getAlias() + public function getAlias(): string { return $this->alias; } - /** @return string|null */ - public function getIndexBy() + public function getIndexBy(): string|null { return $this->indexBy; } - /** @return string */ - public function __toString() + public function __toString(): string { return $this->from . ' ' . $this->alias . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : ''); diff --git a/src/Query/Expr/Func.php b/src/Query/Expr/Func.php index ec6ee54cc4d..41bc79bfee0 100644 --- a/src/Query/Expr/Func.php +++ b/src/Query/Expr/Func.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + use function implode; /** @@ -11,41 +13,35 @@ * * @link www.doctrine-project.org */ -class Func +class Func implements Stringable { - /** @var string */ - protected $name; - /** @var mixed[] */ - protected $arguments; + protected array $arguments; /** * Creates a function, with the given argument. * - * @param string $name - * @param mixed[]|mixed $arguments * @phpstan-param list|mixed $arguments */ - public function __construct($name, $arguments) - { - $this->name = $name; + public function __construct( + protected string $name, + mixed $arguments, + ) { $this->arguments = (array) $arguments; } - /** @return string */ - public function getName() + public function getName(): string { return $this->name; } /** @phpstan-return list */ - public function getArguments() + public function getArguments(): array { return $this->arguments; } - /** @return string */ - public function __toString() + public function __toString(): string { return $this->name . '(' . implode(', ', $this->arguments) . ')'; } diff --git a/src/Query/Expr/GroupBy.php b/src/Query/Expr/GroupBy.php index b3a25bfe160..2585196f482 100644 --- a/src/Query/Expr/GroupBy.php +++ b/src/Query/Expr/GroupBy.php @@ -11,17 +11,14 @@ */ class GroupBy extends Base { - /** @var string */ - protected $preSeparator = ''; - - /** @var string */ - protected $postSeparator = ''; + protected string $preSeparator = ''; + protected string $postSeparator = ''; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } diff --git a/src/Query/Expr/Join.php b/src/Query/Expr/Join.php index eec23670375..80a1acaa4d0 100644 --- a/src/Query/Expr/Join.php +++ b/src/Query/Expr/Join.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + use function strtoupper; /** @@ -11,102 +13,61 @@ * * @link www.doctrine-project.org */ -class Join +class Join implements Stringable { - public const INNER_JOIN = 'INNER'; - public const LEFT_JOIN = 'LEFT'; - - public const ON = 'ON'; - public const WITH = 'WITH'; - - /** - * @var string - * @phpstan-var self::INNER_JOIN|self::LEFT_JOIN - */ - protected $joinType; - - /** @var string */ - protected $join; - - /** @var string|null */ - protected $alias; - - /** - * @var string|null - * @phpstan-var self::ON|self::WITH|null - */ - protected $conditionType; + final public const INNER_JOIN = 'INNER'; + final public const LEFT_JOIN = 'LEFT'; - /** @var string|Comparison|Composite|Func|null */ - protected $condition; - - /** @var string|null */ - protected $indexBy; + final public const ON = 'ON'; + final public const WITH = 'WITH'; /** - * @param string $joinType The condition type constant. Either INNER_JOIN or LEFT_JOIN. - * @param string $join The relationship to join. - * @param string|null $alias The alias of the join. - * @param string|null $conditionType The condition type constant. Either ON or WITH. - * @param string|Comparison|Composite|Func|null $condition The condition for the join. - * @param string|null $indexBy The index for the join. * @phpstan-param self::INNER_JOIN|self::LEFT_JOIN $joinType * @phpstan-param self::ON|self::WITH|null $conditionType */ - public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null) - { - $this->joinType = $joinType; - $this->join = $join; - $this->alias = $alias; - $this->conditionType = $conditionType; - $this->condition = $condition; - $this->indexBy = $indexBy; + public function __construct( + protected string $joinType, + protected string $join, + protected string|null $alias = null, + protected string|null $conditionType = null, + protected string|Comparison|Composite|Func|null $condition = null, + protected string|null $indexBy = null, + ) { } - /** - * @return string - * @phpstan-return self::INNER_JOIN|self::LEFT_JOIN - */ - public function getJoinType() + /** @phpstan-return self::INNER_JOIN|self::LEFT_JOIN */ + public function getJoinType(): string { return $this->joinType; } - /** @return string */ - public function getJoin() + public function getJoin(): string { return $this->join; } - /** @return string|null */ - public function getAlias() + public function getAlias(): string|null { return $this->alias; } - /** - * @return string|null - * @phpstan-return self::ON|self::WITH|null - */ - public function getConditionType() + /** @phpstan-return self::ON|self::WITH|null */ + public function getConditionType(): string|null { return $this->conditionType; } - /** @return string|Comparison|Composite|Func|null */ - public function getCondition() + public function getCondition(): string|Comparison|Composite|Func|null { return $this->condition; } - /** @return string|null */ - public function getIndexBy() + public function getIndexBy(): string|null { return $this->indexBy; } - /** @return string */ - public function __toString() + public function __toString(): string { return strtoupper($this->joinType) . ' JOIN ' . $this->join . ($this->alias ? ' ' . $this->alias : '') diff --git a/src/Query/Expr/Literal.php b/src/Query/Expr/Literal.php index 336567dd254..4e2618b1a78 100644 --- a/src/Query/Expr/Literal.php +++ b/src/Query/Expr/Literal.php @@ -11,17 +11,14 @@ */ class Literal extends Base { - /** @var string */ - protected $preSeparator = ''; - - /** @var string */ - protected $postSeparator = ''; + protected string $preSeparator = ''; + protected string $postSeparator = ''; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } diff --git a/src/Query/Expr/Math.php b/src/Query/Expr/Math.php index 4e6ac5dd71a..05e0b39b6b8 100644 --- a/src/Query/Expr/Math.php +++ b/src/Query/Expr/Math.php @@ -4,56 +4,41 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + /** * Expression class for DQL math statements. * * @link www.doctrine-project.org */ -class Math +class Math implements Stringable { - /** @var mixed */ - protected $leftExpr; - - /** @var string */ - protected $operator; - - /** @var mixed */ - protected $rightExpr; - /** * Creates a mathematical expression with the given arguments. - * - * @param mixed $leftExpr - * @param string $operator - * @param mixed $rightExpr */ - public function __construct($leftExpr, $operator, $rightExpr) - { - $this->leftExpr = $leftExpr; - $this->operator = $operator; - $this->rightExpr = $rightExpr; + public function __construct( + protected mixed $leftExpr, + protected string $operator, + protected mixed $rightExpr, + ) { } - /** @return mixed */ - public function getLeftExpr() + public function getLeftExpr(): mixed { return $this->leftExpr; } - /** @return string */ - public function getOperator() + public function getOperator(): string { return $this->operator; } - /** @return mixed */ - public function getRightExpr() + public function getRightExpr(): mixed { return $this->rightExpr; } - /** @return string */ - public function __toString() + public function __toString(): string { // Adjusting Left Expression $leftExpr = (string) $this->leftExpr; diff --git a/src/Query/Expr/OrderBy.php b/src/Query/Expr/OrderBy.php index 1c505f6a449..bbaa7f9ec3d 100644 --- a/src/Query/Expr/OrderBy.php +++ b/src/Query/Expr/OrderBy.php @@ -4,6 +4,8 @@ namespace Doctrine\ORM\Query\Expr; +use Stringable; + use function count; use function implode; @@ -12,63 +14,46 @@ * * @link www.doctrine-project.org */ -class OrderBy +class OrderBy implements Stringable { - /** @var string */ - protected $preSeparator = ''; - - /** @var string */ - protected $separator = ', '; - - /** @var string */ - protected $postSeparator = ''; + protected string $preSeparator = ''; + protected string $separator = ', '; + protected string $postSeparator = ''; /** @var string[] */ - protected $allowedClasses = []; + protected array $allowedClasses = []; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; - /** - * @param string|null $sort - * @param string|null $order - */ - public function __construct($sort = null, $order = null) - { + public function __construct( + string|null $sort = null, + string|null $order = null, + ) { if ($sort) { $this->add($sort, $order); } } - /** - * @param string $sort - * @param string|null $order - * - * @return void - */ - public function add($sort, $order = null) + public function add(string $sort, string|null $order = null): void { $order = ! $order ? 'ASC' : $order; $this->parts[] = $sort . ' ' . $order; } - /** - * @return int - * @phpstan-return 0|positive-int - */ - public function count() + /** @phpstan-return 0|positive-int */ + public function count(): int { return count($this->parts); } /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } - /** @return string */ - public function __toString() + public function __toString(): string { return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; } diff --git a/src/Query/Expr/Orx.php b/src/Query/Expr/Orx.php index 37493ec62a0..0945e265226 100644 --- a/src/Query/Expr/Orx.php +++ b/src/Query/Expr/Orx.php @@ -11,11 +11,10 @@ */ class Orx extends Composite { - /** @var string */ - protected $separator = ' OR '; + protected string $separator = ' OR '; /** @var string[] */ - protected $allowedClasses = [ + protected array $allowedClasses = [ Comparison::class, Func::class, Andx::class, @@ -23,10 +22,10 @@ class Orx extends Composite ]; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } diff --git a/src/Query/Expr/Select.php b/src/Query/Expr/Select.php index 0a22900f3b8..beedcd3ac80 100644 --- a/src/Query/Expr/Select.php +++ b/src/Query/Expr/Select.php @@ -11,20 +11,17 @@ */ class Select extends Base { - /** @var string */ - protected $preSeparator = ''; - - /** @var string */ - protected $postSeparator = ''; + protected string $preSeparator = ''; + protected string $postSeparator = ''; /** @var string[] */ - protected $allowedClasses = [Func::class]; + protected array $allowedClasses = [Func::class]; /** @phpstan-var list */ - protected $parts = []; + protected array $parts = []; /** @phpstan-return list */ - public function getParts() + public function getParts(): array { return $this->parts; } diff --git a/src/Query/Filter/FilterException.php b/src/Query/Filter/FilterException.php index dc80d74de30..37f12bc27b0 100644 --- a/src/Query/Filter/FilterException.php +++ b/src/Query/Filter/FilterException.php @@ -5,10 +5,11 @@ namespace Doctrine\ORM\Query\Filter; use Doctrine\ORM\Exception\ORMException; +use LogicException; use function sprintf; -class FilterException extends ORMException +class FilterException extends LogicException implements ORMException { public static function cannotConvertListParameterIntoSingleValue(string $name): self { diff --git a/src/Query/Filter/SQLFilter.php b/src/Query/Filter/SQLFilter.php index d1079096f1e..4be63539cab 100644 --- a/src/Query/Filter/SQLFilter.php +++ b/src/Query/Filter/SQLFilter.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\ParameterTypeInferer; use InvalidArgumentException; +use Stringable; use function array_map; use function implode; @@ -23,39 +24,30 @@ * * @abstract */ -abstract class SQLFilter +abstract class SQLFilter implements Stringable { - /** - * The entity manager. - * - * @var EntityManagerInterface - */ - private $em; - /** * Parameters for the filter. * * @phpstan-var array */ - private $parameters = []; + private array $parameters = []; - /** @param EntityManagerInterface $em The entity manager. */ - final public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + final public function __construct( + private readonly EntityManagerInterface $em, + ) { } /** * Sets a parameter list that can be used by the filter. * - * @param string $name Name of the parameter. * @param array $values List of parameter values. * @param string $type The parameter type. If specified, the given value will be run through * the type conversion of this type. * * @return $this */ - final public function setParameterList(string $name, array $values, string $type = Types::STRING): self + final public function setParameterList(string $name, array $values, string $type = Types::STRING): static { $this->parameters[$name] = ['value' => $values, 'type' => $type, 'is_list' => true]; @@ -71,15 +63,13 @@ final public function setParameterList(string $name, array $values, string $type /** * Sets a parameter that can be used by the filter. * - * @param string $name Name of the parameter. - * @param mixed $value Value of the parameter. - * @param string|null $type The parameter type. If specified, the given value will be run through - * the type conversion of this type. This is usually not needed for - * strings and numeric types. + * @param string|null $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. * * @return $this */ - final public function setParameter($name, $value, $type = null): self + final public function setParameter(string $name, mixed $value, string|null $type = null): static { if ($type === null) { $type = ParameterTypeInferer::inferType($value); @@ -102,13 +92,11 @@ final public function setParameter($name, $value, $type = null): self * The function is responsible for the right output escaping to use the * value in a query. * - * @param string $name Name of the parameter. - * * @return string The SQL escaped parameter to use in a query. * * @throws InvalidArgumentException */ - final public function getParameter($name) + final public function getParameter(string $name): string { if (! isset($this->parameters[$name])) { throw new InvalidArgumentException("Parameter '" . $name . "' does not exist."); @@ -118,9 +106,7 @@ final public function getParameter($name) throw FilterException::cannotConvertListParameterIntoSingleValue($name); } - $param = $this->parameters[$name]; - - return $this->em->getConnection()->quote($param['value'], $param['type']); + return $this->em->getConnection()->quote((string) $this->parameters[$name]['value']); } /** @@ -130,10 +116,6 @@ final public function getParameter($name) * value in a query, separating each entry by comma to inline it into * an IN() query part. * - * @param string $name Name of the parameter. - * - * @return string The SQL escaped parameter to use in a query. - * * @throws InvalidArgumentException */ final public function getParameterList(string $name): string @@ -149,31 +131,26 @@ final public function getParameterList(string $name): string $param = $this->parameters[$name]; $connection = $this->em->getConnection(); - $quoted = array_map(static function ($value) use ($connection, $param) { - return $connection->quote($value, $param['type']); - }, $param['value']); + $quoted = array_map( + static fn (mixed $value): string => $connection->quote((string) $value), + $param['value'], + ); return implode(',', $quoted); } /** * Checks if a parameter was set for the filter. - * - * @param string $name Name of the parameter. - * - * @return bool */ - final public function hasParameter($name) + final public function hasParameter(string $name): bool { return isset($this->parameters[$name]); } /** * Returns as string representation of the SQLFilter parameters (the state). - * - * @return string String representation of the SQLFilter. */ - final public function __toString() + final public function __toString(): string { return serialize($this->parameters); } @@ -189,10 +166,9 @@ final protected function getConnection(): Connection /** * Gets the SQL query part to add to a query. * - * @param string $targetTableAlias * @phpstan-param ClassMetadata $targetEntity * * @return string The constraint SQL if there is available, empty string otherwise. */ - abstract public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias); + abstract public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string; } diff --git a/src/Query/FilterCollection.php b/src/Query/FilterCollection.php index cfe2bf16dda..818f76d94b4 100644 --- a/src/Query/FilterCollection.php +++ b/src/Query/FilterCollection.php @@ -29,27 +29,17 @@ class FilterCollection */ public const FILTERS_STATE_DIRTY = 2; - /** - * The used Configuration. - * - * @var Configuration - */ - private $config; - - /** - * The EntityManager that "owns" this FilterCollection instance. - * - * @var EntityManagerInterface - */ - private $em; + private readonly Configuration $config; /** * Instances of enabled filters. * - * @var SQLFilter[] - * @phpstan-var array + * @var array */ - private $enabledFilters = []; + private array $enabledFilters = []; + + /** The filter hash from the last time the query was parsed. */ + private string $filterHash = ''; /** * Instances of suspended filters. @@ -57,36 +47,27 @@ class FilterCollection * @var SQLFilter[] * @phpstan-var array */ - private $suspendedFilters = []; - - /** - * The filter hash from the last time the query was parsed. - * - * @var string - */ - private $filterHash = ''; + private array $suspendedFilters = []; /** * The current state of this filter. * - * @var int * @phpstan-var self::FILTERS_STATE_* */ - private $filtersState = self::FILTERS_STATE_CLEAN; + private int $filtersState = self::FILTERS_STATE_CLEAN; - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + private readonly EntityManagerInterface $em, + ) { $this->config = $em->getConfiguration(); } /** * Gets all the enabled filters. * - * @return SQLFilter[] The enabled filters. - * @phpstan-return array + * @return array The enabled filters. */ - public function getEnabledFilters() + public function getEnabledFilters(): array { return $this->enabledFilters; } @@ -105,13 +86,9 @@ public function getSuspendedFilters(): array /** * Enables a filter from the collection. * - * @param string $name Name of the filter. - * - * @return SQLFilter The enabled filter. - * * @throws InvalidArgumentException If the filter does not exist. */ - public function enable($name) + public function enable(string $name): SQLFilter { if (! $this->has($name)) { throw new InvalidArgumentException("Filter '" . $name . "' does not exist."); @@ -139,13 +116,9 @@ public function enable($name) /** * Disables a filter. * - * @param string $name Name of the filter. - * - * @return SQLFilter The disabled filter. - * * @throws InvalidArgumentException If the filter does not exist. */ - public function disable($name) + public function disable(string $name): SQLFilter { // Get the filter to return it $filter = $this->getFilter($name); @@ -208,13 +181,9 @@ public function restore(string $name): SQLFilter /** * Gets an enabled filter from the collection. * - * @param string $name Name of the filter. - * - * @return SQLFilter The filter. - * * @throws InvalidArgumentException If the filter is not enabled. */ - public function getFilter($name) + public function getFilter(string $name): SQLFilter { if (! $this->isEnabled($name)) { throw new InvalidArgumentException("Filter '" . $name . "' is not enabled."); @@ -225,24 +194,16 @@ public function getFilter($name) /** * Checks whether filter with given name is defined. - * - * @param string $name Name of the filter. - * - * @return bool true if the filter exists, false if not. */ - public function has($name) + public function has(string $name): bool { return $this->config->getFilterClassName($name) !== null; } /** * Checks if a filter is enabled. - * - * @param string $name Name of the filter. - * - * @return bool True if the filter is enabled, false otherwise. */ - public function isEnabled($name) + public function isEnabled(string $name): bool { return isset($this->enabledFilters[$name]); } @@ -261,20 +222,16 @@ public function isSuspended(string $name): bool /** * Checks if the filter collection is clean. - * - * @return bool */ - public function isClean() + public function isClean(): bool { return $this->filtersState === self::FILTERS_STATE_CLEAN; } /** * Generates a string of currently enabled filters to use for the cache id. - * - * @return string */ - public function getHash() + public function getHash(): string { // If there are only clean filters, the previous hash can be returned if ($this->filtersState === self::FILTERS_STATE_CLEAN) { @@ -295,10 +252,8 @@ public function getHash() /** * Sets the filter state to dirty. - * - * @return void */ - public function setFiltersStateDirty() + public function setFiltersStateDirty(): void { $this->filtersState = self::FILTERS_STATE_DIRTY; } diff --git a/src/Query/Lexer.php b/src/Query/Lexer.php index 0c6050c3b55..c446675d908 100644 --- a/src/Query/Lexer.php +++ b/src/Query/Lexer.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Query; use Doctrine\Common\Lexer\AbstractLexer; -use Doctrine\Deprecations\Deprecation; use function constant; use function ctype_alpha; @@ -21,260 +20,16 @@ /** * Scans a DQL query for tokens. * - * @extends AbstractLexer + * @extends AbstractLexer */ class Lexer extends AbstractLexer { - // All tokens that are not valid identifiers must be < 100 - /** @deprecated use {@see TokenType::T_NONE} */ - public const T_NONE = TokenType::T_NONE; - - /** @deprecated use {@see TokenType::T_INTEGER} */ - public const T_INTEGER = TokenType::T_INTEGER; - - /** @deprecated use {@see TokenType::T_STRING} */ - public const T_STRING = TokenType::T_STRING; - - /** @deprecated use {@see TokenType::T_INPUT_PARAMETER} */ - public const T_INPUT_PARAMETER = TokenType::T_INPUT_PARAMETER; - - /** @deprecated use {@see TokenType::T_FLOAT} */ - public const T_FLOAT = TokenType::T_FLOAT; - - /** @deprecated use {@see TokenType::T_CLOSE_PARENTHESIS} */ - public const T_CLOSE_PARENTHESIS = TokenType::T_CLOSE_PARENTHESIS; - - /** @deprecated use {@see TokenType::T_OPEN_PARENTHESIS} */ - public const T_OPEN_PARENTHESIS = TokenType::T_OPEN_PARENTHESIS; - - /** @deprecated use {@see TokenType::T_COMMA} */ - public const T_COMMA = TokenType::T_COMMA; - - /** @deprecated use {@see TokenType::T_DIVIDE} */ - public const T_DIVIDE = TokenType::T_DIVIDE; - - /** @deprecated use {@see TokenType::T_DOT} */ - public const T_DOT = TokenType::T_DOT; - - /** @deprecated use {@see TokenType::T_EQUALS} */ - public const T_EQUALS = TokenType::T_EQUALS; - - /** @deprecated use {@see TokenType::T_GREATER_THAN} */ - public const T_GREATER_THAN = TokenType::T_GREATER_THAN; - - /** @deprecated use {@see TokenType::T_LOWER_THAN} */ - public const T_LOWER_THAN = TokenType::T_LOWER_THAN; - - /** @deprecated use {@see TokenType::T_MINUS} */ - public const T_MINUS = TokenType::T_MINUS; - - /** @deprecated use {@see TokenType::T_MULTIPLY} */ - public const T_MULTIPLY = TokenType::T_MULTIPLY; - - /** @deprecated use {@see TokenType::T_NEGATE} */ - public const T_NEGATE = TokenType::T_NEGATE; - - /** @deprecated use {@see TokenType::T_PLUS} */ - public const T_PLUS = TokenType::T_PLUS; - - /** @deprecated use {@see TokenType::T_OPEN_CURLY_BRACE} */ - public const T_OPEN_CURLY_BRACE = TokenType::T_OPEN_CURLY_BRACE; - - /** @deprecated use {@see TokenType::T_CLOSE_CURLY_BRACE} */ - public const T_CLOSE_CURLY_BRACE = TokenType::T_CLOSE_CURLY_BRACE; - - // All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100 - /** - * @deprecated No Replacement planned. - * - * @phpstan-ignore classConstant.deprecated - */ - public const T_ALIASED_NAME = TokenType::T_ALIASED_NAME; - - /** @deprecated use {@see TokenType::T_FULLY_QUALIFIED_NAME} */ - public const T_FULLY_QUALIFIED_NAME = TokenType::T_FULLY_QUALIFIED_NAME; - - /** @deprecated use {@see TokenType::T_IDENTIFIER} */ - public const T_IDENTIFIER = TokenType::T_IDENTIFIER; - - // All keyword tokens should be >= 200 - /** @deprecated use {@see TokenType::T_ALL} */ - public const T_ALL = TokenType::T_ALL; - - /** @deprecated use {@see TokenType::T_AND} */ - public const T_AND = TokenType::T_AND; - - /** @deprecated use {@see TokenType::T_ANY} */ - public const T_ANY = TokenType::T_ANY; - - /** @deprecated use {@see TokenType::T_AS} */ - public const T_AS = TokenType::T_AS; - - /** @deprecated use {@see TokenType::T_ASC} */ - public const T_ASC = TokenType::T_ASC; - - /** @deprecated use {@see TokenType::T_AVG} */ - public const T_AVG = TokenType::T_AVG; - - /** @deprecated use {@see TokenType::T_BETWEEN} */ - public const T_BETWEEN = TokenType::T_BETWEEN; - - /** @deprecated use {@see TokenType::T_BOTH} */ - public const T_BOTH = TokenType::T_BOTH; - - /** @deprecated use {@see TokenType::T_BY} */ - public const T_BY = TokenType::T_BY; - - /** @deprecated use {@see TokenType::T_CASE} */ - public const T_CASE = TokenType::T_CASE; - - /** @deprecated use {@see TokenType::T_COALESCE} */ - public const T_COALESCE = TokenType::T_COALESCE; - - /** @deprecated use {@see TokenType::T_COUNT} */ - public const T_COUNT = TokenType::T_COUNT; - - /** @deprecated use {@see TokenType::T_DELETE} */ - public const T_DELETE = TokenType::T_DELETE; - - /** @deprecated use {@see TokenType::T_DESC} */ - public const T_DESC = TokenType::T_DESC; - - /** @deprecated use {@see TokenType::T_DISTINCT} */ - public const T_DISTINCT = TokenType::T_DISTINCT; - - /** @deprecated use {@see TokenType::T_ELSE} */ - public const T_ELSE = TokenType::T_ELSE; - - /** @deprecated use {@see TokenType::T_EMPTY} */ - public const T_EMPTY = TokenType::T_EMPTY; - - /** @deprecated use {@see TokenType::T_END} */ - public const T_END = TokenType::T_END; - - /** @deprecated use {@see TokenType::T_ESCAPE} */ - public const T_ESCAPE = TokenType::T_ESCAPE; - - /** @deprecated use {@see TokenType::T_EXISTS} */ - public const T_EXISTS = TokenType::T_EXISTS; - - /** @deprecated use {@see TokenType::T_FALSE} */ - public const T_FALSE = TokenType::T_FALSE; - - /** @deprecated use {@see TokenType::T_FROM} */ - public const T_FROM = TokenType::T_FROM; - - /** @deprecated use {@see TokenType::T_GROUP} */ - public const T_GROUP = TokenType::T_GROUP; - - /** @deprecated use {@see TokenType::T_HAVING} */ - public const T_HAVING = TokenType::T_HAVING; - - /** @deprecated use {@see TokenType::T_HIDDEN} */ - public const T_HIDDEN = TokenType::T_HIDDEN; - - /** @deprecated use {@see TokenType::T_IN} */ - public const T_IN = TokenType::T_IN; - - /** @deprecated use {@see TokenType::T_INDEX} */ - public const T_INDEX = TokenType::T_INDEX; - - /** @deprecated use {@see TokenType::T_INNER} */ - public const T_INNER = TokenType::T_INNER; - - /** @deprecated use {@see TokenType::T_INSTANCE} */ - public const T_INSTANCE = TokenType::T_INSTANCE; - - /** @deprecated use {@see TokenType::T_IS} */ - public const T_IS = TokenType::T_IS; - - /** @deprecated use {@see TokenType::T_JOIN} */ - public const T_JOIN = TokenType::T_JOIN; - - /** @deprecated use {@see TokenType::T_LEADING} */ - public const T_LEADING = TokenType::T_LEADING; - - /** @deprecated use {@see TokenType::T_LEFT} */ - public const T_LEFT = TokenType::T_LEFT; - - /** @deprecated use {@see TokenType::T_LIKE} */ - public const T_LIKE = TokenType::T_LIKE; - - /** @deprecated use {@see TokenType::T_MAX} */ - public const T_MAX = TokenType::T_MAX; - - /** @deprecated use {@see TokenType::T_MEMBER} */ - public const T_MEMBER = TokenType::T_MEMBER; - - /** @deprecated use {@see TokenType::T_MIN} */ - public const T_MIN = TokenType::T_MIN; - - /** @deprecated use {@see TokenType::T_NEW} */ - public const T_NEW = TokenType::T_NEW; - - /** @deprecated use {@see TokenType::T_NOT} */ - public const T_NOT = TokenType::T_NOT; - - /** @deprecated use {@see TokenType::T_NULL} */ - public const T_NULL = TokenType::T_NULL; - - /** @deprecated use {@see TokenType::T_NULLIF} */ - public const T_NULLIF = TokenType::T_NULLIF; - - /** @deprecated use {@see TokenType::T_OF} */ - public const T_OF = TokenType::T_OF; - - /** @deprecated use {@see TokenType::T_OR} */ - public const T_OR = TokenType::T_OR; - - /** @deprecated use {@see TokenType::T_ORDER} */ - public const T_ORDER = TokenType::T_ORDER; - - /** @deprecated use {@see TokenType::T_OUTER} */ - public const T_OUTER = TokenType::T_OUTER; - - /** @deprecated use {@see TokenType::T_PARTIAL} */ - public const T_PARTIAL = TokenType::T_PARTIAL; - - /** @deprecated use {@see TokenType::T_SELECT} */ - public const T_SELECT = TokenType::T_SELECT; - - /** @deprecated use {@see TokenType::T_SET} */ - public const T_SET = TokenType::T_SET; - - /** @deprecated use {@see TokenType::T_SOME} */ - public const T_SOME = TokenType::T_SOME; - - /** @deprecated use {@see TokenType::T_SUM} */ - public const T_SUM = TokenType::T_SUM; - - /** @deprecated use {@see TokenType::T_THEN} */ - public const T_THEN = TokenType::T_THEN; - - /** @deprecated use {@see TokenType::T_TRAILING} */ - public const T_TRAILING = TokenType::T_TRAILING; - - /** @deprecated use {@see TokenType::T_TRUE} */ - public const T_TRUE = TokenType::T_TRUE; - - /** @deprecated use {@see TokenType::T_UPDATE} */ - public const T_UPDATE = TokenType::T_UPDATE; - - /** @deprecated use {@see TokenType::T_WHEN} */ - public const T_WHEN = TokenType::T_WHEN; - - /** @deprecated use {@see TokenType::T_WHERE} */ - public const T_WHERE = TokenType::T_WHERE; - - /** @deprecated use {@see TokenType::T_WITH} */ - public const T_WITH = TokenType::T_WITH; - /** * Creates a new query scanner object. * * @param string $input A query string. */ - public function __construct($input) + public function __construct(string $input) { $this->setInput($input); } @@ -282,7 +37,7 @@ public function __construct($input) /** * {@inheritDoc} */ - protected function getCatchablePatterns() + protected function getCatchablePatterns(): array { return [ '[a-z_][a-z0-9_]*\:[a-z_][a-z0-9_]*(?:\\\[a-z_][a-z0-9_]*)*', // aliased name @@ -296,17 +51,12 @@ protected function getCatchablePatterns() /** * {@inheritDoc} */ - protected function getNonCatchablePatterns() + protected function getNonCatchablePatterns(): array { return ['\s+', '--.*', '(.)']; } - /** - * {@inheritDoc} - * - * @param string $value - */ - protected function getType(&$value) + protected function getType(string &$value): TokenType { $type = TokenType::T_NONE; @@ -332,23 +82,11 @@ protected function getType(&$value) if (defined($name)) { $type = constant($name); - if ($type > 100) { + if ($type->value > 100) { return $type; } } - if (str_contains($value, ':')) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8818', - 'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.', - $value - ); - - // @phpstan-ignore classConstant.deprecated - return TokenType::T_ALIASED_NAME; - } - if (str_contains($value, '\\')) { return TokenType::T_FULLY_QUALIFIED_NAME; } diff --git a/src/Query/OutputWalker.php b/src/Query/OutputWalker.php index c4b6372b35f..d656702197b 100644 --- a/src/Query/OutputWalker.php +++ b/src/Query/OutputWalker.php @@ -23,6 +23,5 @@ */ interface OutputWalker { - /** @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST */ - public function getFinalizer($AST): SqlFinalizer; + public function getFinalizer(AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST): SqlFinalizer; } diff --git a/src/Query/Parameter.php b/src/Query/Parameter.php index 8fe083b6f8a..43eb7a41239 100644 --- a/src/Query/Parameter.php +++ b/src/Query/Parameter.php @@ -15,50 +15,33 @@ class Parameter { /** * Returns the internal representation of a parameter name. - * - * @param string|int $name The parameter name or position. - * - * @return string The normalized parameter name. */ - public static function normalizeName($name) + public static function normalizeName(int|string $name): string { return trim((string) $name, ':'); } /** * The parameter name. - * - * @var string */ - private $name; + private readonly string $name; /** * The parameter value. - * - * @var mixed */ - private $value; + private mixed $value; /** * The parameter type. - * - * @var mixed */ - private $type; + private mixed $type; /** * Whether the parameter type was explicitly specified or not - * - * @var bool */ - private $typeSpecified; + private readonly bool $typeSpecified; - /** - * @param string|int $name Parameter name - * @param mixed $value Parameter value - * @param mixed $type Parameter type - */ - public function __construct($name, $value, $type = null) + public function __construct(int|string $name, mixed $value, mixed $type = null) { $this->name = self::normalizeName($name); $this->typeSpecified = $type !== null; @@ -68,43 +51,32 @@ public function __construct($name, $value, $type = null) /** * Retrieves the Parameter name. - * - * @return string */ - public function getName() + public function getName(): string { return $this->name; } /** * Retrieves the Parameter value. - * - * @return mixed */ - public function getValue() + public function getValue(): mixed { return $this->value; } /** * Retrieves the Parameter type. - * - * @return mixed */ - public function getType() + public function getType(): mixed { return $this->type; } /** * Defines the Parameter value. - * - * @param mixed $value Parameter value. - * @param mixed $type Parameter type. - * - * @return void */ - public function setValue($value, $type = null) + public function setValue(mixed $value, mixed $type = null): void { $this->value = $value; $this->type = $type ?: ParameterTypeInferer::inferType($value); diff --git a/src/Query/ParameterTypeInferer.php b/src/Query/ParameterTypeInferer.php index 0de508279cd..dae28faee18 100644 --- a/src/Query/ParameterTypeInferer.php +++ b/src/Query/ParameterTypeInferer.php @@ -9,11 +9,9 @@ use DateTimeImmutable; use DateTimeInterface; use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Types\Types; -use function class_exists; use function current; use function is_array; use function is_bool; @@ -24,18 +22,14 @@ * * @link www.doctrine-project.org */ -class ParameterTypeInferer +final class ParameterTypeInferer { /** * Infers type of a given value, returning a compatible constant: * - Type (\Doctrine\DBAL\Types\Type::*) * - Connection (\Doctrine\DBAL\Connection::PARAM_*) - * - * @param mixed $value Parameter value. - * - * @return int|string Parameter type constant. */ - public static function inferType($value) + public static function inferType(mixed $value): ParameterType|ArrayParameterType|int|string { if (is_int($value)) { return Types::INTEGER; @@ -69,14 +63,6 @@ public static function inferType($value) $firstValue = $firstValue->value; } - if (! class_exists(ArrayParameterType::class)) { - return is_int($firstValue) - // @phpstan-ignore classConstant.deprecated - ? Connection::PARAM_INT_ARRAY - // @phpstan-ignore classConstant.deprecated - : Connection::PARAM_STR_ARRAY; - } - return is_int($firstValue) ? ArrayParameterType::INTEGER : ArrayParameterType::STRING; @@ -84,4 +70,8 @@ public static function inferType($value) return ParameterType::STRING; } + + private function __construct() + { + } } diff --git a/src/Query/Parser.php b/src/Query/Parser.php index 8ee44061467..86fb8243b49 100644 --- a/src/Query/Parser.php +++ b/src/Query/Parser.php @@ -7,6 +7,9 @@ use Doctrine\Common\Lexer\Token; use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Exception\DuplicateFieldException; +use Doctrine\ORM\Exception\NoMatchingPropertyException; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\AST\Functions; @@ -15,11 +18,11 @@ use ReflectionClass; use function array_intersect; +use function array_key_exists; use function array_search; use function assert; use function class_exists; use function count; -use function explode; use function implode; use function in_array; use function interface_exists; @@ -31,13 +34,13 @@ use function strrpos; use function strtolower; use function substr; +use function trim; /** * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. * Parses a DQL query, reports any errors in it, and generates an AST. * - * @phpstan-import-type AssociationMapping from ClassMetadata - * @phpstan-type DqlToken = Token + * @phpstan-type DqlToken = Token * @phpstan-type QueryComponent = array{ * metadata?: ClassMetadata, * parent?: string|null, @@ -48,13 +51,13 @@ * token: DqlToken, * } */ -class Parser +final class Parser { /** * @readonly Maps BUILT-IN string function names to AST class names. * @var array> */ - private static $stringFunctions = [ + private static array $stringFunctions = [ 'concat' => Functions\ConcatFunction::class, 'substring' => Functions\SubstringFunction::class, 'trim' => Functions\TrimFunction::class, @@ -67,7 +70,7 @@ class Parser * @readonly Maps BUILT-IN numeric function names to AST class names. * @var array> */ - private static $numericFunctions = [ + private static array $numericFunctions = [ 'length' => Functions\LengthFunction::class, 'locate' => Functions\LocateFunction::class, 'abs' => Functions\AbsFunction::class, @@ -90,7 +93,7 @@ class Parser * @readonly Maps BUILT-IN datetime function names to AST class names. * @var array> */ - private static $datetimeFunctions = [ + private static array $datetimeFunctions = [ 'current_date' => Functions\CurrentDateFunction::class, 'current_time' => Functions\CurrentTimeFunction::class, 'current_timestamp' => Functions\CurrentTimestampFunction::class, @@ -104,68 +107,53 @@ class Parser */ /** @phpstan-var list */ - private $deferredIdentificationVariables = []; + private array $deferredIdentificationVariables = []; /** @phpstan-var list */ - private $deferredPartialObjectExpressions = []; + private array $deferredPartialObjectExpressions = []; /** @phpstan-var list */ - private $deferredPathExpressions = []; + private array $deferredPathExpressions = []; /** @phpstan-var list */ - private $deferredResultVariables = []; + private array $deferredResultVariables = []; /** @phpstan-var list */ - private $deferredNewObjectExpressions = []; + private array $deferredNewObjectExpressions = []; /** * The lexer. - * - * @var Lexer */ - private $lexer; + private readonly Lexer $lexer; /** * The parser result. - * - * @var ParserResult */ - private $parserResult; + private readonly ParserResult $parserResult; /** * The EntityManager. - * - * @var EntityManagerInterface - */ - private $em; - - /** - * The Query to parse. - * - * @var Query */ - private $query; + private readonly EntityManagerInterface $em; /** * Map of declared query components in the parsed query. * * @phpstan-var array */ - private $queryComponents = []; + private array $queryComponents = []; /** * Keeps the nesting level of defined ResultVariables. - * - * @var int */ - private $nestingLevel = 0; + private int $nestingLevel = 0; /** * Any additional custom tree walkers that modify the AST. * * @var list> */ - private $customTreeWalkers = []; + private array $customTreeWalkers = []; /** * The custom last tree walker, if any, that is responsible for producing the output. @@ -175,16 +163,15 @@ class Parser private $customOutputWalker; /** @phpstan-var array */ - private $identVariableExpressions = []; + private array $identVariableExpressions = []; /** * Creates a new query parser object. * * @param Query $query The Query to parse. */ - public function __construct(Query $query) + public function __construct(private readonly Query $query) { - $this->query = $query; $this->em = $query->getEntityManager(); $this->lexer = new Lexer((string) $query->getDQL()); $this->parserResult = new ParserResult(); @@ -195,16 +182,14 @@ public function __construct(Query $query) * This tree walker will be run last over the AST, after any other walkers. * * @param class-string $className - * - * @return void */ - public function setCustomOutputTreeWalker($className) + public function setCustomOutputTreeWalker(string $className): void { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/orm/pull/11641', '%s is deprecated, set the output walker class with the \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER query hint instead', - __METHOD__ + __METHOD__, ); $this->customOutputWalker = $className; @@ -214,50 +199,40 @@ public function setCustomOutputTreeWalker($className) * Adds a custom tree walker for modifying the AST. * * @param class-string $className - * - * @return void */ - public function addCustomTreeWalker($className) + public function addCustomTreeWalker(string $className): void { $this->customTreeWalkers[] = $className; } /** * Gets the lexer used by the parser. - * - * @return Lexer */ - public function getLexer() + public function getLexer(): Lexer { return $this->lexer; } /** * Gets the ParserResult that is being filled with information during parsing. - * - * @return ParserResult */ - public function getParserResult() + public function getParserResult(): ParserResult { return $this->parserResult; } /** * Gets the EntityManager used by the parser. - * - * @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { return $this->em; } /** * Parses and builds AST for the given Query. - * - * @return AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement */ - public function getAST() + public function getAST(): AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement { // Parse & build AST $AST = $this->QueryLanguage(); @@ -296,13 +271,9 @@ public function getAST() * If they match, updates the lookahead token; otherwise raises a syntax * error. * - * @param TokenType::T_* $token The token type. - * - * @return void - * * @throws QueryException If the tokens don't match. */ - public function match($token) + public function match(TokenType $token): void { $lookaheadType = $this->lexer->lookahead->type ?? null; @@ -314,17 +285,17 @@ public function match($token) } // If parameter is not identifier (1-99) must be exact match - if ($token < TokenType::T_IDENTIFIER) { + if ($token->value < TokenType::T_IDENTIFIER->value) { $this->syntaxError($this->lexer->getLiteral($token)); } // If parameter is keyword (200+) must be exact match - if ($token > TokenType::T_IDENTIFIER) { + if ($token->value > TokenType::T_IDENTIFIER->value) { $this->syntaxError($this->lexer->getLiteral($token)); } // If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+) - if ($token === TokenType::T_IDENTIFIER && $lookaheadType < TokenType::T_IDENTIFIER) { + if ($token->value === TokenType::T_IDENTIFIER->value && $lookaheadType->value < TokenType::T_IDENTIFIER->value) { $this->syntaxError($this->lexer->getLiteral($token)); } @@ -336,10 +307,8 @@ public function match($token) * * @param bool $deep Whether to clean peek and reset errors. * @param int $position Position to reset. - * - * @return void */ - public function free($deep = false, $position = 0) + public function free(bool $deep = false, int $position = 0): void { // WARNING! Use this method with care. It resets the scanner! $this->lexer->resetPosition($position); @@ -355,10 +324,8 @@ public function free($deep = false, $position = 0) /** * Parses a query string. - * - * @return ParserResult */ - public function parse() + public function parse(): ParserResult { $AST = $this->getAST(); @@ -380,19 +347,11 @@ public function parse() $treeWalkerChain->addTreeWalker($walker); } - switch (true) { - case $AST instanceof AST\UpdateStatement: - $treeWalkerChain->walkUpdateStatement($AST); - break; - - case $AST instanceof AST\DeleteStatement: - $treeWalkerChain->walkDeleteStatement($AST); - break; - - case $AST instanceof AST\SelectStatement: - default: - $treeWalkerChain->walkSelectStatement($AST); - } + match (true) { + $AST instanceof AST\UpdateStatement => $treeWalkerChain->walkUpdateStatement($AST), + $AST instanceof AST\DeleteStatement => $treeWalkerChain->walkDeleteStatement($AST), + $AST instanceof AST\SelectStatement => $treeWalkerChain->walkSelectStatement($AST), + }; $this->queryComponents = $treeWalkerChain->getQueryComponents(); } @@ -410,7 +369,7 @@ public function parse() 'Your output walker class %s should implement %s in order to provide a %s. This also means the output walker should not use the query firstResult/maxResult values, which should be read from the query by the SqlFinalizer only.', $outputWalkerClass, OutputWalker::class, - SqlFinalizer::class + SqlFinalizer::class, ); // @phpstan-ignore method.deprecated $executor = $outputWalker->getExecutor($AST); @@ -427,10 +386,8 @@ public function parse() * They have to appear in the select clause in the same order as the * declarations (from ... x join ... y join ... z ...) appear in the query * as the hydration process relies on that order for proper operation. - * - * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST */ - private function fixIdentificationVariableOrder(AST\Node $AST): void + private function fixIdentificationVariableOrder(AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST): void { if (count($this->identVariableExpressions) <= 1) { return; @@ -455,16 +412,12 @@ private function fixIdentificationVariableOrder(AST\Node $AST): void /** * Generates a new syntax error. * - * @param string $expected Expected string. - * @param mixed[]|null $token Got token. - * @phpstan-param DqlToken|null $token - * - * @return void - * @phpstan-return no-return + * @param string $expected Expected string. + * @param DqlToken|null $token Got token. * * @throws QueryException */ - public function syntaxError($expected = '', $token = null) + public function syntaxError(string $expected = '', Token|null $token = null): never { if ($token === null) { $token = $this->lexer->lookahead; @@ -482,16 +435,12 @@ public function syntaxError($expected = '', $token = null) /** * Generates a new semantical error. * - * @param string $message Optional message. - * @param mixed[]|null $token Optional token. + * @param string $message Optional message. * @phpstan-param DqlToken|null $token * - * @return void - * @phpstan-return no-return - * * @throws QueryException */ - public function semanticalError($message = '', $token = null) + public function semanticalError(string $message = '', Token|null $token = null): never { if ($token === null) { $token = $this->lexer->lookahead ?? new Token('fake token', 42, 0); @@ -521,10 +470,9 @@ public function semanticalError($message = '', $token = null) * * @param bool $resetPeek Reset peek after finding the closing parenthesis. * - * @return mixed[] * @phpstan-return DqlToken|null */ - private function peekBeyondClosingParenthesis(bool $resetPeek = true) + private function peekBeyondClosingParenthesis(bool $resetPeek = true): Token|null { $token = $this->lexer->peek(); $numUnmatched = 1; @@ -558,7 +506,7 @@ private function peekBeyondClosingParenthesis(bool $resetPeek = true) * * @phpstan-param DqlToken|null $token */ - private function isMathOperator($token): bool + private function isMathOperator(Token|null $token): bool { return $token !== null && in_array($token->type, [TokenType::T_PLUS, TokenType::T_MINUS, TokenType::T_DIVIDE, TokenType::T_MULTIPLY], true); } @@ -576,22 +524,20 @@ private function isFunction(): bool $this->lexer->resetPeek(); - return $lookaheadType >= TokenType::T_IDENTIFIER && $peek !== null && $peek->type === TokenType::T_OPEN_PARENTHESIS; + return $lookaheadType->value >= TokenType::T_IDENTIFIER->value && $peek !== null && $peek->type === TokenType::T_OPEN_PARENTHESIS; } /** * Checks whether the given token type indicates an aggregate function. * - * @phpstan-param TokenType::T_* $tokenType - * * @return bool TRUE if the token type is an aggregate function, FALSE otherwise. */ - private function isAggregateFunction(int $tokenType): bool + private function isAggregateFunction(TokenType $tokenType): bool { return in_array( $tokenType, [TokenType::T_AVG, TokenType::T_MIN, TokenType::T_MAX, TokenType::T_SUM, TokenType::T_COUNT], - true + true, ); } @@ -605,7 +551,7 @@ private function isNextAllAnySome(): bool return in_array( $this->lexer->lookahead->type, [TokenType::T_ALL, TokenType::T_ANY, TokenType::T_SOME], - true + true, ); } @@ -622,7 +568,7 @@ private function processDeferredIdentificationVariables(): void if (! isset($this->queryComponents[$identVariable])) { $this->semanticalError( sprintf("'%s' is not defined.", $identVariable), - $deferredItem['token'] + $deferredItem['token'], ); } @@ -632,7 +578,7 @@ private function processDeferredIdentificationVariables(): void if (! isset($qComp['metadata'])) { $this->semanticalError( sprintf("'%s' does not point to a Class.", $identVariable), - $deferredItem['token'] + $deferredItem['token'], ); } @@ -640,7 +586,7 @@ private function processDeferredIdentificationVariables(): void if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { $this->semanticalError( sprintf("'%s' is used outside the scope of its declaration.", $identVariable), - $deferredItem['token'] + $deferredItem['token'], ); } } @@ -706,8 +652,7 @@ private function processDeferredPartialObjectExpressions(): void if ( isset($class->associationMappings[$field]) && - $class->associationMappings[$field]['isOwningSide'] && - $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE + $class->associationMappings[$field]->isToOneOwningSide() ) { continue; } @@ -715,14 +660,14 @@ private function processDeferredPartialObjectExpressions(): void $this->semanticalError(sprintf( "There is no mapped field named '%s' on class %s.", $field, - $class->name + $class->name, ), $deferredItem['token']); } if (array_intersect($class->identifier, $expr->partialFieldSet) !== $class->identifier) { $this->semanticalError( 'The partial field selection of class ' . $class->name . ' must contain the identifier.', - $deferredItem['token'] + $deferredItem['token'], ); } } @@ -741,7 +686,7 @@ private function processDeferredResultVariables(): void if (! isset($this->queryComponents[$resultVariable])) { $this->semanticalError( sprintf("'%s' is not defined.", $resultVariable), - $deferredItem['token'] + $deferredItem['token'], ); } @@ -751,7 +696,7 @@ private function processDeferredResultVariables(): void if (! isset($qComp['resultVariable'])) { $this->semanticalError( sprintf("'%s' does not point to a ResultVariable.", $resultVariable), - $deferredItem['token'] + $deferredItem['token'], ); } @@ -759,7 +704,7 @@ private function processDeferredResultVariables(): void if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { $this->semanticalError( sprintf("'%s' is used outside the scope of its declaration.", $resultVariable), - $deferredItem['token'] + $deferredItem['token'], ); } } @@ -790,7 +735,7 @@ private function processDeferredPathExpressions(): void if (! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) { $this->semanticalError( 'Class ' . $class->name . ' has no field or association named ' . $field, - $deferredItem['token'] + $deferredItem['token'], ); } @@ -799,7 +744,7 @@ private function processDeferredPathExpressions(): void if (isset($class->associationMappings[$field])) { $assoc = $class->associationMappings[$field]; - $fieldType = $assoc['type'] & ClassMetadata::TO_ONE + $fieldType = $assoc->isToOne() ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION; } @@ -857,10 +802,8 @@ private function processRootEntityAliasSelected(): void /** * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement - * - * @return AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement */ - public function QueryLanguage() + public function QueryLanguage(): AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement { $statement = null; @@ -894,10 +837,8 @@ public function QueryLanguage() /** * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] - * - * @return AST\SelectStatement */ - public function SelectStatement() + public function SelectStatement(): AST\SelectStatement { $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause()); @@ -911,10 +852,8 @@ public function SelectStatement() /** * UpdateStatement ::= UpdateClause [WhereClause] - * - * @return AST\UpdateStatement */ - public function UpdateStatement() + public function UpdateStatement(): AST\UpdateStatement { $updateStatement = new AST\UpdateStatement($this->UpdateClause()); @@ -925,10 +864,8 @@ public function UpdateStatement() /** * DeleteStatement ::= DeleteClause [WhereClause] - * - * @return AST\DeleteStatement */ - public function DeleteStatement() + public function DeleteStatement(): AST\DeleteStatement { $deleteStatement = new AST\DeleteStatement($this->DeleteClause()); @@ -939,10 +876,8 @@ public function DeleteStatement() /** * IdentificationVariable ::= identifier - * - * @return string */ - public function IdentificationVariable() + public function IdentificationVariable(): string { $this->match(TokenType::T_IDENTIFIER); @@ -960,10 +895,8 @@ public function IdentificationVariable() /** * AliasIdentificationVariable = identifier - * - * @return string */ - public function AliasIdentificationVariable() + public function AliasIdentificationVariable(): string { $this->match(TokenType::T_IDENTIFIER); @@ -974,7 +907,7 @@ public function AliasIdentificationVariable() if ($exists) { $this->semanticalError( sprintf("'%s' is already defined.", $aliasIdentVariable), - $this->lexer->token + $this->lexer->token, ); } @@ -982,11 +915,9 @@ public function AliasIdentificationVariable() } /** - * AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier - * - * @return string + * AbstractSchemaName ::= fully_qualified_name | identifier */ - public function AbstractSchemaName() + public function AbstractSchemaName(): string { if ($this->lexer->isNextToken(TokenType::T_FULLY_QUALIFIED_NAME)) { $this->match(TokenType::T_FULLY_QUALIFIED_NAME); @@ -995,28 +926,10 @@ public function AbstractSchemaName() return $this->lexer->token->value; } - if ($this->lexer->isNextToken(TokenType::T_IDENTIFIER)) { - $this->match(TokenType::T_IDENTIFIER); - assert($this->lexer->token !== null); - - return $this->lexer->token->value; - } - - // @phpstan-ignore classConstant.deprecated - $this->match(TokenType::T_ALIASED_NAME); - + $this->match(TokenType::T_IDENTIFIER); assert($this->lexer->token !== null); - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8818', - 'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.', - $this->lexer->token->value - ); - - [$namespaceAlias, $simpleClassName] = explode(':', $this->lexer->token->value); - - return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + return $this->lexer->token->value; } /** @@ -1032,17 +945,15 @@ private function validateAbstractSchemaName(string $schemaName): void if (! (class_exists($schemaName, true) || interface_exists($schemaName, true))) { $this->semanticalError( sprintf("Class '%s' is not defined.", $schemaName), - $this->lexer->token + $this->lexer->token, ); } } /** * AliasResultVariable ::= identifier - * - * @return string */ - public function AliasResultVariable() + public function AliasResultVariable(): string { $this->match(TokenType::T_IDENTIFIER); @@ -1053,7 +964,7 @@ public function AliasResultVariable() if ($exists) { $this->semanticalError( sprintf("'%s' is already defined.", $resultVariable), - $this->lexer->token + $this->lexer->token, ); } @@ -1062,10 +973,8 @@ public function AliasResultVariable() /** * ResultVariable ::= identifier - * - * @return string */ - public function ResultVariable() + public function ResultVariable(): string { $this->match(TokenType::T_IDENTIFIER); @@ -1084,16 +993,14 @@ public function ResultVariable() /** * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) - * - * @return AST\JoinAssociationPathExpression */ - public function JoinAssociationPathExpression() + public function JoinAssociationPathExpression(): AST\JoinAssociationPathExpression { $identVariable = $this->IdentificationVariable(); if (! isset($this->queryComponents[$identVariable])) { $this->semanticalError( - 'Identification Variable ' . $identVariable . ' used in join path expression but was not defined before.' + 'Identification Variable ' . $identVariable . ' used in join path expression but was not defined before.', ); } @@ -1119,12 +1026,9 @@ public function JoinAssociationPathExpression() * * PathExpression ::= IdentificationVariable {"." identifier}* * - * @param int $expectedTypes * @phpstan-param int-mask-of $expectedTypes - * - * @return AST\PathExpression */ - public function PathExpression($expectedTypes) + public function PathExpression(int $expectedTypes): AST\PathExpression { $identVariable = $this->IdentificationVariable(); $field = null; @@ -1158,66 +1062,54 @@ public function PathExpression($expectedTypes) /** * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression - * - * @return AST\PathExpression */ - public function AssociationPathExpression() + public function AssociationPathExpression(): AST\PathExpression { return $this->PathExpression( AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION | - AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION + AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION, ); } /** * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression - * - * @return AST\PathExpression */ - public function SingleValuedPathExpression() + public function SingleValuedPathExpression(): AST\PathExpression { return $this->PathExpression( AST\PathExpression::TYPE_STATE_FIELD | - AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, ); } /** * StateFieldPathExpression ::= IdentificationVariable "." StateField - * - * @return AST\PathExpression */ - public function StateFieldPathExpression() + public function StateFieldPathExpression(): AST\PathExpression { return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD); } /** * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField - * - * @return AST\PathExpression */ - public function SingleValuedAssociationPathExpression() + public function SingleValuedAssociationPathExpression(): AST\PathExpression { return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION); } /** * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField - * - * @return AST\PathExpression */ - public function CollectionValuedPathExpression() + public function CollectionValuedPathExpression(): AST\PathExpression { return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION); } /** * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} - * - * @return AST\SelectClause */ - public function SelectClause() + public function SelectClause(): AST\SelectClause { $isDistinct = false; $this->match(TokenType::T_SELECT); @@ -1244,10 +1136,8 @@ public function SelectClause() /** * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression - * - * @return AST\SimpleSelectClause */ - public function SimpleSelectClause() + public function SimpleSelectClause(): AST\SimpleSelectClause { $isDistinct = false; $this->match(TokenType::T_SELECT); @@ -1263,10 +1153,8 @@ public function SimpleSelectClause() /** * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* - * - * @return AST\UpdateClause */ - public function UpdateClause() + public function UpdateClause(): AST\UpdateClause { $this->match(TokenType::T_UPDATE); assert($this->lexer->lookahead !== null); @@ -1315,10 +1203,8 @@ public function UpdateClause() /** * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable - * - * @return AST\DeleteClause */ - public function DeleteClause() + public function DeleteClause(): AST\DeleteClause { $this->match(TokenType::T_DELETE); @@ -1362,10 +1248,8 @@ public function DeleteClause() /** * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* - * - * @return AST\FromClause */ - public function FromClause() + public function FromClause(): AST\FromClause { $this->match(TokenType::T_FROM); @@ -1383,10 +1267,8 @@ public function FromClause() /** * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* - * - * @return AST\SubselectFromClause */ - public function SubselectFromClause() + public function SubselectFromClause(): AST\SubselectFromClause { $this->match(TokenType::T_FROM); @@ -1404,10 +1286,8 @@ public function SubselectFromClause() /** * WhereClause ::= "WHERE" ConditionalExpression - * - * @return AST\WhereClause */ - public function WhereClause() + public function WhereClause(): AST\WhereClause { $this->match(TokenType::T_WHERE); @@ -1416,10 +1296,8 @@ public function WhereClause() /** * HavingClause ::= "HAVING" ConditionalExpression - * - * @return AST\HavingClause */ - public function HavingClause() + public function HavingClause(): AST\HavingClause { $this->match(TokenType::T_HAVING); @@ -1428,10 +1306,8 @@ public function HavingClause() /** * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* - * - * @return AST\GroupByClause */ - public function GroupByClause() + public function GroupByClause(): AST\GroupByClause { $this->match(TokenType::T_GROUP); $this->match(TokenType::T_BY); @@ -1449,10 +1325,8 @@ public function GroupByClause() /** * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* - * - * @return AST\OrderByClause */ - public function OrderByClause() + public function OrderByClause(): AST\OrderByClause { $this->match(TokenType::T_ORDER); $this->match(TokenType::T_BY); @@ -1471,10 +1345,8 @@ public function OrderByClause() /** * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] - * - * @return AST\Subselect */ - public function Subselect() + public function Subselect(): AST\Subselect { // Increase query nesting level $this->nestingLevel++; @@ -1494,10 +1366,8 @@ public function Subselect() /** * UpdateItem ::= SingleValuedPathExpression "=" NewValue - * - * @return AST\UpdateItem */ - public function UpdateItem() + public function UpdateItem(): AST\UpdateItem { $pathExpr = $this->SingleValuedPathExpression(); @@ -1508,10 +1378,8 @@ public function UpdateItem() /** * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression - * - * @return string|AST\PathExpression */ - public function GroupByItem() + public function GroupByItem(): string|AST\PathExpression { // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression $glimpse = $this->lexer->glimpse(); @@ -1538,10 +1406,8 @@ public function GroupByItem() * SimpleArithmeticExpression | SingleValuedPathExpression | CaseExpression | * ScalarExpression | ResultVariable | FunctionDeclaration * ) ["ASC" | "DESC"] - * - * @return AST\OrderByItem */ - public function OrderByItem() + public function OrderByItem(): AST\OrderByItem { $this->lexer->peek(); // lookahead => '.' $this->lexer->peek(); // lookahead => token after '.' @@ -1553,31 +1419,14 @@ public function OrderByItem() $glimpse = $this->lexer->glimpse(); assert($this->lexer->lookahead !== null); - switch (true) { - case $this->isMathOperator($peek): - $expr = $this->SimpleArithmeticExpression(); - break; - - case $glimpse !== null && $glimpse->type === TokenType::T_DOT: - $expr = $this->SingleValuedPathExpression(); - break; - - case $this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis()): - $expr = $this->ScalarExpression(); - break; - - case $this->lexer->lookahead->type === TokenType::T_CASE: - $expr = $this->CaseExpression(); - break; - - case $this->isFunction(): - $expr = $this->FunctionDeclaration(); - break; - - default: - $expr = $this->ResultVariable(); - break; - } + $expr = match (true) { + $this->isMathOperator($peek) => $this->SimpleArithmeticExpression(), + $glimpse !== null && $glimpse->type === TokenType::T_DOT => $this->SingleValuedPathExpression(), + $this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->ScalarExpression(), + $this->lexer->lookahead->type === TokenType::T_CASE => $this->CaseExpression(), + $this->isFunction() => $this->FunctionDeclaration(), + default => $this->ResultVariable(), + }; $type = 'ASC'; $item = new AST\OrderByItem($expr); @@ -1611,10 +1460,8 @@ public function OrderByItem() * NewValue ::= SimpleArithmeticExpression | "NULL" * * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression - * - * @return AST\ArithmeticExpression|AST\InputParameter|null */ - public function NewValue() + public function NewValue(): AST\ArithmeticExpression|AST\InputParameter|null { if ($this->lexer->isNextToken(TokenType::T_NULL)) { $this->match(TokenType::T_NULL); @@ -1634,10 +1481,8 @@ public function NewValue() /** * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* - * - * @return AST\IdentificationVariableDeclaration */ - public function IdentificationVariableDeclaration() + public function IdentificationVariableDeclaration(): AST\IdentificationVariableDeclaration { $joins = []; $rangeVariableDeclaration = $this->RangeVariableDeclaration(); @@ -1658,7 +1503,7 @@ public function IdentificationVariableDeclaration() return new AST\IdentificationVariableDeclaration( $rangeVariableDeclaration, $indexBy, - $joins + $joins, ); } @@ -1675,10 +1520,8 @@ public function IdentificationVariableDeclaration() * expressions to be injected, but there is no scope to do that. Only scope * accessible is "FROM", prohibiting an easy implementation without larger * changes.} - * - * @return AST\IdentificationVariableDeclaration */ - public function SubselectIdentificationVariableDeclaration() + public function SubselectIdentificationVariableDeclaration(): AST\IdentificationVariableDeclaration { /* NOT YET IMPLEMENTED! @@ -1724,10 +1567,8 @@ public function SubselectIdentificationVariableDeclaration() * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" * (JoinAssociationDeclaration | RangeVariableDeclaration) * ["WITH" ConditionalExpression] - * - * @return AST\Join */ - public function Join() + public function Join(): AST\Join { // Check Join type $joinType = AST\Join::JOIN_TYPE_INNER; @@ -1781,11 +1622,9 @@ public function Join() /** * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable * - * @return AST\RangeVariableDeclaration - * * @throws QueryException */ - public function RangeVariableDeclaration() + public function RangeVariableDeclaration(): AST\RangeVariableDeclaration { if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS) && $this->lexer->glimpse()->type === TokenType::T_SELECT) { $this->semanticalError('Subquery is not supported here', $this->lexer->token); @@ -1821,10 +1660,8 @@ public function RangeVariableDeclaration() /** * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] - * - * @return AST\JoinAssociationDeclaration */ - public function JoinAssociationDeclaration() + public function JoinAssociationDeclaration(): AST\JoinAssociationDeclaration { $joinAssociationPathExpression = $this->JoinAssociationPathExpression(); @@ -1841,7 +1678,7 @@ public function JoinAssociationDeclaration() $field = $joinAssociationPathExpression->associationField; $class = $this->getMetadataForDqlAlias($identificationVariable); - $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]->targetEntity); // Building queryComponent $joinQueryComponent = [ @@ -1861,10 +1698,8 @@ public function JoinAssociationDeclaration() /** * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" - * - * @return AST\PartialObjectExpression */ - public function PartialObjectExpression() + public function PartialObjectExpression(): AST\PartialObjectExpression { $this->match(TokenType::T_PARTIAL); @@ -1919,24 +1754,29 @@ public function PartialObjectExpression() /** * NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")" - * - * @return AST\NewObjectExpression */ - public function NewObjectExpression() + public function NewObjectExpression(): AST\NewObjectExpression { + $useNamedArguments = false; + $args = []; + $argFieldAlias = []; $this->match(TokenType::T_NEW); + if ($this->lexer->isNextToken(TokenType::T_NAMED)) { + $this->match(TokenType::T_NAMED); + $useNamedArguments = true; + } + $className = $this->AbstractSchemaName(); // note that this is not yet validated $token = $this->lexer->token; $this->match(TokenType::T_OPEN_PARENTHESIS); - $args[] = $this->NewObjectArg(); + $this->addArgument($args, $useNamedArguments); while ($this->lexer->isNextToken(TokenType::T_COMMA)) { $this->match(TokenType::T_COMMA); - - $args[] = $this->NewObjectArg(); + $this->addArgument($args, $useNamedArguments); } $this->match(TokenType::T_CLOSE_PARENTHESIS); @@ -1953,35 +1793,77 @@ public function NewObjectExpression() return $expression; } + /** @param array $args */ + public function addArgument(array &$args, bool $useNamedArguments): void + { + $fieldAlias = null; + + if ($useNamedArguments) { + $startToken = $this->lexer->lookahead?->position ?? 0; + + $newArg = $this->NewObjectArg($fieldAlias); + + $key = $fieldAlias ?? $newArg->field ?? null; + + if ($key === null) { + throw NoMatchingPropertyException::create(trim(substr( + ($this->query->getDQL() ?? ''), + $startToken, + ($this->lexer->lookahead->position ?? 0) - $startToken, + ))); + } + + if (array_key_exists($key, $args)) { + throw DuplicateFieldException::create($key, trim(substr( + ($this->query->getDQL() ?? ''), + $startToken, + ($this->lexer->lookahead->position ?? 0) - $startToken, + ))); + } + + $args[$key] = $newArg; + } else { + $args[] = $this->NewObjectArg($fieldAlias); + } + } + /** - * NewObjectArg ::= ScalarExpression | "(" Subselect ")" - * - * @return mixed + * NewObjectArg ::= (ScalarExpression | "(" Subselect ")" | NewObjectExpression) ["AS" AliasResultVariable] */ - public function NewObjectArg() + public function NewObjectArg(string|null &$fieldAlias = null): mixed { + $fieldAlias = null; + assert($this->lexer->lookahead !== null); $token = $this->lexer->lookahead; $peek = $this->lexer->glimpse(); assert($peek !== null); + + $expression = null; + if ($token->type === TokenType::T_OPEN_PARENTHESIS && $peek->type === TokenType::T_SELECT) { $this->match(TokenType::T_OPEN_PARENTHESIS); $expression = $this->Subselect(); $this->match(TokenType::T_CLOSE_PARENTHESIS); + } elseif ($token->type === TokenType::T_NEW) { + $expression = $this->NewObjectExpression(); + } else { + $expression = $this->ScalarExpression(); + } - return $expression; + if ($this->lexer->isNextToken(TokenType::T_AS)) { + $this->match(TokenType::T_AS); + $fieldAlias = $this->AliasIdentificationVariable(); } - return $this->ScalarExpression(); + return $expression; } /** * IndexBy ::= "INDEX" "BY" SingleValuedPathExpression - * - * @return AST\IndexBy */ - public function IndexBy() + public function IndexBy(): AST\IndexBy { $this->match(TokenType::T_INDEX); $this->match(TokenType::T_BY); @@ -2000,7 +1882,7 @@ public function IndexBy() * * @return mixed One of the possible expressions or subexpressions. */ - public function ScalarExpression() + public function ScalarExpression(): mixed { assert($this->lexer->token !== null); assert($this->lexer->lookahead !== null); @@ -2025,14 +1907,11 @@ public function ScalarExpression() return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token->value); case $lookahead === TokenType::T_INPUT_PARAMETER: - switch (true) { - case $this->isMathOperator($peek): - // :param + u.value - return $this->SimpleArithmeticExpression(); + return match (true) { + $this->isMathOperator($peek) => $this->SimpleArithmeticExpression(), + default => $this->InputParameter(), + }; - default: - return $this->InputParameter(); - } case $lookahead === TokenType::T_CASE: case $lookahead === TokenType::T_COALESCE: case $lookahead === TokenType::T_NULLIF: @@ -2045,19 +1924,13 @@ public function ScalarExpression() // this check must be done before checking for a filed path expression case $this->isFunction(): - $this->lexer->peek(); // "(" - - switch (true) { - case $this->isMathOperator($this->peekBeyondClosingParenthesis()): - // SUM(u.id) + COUNT(u.id) - return $this->SimpleArithmeticExpression(); + $this->lexer->peek(); - default: - // IDENTITY(u) - return $this->FunctionDeclaration(); - } + return match (true) { + $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->SimpleArithmeticExpression(), + default => $this->FunctionDeclaration(), + }; - break; // it is no function, so it must be a field path case $lookahead === TokenType::T_IDENTIFIER: $this->lexer->peek(); // lookahead => '.' @@ -2088,7 +1961,7 @@ public function ScalarExpression() * * @return mixed One of the possible expressions or subexpressions. */ - public function CaseExpression() + public function CaseExpression(): mixed { assert($this->lexer->lookahead !== null); $lookahead = $this->lexer->lookahead->type; @@ -2121,10 +1994,8 @@ public function CaseExpression() /** * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" - * - * @return AST\CoalesceExpression */ - public function CoalesceExpression() + public function CoalesceExpression(): AST\CoalesceExpression { $this->match(TokenType::T_COALESCE); $this->match(TokenType::T_OPEN_PARENTHESIS); @@ -2146,10 +2017,8 @@ public function CoalesceExpression() /** * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" - * - * @return AST\NullIfExpression */ - public function NullIfExpression() + public function NullIfExpression(): AST\NullIfExpression { $this->match(TokenType::T_NULLIF); $this->match(TokenType::T_OPEN_PARENTHESIS); @@ -2165,10 +2034,8 @@ public function NullIfExpression() /** * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" - * - * @return AST\GeneralCaseExpression */ - public function GeneralCaseExpression() + public function GeneralCaseExpression(): AST\GeneralCaseExpression { $this->match(TokenType::T_CASE); @@ -2189,10 +2056,8 @@ public function GeneralCaseExpression() /** * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator - * - * @return AST\SimpleCaseExpression */ - public function SimpleCaseExpression() + public function SimpleCaseExpression(): AST\SimpleCaseExpression { $this->match(TokenType::T_CASE); $caseOperand = $this->StateFieldPathExpression(); @@ -2213,10 +2078,8 @@ public function SimpleCaseExpression() /** * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression - * - * @return AST\WhenClause */ - public function WhenClause() + public function WhenClause(): AST\WhenClause { $this->match(TokenType::T_WHEN); $conditionalExpression = $this->ConditionalExpression(); @@ -2227,10 +2090,8 @@ public function WhenClause() /** * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression - * - * @return AST\SimpleWhenClause */ - public function SimpleWhenClause() + public function SimpleWhenClause(): AST\SimpleWhenClause { $this->match(TokenType::T_WHEN); $conditionalExpression = $this->ScalarExpression(); @@ -2244,10 +2105,8 @@ public function SimpleWhenClause() * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | * PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression * ) [["AS"] ["HIDDEN"] AliasResultVariable] - * - * @return AST\SelectExpression */ - public function SelectExpression() + public function SelectExpression(): AST\SelectExpression { assert($this->lexer->lookahead !== null); $expression = null; @@ -2278,17 +2137,10 @@ public function SelectExpression() case $this->isFunction(): $this->lexer->peek(); // "(" - switch (true) { - case $this->isMathOperator($this->peekBeyondClosingParenthesis()): - // SUM(u.id) + COUNT(u.id) - $expression = $this->ScalarExpression(); - break; - - default: - // IDENTITY(u) - $expression = $this->FunctionDeclaration(); - break; - } + $expression = match (true) { + $this->isMathOperator($this->peekBeyondClosingParenthesis()) => $this->ScalarExpression(), + default => $this->FunctionDeclaration(), + }; break; @@ -2324,7 +2176,7 @@ public function SelectExpression() default: $this->syntaxError( 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', - $this->lexer->lookahead + $this->lexer->lookahead, ); } @@ -2376,10 +2228,8 @@ public function SelectExpression() * StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | * AggregateExpression | "(" Subselect ")" | ScalarExpression * ) [["AS"] AliasResultVariable] - * - * @return AST\SimpleSelectExpression */ - public function SimpleSelectExpression() + public function SimpleSelectExpression(): AST\SimpleSelectExpression { assert($this->lexer->lookahead !== null); $peek = $this->lexer->glimpse(); @@ -2464,10 +2314,8 @@ public function SimpleSelectExpression() /** * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* - * - * @return AST\ConditionalExpression|AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm */ - public function ConditionalExpression() + public function ConditionalExpression(): AST\ConditionalExpression|AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm { $conditionalTerms = []; $conditionalTerms[] = $this->ConditionalTerm(); @@ -2489,10 +2337,8 @@ public function ConditionalExpression() /** * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* - * - * @return AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm */ - public function ConditionalTerm() + public function ConditionalTerm(): AST\ConditionalFactor|AST\ConditionalPrimary|AST\ConditionalTerm { $conditionalFactors = []; $conditionalFactors[] = $this->ConditionalFactor(); @@ -2514,10 +2360,8 @@ public function ConditionalTerm() /** * ConditionalFactor ::= ["NOT"] ConditionalPrimary - * - * @return AST\ConditionalFactor|AST\ConditionalPrimary */ - public function ConditionalFactor() + public function ConditionalFactor(): AST\ConditionalFactor|AST\ConditionalPrimary { $not = false; @@ -2540,10 +2384,8 @@ public function ConditionalFactor() /** * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" - * - * @return AST\ConditionalPrimary */ - public function ConditionalPrimary() + public function ConditionalPrimary(): AST\ConditionalPrimary { $condPrimary = new AST\ConditionalPrimary(); @@ -2581,20 +2423,8 @@ public function ConditionalPrimary() * InExpression | NullComparisonExpression | ExistsExpression | * EmptyCollectionComparisonExpression | CollectionMemberExpression | * InstanceOfExpression - * - * @return (AST\BetweenExpression| - * AST\CollectionMemberExpression| - * AST\ComparisonExpression| - * AST\EmptyCollectionComparisonExpression| - * AST\ExistsExpression| - * AST\InExpression| - * AST\InstanceOfExpression| - * AST\LikeExpression| - * AST\NullComparisonExpression) - * - * @phpstan-ignore return.deprecatedClass */ - public function SimpleConditionalExpression() + public function SimpleConditionalExpression(): AST\ExistsExpression|AST\BetweenExpression|AST\LikeExpression|AST\InListExpression|AST\InSubselectExpression|AST\InstanceOfExpression|AST\CollectionMemberExpression|AST\NullComparisonExpression|AST\EmptyCollectionComparisonExpression|AST\ComparisonExpression { assert($this->lexer->lookahead !== null); if ($this->lexer->isNextToken(TokenType::T_EXISTS)) { @@ -2697,10 +2527,8 @@ public function SimpleConditionalExpression() /** * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" - * - * @return AST\EmptyCollectionComparisonExpression */ - public function EmptyCollectionComparisonExpression() + public function EmptyCollectionComparisonExpression(): AST\EmptyCollectionComparisonExpression { $pathExpression = $this->CollectionValuedPathExpression(); $this->match(TokenType::T_IS); @@ -2715,7 +2543,7 @@ public function EmptyCollectionComparisonExpression() return new AST\EmptyCollectionComparisonExpression( $pathExpression, - $not + $not, ); } @@ -2724,10 +2552,8 @@ public function EmptyCollectionComparisonExpression() * * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression * SimpleEntityExpression ::= IdentificationVariable | InputParameter - * - * @return AST\CollectionMemberExpression */ - public function CollectionMemberExpression() + public function CollectionMemberExpression(): AST\CollectionMemberExpression { $not = false; $entityExpr = $this->EntityExpression(); @@ -2747,16 +2573,14 @@ public function CollectionMemberExpression() return new AST\CollectionMemberExpression( $entityExpr, $this->CollectionValuedPathExpression(), - $not + $not, ); } /** * Literal ::= string | char | integer | float | boolean - * - * @return AST\Literal */ - public function Literal() + public function Literal(): AST\Literal { assert($this->lexer->lookahead !== null); assert($this->lexer->token !== null); @@ -2769,7 +2593,7 @@ public function Literal() case TokenType::T_INTEGER: case TokenType::T_FLOAT: $this->match( - $this->lexer->isNextToken(TokenType::T_INTEGER) ? TokenType::T_INTEGER : TokenType::T_FLOAT + $this->lexer->isNextToken(TokenType::T_INTEGER) ? TokenType::T_INTEGER : TokenType::T_FLOAT, ); return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token->value); @@ -2777,7 +2601,7 @@ public function Literal() case TokenType::T_TRUE: case TokenType::T_FALSE: $this->match( - $this->lexer->isNextToken(TokenType::T_TRUE) ? TokenType::T_TRUE : TokenType::T_FALSE + $this->lexer->isNextToken(TokenType::T_TRUE) ? TokenType::T_TRUE : TokenType::T_FALSE, ); return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token->value); @@ -2789,10 +2613,8 @@ public function Literal() /** * InParameter ::= ArithmeticExpression | InputParameter - * - * @return AST\InputParameter|AST\ArithmeticExpression */ - public function InParameter() + public function InParameter(): AST\InputParameter|AST\ArithmeticExpression { assert($this->lexer->lookahead !== null); if ($this->lexer->lookahead->type === TokenType::T_INPUT_PARAMETER) { @@ -2804,10 +2626,8 @@ public function InParameter() /** * InputParameter ::= PositionalParameter | NamedParameter - * - * @return AST\InputParameter */ - public function InputParameter() + public function InputParameter(): AST\InputParameter { $this->match(TokenType::T_INPUT_PARAMETER); assert($this->lexer->token !== null); @@ -2817,10 +2637,8 @@ public function InputParameter() /** * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" - * - * @return AST\ArithmeticExpression */ - public function ArithmeticExpression() + public function ArithmeticExpression(): AST\ArithmeticExpression { $expr = new AST\ArithmeticExpression(); @@ -2844,10 +2662,8 @@ public function ArithmeticExpression() /** * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* - * - * @return AST\SimpleArithmeticExpression|AST\ArithmeticTerm */ - public function SimpleArithmeticExpression() + public function SimpleArithmeticExpression(): AST\Node|string { $terms = []; $terms[] = $this->ArithmeticTerm(); @@ -2871,10 +2687,8 @@ public function SimpleArithmeticExpression() /** * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* - * - * @return AST\ArithmeticTerm */ - public function ArithmeticTerm() + public function ArithmeticTerm(): AST\Node|string { $factors = []; $factors[] = $this->ArithmeticFactor(); @@ -2898,10 +2712,8 @@ public function ArithmeticTerm() /** * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary - * - * @return AST\ArithmeticFactor */ - public function ArithmeticFactor() + public function ArithmeticFactor(): AST\Node|string|AST\ArithmeticFactor { $sign = null; @@ -2927,10 +2739,8 @@ public function ArithmeticFactor() * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable * | InputParameter | CaseExpression - * - * @return AST\Node|string */ - public function ArithmeticPrimary() + public function ArithmeticPrimary(): AST\Node|string { if ($this->lexer->isNextToken(TokenType::T_OPEN_PARENTHESIS)) { $this->match(TokenType::T_OPEN_PARENTHESIS); @@ -2985,10 +2795,8 @@ public function ArithmeticPrimary() /** * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" - * - * @return AST\Subselect|AST\Node|string */ - public function StringExpression() + public function StringExpression(): AST\Subselect|AST\Node|string { $peek = $this->lexer->glimpse(); assert($peek !== null); @@ -3016,10 +2824,8 @@ public function StringExpression() /** * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression - * - * @return AST\Node */ - public function StringPrimary() + public function StringPrimary(): AST\Node { assert($this->lexer->lookahead !== null); $lookaheadType = $this->lexer->lookahead->type; @@ -3063,16 +2869,14 @@ public function StringPrimary() } $this->syntaxError( - 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression' + 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression', ); } /** * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression - * - * @return AST\InputParameter|AST\PathExpression */ - public function EntityExpression() + public function EntityExpression(): AST\InputParameter|AST\PathExpression { $glimpse = $this->lexer->glimpse(); assert($glimpse !== null); @@ -3086,10 +2890,8 @@ public function EntityExpression() /** * SimpleEntityExpression ::= IdentificationVariable | InputParameter - * - * @return AST\InputParameter|AST\PathExpression */ - public function SimpleEntityExpression() + public function SimpleEntityExpression(): AST\InputParameter|AST\PathExpression { if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) { return $this->InputParameter(); @@ -3101,10 +2903,8 @@ public function SimpleEntityExpression() /** * AggregateExpression ::= * ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" - * - * @return AST\AggregateExpression */ - public function AggregateExpression() + public function AggregateExpression(): AST\AggregateExpression { assert($this->lexer->lookahead !== null); $lookaheadType = $this->lexer->lookahead->type; @@ -3133,10 +2933,8 @@ public function AggregateExpression() /** * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" - * - * @return AST\QuantifiedExpression */ - public function QuantifiedExpression() + public function QuantifiedExpression(): AST\QuantifiedExpression { assert($this->lexer->lookahead !== null); $lookaheadType = $this->lexer->lookahead->type; @@ -3159,10 +2957,8 @@ public function QuantifiedExpression() /** * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression - * - * @return AST\BetweenExpression */ - public function BetweenExpression() + public function BetweenExpression(): AST\BetweenExpression { $not = false; $arithExpr1 = $this->ArithmeticExpression(); @@ -3182,10 +2978,8 @@ public function BetweenExpression() /** * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) - * - * @return AST\ComparisonExpression */ - public function ComparisonExpression() + public function ComparisonExpression(): AST\ComparisonExpression { $this->lexer->glimpse(); @@ -3200,10 +2994,8 @@ public function ComparisonExpression() /** * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" - * - * @return AST\InListExpression|AST\InSubselectExpression */ - public function InExpression() + public function InExpression(): AST\InListExpression|AST\InSubselectExpression { $expression = $this->ArithmeticExpression(); @@ -3220,7 +3012,7 @@ public function InExpression() $inExpression = new AST\InSubselectExpression( $expression, $this->Subselect(), - $not + $not, ); } else { $literals = [$this->InParameter()]; @@ -3233,7 +3025,7 @@ public function InExpression() $inExpression = new AST\InListExpression( $expression, $literals, - $not + $not, ); } @@ -3244,10 +3036,8 @@ public function InExpression() /** * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") - * - * @return AST\InstanceOfExpression */ - public function InstanceOfExpression() + public function InstanceOfExpression(): AST\InstanceOfExpression { $identificationVariable = $this->IdentificationVariable(); @@ -3267,7 +3057,7 @@ public function InstanceOfExpression() return new AST\InstanceOfExpression( $identificationVariable, $exprValues, - $not + $not, ); } @@ -3291,10 +3081,8 @@ public function InstanceOfParameterList(): array /** * InstanceOfParameter ::= AbstractSchemaName | InputParameter - * - * @return AST\InputParameter|string */ - public function InstanceOfParameter() + public function InstanceOfParameter(): AST\InputParameter|string { if ($this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER)) { $this->match(TokenType::T_INPUT_PARAMETER); @@ -3312,10 +3100,8 @@ public function InstanceOfParameter() /** * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] - * - * @return AST\LikeExpression */ - public function LikeExpression() + public function LikeExpression(): AST\LikeExpression { $stringExpr = $this->StringExpression(); $not = false; @@ -3350,10 +3136,8 @@ public function LikeExpression() /** * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" - * - * @return AST\NullComparisonExpression */ - public function NullComparisonExpression() + public function NullComparisonExpression(): AST\NullComparisonExpression { switch (true) { case $this->lexer->isNextToken(TokenType::T_INPUT_PARAMETER): @@ -3426,10 +3210,8 @@ public function NullComparisonExpression() /** * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" - * - * @return AST\ExistsExpression */ - public function ExistsExpression() + public function ExistsExpression(): AST\ExistsExpression { $not = false; @@ -3450,10 +3232,8 @@ public function ExistsExpression() /** * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" - * - * @return string */ - public function ComparisonOperator() + public function ComparisonOperator(): string { assert($this->lexer->lookahead !== null); switch ($this->lexer->lookahead->value) { @@ -3500,10 +3280,8 @@ public function ComparisonOperator() /** * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime - * - * @return Functions\FunctionNode */ - public function FunctionDeclaration() + public function FunctionDeclaration(): Functions\FunctionNode { assert($this->lexer->lookahead !== null); $token = $this->lexer->lookahead; @@ -3533,7 +3311,7 @@ public function FunctionDeclaration() /** * Helper function for FunctionDeclaration grammar rule. */ - private function CustomFunctionDeclaration(): ?Functions\FunctionNode + private function CustomFunctionDeclaration(): Functions\FunctionNode|null { assert($this->lexer->lookahead !== null); $token = $this->lexer->lookahead; @@ -3542,19 +3320,12 @@ private function CustomFunctionDeclaration(): ?Functions\FunctionNode // Check for custom functions afterwards $config = $this->em->getConfiguration(); - switch (true) { - case $config->getCustomStringFunction($funcName) !== null: - return $this->CustomFunctionsReturningStrings(); - - case $config->getCustomNumericFunction($funcName) !== null: - return $this->CustomFunctionsReturningNumerics(); - - case $config->getCustomDatetimeFunction($funcName) !== null: - return $this->CustomFunctionsReturningDatetime(); - - default: - return null; - } + return match (true) { + $config->getCustomStringFunction($funcName) !== null => $this->CustomFunctionsReturningStrings(), + $config->getCustomNumericFunction($funcName) !== null => $this->CustomFunctionsReturningNumerics(), + $config->getCustomDatetimeFunction($funcName) !== null => $this->CustomFunctionsReturningDatetime(), + default => null, + }; } /** @@ -3568,10 +3339,8 @@ private function CustomFunctionDeclaration(): ?Functions\FunctionNode * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" - * - * @return Functions\FunctionNode */ - public function FunctionsReturningNumerics() + public function FunctionsReturningNumerics(): AST\Functions\FunctionNode { assert($this->lexer->lookahead !== null); $funcNameLower = strtolower($this->lexer->lookahead->value); @@ -3583,8 +3352,7 @@ public function FunctionsReturningNumerics() return $function; } - /** @return Functions\FunctionNode */ - public function CustomFunctionsReturningNumerics() + public function CustomFunctionsReturningNumerics(): AST\Functions\FunctionNode { assert($this->lexer->lookahead !== null); // getCustomNumericFunction is case-insensitive @@ -3593,9 +3361,12 @@ public function CustomFunctionsReturningNumerics() assert($functionClass !== null); - $function = is_string($functionClass) - ? new $functionClass($functionName) - : $functionClass($functionName); + if (is_string($functionClass)) { + $function = new $functionClass($functionName); + assert($function instanceof AST\Functions\FunctionNode); + } else { + $function = $functionClass($functionName); + } $function->parse($this); @@ -3609,10 +3380,8 @@ public function CustomFunctionsReturningNumerics() * "CURRENT_TIMESTAMP" | * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | * "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" - * - * @return Functions\FunctionNode */ - public function FunctionsReturningDatetime() + public function FunctionsReturningDatetime(): AST\Functions\FunctionNode { assert($this->lexer->lookahead !== null); $funcNameLower = strtolower($this->lexer->lookahead->value); @@ -3624,8 +3393,7 @@ public function FunctionsReturningDatetime() return $function; } - /** @return Functions\FunctionNode */ - public function CustomFunctionsReturningDatetime() + public function CustomFunctionsReturningDatetime(): AST\Functions\FunctionNode { assert($this->lexer->lookahead !== null); // getCustomDatetimeFunction is case-insensitive @@ -3651,10 +3419,8 @@ public function CustomFunctionsReturningDatetime() * "LOWER" "(" StringPrimary ")" | * "UPPER" "(" StringPrimary ")" | * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" - * - * @return Functions\FunctionNode */ - public function FunctionsReturningStrings() + public function FunctionsReturningStrings(): AST\Functions\FunctionNode { assert($this->lexer->lookahead !== null); $funcNameLower = strtolower($this->lexer->lookahead->value); @@ -3666,8 +3432,7 @@ public function FunctionsReturningStrings() return $function; } - /** @return Functions\FunctionNode */ - public function CustomFunctionsReturningStrings() + public function CustomFunctionsReturningStrings(): Functions\FunctionNode { assert($this->lexer->lookahead !== null); // getCustomStringFunction is case-insensitive diff --git a/src/Query/ParserResult.php b/src/Query/ParserResult.php index 28462dbc5be..34225e046e4 100644 --- a/src/Query/ParserResult.php +++ b/src/Query/ParserResult.php @@ -19,40 +19,27 @@ */ class ParserResult { - private const LEGACY_PROPERTY_MAPPING = [ - 'sqlExecutor' => '_sqlExecutor', - 'resultSetMapping' => '_resultSetMapping', - 'parameterMappings' => '_parameterMappings', - 'sqlFinalizer' => 'sqlFinalizer', - ]; - /** * The SQL executor used for executing the SQL. - * - * @var ?AbstractSqlExecutor */ - private $sqlExecutor; + private AbstractSqlExecutor|null $sqlExecutor = null; /** * The SQL executor used for executing the SQL. - * - * @var ?SqlFinalizer */ - private $sqlFinalizer; + private SqlFinalizer|null $sqlFinalizer = null; /** * The ResultSetMapping that describes how to map the SQL result set. - * - * @var ResultSetMapping */ - private $resultSetMapping; + private ResultSetMapping $resultSetMapping; /** * The mappings of DQL parameter names/positions to SQL parameter positions. * * @phpstan-var array> */ - private $parameterMappings = []; + private array $parameterMappings = []; /** * Initializes a new instance of the ParserResult class. @@ -68,17 +55,15 @@ public function __construct() * * @return ResultSetMapping The result set mapping of the parsed query */ - public function getResultSetMapping() + public function getResultSetMapping(): ResultSetMapping { return $this->resultSetMapping; } /** * Sets the ResultSetMapping of the parsed query. - * - * @return void */ - public function setResultSetMapping(ResultSetMapping $rsm) + public function setResultSetMapping(ResultSetMapping $rsm): void { $this->resultSetMapping = $rsm; } @@ -87,12 +72,8 @@ public function setResultSetMapping(ResultSetMapping $rsm) * Sets the SQL executor that should be used for this ParserResult. * * @deprecated - * - * @param AbstractSqlExecutor $executor - * - * @return void */ - public function setSqlExecutor($executor) + public function setSqlExecutor(AbstractSqlExecutor $executor): void { $this->sqlExecutor = $executor; } @@ -101,11 +82,16 @@ public function setSqlExecutor($executor) * Gets the SQL executor used by this ParserResult. * * @deprecated - * - * @return ?AbstractSqlExecutor */ - public function getSqlExecutor() + public function getSqlExecutor(): AbstractSqlExecutor { + if ($this->sqlExecutor === null) { + throw new LogicException(sprintf( + 'Executor not set yet. Call %s::setSqlExecutor() first.', + self::class, + )); + } + return $this->sqlExecutor; } @@ -130,13 +116,8 @@ public function prepareSqlExecutor(Query $query): AbstractSqlExecutor /** * Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to * several SQL parameter positions. - * - * @param string|int $dqlPosition - * @param int $sqlPosition - * - * @return void */ - public function addParameterMapping($dqlPosition, $sqlPosition) + public function addParameterMapping(string|int $dqlPosition, int $sqlPosition): void { $this->parameterMappings[$dqlPosition][] = $sqlPosition; } @@ -146,7 +127,7 @@ public function addParameterMapping($dqlPosition, $sqlPosition) * * @phpstan-return array> The parameter mappings. */ - public function getParameterMappings() + public function getParameterMappings(): array { return $this->parameterMappings; } @@ -159,26 +140,8 @@ public function getParameterMappings() * @return int[] The positions of the corresponding SQL parameters. * @phpstan-return list */ - public function getSqlParameterPositions($dqlPosition) + public function getSqlParameterPositions(string|int $dqlPosition): array { return $this->parameterMappings[$dqlPosition]; } - - public function __wakeup(): void - { - $this->__unserialize((array) $this); - } - - /** @param array $data */ - public function __unserialize(array $data): void - { - foreach (self::LEGACY_PROPERTY_MAPPING as $property => $legacyProperty) { - $this->$property = $data[sprintf("\0%s\0%s", self::class, $legacyProperty)] - ?? $data[self::class][$legacyProperty] - ?? $data[sprintf("\0%s\0%s", self::class, $property)] - ?? $data[self::class][$property] - ?? $this->$property - ?? null; - } - } } diff --git a/src/Query/Printer.php b/src/Query/Printer.php index 561697ec79f..db1f159ae13 100644 --- a/src/Query/Printer.php +++ b/src/Query/Printer.php @@ -13,28 +13,16 @@ */ class Printer { - /** - * Current indentation level - * - * @var int - */ - protected $_indent = 0; - - /** - * Defines whether parse tree is printed (default, false) or not (true). - * - * @var bool - */ - protected $_silent; + /** Current indentation level */ + protected int $indent = 0; /** * Constructs a new parse tree printer. * * @param bool $silent Parse tree will not be printed if true. */ - public function __construct($silent = false) + public function __construct(protected bool $silent = false) { - $this->_silent = $silent; } /** @@ -44,25 +32,21 @@ public function __construct($silent = false) * This method is called before executing a production. * * @param string $name Production name. - * - * @return void */ - public function startProduction($name) + public function startProduction(string $name): void { $this->println('(' . $name); - $this->_indent++; + $this->indent++; } /** * Decreases indentation level by one and prints a closing parenthesis. * * This method is called after executing a production. - * - * @return void */ - public function endProduction() + public function endProduction(): void { - $this->_indent--; + $this->indent--; $this->println(')'); } @@ -70,13 +54,11 @@ public function endProduction() * Prints text indented with spaces depending on current indentation level. * * @param string $str The text. - * - * @return void */ - public function println($str) + public function println(string $str): void { - if (! $this->_silent) { - echo str_repeat(' ', $this->_indent), $str, "\n"; + if (! $this->silent) { + echo str_repeat(' ', $this->indent), $str, "\n"; } } } diff --git a/src/Query/QueryException.php b/src/Query/QueryException.php index 8b572540876..4b8efaeff7e 100644 --- a/src/Query/QueryException.php +++ b/src/Query/QueryException.php @@ -5,250 +5,160 @@ namespace Doctrine\ORM\Query; use Doctrine\ORM\Exception\ORMException; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Query\AST\PathExpression; use Exception; use Stringable; +use Throwable; -/** @phpstan-import-type AssociationMapping from ClassMetadata */ -class QueryException extends ORMException +class QueryException extends Exception implements ORMException { - /** - * @param string $dql - * - * @return QueryException - */ - public static function dqlError($dql) + public static function dqlError(string $dql): self { return new self($dql); } - /** - * @param string $message - * @param Exception|null $previous - * - * @return QueryException - */ - public static function syntaxError($message, $previous = null) + public static function syntaxError(string $message, Throwable|null $previous = null): self { return new self('[Syntax Error] ' . $message, 0, $previous); } - /** - * @param string $message - * @param Exception|null $previous - * - * @return QueryException - */ - public static function semanticalError($message, $previous = null) + public static function semanticalError(string $message, Throwable|null $previous = null): self { return new self('[Semantical Error] ' . $message, 0, $previous); } - /** @return QueryException */ - public static function invalidLockMode() + public static function invalidLockMode(): self { return new self('Invalid lock mode hint provided.'); } - /** - * @param string $expected - * @param string $received - * - * @return QueryException - */ - public static function invalidParameterType($expected, $received) + public static function invalidParameterType(string $expected, string $received): self { return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); } - /** - * @param string $pos - * - * @return QueryException - */ - public static function invalidParameterPosition($pos) + public static function invalidParameterPosition(string $pos): self { return new self('Invalid parameter position: ' . $pos); } - /** - * @param int $expected - * @param int $received - * - * @return QueryException - */ - public static function tooManyParameters($expected, $received) + public static function tooManyParameters(int $expected, int $received): self { return new self('Too many parameters: the query defines ' . $expected . ' parameters and you bound ' . $received); } - /** - * @param int $expected - * @param int $received - * - * @return QueryException - */ - public static function tooFewParameters($expected, $received) + public static function tooFewParameters(int $expected, int $received): self { return new self('Too few parameters: the query defines ' . $expected . ' parameters but you only bound ' . $received); } - /** - * @param string $value - * - * @return QueryException - */ - public static function invalidParameterFormat($value) + public static function invalidParameterFormat(string $value): self { return new self('Invalid parameter format, ' . $value . ' given, but : or ? expected.'); } - /** - * @param string $key - * - * @return QueryException - */ - public static function unknownParameter($key) + public static function unknownParameter(string $key): self { return new self('Invalid parameter: token ' . $key . ' is not defined in the query.'); } - /** @return QueryException */ - public static function parameterTypeMismatch() + public static function parameterTypeMismatch(): self { return new self('DQL Query parameter and type numbers mismatch, but have to be exactly equal.'); } - /** - * @param PathExpression $pathExpr - * - * @return QueryException - */ - public static function invalidPathExpression($pathExpr) + public static function invalidPathExpression(PathExpression $pathExpr): self { return new self( - "Invalid PathExpression '" . $pathExpr->identificationVariable . '.' . $pathExpr->field . "'." + "Invalid PathExpression '" . $pathExpr->identificationVariable . '.' . $pathExpr->field . "'.", ); } - /** - * @param string|Stringable $literal - * - * @return QueryException - */ - public static function invalidLiteral($literal) + public static function invalidLiteral(string|Stringable $literal): self { return new self("Invalid literal '" . $literal . "'"); } - /** - * @param string[] $assoc - * @phpstan-param AssociationMapping $assoc - * - * @return QueryException - */ - public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + public static function iterateWithFetchJoinCollectionNotAllowed(AssociationMapping $assoc): self { return new self( 'Invalid query operation: Not allowed to iterate over fetch join collections ' . - 'in class ' . $assoc['sourceEntity'] . ' association ' . $assoc['fieldName'] + 'in class ' . $assoc->sourceEntity . ' association ' . $assoc->fieldName, ); } - /** @return QueryException */ - public static function partialObjectsAreDangerous() + public static function partialObjectsAreDangerous(): self { return new self( 'Loading partial objects is dangerous. Fetch full objects or consider ' . 'using a different fetch mode. If you really want partial objects, ' . - 'set the doctrine.forcePartialLoad query hint to TRUE.' + 'set the doctrine.forcePartialLoad query hint to TRUE.', ); } /** * @param string[] $assoc * @phpstan-param array $assoc - * - * @return QueryException */ - public static function overwritingJoinConditionsNotYetSupported($assoc) + public static function overwritingJoinConditionsNotYetSupported(array $assoc): self { return new self( 'Unsupported query operation: It is not yet possible to overwrite the join ' . 'conditions in class ' . $assoc['sourceEntityName'] . ' association ' . $assoc['fieldName'] . '. ' . - 'Use WITH to append additional join conditions to the association.' + 'Use WITH to append additional join conditions to the association.', ); } - /** @return QueryException */ - public static function associationPathInverseSideNotSupported(PathExpression $pathExpr) + public static function associationPathInverseSideNotSupported(PathExpression $pathExpr): self { return new self( 'A single-valued association path expression to an inverse side is not supported in DQL queries. ' . - 'Instead of "' . $pathExpr->identificationVariable . '.' . $pathExpr->field . '" use an explicit join.' + 'Instead of "' . $pathExpr->identificationVariable . '.' . $pathExpr->field . '" use an explicit join.', ); } - /** - * @param string[] $assoc - * @phpstan-param AssociationMapping $assoc - * - * @return QueryException - */ - public static function iterateWithFetchJoinNotAllowed($assoc) + public static function iterateWithFetchJoinNotAllowed(AssociationMapping $assoc): self { return new self( - 'Iterate with fetch join in class ' . $assoc['sourceEntity'] . - ' using association ' . $assoc['fieldName'] . ' not allowed.' + 'Iterate with fetch join in class ' . $assoc->sourceEntity . + ' using association ' . $assoc->fieldName . ' not allowed.', ); } - public static function eagerFetchJoinWithNotAllowed(string $sourceEntity, string $fieldName): QueryException + public static function eagerFetchJoinWithNotAllowed(string $sourceEntity, string $fieldName): self { return new self( 'Associations with fetch-mode=EAGER may not be using WITH conditions in - "' . $sourceEntity . '#' . $fieldName . '".' + "' . $sourceEntity . '#' . $fieldName . '".', ); } - public static function iterateWithMixedResultNotAllowed(): QueryException + public static function iterateWithMixedResultNotAllowed(): self { return new self('Iterating a query with mixed results (using scalars) is not supported.'); } - /** @return QueryException */ - public static function associationPathCompositeKeyNotSupported() + public static function associationPathCompositeKeyNotSupported(): self { return new self( 'A single-valued association path expression to an entity with a composite primary ' . 'key is not supported. Explicitly name the components of the composite primary key ' . - 'in the query.' + 'in the query.', ); } - /** - * @param string $className - * @param string $rootClass - * - * @return QueryException - */ - public static function instanceOfUnrelatedClass($className, $rootClass) + public static function instanceOfUnrelatedClass(string $className, string $rootClass): self { return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . 'inheritance hierarchy does not exists between these two classes.'); } - /** - * @param string $dqlAlias - * - * @return QueryException - */ - public static function invalidQueryComponent($dqlAlias) + public static function invalidQueryComponent(string $dqlAlias): self { return new self( "Invalid query component given for DQL alias '" . $dqlAlias . "', " . - "requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys." + "requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys.", ); } } diff --git a/src/Query/QueryExpressionVisitor.php b/src/Query/QueryExpressionVisitor.php index afd90334297..3e0ec655a46 100644 --- a/src/Query/QueryExpressionVisitor.php +++ b/src/Query/QueryExpressionVisitor.php @@ -12,7 +12,6 @@ use RuntimeException; use function count; -use function defined; use function str_replace; use function str_starts_with; @@ -21,28 +20,23 @@ */ class QueryExpressionVisitor extends ExpressionVisitor { - /** @var array */ - private static $operatorMap = [ + private const OPERATOR_MAP = [ Comparison::GT => Expr\Comparison::GT, Comparison::GTE => Expr\Comparison::GTE, Comparison::LT => Expr\Comparison::LT, Comparison::LTE => Expr\Comparison::LTE, ]; - /** @var mixed[] */ - private $queryAliases; - - /** @var Expr */ - private $expr; + private readonly Expr $expr; /** @var list */ - private $parameters = []; + private array $parameters = []; /** @param mixed[] $queryAliases */ - public function __construct($queryAliases) - { - $this->queryAliases = $queryAliases; - $this->expr = new Expr(); + public function __construct( + private readonly array $queryAliases, + ) { + $this->expr = new Expr(); } /** @@ -51,37 +45,25 @@ public function __construct($queryAliases) * * @return ArrayCollection */ - public function getParameters() + public function getParameters(): ArrayCollection { return new ArrayCollection($this->parameters); } - /** - * Clears parameters. - * - * @return void - */ - public function clearParameters() + public function clearParameters(): void { $this->parameters = []; } /** * Converts Criteria expression to Query one based on static map. - * - * @param string $criteriaOperator - * - * @return string|null */ - private static function convertComparisonOperator($criteriaOperator) + private static function convertComparisonOperator(string $criteriaOperator): string|null { - return self::$operatorMap[$criteriaOperator] ?? null; + return self::OPERATOR_MAP[$criteriaOperator] ?? null; } - /** - * {@inheritDoc} - */ - public function walkCompositeExpression(CompositeExpression $expr) + public function walkCompositeExpression(CompositeExpression $expr): mixed { $expressionList = []; @@ -89,27 +71,15 @@ public function walkCompositeExpression(CompositeExpression $expr) $expressionList[] = $this->dispatch($child); } - switch ($expr->getType()) { - case CompositeExpression::TYPE_AND: - return new Expr\Andx($expressionList); - - case CompositeExpression::TYPE_OR: - return new Expr\Orx($expressionList); - - default: - // Multiversion support for `doctrine/collections` before and after v2.1.0 - if (defined(CompositeExpression::class . '::TYPE_NOT') && $expr->getType() === CompositeExpression::TYPE_NOT) { - return $this->expr->not($expressionList[0]); - } - - throw new RuntimeException('Unknown composite ' . $expr->getType()); - } + return match ($expr->getType()) { + CompositeExpression::TYPE_AND => new Expr\Andx($expressionList), + CompositeExpression::TYPE_OR => new Expr\Orx($expressionList), + CompositeExpression::TYPE_NOT => $this->expr->not($expressionList[0]), + default => throw new RuntimeException('Unknown composite ' . $expr->getType()), + }; } - /** - * {@inheritDoc} - */ - public function walkComparison(Comparison $comparison) + public function walkComparison(Comparison $comparison): mixed { if (! isset($this->queryAliases[0])) { throw new QueryException('No aliases are set before invoking walkComparison().'); @@ -195,7 +165,7 @@ public function walkComparison(Comparison $comparison) return new Expr\Comparison( $field, $operator, - $placeholder + $placeholder, ); } @@ -203,10 +173,7 @@ public function walkComparison(Comparison $comparison) } } - /** - * {@inheritDoc} - */ - public function walkValue(Value $value) + public function walkValue(Value $value): mixed { return $value->getValue(); } diff --git a/src/Query/ResultSetMapping.php b/src/Query/ResultSetMapping.php index 1f3c1843f51..c0ccc127fd5 100644 --- a/src/Query/ResultSetMapping.php +++ b/src/Query/ResultSetMapping.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Query; -use function array_merge; use function count; /** @@ -25,17 +24,15 @@ class ResultSetMapping * Whether the result is mixed (contains scalar values together with field values). * * @ignore - * @var bool */ - public $isMixed = false; + public bool $isMixed = false; /** * Whether the result is a select statement. * * @ignore - * @var bool */ - public $isSelect = true; + public bool $isSelect = true; /** * Maps alias names to class names. @@ -43,7 +40,7 @@ class ResultSetMapping * @ignore * @var array */ - public $aliasMap = []; + public array $aliasMap = []; /** * Maps alias names to related association field names. @@ -51,7 +48,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $relationMap = []; + public array $relationMap = []; /** * Maps alias names to parent alias names. @@ -59,7 +56,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $parentAliasMap = []; + public array $parentAliasMap = []; /** * Maps column names in the result set to field names for each class. @@ -67,14 +64,14 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $fieldMappings = []; + public array $fieldMappings = []; /** * Map field names for each class to alias * * @var array>> */ - public $columnAliasMappings = []; + public array $columnAliasMappings = []; /** * Maps column names in the result set to the alias/field name to use in the mapped result. @@ -82,7 +79,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $scalarMappings = []; + public array $scalarMappings = []; /** * Maps scalar columns to enums @@ -98,7 +95,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $typeMappings = []; + public array $typeMappings = []; /** * Maps entities in the result set to the alias name to use in the mapped result. @@ -106,7 +103,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $entityMappings = []; + public array $entityMappings = []; /** * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. @@ -114,7 +111,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $metaMappings = []; + public array $metaMappings = []; /** * Maps column names in the result set to the alias they belong to. @@ -122,7 +119,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $columnOwnerMap = []; + public array $columnOwnerMap = []; /** * List of columns in the result set that are used as discriminator columns. @@ -130,7 +127,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $discriminatorColumns = []; + public array $discriminatorColumns = []; /** * Maps alias names to field names that should be used for indexing. @@ -138,7 +135,7 @@ class ResultSetMapping * @ignore * @phpstan-var array */ - public $indexByMap = []; + public array $indexByMap = []; /** * Map from column names to class names that declare the field the column is mapped to. @@ -146,35 +143,42 @@ class ResultSetMapping * @ignore * @var array */ - public $declaringClasses = []; + public array $declaringClasses = []; /** * This is necessary to hydrate derivate foreign keys correctly. * * @phpstan-var array> */ - public $isIdentifierColumn = []; + public array $isIdentifierColumn = []; /** * Maps column names in the result set to field names for each new object expression. * * @phpstan-var array> */ - public $newObjectMappings = []; + public array $newObjectMappings = []; + + /** + * Maps last argument for new objects in order to initiate object construction + * + * @phpstan-var array + */ + public array $nestedNewObjectArguments = []; /** * Maps metadata parameter names to the metadata attribute. * * @phpstan-var array */ - public $metadataParameterMapping = []; + public array $metadataParameterMapping = []; /** * Contains query parameter names to be resolved as discriminator values * * @phpstan-var array */ - public $discriminatorParameters = []; + public array $discriminatorParameters = []; /** * Adds an entity result to this ResultSetMapping. @@ -189,7 +193,7 @@ class ResultSetMapping * * @todo Rename: addRootEntity */ - public function addEntityResult($class, $alias, $resultAlias = null) + public function addEntityResult(string $class, string $alias, string|null $resultAlias = null): static { $this->aliasMap[$alias] = $class; $this->entityMappings[$alias] = $resultAlias; @@ -214,7 +218,7 @@ public function addEntityResult($class, $alias, $resultAlias = null) * * @todo Rename: addDiscriminatorColumn */ - public function setDiscriminatorColumn($alias, $discrColumn) + public function setDiscriminatorColumn(string $alias, string $discrColumn): static { $this->discriminatorColumns[$alias] = $discrColumn; $this->columnOwnerMap[$discrColumn] = $alias; @@ -230,11 +234,11 @@ public function setDiscriminatorColumn($alias, $discrColumn) * * @return $this */ - public function addIndexBy($alias, $fieldName) + public function addIndexBy(string $alias, string $fieldName): static { $found = false; - foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) { + foreach ([...$this->metaMappings, ...$this->fieldMappings] as $columnName => $columnFieldName) { if (! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) { continue; } @@ -263,11 +267,9 @@ public function addIndexBy($alias, $fieldName) /** * Sets to index by a scalar result column name. * - * @param string $resultColumnName - * * @return $this */ - public function addIndexByScalar($resultColumnName) + public function addIndexByScalar(string $resultColumnName): static { $this->indexByMap['scalars'] = $resultColumnName; @@ -277,12 +279,9 @@ public function addIndexByScalar($resultColumnName) /** * Sets a column to use for indexing an entity or joined entity result by the given alias name. * - * @param string $alias - * @param string $resultColumnName - * * @return $this */ - public function addIndexByColumn($alias, $resultColumnName) + public function addIndexByColumn(string $alias, string $resultColumnName): static { $this->indexByMap[$alias] = $resultColumnName; @@ -293,13 +292,9 @@ public function addIndexByColumn($alias, $resultColumnName) * Checks whether an entity result or joined entity result with a given alias has * a field set for indexing. * - * @param string $alias - * - * @return bool - * * @todo Rename: isIndexed($alias) */ - public function hasIndexBy($alias) + public function hasIndexBy(string $alias): bool { return isset($this->indexByMap[$alias]); } @@ -310,11 +305,9 @@ public function hasIndexBy($alias) * * @param string $columnName The name of the column in the SQL result set. * - * @return bool - * * @todo Rename: isField */ - public function isFieldResult($columnName) + public function isFieldResult(string $columnName): bool { return isset($this->fieldMappings[$columnName]); } @@ -335,7 +328,7 @@ public function isFieldResult($columnName) * * @todo Rename: addField */ - public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null) + public function addFieldResult(string $alias, string $columnName, string $fieldName, string|null $declaringClass = null): static { // column name (in result set) => field name $this->fieldMappings[$columnName] = $fieldName; @@ -381,7 +374,7 @@ public function getColumnAliasByField(string $alias, string $fieldName): string * * @todo Rename: addJoinedEntity */ - public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) + public function addJoinedEntityResult(string $class, string $alias, string $parentAlias, string $relation): static { $this->aliasMap[$alias] = $class; $this->parentAliasMap[$alias] = $parentAlias; @@ -401,7 +394,7 @@ public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) * * @todo Rename: addScalar */ - public function addScalarResult($columnName, $alias, $type = 'string') + public function addScalarResult(string $columnName, string|int $alias, string $type = 'string'): static { $this->scalarMappings[$columnName] = $alias; $this->typeMappings[$columnName] = $type; @@ -421,7 +414,7 @@ public function addScalarResult($columnName, $alias, $type = 'string') * * @return $this */ - public function addEnumResult($columnName, $enumType) + public function addEnumResult(string $columnName, string $enumType): static { $this->enumMappings[$columnName] = $enumType; @@ -430,13 +423,8 @@ public function addEnumResult($columnName, $enumType) /** * Adds a metadata parameter mappings. - * - * @param string|int $parameter The parameter name in the SQL result set. - * @param string $attribute The metadata attribute. - * - * @return void */ - public function addMetadataParameterMapping($parameter, $attribute) + public function addMetadataParameterMapping(string|int $parameter, string $attribute): void { $this->metadataParameterMapping[$parameter] = $attribute; } @@ -444,13 +432,9 @@ public function addMetadataParameterMapping($parameter, $attribute) /** * Checks whether a column with a given name is mapped as a scalar result. * - * @param string $columnName The name of the column in the SQL result set. - * - * @return bool - * * @todo Rename: isScalar */ - public function isScalarResult($columnName) + public function isScalarResult(string $columnName): bool { return isset($this->scalarMappings[$columnName]); } @@ -459,11 +443,9 @@ public function isScalarResult($columnName) * Gets the name of the class of an entity result or joined entity result, * identified by the given unique alias. * - * @param string $alias - * - * @return class-string + * @phpstan-return class-string */ - public function getClassName($alias) + public function getClassName(string $alias): string { return $this->aliasMap[$alias]; } @@ -472,10 +454,8 @@ public function getClassName($alias) * Gets the field alias for a column that is mapped as a scalar value. * * @param string $columnName The name of the column in the SQL result set. - * - * @return string|int */ - public function getScalarAlias($columnName) + public function getScalarAlias(string $columnName): string|int { return $this->scalarMappings[$columnName]; } @@ -483,85 +463,57 @@ public function getScalarAlias($columnName) /** * Gets the name of the class that owns a field mapping for the specified column. * - * @param string $columnName - * - * @return class-string + * @phpstan-return class-string */ - public function getDeclaringClass($columnName) + public function getDeclaringClass(string $columnName): string { return $this->declaringClasses[$columnName]; } - /** - * @param string $alias - * - * @return string - */ - public function getRelation($alias) + public function getRelation(string $alias): string { return $this->relationMap[$alias]; } - /** - * @param string $alias - * - * @return bool - */ - public function isRelation($alias) + public function isRelation(string $alias): bool { return isset($this->relationMap[$alias]); } /** * Gets the alias of the class that owns a field mapping for the specified column. - * - * @param string $columnName - * - * @return string */ - public function getEntityAlias($columnName) + public function getEntityAlias(string $columnName): string { return $this->columnOwnerMap[$columnName]; } /** * Gets the parent alias of the given alias. - * - * @param string $alias - * - * @return string */ - public function getParentAlias($alias) + public function getParentAlias(string $alias): string { return $this->parentAliasMap[$alias]; } /** * Checks whether the given alias has a parent alias. - * - * @param string $alias - * - * @return bool */ - public function hasParentAlias($alias) + public function hasParentAlias(string $alias): bool { return isset($this->parentAliasMap[$alias]); } /** * Gets the field name for a column name. - * - * @param string $columnName - * - * @return string */ - public function getFieldName($columnName) + public function getFieldName(string $columnName): string { return $this->fieldMappings[$columnName]; } /** @return array */ - public function getAliasMap() + public function getAliasMap(): array { return $this->aliasMap; } @@ -569,10 +521,9 @@ public function getAliasMap() /** * Gets the number of different entities that appear in the mapped result. * - * @return int * @phpstan-return 0|positive-int */ - public function getEntityResultCount() + public function getEntityResultCount(): int { return count($this->aliasMap); } @@ -583,10 +534,8 @@ public function getEntityResultCount() * Mixed results can only occur in object and array (graph) hydration. In such a * case a mixed result means that scalar values are mixed with objects/array in * the result. - * - * @return bool */ - public function isMixedResult() + public function isMixedResult(): bool { return $this->isMixed; } @@ -594,18 +543,22 @@ public function isMixedResult() /** * Adds a meta column (foreign key or discriminator column) to the result set. * - * @param string $alias The result alias with which the meta result should be placed in the result structure. - * @param string $columnName The name of the column in the SQL result set. - * @param string $fieldName The name of the field on the declaring class. - * @param bool $isIdentifierColumn - * @param string|null $type The column type + * @param string $alias The result alias with which the meta result should be placed in the result structure. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. + * @param string|null $type The column type * * @return $this * * @todo Make all methods of this class require all parameters and not infer anything */ - public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null) - { + public function addMetaResult( + string $alias, + string $columnName, + string $fieldName, + bool $isIdentifierColumn = false, + string|null $type = null, + ): static { $this->metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; diff --git a/src/Query/ResultSetMappingBuilder.php b/src/Query/ResultSetMappingBuilder.php index e19da1c7391..726564a3f1b 100644 --- a/src/Query/ResultSetMappingBuilder.php +++ b/src/Query/ResultSetMappingBuilder.php @@ -8,23 +8,17 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Internal\SQLResultCasing; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; -use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\Utility\PersisterHelper; use InvalidArgumentException; -use LogicException; +use Stringable; -use function assert; -use function explode; use function in_array; use function sprintf; -use function str_contains; -use function strtolower; /** * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields. */ -class ResultSetMappingBuilder extends ResultSetMapping +class ResultSetMappingBuilder extends ResultSetMapping implements Stringable { use SQLResultCasing; @@ -50,28 +44,13 @@ class ResultSetMappingBuilder extends ResultSetMapping */ public const COLUMN_RENAMING_INCREMENT = 3; - /** @var int */ - private $sqlCounter = 0; + private int $sqlCounter = 0; - /** @var EntityManagerInterface */ - private $em; - - /** - * Default column renaming mode. - * - * @var int - * @phpstan-var self::COLUMN_RENAMING_* - */ - private $defaultRenameMode; - - /** - * @param int $defaultRenameMode - * @phpstan-param self::COLUMN_RENAMING_* $defaultRenameMode - */ - public function __construct(EntityManagerInterface $em, $defaultRenameMode = self::COLUMN_RENAMING_NONE) - { - $this->em = $em; - $this->defaultRenameMode = $defaultRenameMode; + /** @phpstan-param self::COLUMN_RENAMING_* $defaultRenameMode */ + public function __construct( + private readonly EntityManagerInterface $em, + private readonly int $defaultRenameMode = self::COLUMN_RENAMING_NONE, + ) { } /** @@ -80,13 +59,14 @@ public function __construct(EntityManagerInterface $em, $defaultRenameMode = sel * @param class-string $class The class name of the root entity. * @param string $alias The unique alias to use for the root entity. * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). - * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). * @phpstan-param self::COLUMN_RENAMING_*|null $renameMode - * - * @return void */ - public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = [], $renameMode = null) - { + public function addRootEntityFromClassMetadata( + string $class, + string $alias, + array $renamedColumns = [], + int|null $renameMode = null, + ): void { $renameMode = $renameMode ?: $this->defaultRenameMode; $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); @@ -103,13 +83,16 @@ public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = * @param string $relation The association field that connects the parent entity result * with the joined entity result. * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). - * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). * @phpstan-param self::COLUMN_RENAMING_*|null $renameMode - * - * @return void */ - public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = [], $renameMode = null) - { + public function addJoinedEntityFromClassMetadata( + string $class, + string $alias, + string $parentAlias, + string $relation, + array $renamedColumns = [], + int|null $renameMode = null, + ): void { $renameMode = $renameMode ?: $this->defaultRenameMode; $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); @@ -120,16 +103,12 @@ public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $ /** * Adds all fields of the given class to the result set mapping (columns and meta fields). * - * @param string $class - * @param string $alias * @param string[] $columnAliasMap * @phpstan-param array $columnAliasMap * - * @return void - * * @throws InvalidArgumentException */ - protected function addAllClassFields($class, $alias, $columnAliasMap = []) + protected function addAllClassFields(string $class, string $alias, array $columnAliasMap = []): void { $classMetadata = $this->em->getClassMetadata($class); $platform = $this->em->getConnection()->getDatabasePlatform(); @@ -145,32 +124,32 @@ protected function addAllClassFields($class, $alias, $columnAliasMap = []) if (isset($this->fieldMappings[$columnAlias])) { throw new InvalidArgumentException(sprintf( "The column '%s' conflicts with another column in the mapper.", - $columnName + $columnName, )); } $this->addFieldResult($alias, $columnAlias, $propertyName); - $enumType = $classMetadata->getFieldMapping($propertyName)['enumType'] ?? null; + $enumType = $classMetadata->getFieldMapping($propertyName)->enumType ?? null; if (! empty($enumType)) { $this->addEnumResult($columnAlias, $enumType); } } foreach ($classMetadata->associationMappings as $associationMapping) { - if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadata::TO_ONE) { - $targetClass = $this->em->getClassMetadata($associationMapping['targetEntity']); - $isIdentifier = isset($associationMapping['id']) && $associationMapping['id'] === true; + if ($associationMapping->isToOneOwningSide()) { + $targetClass = $this->em->getClassMetadata($associationMapping->targetEntity); + $isIdentifier = isset($associationMapping->id) && $associationMapping->id === true; - foreach ($associationMapping['joinColumns'] as $joinColumn) { - $columnName = $joinColumn['name']; + foreach ($associationMapping->joinColumns as $joinColumn) { + $columnName = $joinColumn->name; $columnAlias = $this->getSQLResultCasing($platform, $columnAliasMap[$columnName]); - $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $columnType = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); if (isset($this->metaMappings[$columnAlias])) { throw new InvalidArgumentException(sprintf( "The column '%s' conflicts with another column in the mapper.", - $columnAlias + $columnAlias, )); } @@ -201,22 +180,12 @@ private function isInheritanceSupported(ClassMetadata $classMetadata): bool */ private function getColumnAlias(string $columnName, int $mode, array $customRenameColumns): string { - switch ($mode) { - case self::COLUMN_RENAMING_INCREMENT: - return $columnName . $this->sqlCounter++; - - case self::COLUMN_RENAMING_CUSTOM: - return $customRenameColumns[$columnName] ?? $columnName; - - case self::COLUMN_RENAMING_NONE: - return $columnName; - - default: - throw new InvalidArgumentException(sprintf( - '%d is not a valid value for $mode', - $mode - )); - } + return match ($mode) { + self::COLUMN_RENAMING_INCREMENT => $columnName . $this->sqlCounter++, + self::COLUMN_RENAMING_CUSTOM => $customRenameColumns[$columnName] ?? $columnName, + self::COLUMN_RENAMING_NONE => $columnName, + default => throw new InvalidArgumentException(sprintf('%d is not a valid value for $mode', $mode)), + }; } /** @@ -233,7 +202,7 @@ private function getColumnAlias(string $columnName, int $mode, array $customRena private function getColumnAliasMap( string $className, int $mode, - array $customRenameColumns + array $customRenameColumns, ): array { if ($customRenameColumns) { // for BC with 2.2-2.3 API $mode = self::COLUMN_RENAMING_CUSTOM; @@ -247,9 +216,9 @@ private function getColumnAliasMap( } foreach ($class->associationMappings as $associationMapping) { - if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadata::TO_ONE) { - foreach ($associationMapping['joinColumns'] as $joinColumn) { - $columnName = $joinColumn['name']; + if ($associationMapping->isToOneOwningSide()) { + foreach ($associationMapping->joinColumns as $joinColumn) { + $columnName = $joinColumn->name; $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); } } @@ -258,189 +227,6 @@ private function getColumnAliasMap( return $columnAlias; } - /** - * Adds the mappings of the results of native SQL queries to the result set. - * - * @deprecated This method is deprecated and will be removed in Doctrine ORM 3.0. - * - * @param mixed[] $queryMapping - * - * @return ResultSetMappingBuilder - */ - public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping) - { - if (isset($queryMapping['resultClass'])) { - return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']); - } - - return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']); - } - - /** - * Adds the class mapping of the results of native SQL queries to the result set. - * - * @deprecated This method is deprecated and will be removed in Doctrine ORM 3.0. - * - * @param string $resultClassName - * - * @return $this - */ - public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName) - { - $classMetadata = $this->em->getClassMetadata($resultClassName); - assert($classMetadata->reflClass !== null); - - $shortName = $classMetadata->reflClass->getShortName(); - $alias = strtolower($shortName[0]) . '0'; - - $this->addEntityResult($class->name, $alias); - - if ($classMetadata->discriminatorColumn) { - $discrColumn = $classMetadata->discriminatorColumn; - - $this->setDiscriminatorColumn($alias, $discrColumn['name']); - $this->addMetaResult($alias, $discrColumn['name'], $discrColumn['fieldName'], false, $discrColumn['type']); - } - - foreach ($classMetadata->getColumnNames() as $key => $columnName) { - $propertyName = $classMetadata->getFieldName($columnName); - - $this->addFieldResult($alias, $columnName, $propertyName); - } - - foreach ($classMetadata->associationMappings as $associationMapping) { - if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadata::TO_ONE) { - $targetClass = $this->em->getClassMetadata($associationMapping['targetEntity']); - - foreach ($associationMapping['joinColumns'] as $joinColumn) { - $columnName = $joinColumn['name']; - $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); - - $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName), $columnType); - } - } - } - - return $this; - } - - /** - * Adds the result set mapping of the results of native SQL queries to the result set. - * - * @deprecated This method is deprecated and will be removed in Doctrine ORM 3.0. - * - * @param string $resultSetMappingName - * - * @return $this - */ - public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) - { - if ($class->reflClass === null) { - throw new LogicException('Given class metadata has now class reflector.'); - } - - $counter = 0; - $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); - $rootShortName = $class->reflClass->getShortName(); - $rootAlias = strtolower($rootShortName[0]) . $counter; - - if (isset($resultMapping['entities'])) { - foreach ($resultMapping['entities'] as $entityMapping) { - $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); - assert($classMetadata->reflClass !== null); - - if ($class->reflClass->name === $classMetadata->reflClass->name) { - $this->addEntityResult($classMetadata->name, $rootAlias); - $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); - } else { - $shortName = $classMetadata->reflClass->getShortName(); - $joinAlias = strtolower($shortName[0]) . ++$counter; - $associations = $class->getAssociationsByTargetClass($classMetadata->name); - - $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); - - foreach ($associations as $relation => $mapping) { - $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); - } - } - } - } - - if (isset($resultMapping['columns'])) { - foreach ($resultMapping['columns'] as $entityMapping) { - $type = isset($class->fieldNames[$entityMapping['name']]) - ? PersisterHelper::getTypeOfColumn($entityMapping['name'], $class, $this->em) - : 'string'; - - $this->addScalarResult($entityMapping['name'], $entityMapping['name'], $type); - } - } - - return $this; - } - - /** - * Adds the entity result mapping of the results of native SQL queries to the result set. - * - * @deprecated This method is deprecated and will be removed in Doctrine ORM 3.0. - * - * @param mixed[] $entityMapping - * @param string $alias - * - * @return $this - * - * @throws MappingException - * @throws InvalidArgumentException - */ - public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias) - { - if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) { - $discriminatorColumn = $entityMapping['discriminatorColumn']; - $discriminatorType = $classMetadata->getDiscriminatorColumn()['type']; - - $this->setDiscriminatorColumn($alias, $discriminatorColumn); - $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn, false, $discriminatorType); - } - - if (isset($entityMapping['fields']) && ! empty($entityMapping['fields'])) { - foreach ($entityMapping['fields'] as $field) { - $fieldName = $field['name']; - $relation = null; - - if (str_contains($fieldName, '.')) { - [$relation, $fieldName] = explode('.', $fieldName); - } - - if (isset($classMetadata->associationMappings[$relation])) { - if ($relation) { - $associationMapping = $classMetadata->associationMappings[$relation]; - $joinAlias = $alias . $relation; - $parentAlias = $alias; - - $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation); - $this->addFieldResult($joinAlias, $field['column'], $fieldName); - } else { - $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); - } - } else { - if (! isset($classMetadata->fieldMappings[$fieldName])) { - throw new InvalidArgumentException("Entity '" . $classMetadata->name . "' has no field '" . $fieldName . "'. "); - } - - $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); - } - } - } else { - foreach ($classMetadata->getColumnNames() as $columnName) { - $propertyName = $classMetadata->getFieldName($columnName); - - $this->addFieldResult($alias, $columnName, $propertyName); - } - } - - return $this; - } - /** * Generates the Select clause from this ResultSetMappingBuilder. * @@ -449,10 +235,8 @@ public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classM * * @param string[] $tableAliases * @phpstan-param array $tableAliases - * - * @return string */ - public function generateSelectClause($tableAliases = []) + public function generateSelectClause(array $tableAliases = []): string { $sql = ''; @@ -467,12 +251,10 @@ public function generateSelectClause($tableAliases = []) $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); $fieldName = $this->fieldMappings[$columnName]; $classFieldMapping = $class->fieldMappings[$fieldName]; - $columnSql = $tableAlias . '.' . $classFieldMapping['columnName']; + $columnSql = $tableAlias . '.' . $classFieldMapping->columnName; - if (isset($classFieldMapping['requireSQLConversion']) && $classFieldMapping['requireSQLConversion'] === true) { - $type = Type::getType($classFieldMapping['type']); - $columnSql = $type->convertToPHPValueSQL($columnSql, $this->em->getConnection()->getDatabasePlatform()); - } + $type = Type::getType($classFieldMapping->type); + $columnSql = $type->convertToPHPValueSQL($columnSql, $this->em->getConnection()->getDatabasePlatform()); $sql .= $columnSql; } elseif (isset($this->metaMappings[$columnName])) { @@ -487,8 +269,7 @@ public function generateSelectClause($tableAliases = []) return $sql; } - /** @return string */ - public function __toString() + public function __toString(): string { return $this->generateSelectClause([]); } diff --git a/src/Query/SqlOutputWalker.php b/src/Query/SqlOutputWalker.php index e737e1c9c53..96cf347fc6a 100644 --- a/src/Query/SqlOutputWalker.php +++ b/src/Query/SqlOutputWalker.php @@ -11,7 +11,7 @@ class SqlOutputWalker extends SqlWalker implements OutputWalker { - public function getFinalizer($AST): SqlFinalizer + public function getFinalizer(AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST): SqlFinalizer { switch (true) { case $AST instanceof AST\SelectStatement: diff --git a/src/Query/SqlWalker.php b/src/Query/SqlWalker.php index 1d9d38a7228..9089995e432 100644 --- a/src/Query/SqlWalker.php +++ b/src/Query/SqlWalker.php @@ -9,7 +9,6 @@ use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\QuoteStrategy; @@ -25,12 +24,15 @@ use function array_keys; use function array_map; use function array_merge; +use function array_pop; use function assert; use function count; +use function end; use function implode; use function in_array; use function is_array; use function is_float; +use function is_int; use function is_numeric; use function is_string; use function preg_match; @@ -46,7 +48,7 @@ * @phpstan-import-type QueryComponent from Parser * @phpstan-consistent-constructor */ -class SqlWalker implements TreeWalker +class SqlWalker { public const HINT_DISTINCT = 'doctrine.distinct'; @@ -55,166 +57,130 @@ class SqlWalker implements TreeWalker */ public const HINT_PARTIAL = 'doctrine.partial'; - /** @var ResultSetMapping */ - private $rsm; + private readonly ResultSetMapping $rsm; /** * Counter for generating unique column aliases. - * - * @var int */ - private $aliasCounter = 0; + private int $aliasCounter = 0; /** * Counter for generating unique table aliases. - * - * @var int */ - private $tableAliasCounter = 0; + private int $tableAliasCounter = 0; /** * Counter for generating unique scalar result. - * - * @var int */ - private $scalarResultCounter = 1; + private int $scalarResultCounter = 1; /** * Counter for generating unique parameter indexes. - * - * @var int */ - private $sqlParamIndex = 0; + private int $sqlParamIndex = 0; /** * Counter for generating indexes. - * - * @var int */ - private $newObjectCounter = 0; + private int $newObjectCounter = 0; - /** @var ParserResult */ - private $parserResult; - - /** @var EntityManagerInterface */ - private $em; - - /** @var Connection */ - private $conn; + /** + * Contains nesting levels of new objects arguments + * + * @phpstan-var array + */ + private array $newObjectStack = []; - /** @var Query */ - private $query; + private readonly EntityManagerInterface $em; + private readonly Connection $conn; /** @var mixed[] */ - private $tableAliasMap = []; + private array $tableAliasMap = []; /** * Map from result variable names to their SQL column alias names. * * @phpstan-var array> */ - private $scalarResultAliasMap = []; + private array $scalarResultAliasMap = []; /** * Map from Table-Alias + Column-Name to OrderBy-Direction. * * @var array */ - private $orderedColumnsMap = []; + private array $orderedColumnsMap = []; /** * Map from DQL-Alias + Field-Name to SQL Column Alias. * * @var array> */ - private $scalarFields = []; - - /** - * Map of all components/classes that appear in the DQL query. - * - * @phpstan-var array - */ - private $queryComponents; + private array $scalarFields = []; /** * A list of classes that appear in non-scalar SelectExpressions. * * @phpstan-var array */ - private $selectedClasses = []; + private array $selectedClasses = []; /** * The DQL alias of the root class of the currently traversed query. * * @phpstan-var list */ - private $rootAliases = []; + private array $rootAliases = []; /** * Flag that indicates whether to generate SQL table aliases in the SQL. * These should only be generated for SELECT queries, not for UPDATE/DELETE. - * - * @var bool */ - private $useSqlTableAliases = true; + private bool $useSqlTableAliases = true; /** * The database platform abstraction. - * - * @var AbstractPlatform */ - private $platform; + private readonly AbstractPlatform $platform; /** * The quote strategy. - * - * @var QuoteStrategy */ - private $quoteStrategy; + private readonly QuoteStrategy $quoteStrategy; - /** - * @param Query $query The parsed Query. - * @param ParserResult $parserResult The result of the parsing process. - * @phpstan-param array $queryComponents The query components (symbol table). - */ - public function __construct($query, $parserResult, array $queryComponents) - { - $this->query = $query; - $this->parserResult = $parserResult; - $this->queryComponents = $queryComponents; - $this->rsm = $parserResult->getResultSetMapping(); - $this->em = $query->getEntityManager(); - $this->conn = $this->em->getConnection(); - $this->platform = $this->conn->getDatabasePlatform(); - $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + /** @phpstan-param array $queryComponents The query components (symbol table). */ + public function __construct( + private readonly Query $query, + private readonly ParserResult $parserResult, + private array $queryComponents, + ) { + $this->rsm = $parserResult->getResultSetMapping(); + $this->em = $query->getEntityManager(); + $this->conn = $this->em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); } /** * Gets the Query instance used by the walker. - * - * @return Query */ - public function getQuery() + public function getQuery(): Query { return $this->query; } /** * Gets the Connection used by the walker. - * - * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return $this->conn; } /** * Gets the EntityManager used by the walker. - * - * @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { return $this->em; } @@ -227,18 +193,15 @@ public function getEntityManager() * @return mixed[] * @phpstan-return QueryComponent */ - public function getQueryComponent($dqlAlias) + public function getQueryComponent(string $dqlAlias): array { return $this->queryComponents[$dqlAlias]; } public function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata { - if (! isset($this->queryComponents[$dqlAlias]['metadata'])) { - throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias)); - } - - return $this->queryComponents[$dqlAlias]['metadata']; + return $this->queryComponents[$dqlAlias]['metadata'] + ?? throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias)); } /** @@ -246,7 +209,7 @@ public function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata * * @return array */ - public function getQueryComponents() + public function getQueryComponents(): array { return $this->queryComponents; } @@ -254,14 +217,9 @@ public function getQueryComponents() /** * Sets or overrides a query component for a given dql alias. * - * @param string $dqlAlias The DQL alias. * @phpstan-param QueryComponent $queryComponent - * - * @return void - * - * @not-deprecated */ - public function setQueryComponent($dqlAlias, array $queryComponent) + public function setQueryComponent(string $dqlAlias, array $queryComponent): void { $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token']; @@ -278,23 +236,14 @@ public function setQueryComponent($dqlAlias, array $queryComponent) * @deprecated Output walkers should no longer create the executor directly, but instead provide * a SqlFinalizer by implementing the `OutputWalker` interface. Thus, this method is * no longer needed and will be removed in 4.0. - * - * @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST - * - * @return Exec\AbstractSqlExecutor */ - public function getExecutor($AST) + public function getExecutor(AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement $statement): Exec\AbstractSqlExecutor { - switch (true) { - case $AST instanceof AST\DeleteStatement: - return $this->createDeleteStatementExecutor($AST); - - case $AST instanceof AST\UpdateStatement: - return $this->createUpdateStatementExecutor($AST); - - default: - return new Exec\SingleSelectExecutor($AST, $this); - } + return match (true) { + $statement instanceof AST\UpdateStatement => $this->createUpdateStatementExecutor($statement), + $statement instanceof AST\DeleteStatement => $this->createDeleteStatementExecutor($statement), + default => new Exec\SingleSelectExecutor($statement, $this), + }; } protected function createUpdateStatementExecutor(AST\UpdateStatement $AST): Exec\AbstractSqlExecutor @@ -317,13 +266,8 @@ protected function createDeleteStatementExecutor(AST\DeleteStatement $AST): Exec /** * Generates a unique, short SQL table alias. - * - * @param string $tableName Table name - * @param string $dqlAlias The DQL alias. - * - * @return string Generated table alias. */ - public function getSQLTableAlias($tableName, $dqlAlias = '') + public function getSQLTableAlias(string $tableName, string $dqlAlias = ''): string { $tableName .= $dqlAlias ? '@[' . $dqlAlias . ']' : ''; @@ -338,14 +282,8 @@ public function getSQLTableAlias($tableName, $dqlAlias = '') /** * Forces the SqlWalker to use a specific alias for a table name, rather than * generating an alias on its own. - * - * @param string $tableName - * @param string $alias - * @param string $dqlAlias - * - * @return string */ - public function setSQLTableAlias($tableName, $alias, $dqlAlias = '') + public function setSQLTableAlias(string $tableName, string $alias, string $dqlAlias = ''): string { $tableName .= $dqlAlias ? '@[' . $dqlAlias . ']' : ''; @@ -356,12 +294,8 @@ public function setSQLTableAlias($tableName, $alias, $dqlAlias = '') /** * Gets an SQL column alias for a column name. - * - * @param string $columnName - * - * @return string */ - public function getSQLColumnAlias($columnName) + public function getSQLColumnAlias(string $columnName): string { return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform); } @@ -369,15 +303,10 @@ public function getSQLColumnAlias($columnName) /** * Generates the SQL JOINs that are necessary for Class Table Inheritance * for the given class. - * - * @param ClassMetadata $class The class for which to generate the joins. - * @param string $dqlAlias The DQL alias of the class. - * - * @return string The SQL. */ private function generateClassTableInheritanceJoins( ClassMetadata $class, - string $dqlAlias + string $dqlAlias, ): string { $sql = ''; @@ -436,14 +365,14 @@ private function generateOrderedCollectionOrderByItems(): string $dqlAlias = $selectedClass['dqlAlias']; $qComp = $this->queryComponents[$dqlAlias]; - if (! isset($qComp['relation']['orderBy'])) { + if (! isset($qComp['relation']->orderBy)) { continue; } assert(isset($qComp['metadata'])); $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name); - foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { + foreach ($qComp['relation']->orderBy as $fieldName => $orientation) { $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); $tableName = $qComp['metadata']->isInheritanceTypeJoined() ? $persister->getOwningTable($fieldName) @@ -488,7 +417,9 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str $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) { @@ -500,11 +431,13 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str continue; } - $values[] = $conn->quote($subclassMetadata->discriminatorValue); + $values[] = $subclassMetadata->getDiscriminatorColumn()->type === 'integer' && is_int($subclassMetadata->discriminatorValue) + ? $subclassMetadata->discriminatorValue + : $conn->quote((string) $subclassMetadata->discriminatorValue); } if ($values !== []) { - $sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')'; + $sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()->name . ' IN (' . implode(', ', $values) . ')'; } else { $sqlParts[] = '1=0'; // impossible condition } @@ -517,15 +450,10 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str /** * Generates the filter SQL for a given entity and table alias. - * - * @param ClassMetadata $targetEntity Metadata of the target entity. - * @param string $targetTableAlias The table alias of the joined/selected table. - * - * @return string The SQL query part to add to a query. */ private function generateFilterConditionSQL( ClassMetadata $targetEntity, - string $targetTableAlias + string $targetTableAlias, ): string { if (! $this->em->hasFilters()) { return ''; @@ -565,37 +493,35 @@ private function generateFilterConditionSQL( /** * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. - * - * @return string */ - public function walkSelectStatement(AST\SelectStatement $AST) + public function walkSelectStatement(AST\SelectStatement $selectStatement): string { - $sql = $this->createSqlForFinalizer($AST); + $sql = $this->createSqlForFinalizer($selectStatement); $finalizer = new Exec\SingleSelectSqlFinalizer($sql); return $finalizer->finalizeSql($this->query); } - protected function createSqlForFinalizer(AST\SelectStatement $AST): string + protected function createSqlForFinalizer(AST\SelectStatement $selectStatement): string { - $sql = $this->walkSelectClause($AST->selectClause) - . $this->walkFromClause($AST->fromClause) - . $this->walkWhereClause($AST->whereClause); + $sql = $this->walkSelectClause($selectStatement->selectClause) + . $this->walkFromClause($selectStatement->fromClause) + . $this->walkWhereClause($selectStatement->whereClause); - if ($AST->groupByClause) { - $sql .= $this->walkGroupByClause($AST->groupByClause); + if ($selectStatement->groupByClause) { + $sql .= $this->walkGroupByClause($selectStatement->groupByClause); } - if ($AST->havingClause) { - $sql .= $this->walkHavingClause($AST->havingClause); + if ($selectStatement->havingClause) { + $sql .= $this->walkHavingClause($selectStatement->havingClause); } - if ($AST->orderByClause) { - $sql .= $this->walkOrderByClause($AST->orderByClause); + if ($selectStatement->orderByClause) { + $sql .= $this->walkOrderByClause($selectStatement->orderByClause); } $orderBySql = $this->generateOrderedCollectionOrderByItems(); - if (! $AST->orderByClause && $orderBySql) { + if (! $selectStatement->orderByClause && $orderBySql) { $sql .= ' ORDER BY ' . $orderBySql; } @@ -618,44 +544,34 @@ private function assertOptimisticLockingHasAllClassesVersioned(): void } /** - * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. - * - * @return string + * Walks down a UpdateStatement AST node, thereby generating the appropriate SQL. */ - public function walkUpdateStatement(AST\UpdateStatement $AST) + public function walkUpdateStatement(AST\UpdateStatement $updateStatement): string { $this->useSqlTableAliases = false; $this->rsm->isSelect = false; - return $this->walkUpdateClause($AST->updateClause) - . $this->walkWhereClause($AST->whereClause); + return $this->walkUpdateClause($updateStatement->updateClause) + . $this->walkWhereClause($updateStatement->whereClause); } /** * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. - * - * @return string */ - public function walkDeleteStatement(AST\DeleteStatement $AST) + public function walkDeleteStatement(AST\DeleteStatement $deleteStatement): string { $this->useSqlTableAliases = false; $this->rsm->isSelect = false; - return $this->walkDeleteClause($AST->deleteClause) - . $this->walkWhereClause($AST->whereClause); + return $this->walkDeleteClause($deleteStatement->deleteClause) + . $this->walkWhereClause($deleteStatement->whereClause); } /** * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL. * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers. - * - * @param string $identVariable - * - * @return string - * - * @not-deprecated */ - public function walkEntityIdentificationVariable($identVariable) + public function walkEntityIdentificationVariable(string $identVariable): string { $class = $this->getMetadataForDqlAlias($identVariable); $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable); @@ -670,23 +586,16 @@ public function walkEntityIdentificationVariable($identVariable) /** * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL. - * - * @param string $identificationVariable - * @param string $fieldName - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkIdentificationVariable($identificationVariable, $fieldName = null) + public function walkIdentificationVariable(string $identificationVariable, string|null $fieldName = null): string { $class = $this->getMetadataForDqlAlias($identificationVariable); if ( $fieldName !== null && $class->isInheritanceTypeJoined() && - isset($class->fieldMappings[$fieldName]['inherited']) + isset($class->fieldMappings[$fieldName]->inherited) ) { - $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); + $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]->inherited); } return $this->getSQLTableAlias($class->getTableName(), $identificationVariable); @@ -694,14 +603,8 @@ public function walkIdentificationVariable($identificationVariable, $fieldName = /** * Walks down a PathExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\PathExpression $pathExpr - * - * @return string - * - * @not-deprecated */ - public function walkPathExpression($pathExpr) + public function walkPathExpression(AST\PathExpression $pathExpr): string { $sql = ''; assert($pathExpr->field !== null); @@ -726,18 +629,20 @@ public function walkPathExpression($pathExpr) $dqlAlias = $pathExpr->identificationVariable; $class = $this->getMetadataForDqlAlias($dqlAlias); - if (isset($class->associationMappings[$fieldName]['inherited'])) { - $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); + if (isset($class->associationMappings[$fieldName]->inherited)) { + $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]->inherited); } $assoc = $class->associationMappings[$fieldName]; - if (! $assoc['isOwningSide']) { + if (! $assoc->isOwningSide()) { throw QueryException::associationPathInverseSideNotSupported($pathExpr); } + assert($assoc->isToOneOwningSide()); + // COMPOSITE KEYS NOT (YET?) SUPPORTED - if (count($assoc['sourceToTargetKeyColumns']) > 1) { + if (count($assoc->sourceToTargetKeyColumns) > 1) { throw QueryException::associationPathCompositeKeyNotSupported(); } @@ -745,7 +650,7 @@ public function walkPathExpression($pathExpr) $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'; } - $sql .= reset($assoc['targetToSourceKeyColumns']); + $sql .= reset($assoc->targetToSourceKeyColumns); break; default: @@ -757,17 +662,11 @@ public function walkPathExpression($pathExpr) /** * Walks down a SelectClause AST node, thereby generating the appropriate SQL. - * - * @param AST\SelectClause $selectClause - * - * @return string - * - * @not-deprecated */ - public function walkSelectClause($selectClause) + public function walkSelectClause(AST\SelectClause $selectClause): string { $sql = 'SELECT ' . ($selectClause->isDistinct ? 'DISTINCT ' : ''); - $sqlSelectExpressions = array_filter(array_map([$this, 'walkSelectExpression'], $selectClause->selectExpressions)); + $sqlSelectExpressions = array_filter(array_map($this->walkSelectExpression(...), $selectClause->selectExpressions)); if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) === true && $selectClause->isDistinct) { $this->query->setHint(self::HINT_DISTINCT, true); @@ -792,7 +691,7 @@ public function walkSelectClause($selectClause) $class->name, $dqlAlias, $this->queryComponents[$dqlAlias]['parent'], - $this->queryComponents[$dqlAlias]['relation']['fieldName'] + $this->queryComponents[$dqlAlias]['relation']->fieldName, ); } @@ -801,14 +700,14 @@ public function walkSelectClause($selectClause) $rootClass = $this->em->getClassMetadata($class->rootEntityName); $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); $discrColumn = $rootClass->getDiscriminatorColumn(); - $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); + $columnAlias = $this->getSQLColumnAlias($discrColumn->name); - $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; + $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn->name . ' AS ' . $columnAlias; $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); - $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName'], false, $discrColumn['type']); - if (! empty($discrColumn['enumType'])) { - $this->rsm->addEnumResult($columnAlias, $discrColumn['enumType']); + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn->fieldName, false, $discrColumn->type); + if (! empty($discrColumn->enumType)) { + $this->rsm->addEnumResult($columnAlias, $discrColumn->enumType); } } @@ -820,21 +719,21 @@ public function walkSelectClause($selectClause) // Add foreign key columns of class and also parent classes foreach ($class->associationMappings as $assoc) { if ( - ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) - || ( ! $addMetaColumns && ! isset($assoc['id'])) + ! $assoc->isToOneOwningSide() + || ( ! $addMetaColumns && ! isset($assoc->id)) ) { continue; } - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); - $isIdentifier = (isset($assoc['id']) && $assoc['id'] === true); - $owningClass = isset($assoc['inherited']) ? $this->em->getClassMetadata($assoc['inherited']) : $class; + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); + $isIdentifier = (isset($assoc->id) && $assoc->id === true); + $owningClass = isset($assoc->inherited) ? $this->em->getClassMetadata($assoc->inherited) : $class; $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); - foreach ($assoc['joinColumns'] as $joinColumn) { - $columnName = $joinColumn['name']; + foreach ($assoc->joinColumns as $joinColumn) { + $columnName = $joinColumn->name; $columnAlias = $this->getSQLColumnAlias($columnName); - $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $columnType = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias; @@ -855,17 +754,17 @@ public function walkSelectClause($selectClause) foreach ($subClass->associationMappings as $assoc) { // Skip if association is inherited - if (isset($assoc['inherited'])) { + if (isset($assoc->inherited)) { continue; } - if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + if ($assoc->isToOneOwningSide()) { + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); - foreach ($assoc['joinColumns'] as $joinColumn) { - $columnName = $joinColumn['name']; + foreach ($assoc->joinColumns as $joinColumn) { + $columnName = $joinColumn->name; $columnAlias = $this->getSQLColumnAlias($columnName); - $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + $columnType = PersisterHelper::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $this->em); $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform); $sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias; @@ -882,14 +781,8 @@ public function walkSelectClause($selectClause) /** * Walks down a FromClause AST node, thereby generating the appropriate SQL. - * - * @param AST\FromClause $fromClause - * - * @return string - * - * @not-deprecated */ - public function walkFromClause($fromClause) + public function walkFromClause(AST\FromClause $fromClause): string { $identificationVarDecls = $fromClause->identificationVariableDeclarations; $sqlParts = []; @@ -903,14 +796,8 @@ public function walkFromClause($fromClause) /** * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL. - * - * @param AST\IdentificationVariableDeclaration $identificationVariableDecl - * - * @return string - * - * @not-deprecated */ - public function walkIdentificationVariableDeclaration($identificationVariableDecl) + public function walkIdentificationVariableDeclaration(AST\IdentificationVariableDeclaration $identificationVariableDecl): string { $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration); @@ -927,14 +814,8 @@ public function walkIdentificationVariableDeclaration($identificationVariableDec /** * Walks down a IndexBy AST node. - * - * @param AST\IndexBy $indexBy - * - * @return void - * - * @not-deprecated */ - public function walkIndexBy($indexBy) + public function walkIndexBy(AST\IndexBy $indexBy): void { $pathExpression = $indexBy->singleValuedPathExpression; $alias = $pathExpression->identificationVariable; @@ -950,21 +831,23 @@ public function walkIndexBy($indexBy) $fieldName = $pathExpression->field; $class = $this->getMetadataForDqlAlias($alias); - if (isset($class->associationMappings[$fieldName]['inherited'])) { - $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); + if (isset($class->associationMappings[$fieldName]->inherited)) { + $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]->inherited); } $association = $class->associationMappings[$fieldName]; - if (! $association['isOwningSide']) { + if (! $association->isOwningSide()) { throw QueryException::associationPathInverseSideNotSupported($pathExpression); } - if (count($association['sourceToTargetKeyColumns']) > 1) { + assert($association->isToOneOwningSide()); + + if (count($association->sourceToTargetKeyColumns) > 1) { throw QueryException::associationPathCompositeKeyNotSupported(); } - $field = reset($association['targetToSourceKeyColumns']); + $field = reset($association->targetToSourceKeyColumns); break; default: @@ -982,14 +865,8 @@ public function walkIndexBy($indexBy) /** * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL. - * - * @param AST\RangeVariableDeclaration $rangeVariableDeclaration - * - * @return string - * - * @not-deprecated */ - public function walkRangeVariableDeclaration($rangeVariableDeclaration) + public function walkRangeVariableDeclaration(AST\RangeVariableDeclaration $rangeVariableDeclaration): string { return $this->generateRangeVariableDeclarationSQL($rangeVariableDeclaration, false); } @@ -999,7 +876,7 @@ public function walkRangeVariableDeclaration($rangeVariableDeclaration) */ private function generateRangeVariableDeclarationSQL( AST\RangeVariableDeclaration $rangeVariableDeclaration, - bool $buildNestedJoins + bool $buildNestedJoins, ): string { $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; @@ -1011,7 +888,7 @@ private function generateRangeVariableDeclarationSQL( $sql = $this->platform->appendLockHint( $this->quoteStrategy->getTableName($class, $this->platform) . ' ' . $this->getSQLTableAlias($class->getTableName(), $dqlAlias), - $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE + $this->query->getHint(Query::HINT_LOCK_MODE) ?: LockMode::NONE, ); if (! $class->isInheritanceTypeJoined()) { @@ -1030,19 +907,15 @@ private function generateRangeVariableDeclarationSQL( /** * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. * - * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration - * @param int $joinType - * @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr * @phpstan-param AST\Join::JOIN_TYPE_* $joinType * - * @return string - * * @throws QueryException - * - * @not-deprecated */ - public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null) - { + public function walkJoinAssociationDeclaration( + AST\JoinAssociationDeclaration $joinAssociationDeclaration, + int $joinType = AST\Join::JOIN_TYPE_INNER, + AST\ConditionalExpression|AST\Phase2OptimizableConditional|null $condExpr = null, + ): string { $sql = ''; $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression; @@ -1051,40 +924,41 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi $relation = $this->queryComponents[$joinedDqlAlias]['relation'] ?? null; assert($relation !== null); - $targetClass = $this->em->getClassMetadata($relation['targetEntity']); - $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']); + $targetClass = $this->em->getClassMetadata($relation->targetEntity); + $sourceClass = $this->em->getClassMetadata($relation->sourceEntity); $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable); // Ensure we got the owning side, since it has all mapping info - $assoc = ! $relation['isOwningSide'] ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; + $assoc = $this->em->getMetadataFactory()->getOwningSide($relation); if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) === true && (! $this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) { - if ($relation['type'] === ClassMetadata::ONE_TO_MANY || $relation['type'] === ClassMetadata::MANY_TO_MANY) { + if ($relation->isToMany()) { throw QueryException::iterateWithFetchJoinNotAllowed($assoc); } } - $fetchMode = $this->query->getHint('fetchMode')[$assoc['sourceEntity']][$assoc['fieldName']] ?? $relation['fetch']; + $fetchMode = $this->query->getHint('fetchMode')[$assoc->sourceEntity][$assoc->fieldName] ?? $relation->fetch; if ($fetchMode === ClassMetadata::FETCH_EAGER && $condExpr !== null) { - throw QueryException::eagerFetchJoinWithNotAllowed($assoc['sourceEntity'], $assoc['fieldName']); + throw QueryException::eagerFetchJoinWithNotAllowed($assoc->sourceEntity, $assoc->fieldName); } // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot // be the owning side and previously we ensured that $assoc is always the owning side of the associations. // The owning side is necessary at this point because only it contains the JoinColumn information. switch (true) { - case $assoc['type'] & ClassMetadata::TO_ONE: + case $assoc->isToOne(): + assert($assoc->isToOneOwningSide()); $conditions = []; - foreach ($assoc['joinColumns'] as $joinColumn) { + foreach ($assoc->joinColumns as $joinColumn) { $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); - if ($relation['isOwningSide']) { + if ($relation->isOwningSide()) { $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; continue; @@ -1113,16 +987,16 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi ]; break; - case $assoc['type'] === ClassMetadata::MANY_TO_MANY: + case $assoc->isManyToMany(): // Join relation table - $joinTable = $assoc['joinTable']; - $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $joinTable = $assoc->joinTable; + $joinTableAlias = $this->getSQLTableAlias($joinTable->name, $joinedDqlAlias); $joinTableName = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform); $conditions = []; - $relationColumns = $relation['isOwningSide'] - ? $assoc['joinTable']['joinColumns'] - : $assoc['joinTable']['inverseJoinColumns']; + $relationColumns = $relation->isOwningSide() + ? $assoc->joinTable->joinColumns + : $assoc->joinTable->inverseJoinColumns; foreach ($relationColumns as $joinColumn) { $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); @@ -1137,9 +1011,9 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi $sql .= $joinType === AST\Join::JOIN_TYPE_LEFT || $joinType === AST\Join::JOIN_TYPE_LEFTOUTER ? ' LEFT JOIN ' : ' INNER JOIN '; $conditions = []; - $relationColumns = $relation['isOwningSide'] - ? $assoc['joinTable']['inverseJoinColumns'] - : $assoc['joinTable']['joinColumns']; + $relationColumns = $relation->isOwningSide() + ? $assoc->joinTable->inverseJoinColumns + : $assoc->joinTable->joinColumns; foreach ($relationColumns as $joinColumn) { $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); @@ -1195,8 +1069,8 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi if ($indexBy) { // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. $this->walkIndexBy($indexBy); - } elseif (isset($relation['indexBy'])) { - $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } elseif ($relation->isIndexed()) { + $this->rsm->addIndexBy($joinedDqlAlias, $relation->indexBy()); } return $sql; @@ -1204,30 +1078,18 @@ public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joi /** * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. - * - * @param AST\Functions\FunctionNode $function - * - * @return string - * - * @not-deprecated */ - public function walkFunction($function) + public function walkFunction(AST\Functions\FunctionNode $function): string { return $function->getSql($this); } /** * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. - * - * @param AST\OrderByClause $orderByClause - * - * @return string - * - * @not-deprecated */ - public function walkOrderByClause($orderByClause) + public function walkOrderByClause(AST\OrderByClause $orderByClause): string { - $orderByItems = array_map([$this, 'walkOrderByItem'], $orderByClause->orderByItems); + $orderByItems = array_map($this->walkOrderByItem(...), $orderByClause->orderByItems); $collectionOrderByItems = $this->generateOrderedCollectionOrderByItems(); if ($collectionOrderByItems !== '') { @@ -1239,14 +1101,8 @@ public function walkOrderByClause($orderByClause) /** * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. - * - * @param AST\OrderByItem $orderByItem - * - * @return string - * - * @not-deprecated */ - public function walkOrderByItem($orderByItem) + public function walkOrderByItem(AST\OrderByItem $orderByItem): string { $type = strtoupper($orderByItem->type); $expr = $orderByItem->expression; @@ -1265,28 +1121,16 @@ public function walkOrderByItem($orderByItem) /** * Walks down a HavingClause AST node, thereby generating the appropriate SQL. - * - * @param AST\HavingClause $havingClause - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkHavingClause($havingClause) + public function walkHavingClause(AST\HavingClause $havingClause): string { return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); } /** * Walks down a Join AST node and creates the corresponding SQL. - * - * @param AST\Join $join - * - * @return string - * - * @not-deprecated */ - public function walkJoin($join) + public function walkJoin(AST\Join $join): string { $joinType = $join->joinType; $joinDeclaration = $join->joinAssociationDeclaration; @@ -1343,14 +1187,8 @@ public function walkJoin($join) /** * Walks down a CoalesceExpression AST node and generates the corresponding SQL. - * - * @param AST\CoalesceExpression $coalesceExpression - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkCoalesceExpression($coalesceExpression) + public function walkCoalesceExpression(AST\CoalesceExpression $coalesceExpression): string { $sql = 'COALESCE('; @@ -1365,14 +1203,8 @@ public function walkCoalesceExpression($coalesceExpression) /** * Walks down a NullIfExpression AST node and generates the corresponding SQL. - * - * @param AST\NullIfExpression $nullIfExpression - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkNullIfExpression($nullIfExpression) + public function walkNullIfExpression(AST\NullIfExpression $nullIfExpression): string { $firstExpression = is_string($nullIfExpression->firstExpression) ? $this->conn->quote($nullIfExpression->firstExpression) @@ -1387,12 +1219,8 @@ public function walkNullIfExpression($nullIfExpression) /** * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL. - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression) + public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression): string { $sql = 'CASE'; @@ -1408,14 +1236,8 @@ public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCase /** * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL. - * - * @param AST\SimpleCaseExpression $simpleCaseExpression - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkSimpleCaseExpression($simpleCaseExpression) + public function walkSimpleCaseExpression(AST\SimpleCaseExpression $simpleCaseExpression): string { $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand); @@ -1431,14 +1253,8 @@ public function walkSimpleCaseExpression($simpleCaseExpression) /** * Walks down a SelectExpression AST node and generates the corresponding SQL. - * - * @param AST\SelectExpression $selectExpression - * - * @return string - * - * @not-deprecated */ - public function walkSelectExpression($selectExpression) + public function walkSelectExpression(AST\SelectExpression $selectExpression): string { $sql = ''; $expr = $selectExpression->expression; @@ -1463,24 +1279,22 @@ public function walkSelectExpression($selectExpression) $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); $fieldMapping = $class->fieldMappings[$fieldName]; $columnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); - $columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']); + $columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName); $col = $sqlTableAlias . '.' . $columnName; - if (isset($fieldMapping['requireSQLConversion'])) { - $type = Type::getType($fieldMapping['type']); - $col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform()); - } + $type = Type::getType($fieldMapping->type); + $col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform()); $sql .= $col . ' AS ' . $columnAlias; $this->scalarResultAliasMap[$resultAlias] = $columnAlias; if (! $hidden) { - $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldMapping['type']); + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldMapping->type); $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias; - if (! empty($fieldMapping['enumType'])) { - $this->rsm->addEnumResult($columnAlias, $fieldMapping['enumType']); + if (! empty($fieldMapping->enumType)) { + $this->rsm->addEnumResult($columnAlias, $fieldMapping->enumType); } } @@ -1570,20 +1384,18 @@ public function walkSelectExpression($selectExpression) continue; } - $tableName = isset($mapping['inherited']) - ? $this->em->getClassMetadata($mapping['inherited'])->getTableName() + $tableName = isset($mapping->inherited) + ? $this->em->getClassMetadata($mapping->inherited)->getTableName() : $class->getTableName(); $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); - $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $columnAlias = $this->getSQLColumnAlias($mapping->columnName); $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); $col = $sqlTableAlias . '.' . $quotedColumnName; - if (isset($mapping['requireSQLConversion'])) { - $type = Type::getType($mapping['type']); - $col = $type->convertToPHPValueSQL($col, $this->platform); - } + $type = Type::getType($mapping->type); + $col = $type->convertToPHPValueSQL($col, $this->platform); $sqlParts[] = $col . ' AS ' . $columnAlias; @@ -1591,8 +1403,8 @@ public function walkSelectExpression($selectExpression) $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); - if (! empty($mapping['enumType'])) { - $this->rsm->addEnumResult($columnAlias, $mapping['enumType']); + if (! empty($mapping->enumType)) { + $this->rsm->addEnumResult($columnAlias, $mapping->enumType); } } @@ -1606,19 +1418,17 @@ public function walkSelectExpression($selectExpression) $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited']) || ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true))) { + if (isset($mapping->inherited) || ($partialFieldSet && ! in_array($fieldName, $partialFieldSet, true))) { continue; } - $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $columnAlias = $this->getSQLColumnAlias($mapping->columnName); $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform); $col = $sqlTableAlias . '.' . $quotedColumnName; - if (isset($mapping['requireSQLConversion'])) { - $type = Type::getType($mapping['type']); - $col = $type->convertToPHPValueSQL($col, $this->platform); - } + $type = Type::getType($mapping->type); + $col = $type->convertToPHPValueSQL($col, $this->platform); $sqlParts[] = $col . ' AS ' . $columnAlias; @@ -1635,30 +1445,15 @@ public function walkSelectExpression($selectExpression) return $sql; } - /** - * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\QuantifiedExpression $qExpr - * - * @return string - * - * @not-deprecated - */ - public function walkQuantifiedExpression($qExpr) + public function walkQuantifiedExpression(AST\QuantifiedExpression $qExpr): string { return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; } /** * Walks down a Subselect AST node, thereby generating the appropriate SQL. - * - * @param AST\Subselect $subselect - * - * @return string - * - * @not-deprecated */ - public function walkSubselect($subselect) + public function walkSubselect(AST\Subselect $subselect): string { $useAliasesBefore = $this->useSqlTableAliases; $rootAliasesBefore = $this->rootAliases; @@ -1682,14 +1477,8 @@ public function walkSubselect($subselect) /** * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. - * - * @param AST\SubselectFromClause $subselectFromClause - * - * @return string - * - * @not-deprecated */ - public function walkSubselectFromClause($subselectFromClause) + public function walkSubselectFromClause(AST\SubselectFromClause $subselectFromClause): string { $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; $sqlParts = []; @@ -1703,35 +1492,29 @@ public function walkSubselectFromClause($subselectFromClause) /** * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. - * - * @param AST\SimpleSelectClause $simpleSelectClause - * - * @return string - * - * @not-deprecated */ - public function walkSimpleSelectClause($simpleSelectClause) + public function walkSimpleSelectClause(AST\SimpleSelectClause $simpleSelectClause): string { return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); } - /** @return string */ - public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression) + public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression): string { return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this)); } - /** - * @param AST\NewObjectExpression $newObjectExpression - * @param string|null $newObjectResultAlias - * - * @return string The SQL. - */ - public function walkNewObject($newObjectExpression, $newObjectResultAlias = null) + public function walkNewObject(AST\NewObjectExpression $newObjectExpression, string|null $newObjectResultAlias = null): string { $sqlSelectExpressions = []; - $objIndex = $newObjectResultAlias ?: $this->newObjectCounter++; + $objOwner = $objOwnerIdx = null; + + if ($this->newObjectStack !== []) { + [$objOwner, $objOwnerIdx] = end($this->newObjectStack); + $objIndex = $objOwner . ':' . $objOwnerIdx; + } else { + $objIndex = $newObjectResultAlias ?: $this->newObjectCounter++; + } foreach ($newObjectExpression->args as $argIndex => $e) { $resultAlias = $this->scalarResultCounter++; @@ -1740,7 +1523,10 @@ public function walkNewObject($newObjectExpression, $newObjectResultAlias = null switch (true) { case $e instanceof AST\NewObjectExpression: + $this->newObjectStack[] = [$objIndex, $argIndex]; $sqlSelectExpressions[] = $e->dispatch($this); + array_pop($this->newObjectStack); + $this->rsm->nestedNewObjectArguments[$columnAlias] = ['ownerIndex' => $objIndex, 'argIndex' => $argIndex]; break; case $e instanceof AST\Subselect: @@ -1753,18 +1539,16 @@ public function walkNewObject($newObjectExpression, $newObjectResultAlias = null $class = $this->getMetadataForDqlAlias($dqlAlias); $fieldName = $e->field; $fieldMapping = $class->fieldMappings[$fieldName]; - $fieldType = $fieldMapping['type']; + $fieldType = $fieldMapping->type; $col = trim($e->dispatch($this)); - if (isset($fieldMapping['requireSQLConversion'])) { - $type = Type::getType($fieldType); - $col = $type->convertToPHPValueSQL($col, $this->platform); - } + $type = Type::getType($fieldType); + $col = $type->convertToPHPValueSQL($col, $this->platform); $sqlSelectExpressions[] = $col . ' AS ' . $columnAlias; - if (! empty($fieldMapping['enumType'])) { - $this->rsm->addEnumResult($columnAlias, $fieldMapping['enumType']); + if (! empty($fieldMapping->enumType)) { + $this->rsm->addEnumResult($columnAlias, $fieldMapping->enumType); } break; @@ -1803,14 +1587,8 @@ public function walkNewObject($newObjectExpression, $newObjectResultAlias = null /** * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\SimpleSelectExpression $simpleSelectExpression - * - * @return string - * - * @not-deprecated */ - public function walkSimpleSelectExpression($simpleSelectExpression) + public function walkSimpleSelectExpression(AST\SimpleSelectExpression $simpleSelectExpression): string { $expr = $simpleSelectExpression->expression; $sql = ' '; @@ -1860,14 +1638,8 @@ public function walkSimpleSelectExpression($simpleSelectExpression) /** * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\AggregateExpression $aggExpression - * - * @return string - * - * @not-deprecated */ - public function walkAggregateExpression($aggExpression) + public function walkAggregateExpression(AST\AggregateExpression $aggExpression): string { return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; @@ -1875,14 +1647,8 @@ public function walkAggregateExpression($aggExpression) /** * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. - * - * @param AST\GroupByClause $groupByClause - * - * @return string - * - * @not-deprecated */ - public function walkGroupByClause($groupByClause) + public function walkGroupByClause(AST\GroupByClause $groupByClause): string { $sqlParts = []; @@ -1895,14 +1661,8 @@ public function walkGroupByClause($groupByClause) /** * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. - * - * @param AST\PathExpression|string $groupByItem - * - * @return string - * - * @not-deprecated */ - public function walkGroupByItem($groupByItem) + public function walkGroupByItem(AST\PathExpression|string $groupByItem): string { // StateFieldPathExpression if (! is_string($groupByItem)) { @@ -1935,8 +1695,8 @@ public function walkGroupByItem($groupByItem) } foreach ($this->getMetadataForDqlAlias($groupByItem)->associationMappings as $mapping) { - if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadata::TO_ONE) { - $item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']); + if ($mapping->isToOneOwningSide()) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping->fieldName); $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; $sqlParts[] = $this->walkPathExpression($item); @@ -1948,12 +1708,8 @@ public function walkGroupByItem($groupByItem) /** * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. - * - * @return string - * - * @not-deprecated */ - public function walkDeleteClause(AST\DeleteClause $deleteClause) + public function walkDeleteClause(AST\DeleteClause $deleteClause): string { $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); $tableName = $class->getTableName(); @@ -1967,14 +1723,8 @@ public function walkDeleteClause(AST\DeleteClause $deleteClause) /** * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. - * - * @param AST\UpdateClause $updateClause - * - * @return string - * - * @not-deprecated */ - public function walkUpdateClause($updateClause) + public function walkUpdateClause(AST\UpdateClause $updateClause): string { $class = $this->em->getClassMetadata($updateClause->abstractSchemaName); $tableName = $class->getTableName(); @@ -1983,19 +1733,13 @@ public function walkUpdateClause($updateClause) $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable); $this->rootAliases[] = $updateClause->aliasIdentificationVariable; - return $sql . ' SET ' . implode(', ', array_map([$this, 'walkUpdateItem'], $updateClause->updateItems)); + return $sql . ' SET ' . implode(', ', array_map($this->walkUpdateItem(...), $updateClause->updateItems)); } /** * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. - * - * @param AST\UpdateItem $updateItem - * - * @return string - * - * @not-deprecated */ - public function walkUpdateItem($updateItem) + public function walkUpdateItem(AST\UpdateItem $updateItem): string { $useTableAliasesBefore = $this->useSqlTableAliases; $this->useSqlTableAliases = false; @@ -2003,19 +1747,10 @@ public function walkUpdateItem($updateItem) $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = '; $newValue = $updateItem->newValue; - switch (true) { - case $newValue instanceof AST\Node: - $sql .= $newValue->dispatch($this); - break; - - case $newValue === null: - $sql .= 'NULL'; - break; - - default: - $sql .= $this->conn->quote($newValue); - break; - } + $sql .= match (true) { + $newValue instanceof AST\Node => $newValue->dispatch($this), + $newValue === null => 'NULL', + }; $this->useSqlTableAliases = $useTableAliasesBefore; @@ -2024,15 +1759,10 @@ public function walkUpdateItem($updateItem) /** * Walks down a WhereClause AST node, thereby generating the appropriate SQL. - * WhereClause or not, the appropriate discriminator sql is added. * - * @param AST\WhereClause $whereClause - * - * @return string - * - * @not-deprecated + * WhereClause or not, the appropriate discriminator sql is added. */ - public function walkWhereClause($whereClause) + public function walkWhereClause(AST\WhereClause|null $whereClause): string { $condSql = $whereClause !== null ? $this->walkConditionalExpression($whereClause->conditionalExpression) : ''; $discrSql = $this->generateDiscriminatorColumnConditionSQL($this->rootAliases); @@ -2071,55 +1801,40 @@ public function walkWhereClause($whereClause) /** * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr - * - * @return string - * - * @not-deprecated */ - public function walkConditionalExpression($condExpr) - { + public function walkConditionalExpression( + AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr, + ): string { // Phase 2 AST optimization: Skip processing of ConditionalExpression // if only one ConditionalTerm is defined if (! ($condExpr instanceof AST\ConditionalExpression)) { return $this->walkConditionalTerm($condExpr); } - return implode(' OR ', array_map([$this, 'walkConditionalTerm'], $condExpr->conditionalTerms)); + return implode(' OR ', array_map($this->walkConditionalTerm(...), $condExpr->conditionalTerms)); } /** * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. - * - * @param AST\ConditionalTerm|AST\ConditionalFactor|AST\ConditionalPrimary $condTerm - * - * @return string - * - * @not-deprecated */ - public function walkConditionalTerm($condTerm) - { + public function walkConditionalTerm( + AST\ConditionalTerm|AST\ConditionalPrimary|AST\ConditionalFactor $condTerm, + ): string { // Phase 2 AST optimization: Skip processing of ConditionalTerm // if only one ConditionalFactor is defined if (! ($condTerm instanceof AST\ConditionalTerm)) { return $this->walkConditionalFactor($condTerm); } - return implode(' AND ', array_map([$this, 'walkConditionalFactor'], $condTerm->conditionalFactors)); + return implode(' AND ', array_map($this->walkConditionalFactor(...), $condTerm->conditionalFactors)); } /** * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. - * - * @param AST\ConditionalFactor|AST\ConditionalPrimary $factor - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkConditionalFactor($factor) - { + public function walkConditionalFactor( + AST\ConditionalFactor|AST\ConditionalPrimary $factor, + ): string { // Phase 2 AST optimization: Skip processing of ConditionalFactor // if only one ConditionalPrimary is defined return ! ($factor instanceof AST\ConditionalFactor) @@ -2129,14 +1844,8 @@ public function walkConditionalFactor($factor) /** * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. - * - * @param AST\ConditionalPrimary $primary - * - * @return string - * - * @not-deprecated */ - public function walkConditionalPrimary($primary) + public function walkConditionalPrimary(AST\ConditionalPrimary $primary): string { if ($primary->isSimpleConditionalExpression()) { return $primary->simpleConditionalExpression->dispatch($this); @@ -2147,18 +1856,14 @@ public function walkConditionalPrimary($primary) return '(' . $this->walkConditionalExpression($condExpr) . ')'; } + + throw new LogicException('Unexpected state of ConditionalPrimary node.'); } /** * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\ExistsExpression $existsExpr - * - * @return string - * - * @not-deprecated */ - public function walkExistsExpression($existsExpr) + public function walkExistsExpression(AST\ExistsExpression $existsExpr): string { $sql = $existsExpr->not ? 'NOT ' : ''; @@ -2169,14 +1874,8 @@ public function walkExistsExpression($existsExpr) /** * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\CollectionMemberExpression $collMemberExpr - * - * @return string - * - * @not-deprecated */ - public function walkCollectionMemberExpression($collMemberExpr) + public function walkCollectionMemberExpression(AST\CollectionMemberExpression $collMemberExpr): string { $sql = $collMemberExpr->not ? 'NOT ' : ''; $sql .= 'EXISTS (SELECT 1 FROM '; @@ -2208,17 +1907,18 @@ public function walkCollectionMemberExpression($collMemberExpr) $assoc = $class->associationMappings[$fieldName]; - if ($assoc['type'] === ClassMetadata::ONE_TO_MANY) { - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + if ($assoc->isOneToMany()) { + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE '; - $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; - $sqlParts = []; + $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy]; + assert($owningAssoc->isManyToOne()); + $sqlParts = []; - foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform); $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; @@ -2234,34 +1934,35 @@ public function walkCollectionMemberExpression($collMemberExpr) $sql .= implode(' AND ', $sqlParts); } else { // many-to-many - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); - $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; - $joinTable = $owningAssoc['joinTable']; + $owningAssoc = $this->em->getMetadataFactory()->getOwningSide($assoc); + assert($owningAssoc->isManyToManyOwningSide()); + $joinTable = $owningAssoc->joinTable; // SQL table aliases - $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); + $joinTableAlias = $this->getSQLTableAlias($joinTable->name); $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias . ' WHERE '; - $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; + $joinColumns = $assoc->isOwningSide() ? $joinTable->joinColumns : $joinTable->inverseJoinColumns; $sqlParts = []; foreach ($joinColumns as $joinColumn) { - $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform); + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn->referencedColumnName], $class, $this->platform); - $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn; + $sqlParts[] = $joinTableAlias . '.' . $joinColumn->name . ' = ' . $sourceTableAlias . '.' . $targetColumn; } - $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; + $joinColumns = $assoc->isOwningSide() ? $joinTable->inverseJoinColumns : $joinTable->joinColumns; foreach ($joinColumns as $joinColumn) { if (isset($dqlParamKey)) { $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); } - $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' IN (' . $entitySql . ')'; + $sqlParts[] = $joinTableAlias . '.' . $joinColumn->name . ' IN (' . $entitySql . ')'; } $sql .= implode(' AND ', $sqlParts); @@ -2272,14 +1973,8 @@ public function walkCollectionMemberExpression($collMemberExpr) /** * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\EmptyCollectionComparisonExpression $emptyCollCompExpr - * - * @return string - * - * @not-deprecated */ - public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + public function walkEmptyCollectionComparisonExpression(AST\EmptyCollectionComparisonExpression $emptyCollCompExpr): string { $sizeFunc = new AST\Functions\SizeFunction('size'); $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; @@ -2289,14 +1984,8 @@ public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) /** * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\NullComparisonExpression $nullCompExpr - * - * @return string - * - * @not-deprecated */ - public function walkNullComparisonExpression($nullCompExpr) + public function walkNullComparisonExpression(AST\NullComparisonExpression $nullCompExpr): string { $expression = $nullCompExpr->expression; $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; @@ -2311,44 +2000,9 @@ public function walkNullComparisonExpression($nullCompExpr) return $this->walkInputParameter($expression) . $comparison; } - return $expression->dispatch($this) . $comparison; - } + assert(! is_string($expression)); - /** - * Walks down an InExpression AST node, thereby generating the appropriate SQL. - * - * @deprecated Use {@see walkInListExpression()} or {@see walkInSubselectExpression()} instead. - * - * @param AST\InExpression $inExpr - * - * @return string - */ - public function walkInExpression($inExpr) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10267', - '%s() is deprecated, call walkInListExpression() or walkInSubselectExpression() instead.', - __METHOD__ - ); - - if ($inExpr instanceof AST\InListExpression) { - return $this->walkInListExpression($inExpr); - } - - if ($inExpr instanceof AST\InSubselectExpression) { - return $this->walkInSubselectExpression($inExpr); - } - - $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN ('; - - $sql .= $inExpr->subselect - ? $this->walkSubselect($inExpr->subselect) - : implode(', ', array_map([$this, 'walkInParameter'], $inExpr->literals)); - - $sql .= ')'; - - return $sql; + return $expression->dispatch($this) . $comparison; } /** @@ -2358,7 +2012,7 @@ public function walkInListExpression(AST\InListExpression $inExpr): string { return $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN (' - . implode(', ', array_map([$this, 'walkInParameter'], $inExpr->literals)) + . implode(', ', array_map($this->walkInParameter(...), $inExpr->literals)) . ')'; } @@ -2376,15 +2030,9 @@ public function walkInSubselectExpression(AST\InSubselectExpression $inExpr): st /** * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. * - * @param AST\InstanceOfExpression $instanceOfExpr - * - * @return string - * * @throws QueryException - * - * @not-deprecated */ - public function walkInstanceOfExpression($instanceOfExpr) + public function walkInstanceOfExpression(AST\InstanceOfExpression $instanceOfExpr): string { $sql = ''; @@ -2399,20 +2047,13 @@ public function walkInstanceOfExpression($instanceOfExpr) $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.'; } - $sql .= $class->getDiscriminatorColumn()['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); + $sql .= $class->getDiscriminatorColumn()->name . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); $sql .= $this->getChildDiscriminatorsFromClassMetadata($discrClass, $instanceOfExpr); return $sql; } - /** - * @param mixed $inParam - * - * @return string - * - * @not-deprecated - */ - public function walkInParameter($inParam) + public function walkInParameter(mixed $inParam): string { return $inParam instanceof AST\InputParameter ? $this->walkInputParameter($inParam) @@ -2421,40 +2062,21 @@ public function walkInParameter($inParam) /** * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. - * - * @param AST\Literal $literal - * - * @return string - * - * @not-deprecated */ - public function walkLiteral($literal) + public function walkLiteral(AST\Literal $literal): string { - switch ($literal->type) { - case AST\Literal::STRING: - return $this->conn->quote($literal->value); - - case AST\Literal::BOOLEAN: - return (string) $this->conn->getDatabasePlatform()->convertBooleans(strtolower($literal->value) === 'true'); - - case AST\Literal::NUMERIC: - return (string) $literal->value; - - default: - throw QueryException::invalidLiteral($literal); - } + return match ($literal->type) { + AST\Literal::STRING => $this->conn->quote($literal->value), + AST\Literal::BOOLEAN => (string) $this->conn->getDatabasePlatform()->convertBooleans(strtolower($literal->value) === 'true'), + AST\Literal::NUMERIC => (string) $literal->value, + default => throw QueryException::invalidLiteral($literal), + }; } /** * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\BetweenExpression $betweenExpr - * - * @return string - * - * @not-deprecated */ - public function walkBetweenExpression($betweenExpr) + public function walkBetweenExpression(AST\BetweenExpression $betweenExpr): string { $sql = $this->walkArithmeticExpression($betweenExpr->expression); @@ -2470,14 +2092,8 @@ public function walkBetweenExpression($betweenExpr) /** * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\LikeExpression $likeExpr - * - * @return string - * - * @not-deprecated */ - public function walkLikeExpression($likeExpr) + public function walkLikeExpression(AST\LikeExpression $likeExpr): string { $stringExpr = $likeExpr->stringExpression; if (is_string($stringExpr)) { @@ -2511,28 +2127,16 @@ public function walkLikeExpression($likeExpr) /** * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\PathExpression $stateFieldPathExpression - * - * @return string - * - * @not-deprecated */ - public function walkStateFieldPathExpression($stateFieldPathExpression) + public function walkStateFieldPathExpression(AST\PathExpression $stateFieldPathExpression): string { return $this->walkPathExpression($stateFieldPathExpression); } /** * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\ComparisonExpression $compExpr - * - * @return string - * - * @not-deprecated */ - public function walkComparisonExpression($compExpr) + public function walkComparisonExpression(AST\ComparisonExpression $compExpr): string { $leftExpr = $compExpr->leftExpression; $rightExpr = $compExpr->rightExpression; @@ -2553,14 +2157,8 @@ public function walkComparisonExpression($compExpr) /** * Walks down an InputParameter AST node, thereby generating the appropriate SQL. - * - * @param AST\InputParameter $inputParam - * - * @return string - * - * @not-deprecated */ - public function walkInputParameter($inputParam) + public function walkInputParameter(AST\InputParameter $inputParam): string { $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++); @@ -2568,7 +2166,7 @@ public function walkInputParameter($inputParam) if ($parameter) { $type = $parameter->getType(); - if (Type::hasType($type)) { + if (is_string($type) && Type::hasType($type)) { return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform); } } @@ -2578,14 +2176,8 @@ public function walkInputParameter($inputParam) /** * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\ArithmeticExpression $arithmeticExpr - * - * @return string - * - * @not-deprecated */ - public function walkArithmeticExpression($arithmeticExpr) + public function walkArithmeticExpression(AST\ArithmeticExpression $arithmeticExpr): string { return $arithmeticExpr->isSimpleArithmeticExpression() ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) @@ -2594,32 +2186,20 @@ public function walkArithmeticExpression($arithmeticExpr) /** * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. - * - * @param AST\Node|string $simpleArithmeticExpr - * - * @return string - * - * @not-deprecated */ - public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + public function walkSimpleArithmeticExpression(AST\Node|string $simpleArithmeticExpr): string { if (! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { return $this->walkArithmeticTerm($simpleArithmeticExpr); } - return implode(' ', array_map([$this, 'walkArithmeticTerm'], $simpleArithmeticExpr->arithmeticTerms)); + return implode(' ', array_map($this->walkArithmeticTerm(...), $simpleArithmeticExpr->arithmeticTerms)); } /** * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. - * - * @param mixed $term - * - * @return string - * - * @not-deprecated */ - public function walkArithmeticTerm($term) + public function walkArithmeticTerm(AST\Node|string $term): string { if (is_string($term)) { return isset($this->queryComponents[$term]) @@ -2633,19 +2213,13 @@ public function walkArithmeticTerm($term) return $this->walkArithmeticFactor($term); } - return implode(' ', array_map([$this, 'walkArithmeticFactor'], $term->arithmeticFactors)); + return implode(' ', array_map($this->walkArithmeticFactor(...), $term->arithmeticFactors)); } /** * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. - * - * @param mixed $factor - * - * @return string - * - * @not-deprecated */ - public function walkArithmeticFactor($factor) + public function walkArithmeticFactor(AST\Node|string $factor): string { if (is_string($factor)) { return isset($this->queryComponents[$factor]) @@ -2666,14 +2240,8 @@ public function walkArithmeticFactor($factor) /** * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL. - * - * @param mixed $primary - * - * @return string The SQL. - * - * @not-deprecated */ - public function walkArithmeticPrimary($primary) + public function walkArithmeticPrimary(AST\Node|string $primary): string { if ($primary instanceof AST\SimpleArithmeticExpression) { return '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; @@ -2688,14 +2256,8 @@ public function walkArithmeticPrimary($primary) /** * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. - * - * @param mixed $stringPrimary - * - * @return string - * - * @not-deprecated */ - public function walkStringPrimary($stringPrimary) + public function walkStringPrimary(AST\Node|string $stringPrimary): string { return is_string($stringPrimary) ? $this->conn->quote($stringPrimary) @@ -2704,14 +2266,8 @@ public function walkStringPrimary($stringPrimary) /** * Walks down a ResultVariable that represents an AST node, thereby generating the appropriate SQL. - * - * @param string $resultVariable - * - * @return string - * - * @not-deprecated */ - public function walkResultVariable($resultVariable) + public function walkResultVariable(string $resultVariable): string { if (! isset($this->scalarResultAliasMap[$resultVariable])) { throw new InvalidArgumentException(sprintf('Unknown result variable: %s', $resultVariable)); @@ -2733,7 +2289,7 @@ public function walkResultVariable($resultVariable) */ private function getChildDiscriminatorsFromClassMetadata( ClassMetadata $rootClass, - AST\InstanceOfExpression $instanceOfExpr + AST\InstanceOfExpression $instanceOfExpr, ): string { $sqlParameterList = []; $discriminators = []; @@ -2753,8 +2309,10 @@ private function getChildDiscriminatorsFromClassMetadata( $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) . ')'; diff --git a/src/Query/TokenType.php b/src/Query/TokenType.php index 417c7506d25..47cc7912711 100644 --- a/src/Query/TokenType.php +++ b/src/Query/TokenType.php @@ -4,96 +4,90 @@ namespace Doctrine\ORM\Query; -final class TokenType +enum TokenType: int { // All tokens that are not valid identifiers must be < 100 - public const T_NONE = 1; - public const T_INTEGER = 2; - public const T_STRING = 3; - public const T_INPUT_PARAMETER = 4; - public const T_FLOAT = 5; - public const T_CLOSE_PARENTHESIS = 6; - public const T_OPEN_PARENTHESIS = 7; - public const T_COMMA = 8; - public const T_DIVIDE = 9; - public const T_DOT = 10; - public const T_EQUALS = 11; - public const T_GREATER_THAN = 12; - public const T_LOWER_THAN = 13; - public const T_MINUS = 14; - public const T_MULTIPLY = 15; - public const T_NEGATE = 16; - public const T_PLUS = 17; - public const T_OPEN_CURLY_BRACE = 18; - public const T_CLOSE_CURLY_BRACE = 19; + case T_NONE = 1; + case T_INTEGER = 2; + case T_STRING = 3; + case T_INPUT_PARAMETER = 4; + case T_FLOAT = 5; + case T_CLOSE_PARENTHESIS = 6; + case T_OPEN_PARENTHESIS = 7; + case T_COMMA = 8; + case T_DIVIDE = 9; + case T_DOT = 10; + case T_EQUALS = 11; + case T_GREATER_THAN = 12; + case T_LOWER_THAN = 13; + case T_MINUS = 14; + case T_MULTIPLY = 15; + case T_NEGATE = 16; + case T_PLUS = 17; + case T_OPEN_CURLY_BRACE = 18; + case T_CLOSE_CURLY_BRACE = 19; // All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100 - /** @deprecated No Replacement planned. */ - public const T_ALIASED_NAME = 100; - public const T_FULLY_QUALIFIED_NAME = 101; - public const T_IDENTIFIER = 102; + case T_FULLY_QUALIFIED_NAME = 101; + case T_IDENTIFIER = 102; // All keyword tokens should be >= 200 - public const T_ALL = 200; - public const T_AND = 201; - public const T_ANY = 202; - public const T_AS = 203; - public const T_ASC = 204; - public const T_AVG = 205; - public const T_BETWEEN = 206; - public const T_BOTH = 207; - public const T_BY = 208; - public const T_CASE = 209; - public const T_COALESCE = 210; - public const T_COUNT = 211; - public const T_DELETE = 212; - public const T_DESC = 213; - public const T_DISTINCT = 214; - public const T_ELSE = 215; - public const T_EMPTY = 216; - public const T_END = 217; - public const T_ESCAPE = 218; - public const T_EXISTS = 219; - public const T_FALSE = 220; - public const T_FROM = 221; - public const T_GROUP = 222; - public const T_HAVING = 223; - public const T_HIDDEN = 224; - public const T_IN = 225; - public const T_INDEX = 226; - public const T_INNER = 227; - public const T_INSTANCE = 228; - public const T_IS = 229; - public const T_JOIN = 230; - public const T_LEADING = 231; - public const T_LEFT = 232; - public const T_LIKE = 233; - public const T_MAX = 234; - public const T_MEMBER = 235; - public const T_MIN = 236; - public const T_NEW = 237; - public const T_NOT = 238; - public const T_NULL = 239; - public const T_NULLIF = 240; - public const T_OF = 241; - public const T_OR = 242; - public const T_ORDER = 243; - public const T_OUTER = 244; - public const T_PARTIAL = 245; - public const T_SELECT = 246; - public const T_SET = 247; - public const T_SOME = 248; - public const T_SUM = 249; - public const T_THEN = 250; - public const T_TRAILING = 251; - public const T_TRUE = 252; - public const T_UPDATE = 253; - public const T_WHEN = 254; - public const T_WHERE = 255; - public const T_WITH = 256; - - /** @internal */ - private function __construct() - { - } + case T_ALL = 200; + case T_AND = 201; + case T_ANY = 202; + case T_AS = 203; + case T_ASC = 204; + case T_AVG = 205; + case T_BETWEEN = 206; + case T_BOTH = 207; + case T_BY = 208; + case T_CASE = 209; + case T_COALESCE = 210; + case T_COUNT = 211; + case T_DELETE = 212; + case T_DESC = 213; + case T_DISTINCT = 214; + case T_ELSE = 215; + case T_EMPTY = 216; + case T_END = 217; + case T_ESCAPE = 218; + case T_EXISTS = 219; + case T_FALSE = 220; + case T_FROM = 221; + case T_GROUP = 222; + case T_HAVING = 223; + case T_HIDDEN = 224; + case T_IN = 225; + case T_INDEX = 226; + case T_INNER = 227; + case T_INSTANCE = 228; + case T_IS = 229; + case T_JOIN = 230; + case T_LEADING = 231; + case T_LEFT = 232; + case T_LIKE = 233; + case T_MAX = 234; + case T_MEMBER = 235; + case T_MIN = 236; + case T_NEW = 237; + case T_NOT = 238; + case T_NULL = 239; + case T_NULLIF = 240; + case T_OF = 241; + case T_OR = 242; + case T_ORDER = 243; + case T_OUTER = 244; + case T_PARTIAL = 245; + case T_SELECT = 246; + case T_SET = 247; + case T_SOME = 248; + case T_SUM = 249; + case T_THEN = 250; + case T_TRAILING = 251; + case T_TRUE = 252; + case T_UPDATE = 253; + case T_WHEN = 254; + case T_WHERE = 255; + case T_WITH = 256; + case T_NAMED = 257; } diff --git a/src/Query/TreeWalker.php b/src/Query/TreeWalker.php index 010e2b6b28c..964762d8256 100644 --- a/src/Query/TreeWalker.php +++ b/src/Query/TreeWalker.php @@ -16,536 +16,29 @@ interface TreeWalker /** * Initializes TreeWalker with important information about the ASTs to be walked. * - * @param AbstractQuery $query The parsed Query. - * @param ParserResult $parserResult The result of the parsing process. - * @param mixed[] $queryComponents The query components (symbol table). * @phpstan-param array $queryComponents The query components (symbol table). */ - public function __construct($query, $parserResult, array $queryComponents); + public function __construct(AbstractQuery $query, ParserResult $parserResult, array $queryComponents); /** * Returns internal queryComponents array. * - * @return array> * @phpstan-return array */ - public function getQueryComponents(); - - /** - * Sets or overrides a query component for a given dql alias. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param string $dqlAlias The DQL alias. - * @param array $queryComponent - * @phpstan-param QueryComponent $queryComponent - * - * @return void - */ - public function setQueryComponent($dqlAlias, array $queryComponent); + public function getQueryComponents(): array; /** * Walks down a SelectStatement AST node. - * - * @return void - */ - public function walkSelectStatement(AST\SelectStatement $AST); - - /** - * Walks down a SelectClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SelectClause $selectClause - * - * @return void */ - public function walkSelectClause($selectClause); - - /** - * Walks down a FromClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\FromClause $fromClause - * - * @return void - */ - public function walkFromClause($fromClause); - - /** - * Walks down a FunctionNode AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\Functions\FunctionNode $function - * - * @return void - */ - public function walkFunction($function); - - /** - * Walks down an OrderByClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\OrderByClause $orderByClause - * - * @return void - */ - public function walkOrderByClause($orderByClause); - - /** - * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\OrderByItem $orderByItem - * - * @return void - */ - public function walkOrderByItem($orderByItem); - - /** - * Walks down a HavingClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\HavingClause $havingClause - * - * @return void - */ - public function walkHavingClause($havingClause); - - /** - * Walks down a Join AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\Join $join - * - * @return void - */ - public function walkJoin($join); - - /** - * Walks down a SelectExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SelectExpression $selectExpression - * - * @return void - */ - public function walkSelectExpression($selectExpression); - - /** - * Walks down a QuantifiedExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\QuantifiedExpression $qExpr - * - * @return void - */ - public function walkQuantifiedExpression($qExpr); - - /** - * Walks down a Subselect AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\Subselect $subselect - * - * @return void - */ - public function walkSubselect($subselect); - - /** - * Walks down a SubselectFromClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SubselectFromClause $subselectFromClause - * - * @return void - */ - public function walkSubselectFromClause($subselectFromClause); - - /** - * Walks down a SimpleSelectClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SimpleSelectClause $simpleSelectClause - * - * @return void - */ - public function walkSimpleSelectClause($simpleSelectClause); - - /** - * Walks down a SimpleSelectExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SimpleSelectExpression $simpleSelectExpression - * - * @return void - */ - public function walkSimpleSelectExpression($simpleSelectExpression); - - /** - * Walks down an AggregateExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\AggregateExpression $aggExpression - * - * @return void - */ - public function walkAggregateExpression($aggExpression); - - /** - * Walks down a GroupByClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\GroupByClause $groupByClause - * - * @return void - */ - public function walkGroupByClause($groupByClause); - - /** - * Walks down a GroupByItem AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\PathExpression|string $groupByItem - * - * @return void - */ - public function walkGroupByItem($groupByItem); + public function walkSelectStatement(AST\SelectStatement $selectStatement): void; /** * Walks down an UpdateStatement AST node. - * - * @return void */ - public function walkUpdateStatement(AST\UpdateStatement $AST); + public function walkUpdateStatement(AST\UpdateStatement $updateStatement): void; /** * Walks down a DeleteStatement AST node. - * - * @return void - */ - public function walkDeleteStatement(AST\DeleteStatement $AST); - - /** - * Walks down a DeleteClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @return void - */ - public function walkDeleteClause(AST\DeleteClause $deleteClause); - - /** - * Walks down an UpdateClause AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\UpdateClause $updateClause - * - * @return void - */ - public function walkUpdateClause($updateClause); - - /** - * Walks down an UpdateItem AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\UpdateItem $updateItem - * - * @return void - */ - public function walkUpdateItem($updateItem); - - /** - * Walks down a WhereClause AST node. - * - * WhereClause or not, the appropriate discriminator sql is added. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\WhereClause $whereClause - * - * @return void - */ - public function walkWhereClause($whereClause); - - /** - * Walk down a ConditionalExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ConditionalExpression $condExpr - * - * @return void - */ - public function walkConditionalExpression($condExpr); - - /** - * Walks down a ConditionalTerm AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ConditionalTerm $condTerm - * - * @return void - */ - public function walkConditionalTerm($condTerm); - - /** - * Walks down a ConditionalFactor AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ConditionalFactor $factor - * - * @return void - */ - public function walkConditionalFactor($factor); - - /** - * Walks down a ConditionalPrimary AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ConditionalPrimary $primary - * - * @return void - */ - public function walkConditionalPrimary($primary); - - /** - * Walks down an ExistsExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ExistsExpression $existsExpr - * - * @return void - */ - public function walkExistsExpression($existsExpr); - - /** - * Walks down a CollectionMemberExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\CollectionMemberExpression $collMemberExpr - * - * @return void - */ - public function walkCollectionMemberExpression($collMemberExpr); - - /** - * Walks down an EmptyCollectionComparisonExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\EmptyCollectionComparisonExpression $emptyCollCompExpr - * - * @return void - */ - public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); - - /** - * Walks down a NullComparisonExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\NullComparisonExpression $nullCompExpr - * - * @return void - */ - public function walkNullComparisonExpression($nullCompExpr); - - /** - * Walks down an InExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\InExpression $inExpr - * - * @return void - */ - public function walkInExpression($inExpr); - - /** - * Walks down an InstanceOfExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\InstanceOfExpression $instanceOfExpr - * - * @return void - */ - public function walkInstanceOfExpression($instanceOfExpr); - - /** - * Walks down a literal that represents an AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\Literal $literal - * - * @return void - */ - public function walkLiteral($literal); - - /** - * Walks down a BetweenExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\BetweenExpression $betweenExpr - * - * @return void - */ - public function walkBetweenExpression($betweenExpr); - - /** - * Walks down a LikeExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\LikeExpression $likeExpr - * - * @return void - */ - public function walkLikeExpression($likeExpr); - - /** - * Walks down a StateFieldPathExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\PathExpression $stateFieldPathExpression - * - * @return void - */ - public function walkStateFieldPathExpression($stateFieldPathExpression); - - /** - * Walks down a ComparisonExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ComparisonExpression $compExpr - * - * @return void - */ - public function walkComparisonExpression($compExpr); - - /** - * Walks down an InputParameter AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\InputParameter $inputParam - * - * @return void - */ - public function walkInputParameter($inputParam); - - /** - * Walks down an ArithmeticExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\ArithmeticExpression $arithmeticExpr - * - * @return void - */ - public function walkArithmeticExpression($arithmeticExpr); - - /** - * Walks down an ArithmeticTerm AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param mixed $term - * - * @return void - */ - public function walkArithmeticTerm($term); - - /** - * Walks down a StringPrimary that represents an AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param mixed $stringPrimary - * - * @return void - */ - public function walkStringPrimary($stringPrimary); - - /** - * Walks down an ArithmeticFactor that represents an AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param mixed $factor - * - * @return void - */ - public function walkArithmeticFactor($factor); - - /** - * Walks down an SimpleArithmeticExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\SimpleArithmeticExpression $simpleArithmeticExpr - * - * @return void - */ - public function walkSimpleArithmeticExpression($simpleArithmeticExpr); - - /** - * Walks down a PathExpression AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\PathExpression $pathExpr - * - * @return void - */ - public function walkPathExpression($pathExpr); - - /** - * Walks down a ResultVariable that represents an AST node. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param string $resultVariable - * - * @return void - */ - public function walkResultVariable($resultVariable); - - /** - * Gets an executor that can be used to execute the result of this walker. - * - * @deprecated This method will be removed from the interface in 3.0. - * - * @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST - * - * @return Exec\AbstractSqlExecutor */ - public function getExecutor($AST); + public function walkDeleteStatement(AST\DeleteStatement $deleteStatement): void; } diff --git a/src/Query/TreeWalkerAdapter.php b/src/Query/TreeWalkerAdapter.php index cf02e32ce18..dc838a95900 100644 --- a/src/Query/TreeWalkerAdapter.php +++ b/src/Query/TreeWalkerAdapter.php @@ -4,19 +4,14 @@ namespace Doctrine\ORM\Query; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\Mapping\ClassMetadata; use LogicException; use function array_diff; use function array_keys; -use function debug_backtrace; -use function is_a; use function sprintf; -use const DEBUG_BACKTRACE_IGNORE_ARGS; - /** * An adapter implementation of the TreeWalker interface. The methods in this class * are empty. This class exists as convenience for creating tree walkers. @@ -26,67 +21,42 @@ abstract class TreeWalkerAdapter implements TreeWalker { /** - * The original Query. - * - * @var AbstractQuery + * {@inheritDoc} */ - private $query; + public function __construct( + private readonly AbstractQuery $query, + private readonly ParserResult $parserResult, + private array $queryComponents, + ) { + } /** - * The ParserResult of the original query that was produced by the Parser. - * - * @var ParserResult + * {@inheritDoc} */ - private $parserResult; + public function getQueryComponents(): array + { + return $this->queryComponents; + } - /** - * The query components of the original query (the "symbol table") that was produced by the Parser. - * - * @phpstan-var array - */ - private $queryComponents; + public function walkSelectStatement(AST\SelectStatement $selectStatement): void + { + } - /** - * {@inheritDoc} - */ - public function __construct($query, $parserResult, array $queryComponents) + public function walkUpdateStatement(AST\UpdateStatement $updateStatement): void { - $this->query = $query; - $this->parserResult = $parserResult; - $this->queryComponents = $queryComponents; } - /** - * {@inheritDoc} - */ - public function getQueryComponents() + public function walkDeleteStatement(AST\DeleteStatement $deleteStatement): void { - return $this->queryComponents; } /** * Sets or overrides a query component for a given dql alias. * - * @internal This method will be protected in 3.0. - * - * @param string $dqlAlias The DQL alias. - * @param array $queryComponent * @phpstan-param QueryComponent $queryComponent - * - * @return void */ - public function setQueryComponent($dqlAlias, array $queryComponent) + protected function setQueryComponent(string $dqlAlias, array $queryComponent): void { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - if (! isset($trace[1]['class']) || ! is_a($trace[1]['class'], self::class, true)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method %s will be protected in 3.0. Calling it publicly is deprecated.', - __METHOD__ - ); - } - $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token']; if (array_diff($requiredKeys, array_keys($queryComponent))) { @@ -96,723 +66,25 @@ public function setQueryComponent($dqlAlias, array $queryComponent) $this->queryComponents[$dqlAlias] = $queryComponent; } - /** - * Returns internal queryComponents array. - * - * @deprecated Call {@see getQueryComponents()} instead. - * - * @return array> - * @phpstan-return array - */ - protected function _getQueryComponents() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method %s is deprecated, call getQueryComponents() instead.', - __METHOD__ - ); - - return $this->queryComponents; - } - /** * Retrieves the Query Instance responsible for the current walkers execution. - * - * @return AbstractQuery */ - protected function _getQuery() + protected function _getQuery(): AbstractQuery { return $this->query; } /** * Retrieves the ParserResult. - * - * @return ParserResult */ - protected function _getParserResult() + protected function _getParserResult(): ParserResult { return $this->parserResult; } - public function walkSelectStatement(AST\SelectStatement $AST) - { - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSelectClause($selectClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkFromClause($fromClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkFunction($function) + protected function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkOrderByClause($orderByClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkOrderByItem($orderByItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkHavingClause($havingClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkJoin($join) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSelectExpression($selectExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkQuantifiedExpression($qExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSubselect($subselect) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSubselectFromClause($subselectFromClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleSelectClause($simpleSelectClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleSelectExpression($simpleSelectExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkAggregateExpression($aggExpression) - { - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkGroupByClause($groupByClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkGroupByItem($groupByItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - public function walkUpdateStatement(AST\UpdateStatement $AST) - { - } - - public function walkDeleteStatement(AST\DeleteStatement $AST) - { - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkDeleteClause(AST\DeleteClause $deleteClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkUpdateClause($updateClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkUpdateItem($updateItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkWhereClause($whereClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalExpression($condExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalTerm($condTerm) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalFactor($factor) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalPrimary($primary) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkExistsExpression($existsExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkCollectionMemberExpression($collMemberExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkNullComparisonExpression($nullCompExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInExpression($inExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInstanceOfExpression($instanceOfExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkLiteral($literal) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkBetweenExpression($betweenExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkLikeExpression($likeExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkStateFieldPathExpression($stateFieldPathExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkComparisonExpression($compExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInputParameter($inputParam) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticExpression($arithmeticExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticTerm($term) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkStringPrimary($stringPrimary) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticFactor($factor) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleArithmeticExpression($simpleArithmeticExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkPathExpression($pathExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkResultVariable($resultVariable) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function getExecutor($AST) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - return null; - } - - final protected function getMetadataForDqlAlias(string $dqlAlias): ClassMetadata - { - // @phpstan-ignore method.deprecated - $metadata = $this->_getQueryComponents()[$dqlAlias]['metadata'] ?? null; - - if ($metadata === null) { - throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias)); - } - - return $metadata; + return $this->queryComponents[$dqlAlias]['metadata'] + ?? throw new LogicException(sprintf('No metadata for DQL alias: %s', $dqlAlias)); } } diff --git a/src/Query/TreeWalkerChain.php b/src/Query/TreeWalkerChain.php index 23f4bba61b3..ac21aaa7c8f 100644 --- a/src/Query/TreeWalkerChain.php +++ b/src/Query/TreeWalkerChain.php @@ -4,13 +4,9 @@ namespace Doctrine\ORM\Query; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\AbstractQuery; use Generator; -use function array_diff; -use function array_keys; - /** * Represents a chain of tree walkers that modify an AST and finally emit output. * Only the last walker in the chain can emit output. Any previous walkers can modify @@ -25,927 +21,61 @@ class TreeWalkerChain implements TreeWalker * * @var list> */ - private $walkers = []; - - /** @var AbstractQuery */ - private $query; - - /** @var ParserResult */ - private $parserResult; - - /** - * The query components of the original query (the "symbol table") that was produced by the Parser. - * - * @var array> - * @phpstan-var array - */ - private $queryComponents; + private array $walkers = []; /** - * Returns the internal queryComponents array. - * * {@inheritDoc} */ - public function getQueryComponents() - { - return $this->queryComponents; + public function __construct( + private readonly AbstractQuery $query, + private readonly ParserResult $parserResult, + private array $queryComponents, + ) { } /** - * {@inheritDoc} + * Returns the internal queryComponents array. * - * @return void - */ - public function setQueryComponent($dqlAlias, array $queryComponent) - { - $requiredKeys = ['metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token']; - - if (array_diff($requiredKeys, array_keys($queryComponent))) { - throw QueryException::invalidQueryComponent($dqlAlias); - } - - $this->queryComponents[$dqlAlias] = $queryComponent; - } - - /** * {@inheritDoc} */ - public function __construct($query, $parserResult, array $queryComponents) + public function getQueryComponents(): array { - $this->query = $query; - $this->parserResult = $parserResult; - $this->queryComponents = $queryComponents; + return $this->queryComponents; } /** * Adds a tree walker to the chain. * * @param class-string $walkerClass The class of the walker to instantiate. - * - * @return void */ - public function addTreeWalker($walkerClass) + public function addTreeWalker(string $walkerClass): void { $this->walkers[] = $walkerClass; } - public function walkSelectStatement(AST\SelectStatement $AST) + public function walkSelectStatement(AST\SelectStatement $selectStatement): void { foreach ($this->getWalkers() as $walker) { - $walker->walkSelectStatement($AST); + $walker->walkSelectStatement($selectStatement); $this->queryComponents = $walker->getQueryComponents(); } } - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSelectClause($selectClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkSelectClause($selectClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkFromClause($fromClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkFromClause($fromClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkFunction($function) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkFunction($function); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkOrderByClause($orderByClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkOrderByClause($orderByClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkOrderByItem($orderByItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkOrderByItem($orderByItem); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkHavingClause($havingClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkHavingClause($havingClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkJoin($join) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkJoin($join); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSelectExpression($selectExpression) + public function walkUpdateStatement(AST\UpdateStatement $updateStatement): void { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - foreach ($this->getWalkers() as $walker) { - $walker->walkSelectExpression($selectExpression); + $walker->walkUpdateStatement($updateStatement); } } - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkQuantifiedExpression($qExpr) + public function walkDeleteStatement(AST\DeleteStatement $deleteStatement): void { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkQuantifiedExpression($qExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSubselect($subselect) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - foreach ($this->getWalkers() as $walker) { - $walker->walkSubselect($subselect); + $walker->walkDeleteStatement($deleteStatement); } } - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSubselectFromClause($subselectFromClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkSubselectFromClause($subselectFromClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleSelectClause($simpleSelectClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkSimpleSelectClause($simpleSelectClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleSelectExpression($simpleSelectExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkSimpleSelectExpression($simpleSelectExpression); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkAggregateExpression($aggExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkAggregateExpression($aggExpression); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkGroupByClause($groupByClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkGroupByClause($groupByClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkGroupByItem($groupByItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkGroupByItem($groupByItem); - } - } - - public function walkUpdateStatement(AST\UpdateStatement $AST) - { - foreach ($this->getWalkers() as $walker) { - $walker->walkUpdateStatement($AST); - } - } - - public function walkDeleteStatement(AST\DeleteStatement $AST) - { - foreach ($this->getWalkers() as $walker) { - $walker->walkDeleteStatement($AST); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkDeleteClause(AST\DeleteClause $deleteClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkDeleteClause($deleteClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkUpdateClause($updateClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkUpdateClause($updateClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkUpdateItem($updateItem) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkUpdateItem($updateItem); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkWhereClause($whereClause) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkWhereClause($whereClause); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalExpression($condExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkConditionalExpression($condExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalTerm($condTerm) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkConditionalTerm($condTerm); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalFactor($factor) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkConditionalFactor($factor); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkConditionalPrimary($condPrimary) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkConditionalPrimary($condPrimary); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkExistsExpression($existsExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkExistsExpression($existsExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkCollectionMemberExpression($collMemberExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkCollectionMemberExpression($collMemberExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkNullComparisonExpression($nullCompExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkNullComparisonExpression($nullCompExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInExpression($inExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkInExpression($inExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInstanceOfExpression($instanceOfExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkInstanceOfExpression($instanceOfExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkLiteral($literal) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkLiteral($literal); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkBetweenExpression($betweenExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkBetweenExpression($betweenExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkLikeExpression($likeExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkLikeExpression($likeExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkStateFieldPathExpression($stateFieldPathExpression) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkStateFieldPathExpression($stateFieldPathExpression); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkComparisonExpression($compExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkComparisonExpression($compExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkInputParameter($inputParam) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkInputParameter($inputParam); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticExpression($arithmeticExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkArithmeticExpression($arithmeticExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticTerm($term) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkArithmeticTerm($term); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkStringPrimary($stringPrimary) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkStringPrimary($stringPrimary); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkArithmeticFactor($factor) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkArithmeticFactor($factor); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkSimpleArithmeticExpression($simpleArithmeticExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkPathExpression($pathExpr) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkPathExpression($pathExpr); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function walkResultVariable($resultVariable) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - foreach ($this->getWalkers() as $walker) { - $walker->walkResultVariable($resultVariable); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This method will be removed in 3.0. - */ - public function getExecutor($AST) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9551', - 'Method "%s" is deprecated and will be removed in ORM 3.0 without replacement.', - __METHOD__ - ); - - return null; - } - /** @phpstan-return Generator */ private function getWalkers(): Generator { diff --git a/src/Query/TreeWalkerChainIterator.php b/src/Query/TreeWalkerChainIterator.php deleted file mode 100644 index 1ca58e54101..00000000000 --- a/src/Query/TreeWalkerChainIterator.php +++ /dev/null @@ -1,156 +0,0 @@ - - * @template-implements ArrayAccess - */ -class TreeWalkerChainIterator implements Iterator, ArrayAccess -{ - /** @var class-string[] */ - private $walkers = []; - /** @var TreeWalkerChain */ - private $treeWalkerChain; - /** @var AbstractQuery */ - private $query; - /** @var ParserResult */ - private $parserResult; - - /** - * @param AbstractQuery $query - * @param ParserResult $parserResult - */ - public function __construct(TreeWalkerChain $treeWalkerChain, $query, $parserResult) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9511', - '%s is deprecated and will be removed without replacement.', - self::class - ); - - $this->treeWalkerChain = $treeWalkerChain; - $this->query = $query; - $this->parserResult = $parserResult; - } - - /** @return class-string|false */ - #[ReturnTypeWillChange] - public function rewind() - { - return reset($this->walkers); - } - - /** @return TreeWalker|null */ - #[ReturnTypeWillChange] - public function current() - { - return $this->offsetGet(key($this->walkers)); - } - - /** @return int */ - #[ReturnTypeWillChange] - public function key() - { - return key($this->walkers); - } - - /** @return TreeWalker|null */ - #[ReturnTypeWillChange] - public function next() - { - next($this->walkers); - - return $this->offsetGet(key($this->walkers)); - } - - /** - * {@inheritDoc} - * - * @return bool - */ - #[ReturnTypeWillChange] - public function valid() - { - return key($this->walkers) !== null; - } - - /** - * @param mixed $offset - * @phpstan-param array-key|null $offset - * - * @return bool - */ - #[ReturnTypeWillChange] - public function offsetExists($offset) - { - return isset($this->walkers[$offset ?? '']); - } - - /** - * @param mixed $offset - * @phpstan-param array-key|null $offset - * - * @return TreeWalker|null - */ - #[ReturnTypeWillChange] - public function offsetGet($offset) - { - if ($this->offsetExists($offset)) { - return new $this->walkers[$offset]( - $this->query, - $this->parserResult, - $this->treeWalkerChain->getQueryComponents() - ); - } - - return null; - } - - /** - * {@inheritDoc} - * - * @param string $value - * @phpstan-param array-key|null $offset - * - * @return void - */ - #[ReturnTypeWillChange] - public function offsetSet($offset, $value) - { - if ($offset === null) { - $this->walkers[] = $value; - } else { - $this->walkers[$offset] = $value; - } - } - - /** - * @param mixed $offset - * @phpstan-param array-key|null $offset - * - * @return void - */ - #[ReturnTypeWillChange] - public function offsetUnset($offset) - { - if ($this->offsetExists($offset)) { - unset($this->walkers[$offset ?? '']); - } - } -} diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index b125a529231..d695501ba6c 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -6,20 +6,21 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; -use Doctrine\Deprecations\Deprecation; -use Doctrine\ORM\Internal\CriteriaOrderings; +use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\ParameterType; +use Doctrine\ORM\Internal\NoUnknownNamedArguments; +use Doctrine\ORM\Internal\QueryType; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\QueryExpressionVisitor; use InvalidArgumentException; use RuntimeException; +use Stringable; use function array_keys; -use function array_merge; use function array_unshift; use function assert; -use function func_get_args; -use function func_num_args; +use function count; use function implode; use function in_array; use function is_array; @@ -38,38 +39,16 @@ * This class is responsible for building DQL query strings via an object oriented * PHP interface. */ -class QueryBuilder +class QueryBuilder implements Stringable { - use CriteriaOrderings; - - /** @deprecated */ - public const SELECT = 0; - - /** @deprecated */ - public const DELETE = 1; - - /** @deprecated */ - public const UPDATE = 2; - - /** @deprecated */ - public const STATE_DIRTY = 0; - - /** @deprecated */ - public const STATE_CLEAN = 1; - - /** - * The EntityManager used by this QueryBuilder. - * - * @var EntityManagerInterface - */ - private $em; + use NoUnknownNamedArguments; /** * The array of DQL parts collected. * * @phpstan-var array */ - private $dqlParts = [ + private array $dqlParts = [ 'distinct' => false, 'select' => [], 'from' => [], @@ -81,93 +60,71 @@ class QueryBuilder 'orderBy' => [], ]; - /** - * The type of query this is. Can be select, update or delete. - * - * @var int - * @phpstan-var self::SELECT|self::DELETE|self::UPDATE - * @phpstan-ignore classConstant.deprecated - */ - private $type = self::SELECT; - - /** - * The state of the query object. Can be dirty or clean. - * - * @var int - * @phpstan-var self::STATE_* - * @phpstan-ignore classConstant.deprecated - */ - private $state = self::STATE_CLEAN; + private QueryType $type = QueryType::Select; /** * The complete DQL string for this query. - * - * @var string|null */ - private $dql; + private string|null $dql = null; /** * The query parameters. * - * @var ArrayCollection * @phpstan-var ArrayCollection */ - private $parameters; + private ArrayCollection $parameters; /** * The index of the first result to retrieve. - * - * @var int */ - private $firstResult = 0; + private int $firstResult = 0; /** * The maximum number of results to retrieve. - * - * @var int|null */ - private $maxResults = null; + private int|null $maxResults = null; /** * Keeps root entity alias names for join entities. * * @phpstan-var array */ - private $joinRootAliases = []; + private array $joinRootAliases = []; /** * Whether to use second level cache, if available. - * - * @var bool */ - protected $cacheable = false; + protected bool $cacheable = false; /** * Second level cache region name. - * - * @var string|null */ - protected $cacheRegion; + protected string|null $cacheRegion = null; /** * Second level query cache mode. * - * @var int|null * @phpstan-var Cache::MODE_*|null */ - protected $cacheMode; + protected int|null $cacheMode = null; - /** @var int */ - protected $lifetime = 0; + protected int $lifetime = 0; + + /** + * The counter of bound parameters. + * + * @var int<0, max> + */ + private int $boundCounter = 0; /** * Initializes a new QueryBuilder that uses the given EntityManager. * * @param EntityManagerInterface $em The EntityManager to use. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; + public function __construct( + private readonly EntityManagerInterface $em, + ) { $this->parameters = new ArrayCollection(); } @@ -185,10 +142,8 @@ public function __construct(EntityManagerInterface $em) * * For more complex expression construction, consider storing the expression * builder object in a local variable. - * - * @return Query\Expr */ - public function expr() + public function expr(): Expr { return $this->em->getExpressionBuilder(); } @@ -196,35 +151,27 @@ public function expr() /** * Enable/disable second level query (result) caching for this query. * - * @param bool $cacheable - * * @return $this */ - public function setCacheable($cacheable) + public function setCacheable(bool $cacheable): static { - $this->cacheable = (bool) $cacheable; + $this->cacheable = $cacheable; return $this; } /** * Are the query results enabled for second level cache? - * - * @return bool */ - public function isCacheable() + public function isCacheable(): bool { return $this->cacheable; } - /** - * @param string $cacheRegion - * - * @return $this - */ - public function setCacheRegion($cacheRegion) + /** @return $this */ + public function setCacheRegion(string $cacheRegion): static { - $this->cacheRegion = (string) $cacheRegion; + $this->cacheRegion = $cacheRegion; return $this; } @@ -234,13 +181,12 @@ public function setCacheRegion($cacheRegion) * * @return string|null The cache region name; NULL indicates the default region. */ - public function getCacheRegion() + public function getCacheRegion(): string|null { return $this->cacheRegion; } - /** @return int */ - public function getLifetime() + public function getLifetime(): int { return $this->lifetime; } @@ -248,88 +194,41 @@ public function getLifetime() /** * Sets the life-time for this query into second level cache. * - * @param int $lifetime - * * @return $this */ - public function setLifetime($lifetime) + public function setLifetime(int $lifetime): static { - $this->lifetime = (int) $lifetime; + $this->lifetime = $lifetime; return $this; } - /** - * @return int|null - * @phpstan-return Cache::MODE_*|null - */ - public function getCacheMode() + /** @phpstan-return Cache::MODE_*|null */ + public function getCacheMode(): int|null { return $this->cacheMode; } /** - * @param int $cacheMode * @phpstan-param Cache::MODE_* $cacheMode * * @return $this */ - public function setCacheMode($cacheMode) + public function setCacheMode(int $cacheMode): static { - $this->cacheMode = (int) $cacheMode; + $this->cacheMode = $cacheMode; return $this; } - /** - * Gets the type of the currently built query. - * - * @deprecated If necessary, track the type of the query being built outside of the builder. - * - * @return int - * @phpstan-return self::SELECT|self::DELETE|self::UPDATE - */ - public function getType() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/orm/pull/9945', - 'Relying on the type of the query being built is deprecated.' - . ' If necessary, track the type of the query being built outside of the builder.' - ); - - return $this->type; - } - /** * Gets the associated EntityManager for this query builder. - * - * @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { return $this->em; } - /** - * Gets the state of this query builder instance. - * - * @deprecated The builder state is an internal concern. - * - * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. - * @phpstan-return self::STATE_* - */ - public function getState() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/orm/pull/9945', - 'Relying on the query builder state is deprecated as it is an internal concern.' - ); - - return $this->state; - } - /** * Gets the complete DQL string formed by the current specifications of this QueryBuilder. * @@ -339,39 +238,14 @@ public function getState() * ->from('User', 'u'); * echo $qb->getDql(); // SELECT u FROM User u * - * - * @return string The DQL query string. */ - public function getDQL() + public function getDQL(): string { - // @phpstan-ignore classConstant.deprecated - if ($this->dql !== null && $this->state === self::STATE_CLEAN) { - return $this->dql; - } - - switch ($this->type) { - // @phpstan-ignore classConstant.deprecated - case self::DELETE: - $dql = $this->getDQLForDelete(); - break; - - // @phpstan-ignore classConstant.deprecated - case self::UPDATE: - $dql = $this->getDQLForUpdate(); - break; - - // @phpstan-ignore classConstant.deprecated - case self::SELECT: - default: - $dql = $this->getDQLForSelect(); - break; - } - - // @phpstan-ignore classConstant.deprecated - $this->state = self::STATE_CLEAN; - $this->dql = $dql; - - return $dql; + return $this->dql ??= match ($this->type) { + QueryType::Select => $this->getDQLForSelect(), + QueryType::Delete => $this->getDQLForDelete(), + QueryType::Update => $this->getDQLForUpdate(), + }; } /** @@ -384,10 +258,8 @@ public function getDQL() * $q = $qb->getQuery(); * $results = $q->execute(); * - * - * @return Query */ - public function getQuery() + public function getQuery(): Query { $parameters = clone $this->parameters; $query = $this->em->createQuery($this->getDQL()) @@ -452,11 +324,9 @@ private function findRootAlias(string $alias, string $parentAlias): string * * @deprecated Please use $qb->getRootAliases() instead. * - * @return string - * * @throws RuntimeException */ - public function getRootAlias() + public function getRootAlias(): string { $aliases = $this->getRootAliases(); @@ -482,15 +352,17 @@ public function getRootAlias() * @return string[] * @phpstan-return list */ - public function getRootAliases() + public function getRootAliases(): array { $aliases = []; foreach ($this->dqlParts['from'] as &$fromClause) { if (is_string($fromClause)) { $spacePos = strrpos($fromClause, ' '); - $from = substr($fromClause, 0, $spacePos); - $alias = substr($fromClause, $spacePos + 1); + + /** @phpstan-var class-string $from */ + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); $fromClause = new Query\Expr\From($from, $alias); } @@ -517,13 +389,13 @@ public function getRootAliases() * @return string[] * @phpstan-return list */ - public function getAllAliases() + public function getAllAliases(): array { - return array_merge($this->getRootAliases(), array_keys($this->joinRootAliases)); + return [...$this->getRootAliases(), ...array_keys($this->joinRootAliases)]; } /** - * Gets the root entities of the query. This is the entity aliases involved + * Gets the root entities of the query. This is the entity classes involved * in the construction of the query. * * @@ -535,17 +407,19 @@ public function getAllAliases() * * * @return string[] - * @phpstan-return list + * @phpstan-return list */ - public function getRootEntities() + public function getRootEntities(): array { $entities = []; foreach ($this->dqlParts['from'] as &$fromClause) { if (is_string($fromClause)) { $spacePos = strrpos($fromClause, ' '); - $from = substr($fromClause, 0, $spacePos); - $alias = substr($fromClause, $spacePos + 1); + + /** @phpstan-var class-string $from */ + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); $fromClause = new Query\Expr\From($from, $alias); } @@ -567,13 +441,12 @@ public function getRootEntities() * ->setParameter('user_id', 1); * * - * @param string|int $key The parameter position or name. - * @param mixed $value The parameter value. - * @param string|int|null $type ParameterType::* or \Doctrine\DBAL\Types\Type::* constant + * @param string|int $key The parameter position or name. + * @param ParameterType|ArrayParameterType|string|int|null $type ParameterType::*, ArrayParameterType::* or \Doctrine\DBAL\Types\Type::* constant * * @return $this */ - public function setParameter($key, $value, $type = null) + public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static { $existingParameter = $this->getParameter($key); @@ -602,27 +475,12 @@ public function setParameter($key, $value, $type = null) * ))); * * - * @param ArrayCollection|mixed[] $parameters The query parameters to set. - * @phpstan-param ArrayCollection|mixed[] $parameters + * @phpstan-param ArrayCollection $parameters * * @return $this */ - public function setParameters($parameters) + public function setParameters(ArrayCollection $parameters): static { - // BC compatibility with 2.3- - if (is_array($parameters)) { - /** @phpstan-var ArrayCollection $parameterCollection */ - $parameterCollection = new ArrayCollection(); - - foreach ($parameters as $key => $value) { - $parameter = new Parameter($key, $value); - - $parameterCollection->add($parameter); - } - - $parameters = $parameterCollection; - } - $this->parameters = $parameters; return $this; @@ -631,31 +489,22 @@ public function setParameters($parameters) /** * Gets all defined query parameters for the query being constructed. * - * @return ArrayCollection The currently defined query parameters. * @phpstan-return ArrayCollection */ - public function getParameters() + public function getParameters(): ArrayCollection { return $this->parameters; } /** * Gets a (previously set) query parameter of the query being constructed. - * - * @param string|int $key The key (index or name) of the bound parameter. - * - * @return Parameter|null The value of the bound parameter. */ - public function getParameter($key) + public function getParameter(string|int $key): Parameter|null { $key = Parameter::normalizeName($key); $filteredParameters = $this->parameters->filter( - static function (Parameter $parameter) use ($key): bool { - $parameterName = $parameter->getName(); - - return $key === $parameterName; - } + static fn (Parameter $parameter): bool => $key === $parameter->getName() ); return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null; @@ -664,11 +513,9 @@ static function (Parameter $parameter) use ($key): bool { /** * Sets the position of the first result to retrieve (the "offset"). * - * @param int|null $firstResult The first result to return. - * * @return $this */ - public function setFirstResult($firstResult) + public function setFirstResult(int|null $firstResult): static { $this->firstResult = (int) $firstResult; @@ -677,11 +524,8 @@ public function setFirstResult($firstResult) /** * Gets the position of the first result the query object was set to retrieve (the "offset"). - * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. - * - * @return int|null The position of the first result. */ - public function getFirstResult() + public function getFirstResult(): int { return $this->firstResult; } @@ -689,16 +533,10 @@ public function getFirstResult() /** * Sets the maximum number of results to retrieve (the "limit"). * - * @param int|null $maxResults The maximum number of results to retrieve. - * * @return $this */ - public function setMaxResults($maxResults) + public function setMaxResults(int|null $maxResults): static { - if ($maxResults !== null) { - $maxResults = (int) $maxResults; - } - $this->maxResults = $maxResults; return $this; @@ -707,10 +545,8 @@ public function setMaxResults($maxResults) /** * Gets the maximum number of results the query object was set to retrieve (the "limit"). * Returns NULL if {@link setMaxResults} was not applied to this query builder. - * - * @return int|null Maximum number of results. */ - public function getMaxResults() + public function getMaxResults(): int|null { return $this->maxResults; } @@ -721,19 +557,16 @@ public function getMaxResults() * The available parts are: 'select', 'from', 'join', 'set', 'where', * 'groupBy', 'having' and 'orderBy'. * - * @param string $dqlPartName The DQL part name. - * @param string|object|array $dqlPart An Expr object. - * @param bool $append Whether to append (true) or replace (false). * @phpstan-param string|object|list|array{join: array} $dqlPart * * @return $this */ - public function add($dqlPartName, $dqlPart, $append = false) + public function add(string $dqlPartName, string|object|array $dqlPart, bool $append = false): static { if ($append && ($dqlPartName === 'where' || $dqlPartName === 'having')) { throw new InvalidArgumentException( "Using \$append = true does not have an effect with 'where' or 'having' " . - 'parts. See QueryBuilder#andWhere() for an example for correct usage.' + 'parts. See QueryBuilder#andWhere() for an example for correct usage.', ); } @@ -772,8 +605,7 @@ public function add($dqlPartName, $dqlPart, $append = false) $this->dqlParts[$dqlPartName] = $isMultiple ? [$dqlPart] : $dqlPart; } - // @phpstan-ignore classConstant.deprecated - $this->state = self::STATE_DIRTY; + $this->dql = null; return $this; } @@ -789,22 +621,19 @@ public function add($dqlPartName, $dqlPart, $append = false) * ->leftJoin('u.Phonenumbers', 'p'); * * - * @param mixed $select The selection expressions. - * * @return $this */ - public function select($select = null) + public function select(mixed ...$select): static { - // @phpstan-ignore classConstant.deprecated - $this->type = self::SELECT; + self::validateVariadicParameter($select); - if (empty($select)) { + $this->type = QueryType::Select; + + if ($select === []) { return $this; } - $selects = is_array($select) ? $select : func_get_args(); - - return $this->add('select', new Expr\Select($selects), false); + return $this->add('select', new Expr\Select($select), false); } /** @@ -817,18 +646,13 @@ public function select($select = null) * ->from('User', 'u'); * * - * @param bool $flag - * * @return $this */ - public function distinct($flag = true) + public function distinct(bool $flag = true): static { - $flag = (bool) $flag; - if ($this->dqlParts['distinct'] !== $flag) { $this->dqlParts['distinct'] = $flag; - // @phpstan-ignore classConstant.deprecated - $this->state = self::STATE_DIRTY; + $this->dql = null; } return $this; @@ -845,22 +669,19 @@ public function distinct($flag = true) * ->leftJoin('u.Phonenumbers', 'p'); * * - * @param mixed $select The selection expression. - * * @return $this */ - public function addSelect($select = null) + public function addSelect(mixed ...$select): static { - // @phpstan-ignore classConstant.deprecated - $this->type = self::SELECT; + self::validateVariadicParameter($select); - if (empty($select)) { + $this->type = QueryType::Select; + + if ($select === []) { return $this; } - $selects = is_array($select) ? $select : func_get_args(); - - return $this->add('select', new Expr\Select($selects), true); + return $this->add('select', new Expr\Select($select), true); } /** @@ -874,26 +695,25 @@ public function addSelect($select = null) * ->setParameter('user_id', 1); * * - * @param string|null $delete The class/type whose instances are subject to the deletion. - * @param string|null $alias The class/type alias used in the constructed query. + * @param class-string|null $delete The class/type whose instances are subject to the deletion. + * @param string|null $alias The class/type alias used in the constructed query. * * @return $this */ - public function delete($delete = null, $alias = null) + public function delete(string|null $delete = null, string|null $alias = null): static { - // @phpstan-ignore classConstant.deprecated - $this->type = self::DELETE; + $this->type = QueryType::Delete; if (! $delete) { return $this; } if (! $alias) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9733', - 'Omitting the alias is deprecated and will throw an exception in Doctrine 3.0.' - ); + throw new InvalidArgumentException(sprintf( + '%s(): The alias for entity %s must not be omitted.', + __METHOD__, + $delete, + )); } return $this->add('from', new Expr\From($delete, $alias)); @@ -910,26 +730,25 @@ public function delete($delete = null, $alias = null) * ->where('u.id = ?2'); * * - * @param string|null $update The class/type whose instances are subject to the update. - * @param string|null $alias The class/type alias used in the constructed query. + * @param class-string|null $update The class/type whose instances are subject to the update. + * @param string|null $alias The class/type alias used in the constructed query. * * @return $this */ - public function update($update = null, $alias = null) + public function update(string|null $update = null, string|null $alias = null): static { - // @phpstan-ignore classConstant.deprecated - $this->type = self::UPDATE; + $this->type = QueryType::Update; if (! $update) { return $this; } if (! $alias) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/9733', - 'Omitting the alias is deprecated and will throw an exception in Doctrine 3.0.' - ); + throw new InvalidArgumentException(sprintf( + '%s(): The alias for entity %s must not be omitted.', + __METHOD__, + $update, + )); } return $this->add('from', new Expr\From($update, $alias)); @@ -945,13 +764,13 @@ public function update($update = null, $alias = null) * ->from('User', 'u'); * * - * @param string $from The class name. - * @param string $alias The alias of the class. - * @param string|null $indexBy The index for the from. + * @param class-string $from The class name. + * @param string $alias The alias of the class. + * @param string|null $indexBy The index for the from. * * @return $this */ - public function from($from, $alias, $indexBy = null) + public function from(string $from, string $alias, string|null $indexBy = null): static { return $this->add('from', new Expr\From($from, $alias, $indexBy), true); } @@ -972,20 +791,17 @@ public function from($from, $alias, $indexBy = null) * ->from('User', 'u', 'u.id'); * * - * @param string $alias The root alias of the class. - * @param string $indexBy The index for the from. - * * @return $this * * @throws Query\QueryException */ - public function indexBy($alias, $indexBy) + public function indexBy(string $alias, string $indexBy): static { $rootAliases = $this->getRootAliases(); if (! in_array($alias, $rootAliases, true)) { throw new Query\QueryException( - sprintf('Specified root alias %s must be set before invoking indexBy().', $alias) + sprintf('Specified root alias %s must be set before invoking indexBy().', $alias), ); } @@ -1015,17 +831,17 @@ public function indexBy($alias, $indexBy) * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); * * - * @param string $join The relationship to join. - * @param string $alias The alias of the join. - * @param string|null $conditionType The condition type constant. Either ON or WITH. - * @param string|Expr\Comparison|Expr\Composite|Expr\Func|null $condition The condition for the join. - * @param string|null $indexBy The index for the join. * @phpstan-param Expr\Join::ON|Expr\Join::WITH|null $conditionType * * @return $this */ - public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) - { + public function join( + string $join, + string $alias, + string|null $conditionType = null, + string|Expr\Composite|Expr\Comparison|Expr\Func|null $condition = null, + string|null $indexBy = null, + ): static { return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); } @@ -1042,17 +858,17 @@ public function join($join, $alias, $conditionType = null, $condition = null, $i * ->from('User', 'u') * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); * - * @param string $join The relationship to join. - * @param string $alias The alias of the join. - * @param string|null $conditionType The condition type constant. Either ON or WITH. - * @param string|Expr\Comparison|Expr\Composite|Expr\Func|null $condition The condition for the join. - * @param string|null $indexBy The index for the join. * @phpstan-param Expr\Join::ON|Expr\Join::WITH|null $conditionType * * @return $this */ - public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) - { + public function innerJoin( + string $join, + string $alias, + string|null $conditionType = null, + string|Expr\Composite|Expr\Comparison|Expr\Func|null $condition = null, + string|null $indexBy = null, + ): static { $parentAlias = substr($join, 0, (int) strpos($join, '.')); $rootAlias = $this->findRootAlias($alias, $parentAlias); @@ -1063,7 +879,7 @@ public function innerJoin($join, $alias, $conditionType = null, $condition = nul $alias, $conditionType, $condition, - $indexBy + $indexBy, ); return $this->add('join', [$rootAlias => $join], true); @@ -1083,17 +899,17 @@ public function innerJoin($join, $alias, $conditionType = null, $condition = nul * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); * * - * @param string $join The relationship to join. - * @param string $alias The alias of the join. - * @param string|null $conditionType The condition type constant. Either ON or WITH. - * @param string|Expr\Comparison|Expr\Composite|Expr\Func|null $condition The condition for the join. - * @param string|null $indexBy The index for the join. * @phpstan-param Expr\Join::ON|Expr\Join::WITH|null $conditionType * * @return $this */ - public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) - { + public function leftJoin( + string $join, + string $alias, + string|null $conditionType = null, + string|Expr\Composite|Expr\Comparison|Expr\Func|null $condition = null, + string|null $indexBy = null, + ): static { $parentAlias = substr($join, 0, (int) strpos($join, '.')); $rootAlias = $this->findRootAlias($alias, $parentAlias); @@ -1104,7 +920,7 @@ public function leftJoin($join, $alias, $conditionType = null, $condition = null $alias, $conditionType, $condition, - $indexBy + $indexBy, ); return $this->add('join', [$rootAlias => $join], true); @@ -1120,12 +936,9 @@ public function leftJoin($join, $alias, $conditionType = null, $condition = null * ->where('u.id = ?2'); * * - * @param string $key The key/field to set. - * @param mixed $value The value, expression, placeholder, etc. - * * @return $this */ - public function set($key, $value) + public function set(string $key, mixed $value): static { return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); } @@ -1152,14 +965,14 @@ public function set($key, $value) * ->where($or); * * - * @param mixed $predicates The restriction predicates. - * * @return $this */ - public function where($predicates) + public function where(mixed ...$predicates): static { - if (! (func_num_args() === 1 && $predicates instanceof Expr\Composite)) { - $predicates = new Expr\Andx(func_get_args()); + self::validateVariadicParameter($predicates); + + if (! (count($predicates) === 1 && $predicates[0] instanceof Expr\Composite)) { + $predicates = new Expr\Andx($predicates); } return $this->add('where', $predicates); @@ -1179,23 +992,22 @@ public function where($predicates) * * @see where() * - * @param mixed $where The query restrictions. - * * @return $this */ - public function andWhere() + public function andWhere(mixed ...$where): static { - $args = func_get_args(); - $where = $this->getDQLPart('where'); + self::validateVariadicParameter($where); - if ($where instanceof Expr\Andx) { - $where->addMultiple($args); + $dql = $this->getDQLPart('where'); + + if ($dql instanceof Expr\Andx) { + $dql->addMultiple($where); } else { - array_unshift($args, $where); - $where = new Expr\Andx($args); + array_unshift($where, $dql); + $dql = new Expr\Andx($where); } - return $this->add('where', $where); + return $this->add('where', $dql); } /** @@ -1212,23 +1024,22 @@ public function andWhere() * * @see where() * - * @param mixed $where The WHERE statement. - * * @return $this */ - public function orWhere() + public function orWhere(mixed ...$where): static { - $args = func_get_args(); - $where = $this->getDQLPart('where'); + self::validateVariadicParameter($where); + + $dql = $this->getDQLPart('where'); - if ($where instanceof Expr\Orx) { - $where->addMultiple($args); + if ($dql instanceof Expr\Orx) { + $dql->addMultiple($where); } else { - array_unshift($args, $where); - $where = new Expr\Orx($args); + array_unshift($where, $dql); + $dql = new Expr\Orx($where); } - return $this->add('where', $where); + return $this->add('where', $dql); } /** @@ -1242,13 +1053,13 @@ public function orWhere() * ->groupBy('u.id'); * * - * @param string $groupBy The grouping expression. - * * @return $this */ - public function groupBy($groupBy) + public function groupBy(string ...$groupBy): static { - return $this->add('groupBy', new Expr\GroupBy(func_get_args())); + self::validateVariadicParameter($groupBy); + + return $this->add('groupBy', new Expr\GroupBy($groupBy)); } /** @@ -1262,27 +1073,27 @@ public function groupBy($groupBy) * ->addGroupBy('u.createdAt'); * * - * @param string $groupBy The grouping expression. - * * @return $this */ - public function addGroupBy($groupBy) + public function addGroupBy(string ...$groupBy): static { - return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); + self::validateVariadicParameter($groupBy); + + return $this->add('groupBy', new Expr\GroupBy($groupBy), true); } /** * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any. * - * @param mixed $having The restriction over the groups. - * * @return $this */ - public function having($having) + public function having(mixed ...$having): static { - if (! (func_num_args() === 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { - $having = new Expr\Andx(func_get_args()); + self::validateVariadicParameter($having); + + if (! (count($having) === 1 && ($having[0] instanceof Expr\Andx || $having[0] instanceof Expr\Orx))) { + $having = new Expr\Andx($having); } return $this->add('having', $having); @@ -1292,58 +1103,53 @@ public function having($having) * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions. * - * @param mixed $having The restriction to append. - * * @return $this */ - public function andHaving($having) + public function andHaving(mixed ...$having): static { - $args = func_get_args(); - $having = $this->getDQLPart('having'); + self::validateVariadicParameter($having); + + $dql = $this->getDQLPart('having'); - if ($having instanceof Expr\Andx) { - $having->addMultiple($args); + if ($dql instanceof Expr\Andx) { + $dql->addMultiple($having); } else { - array_unshift($args, $having); - $having = new Expr\Andx($args); + array_unshift($having, $dql); + $dql = new Expr\Andx($having); } - return $this->add('having', $having); + return $this->add('having', $dql); } /** * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions. * - * @param mixed $having The restriction to add. - * * @return $this */ - public function orHaving($having) + public function orHaving(mixed ...$having): static { - $args = func_get_args(); - $having = $this->getDQLPart('having'); + self::validateVariadicParameter($having); - if ($having instanceof Expr\Orx) { - $having->addMultiple($args); + $dql = $this->getDQLPart('having'); + + if ($dql instanceof Expr\Orx) { + $dql->addMultiple($having); } else { - array_unshift($args, $having); - $having = new Expr\Orx($args); + array_unshift($having, $dql); + $dql = new Expr\Orx($having); } - return $this->add('having', $having); + return $this->add('having', $dql); } /** * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * - * @param string|Expr\OrderBy $sort The ordering expression. - * @param string|null $order The ordering direction. - * * @return $this */ - public function orderBy($sort, $order = null) + public function orderBy(string|Expr\OrderBy $sort, string|null $order = null): static { $orderBy = $sort instanceof Expr\OrderBy ? $sort : new Expr\OrderBy($sort, $order); @@ -1353,12 +1159,9 @@ public function orderBy($sort, $order = null) /** * Adds an ordering to the query results. * - * @param string|Expr\OrderBy $sort The ordering expression. - * @param string|null $order The ordering direction. - * * @return $this */ - public function addOrderBy($sort, $order = null) + public function addOrderBy(string|Expr\OrderBy $sort, string|null $order = null): static { $orderBy = $sort instanceof Expr\OrderBy ? $sort : new Expr\OrderBy($sort, $order); @@ -1376,7 +1179,7 @@ public function addOrderBy($sort, $order = null) * * @throws Query\QueryException */ - public function addCriteria(Criteria $criteria) + public function addCriteria(Criteria $criteria): static { $allAliases = $this->getAllAliases(); if (! isset($allAliases[0])) { @@ -1393,7 +1196,7 @@ public function addCriteria(Criteria $criteria) } } - foreach (self::getCriteriaOrderings($criteria) as $sort => $order) { + foreach ($criteria->orderings() as $sort => $order) { $hasValidAlias = false; foreach ($allAliases as $alias) { if (str_starts_with($sort . '.', $alias . '.')) { @@ -1406,7 +1209,7 @@ public function addCriteria(Criteria $criteria) $sort = $allAliases[0] . '.' . $sort; } - $this->addOrderBy($sort, $order); + $this->addOrderBy($sort, $order->value); } // Overwrite limits only if they was set in criteria @@ -1425,12 +1228,8 @@ public function addCriteria(Criteria $criteria) /** * Gets a query part by its name. - * - * @param string $queryPartName - * - * @return mixed $queryPart */ - public function getDQLPart($queryPartName) + public function getDQLPart(string $queryPartName): mixed { return $this->dqlParts[$queryPartName]; } @@ -1440,7 +1239,7 @@ public function getDQLPart($queryPartName) * * @phpstan-return array $dqlParts */ - public function getDQLParts() + public function getDQLParts(): array { return $this->dqlParts; } @@ -1520,7 +1319,7 @@ private function getReducedDQLQueryPart(string $queryPartName, array $options = * * @return $this */ - public function resetDQLParts($parts = null) + public function resetDQLParts(array|null $parts = null): static { if ($parts === null) { $parts = array_keys($this->dqlParts); @@ -1536,26 +1335,56 @@ public function resetDQLParts($parts = null) /** * Resets single DQL part. * - * @param string $part - * * @return $this */ - public function resetDQLPart($part) + public function resetDQLPart(string $part): static { $this->dqlParts[$part] = is_array($this->dqlParts[$part]) ? [] : null; - // @phpstan-ignore classConstant.deprecated - $this->state = self::STATE_DIRTY; + $this->dql = null; return $this; } + /** + * Creates a new named parameter and bind the value $value to it. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided createNamedParameter() will automatically + * create a placeholder for you. An automatic placeholder will be of the + * name ':dcValue1', ':dcValue2' etc. + * + * Example: + * + * $qb = $em->createQueryBuilder(); + * $qb + * ->select('u') + * ->from('User', 'u') + * ->where('u.username = ' . $qb->createNamedParameter('Foo', Types::STRING)) + * ->orWhere('u.username = ' . $qb->createNamedParameter('Bar', Types::STRING)) + * + * + * @param ParameterType|ArrayParameterType|string|int|null $type ParameterType::*, ArrayParameterType::* or \Doctrine\DBAL\Types\Type::* constant + * @param non-empty-string|null $placeholder The name to bind with. The string must start with a colon ':'. + * + * @return non-empty-string the placeholder name used. + */ + public function createNamedParameter(mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null, string|null $placeholder = null): string + { + if ($placeholder === null) { + $this->boundCounter++; + $placeholder = ':dcValue' . $this->boundCounter; + } + + $this->setParameter(substr($placeholder, 1), $value, $type); + + return $placeholder; + } + /** * Gets a string representation of this QueryBuilder which corresponds to * the final DQL query being constructed. - * - * @return string The string representation of this QueryBuilder. */ - public function __toString() + public function __toString(): string { return $this->getDQL(); } diff --git a/src/Repository/DefaultRepositoryFactory.php b/src/Repository/DefaultRepositoryFactory.php index fb785e3fd92..59a5afdf48a 100644 --- a/src/Repository/DefaultRepositoryFactory.php +++ b/src/Repository/DefaultRepositoryFactory.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Repository; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; @@ -20,22 +19,15 @@ final class DefaultRepositoryFactory implements RepositoryFactory * The list of EntityRepository instances. * * @var ObjectRepository[] - * @phpstan-var array + * @phpstan-var array */ - private $repositoryList = []; + private array $repositoryList = []; - /** - * {@inheritDoc} - */ - public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository + public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository { $repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_id($entityManager); - if (isset($this->repositoryList[$repositoryHash])) { - return $this->repositoryList[$repositoryHash]; - } - - return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName); + return $this->repositoryList[$repositoryHash] ??= $this->createRepository($entityManager, $entityName); } /** @@ -46,23 +38,12 @@ public function getRepository(EntityManagerInterface $entityManager, $entityName */ private function createRepository( EntityManagerInterface $entityManager, - string $entityName - ): ObjectRepository { + string $entityName, + ): EntityRepository { $metadata = $entityManager->getClassMetadata($entityName); $repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName(); - $repository = new $repositoryClassName($entityManager, $metadata); - if (! $repository instanceof EntityRepository) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9533', - 'Configuring %s as repository class is deprecated because it does not extend %s.', - $repositoryClassName, - EntityRepository::class - ); - } - - return $repository; + return new $repositoryClassName($entityManager, $metadata); } } diff --git a/src/Repository/Exception/InvalidFindByCall.php b/src/Repository/Exception/InvalidFindByCall.php index df00ecdf8d4..c5dd0155e89 100644 --- a/src/Repository/Exception/InvalidFindByCall.php +++ b/src/Repository/Exception/InvalidFindByCall.php @@ -4,18 +4,18 @@ namespace Doctrine\ORM\Repository\Exception; -use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\RepositoryException; +use LogicException; -final class InvalidFindByCall extends ORMException implements RepositoryException +final class InvalidFindByCall extends LogicException implements RepositoryException { public static function fromInverseSideUsage( string $entityName, - string $associationFieldName + string $associationFieldName, ): self { return new self( "You cannot search for the association field '" . $entityName . '#' . $associationFieldName . "', " . - 'because it is the inverse side of an association. Find methods only work on owning side associations.' + 'because it is the inverse side of an association. Find methods only work on owning side associations.', ); } } diff --git a/src/Repository/Exception/InvalidMagicMethodCall.php b/src/Repository/Exception/InvalidMagicMethodCall.php index 01e29c98c9e..1da49cb7d6b 100644 --- a/src/Repository/Exception/InvalidMagicMethodCall.php +++ b/src/Repository/Exception/InvalidMagicMethodCall.php @@ -4,19 +4,19 @@ namespace Doctrine\ORM\Repository\Exception; -use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\RepositoryException; +use LogicException; -final class InvalidMagicMethodCall extends ORMException implements RepositoryException +final class InvalidMagicMethodCall extends LogicException implements RepositoryException { public static function becauseFieldNotFoundIn( string $entityName, string $fieldName, - string $method + string $method, ): self { return new self( "Entity '" . $entityName . "' has no field '" . $fieldName . "'. " . - "You can therefore not call '" . $method . "' on the entities' repository." + "You can therefore not call '" . $method . "' on the entities' repository.", ); } diff --git a/src/Repository/RepositoryFactory.php b/src/Repository/RepositoryFactory.php index 5b00ca01d40..e066eae4e58 100644 --- a/src/Repository/RepositoryFactory.php +++ b/src/Repository/RepositoryFactory.php @@ -5,7 +5,7 @@ namespace Doctrine\ORM\Repository; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\Persistence\ObjectRepository; +use Doctrine\ORM\EntityRepository; /** * Interface for entity repository factory. @@ -18,9 +18,9 @@ interface RepositoryFactory * @param EntityManagerInterface $entityManager The EntityManager instance. * @param class-string $entityName The name of the entity. * - * @return ObjectRepository This type will change to {@see EntityRepository} in 3.0. + * @return EntityRepository * * @template T of object */ - public function getRepository(EntityManagerInterface $entityManager, $entityName); + public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository; } diff --git a/src/Tools/AttachEntityListenersListener.php b/src/Tools/AttachEntityListenersListener.php index 1193db40f38..9203cfe782c 100644 --- a/src/Tools/AttachEntityListenersListener.php +++ b/src/Tools/AttachEntityListenersListener.php @@ -5,8 +5,10 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Events; use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use function assert; use function ltrim; /** @@ -14,34 +16,40 @@ */ class AttachEntityListenersListener { - /** @var mixed[][] */ - private $entityListeners = []; + /** + * @var array> + */ + private array $entityListeners = []; /** * Adds an entity listener for a specific entity. * - * @param string $entityClass The entity to attach the listener. - * @param string $listenerClass The listener class. - * @param string|null $eventName The entity lifecycle event. - * @param string|null $listenerCallback The listener callback method or NULL to use $eventName. - * - * @return void + * @param class-string $entityClass The entity to attach the listener. + * @param class-string $listenerClass The listener class. + * @param Events::*|null $eventName The entity lifecycle event. + * @param non-falsy-string|null $listenerCallback The listener callback method or NULL to use $eventName. */ - public function addEntityListener($entityClass, $listenerClass, $eventName, $listenerCallback = null) - { + public function addEntityListener( + string $entityClass, + string $listenerClass, + string|null $eventName = null, + string|null $listenerCallback = null, + ): void { $this->entityListeners[ltrim($entityClass, '\\')][] = [ 'event' => $eventName, 'class' => $listenerClass, - 'method' => $listenerCallback ?: $eventName, + 'method' => $listenerCallback ?? $eventName, ]; } /** * Processes event and attach the entity listener. - * - * @return void */ - public function loadClassMetadata(LoadClassMetadataEventArgs $event) + public function loadClassMetadata(LoadClassMetadataEventArgs $event): void { $metadata = $event->getClassMetadata(); @@ -53,6 +61,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $event) if ($listener['event'] === null) { EntityListenerBuilder::bindEntityListener($metadata, $listener['class']); } else { + assert($listener['method'] !== null); $metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']); } } diff --git a/src/Tools/Console/Command/AbstractEntityManagerCommand.php b/src/Tools/Console/Command/AbstractEntityManagerCommand.php index 654106bc9f8..370f4fb6026 100644 --- a/src/Tools/Console/Command/AbstractEntityManagerCommand.php +++ b/src/Tools/Console/Command/AbstractEntityManagerCommand.php @@ -4,44 +4,20 @@ namespace Doctrine\ORM\Tools\Console\Command; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\Console\EntityManagerProvider; -use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use function assert; - abstract class AbstractEntityManagerCommand extends Command { - /** @var EntityManagerProvider|null */ - private $entityManagerProvider; - - public function __construct(?EntityManagerProvider $entityManagerProvider = null) + public function __construct(private readonly EntityManagerProvider $entityManagerProvider) { parent::__construct(); - - $this->entityManagerProvider = $entityManagerProvider; } final protected function getEntityManager(InputInterface $input): EntityManagerInterface { - // This is a backwards compatibility required check for commands extending Doctrine ORM commands - if (! $input->hasOption('em') || $this->entityManagerProvider === null) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8327', - 'Not passing EntityManagerProvider as a dependency to command class "%s" is deprecated', - static::class - ); - - $helper = $this->getHelper('em'); - assert($helper instanceof EntityManagerHelper); - - return $helper->getEntityManager(); - } - return $input->getOption('em') === null ? $this->entityManagerProvider->getDefaultManager() : $this->entityManagerProvider->getManager($input->getOption('em')); diff --git a/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php index 84e911f171b..b4c6efab337 100644 --- a/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -6,7 +6,6 @@ use Doctrine\ORM\Cache; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -21,10 +20,7 @@ */ class CollectionRegionCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:region:collection') ->setDescription('Clear a second-level cache collection region') @@ -57,11 +53,10 @@ protected function configure() Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, because of a limitation of its execution nature. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -87,8 +82,8 @@ private function doExecute(InputInterface $input, OutputInterface $output): int sprintf( 'Flushing cache provider configured for "%s#%s"', $ownerClass, - $assoc - ) + $assoc, + ), ); return 0; @@ -108,8 +103,8 @@ private function doExecute(InputInterface $input, OutputInterface $output): int 'Clearing second-level cache entry for collection "%s#%s" owner entity identified by "%s"', $ownerClass, $assoc, - $ownerId - ) + $ownerId, + ), ); $cache->evictCollection($ownerClass, $assoc, $ownerId); diff --git a/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php b/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php index 51d08946521..c5f2d659eaf 100644 --- a/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -6,7 +6,6 @@ use Doctrine\ORM\Cache; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -21,10 +20,7 @@ */ class EntityRegionCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:region:entity') ->setDescription('Clear a second-level cache entity region') @@ -56,11 +52,10 @@ protected function configure() Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, because of a limitation of its execution nature. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -99,8 +94,8 @@ private function doExecute(InputInterface $input, OutputInterface $output): int sprintf( 'Clearing second-level cache entry for entity "%s" identified by "%s"', $entityClass, - $entityId - ) + $entityId, + ), ); $cache->evictEntity($entityClass, $entityId); diff --git a/src/Tools/Console/Command/ClearCache/MetadataCommand.php b/src/Tools/Console/Command/ClearCache/MetadataCommand.php index 96decbc50a9..147795b229f 100644 --- a/src/Tools/Console/Command/ClearCache/MetadataCommand.php +++ b/src/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -19,10 +18,7 @@ */ class MetadataCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:metadata') ->setDescription('Clear all metadata cache of the various cache drivers') @@ -30,11 +26,10 @@ protected function configure() ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, cache entries will be flushed instead of deleted/invalidated.') ->setHelp(<<<'EOT' The %command.name% command is meant to clear the metadata cache of associated Entity Manager. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); diff --git a/src/Tools/Console/Command/ClearCache/QueryCommand.php b/src/Tools/Console/Command/ClearCache/QueryCommand.php index 06f76dcfaa4..83edd7a2619 100644 --- a/src/Tools/Console/Command/ClearCache/QueryCommand.php +++ b/src/Tools/Console/Command/ClearCache/QueryCommand.php @@ -4,12 +4,7 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache; -use Doctrine\Common\Cache\ApcCache; -use Doctrine\Common\Cache\ClearableCache; -use Doctrine\Common\Cache\FlushableCache; -use Doctrine\Common\Cache\XcacheCache; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; use LogicException; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -18,9 +13,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use function get_debug_type; -use function sprintf; - /** * Command to clear the query cache of the various cache drivers. * @@ -28,87 +20,32 @@ */ class QueryCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:query') ->setDescription('Clear all query cache of the various cache drivers') ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on') - ->addOption('flush', null, InputOption::VALUE_NONE, 'If defined, cache entries will be flushed instead of deleted/invalidated.') - ->setHelp(<<<'EOT' -The %command.name% command is meant to clear the query cache of associated Entity Manager. -It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider -instance completely. - -The execution type differ on how you execute the command. -If you want to invalidate the entries (and not delete from cache instance), this command would do the work: - -%command.name% - -Alternatively, if you want to flush the cache provider using this command: - -%command.name% --flush - -Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, -because of a limitation of its execution nature. -EOT - ); + ->setHelp('The %command.name% command is meant to clear the query cache of associated Entity Manager.'); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $cache = $em->getConfiguration()->getQueryCache(); - if ($cache instanceof ApcuAdapter) { - throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); - } - - $cacheDriver = null; if (! $cache) { - // @phpstan-ignore method.deprecated - $cacheDriver = $em->getConfiguration()->getQueryCacheImpl(); - - if (! $cacheDriver) { - throw new InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); - } - - if ($cacheDriver instanceof ApcCache) { - throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); - } - - if ($cacheDriver instanceof XcacheCache) { - throw new LogicException('Cannot clear XCache Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); - } + throw new InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); + } - if (! ($cacheDriver instanceof ClearableCache)) { - throw new LogicException(sprintf( - 'Can only clear cache when ClearableCache interface is implemented, %s does not implement.', - get_debug_type($cacheDriver) - )); - } + if ($cache instanceof ApcuAdapter) { + throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); } $ui->comment('Clearing all Query cache entries'); - $result = $cache ? $cache->clear() : $cacheDriver->deleteAll(); - $message = $result ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; - - if ($input->getOption('flush') === true && ! $cache) { - if (! ($cacheDriver instanceof FlushableCache)) { - throw new LogicException(sprintf( - 'Can only clear cache when FlushableCache interface is implemented, %s does not implement.', - get_debug_type($cacheDriver) - )); - } - - $result = $cacheDriver->flushAll(); - $message = $result ? 'Successfully flushed cache entries.' : $message; - } + $message = $cache->clear() ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; $ui->success($message); diff --git a/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php b/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php index faca872e0f1..e80fb906b62 100644 --- a/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -6,7 +6,6 @@ use Doctrine\ORM\Cache; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -21,10 +20,7 @@ */ class QueryRegionCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:region:query') ->setDescription('Clear a second-level cache query region') @@ -55,11 +51,10 @@ protected function configure() Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, because of a limitation of its execution nature. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -83,8 +78,8 @@ private function doExecute(InputInterface $input, OutputInterface $output): int $ui->comment( sprintf( 'Flushing cache provider configured for second-level cache query region named "%s"', - $name - ) + $name, + ), ); return 0; diff --git a/src/Tools/Console/Command/ClearCache/ResultCommand.php b/src/Tools/Console/Command/ClearCache/ResultCommand.php index b61a55e0fe7..4f84e0bd140 100644 --- a/src/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/src/Tools/Console/Command/ClearCache/ResultCommand.php @@ -4,25 +4,13 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache; -use Doctrine\Common\Cache\ApcCache; -use Doctrine\Common\Cache\ClearableCache; -use Doctrine\Common\Cache\FlushableCache; -use Doctrine\Common\Cache\XcacheCache; -use Doctrine\ORM\Configuration; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use InvalidArgumentException; -use LogicException; -use Symfony\Component\Cache\Adapter\ApcuAdapter; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use function get_debug_type; -use function method_exists; -use function sprintf; - /** * Command to clear the result cache of the various cache drivers. * @@ -30,10 +18,7 @@ */ class ResultCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:clear-cache:result') ->setDescription('Clear all result cache of the various cache drivers') @@ -55,54 +40,23 @@ protected function configure() Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, because of a limitation of its execution nature. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); $em = $this->getEntityManager($input); $cache = $em->getConfiguration()->getResultCache(); - // @phpstan-ignore method.deprecated - $cacheDriver = method_exists(Configuration::class, 'getResultCacheImpl') ? $em->getConfiguration()->getResultCacheImpl() : null; - if (! $cacheDriver && ! $cache) { + if (! $cache) { throw new InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); } - if ($cacheDriver instanceof ApcCache || $cache instanceof ApcuAdapter) { - throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); - } - - if ($cacheDriver instanceof XcacheCache) { - throw new LogicException('Cannot clear XCache Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.'); - } - - if (! $cache && ! ($cacheDriver instanceof ClearableCache)) { - throw new LogicException(sprintf( - 'Can only clear cache when ClearableCache interface is implemented, %s does not implement.', - get_debug_type($cacheDriver) - )); - } - $ui->comment('Clearing all Result cache entries'); - $result = $cache ? $cache->clear() : $cacheDriver->deleteAll(); - $message = $result ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; - - if ($input->getOption('flush') === true && ! $cache) { - if (! ($cacheDriver instanceof FlushableCache)) { - throw new LogicException(sprintf( - 'Can only clear cache when FlushableCache interface is implemented, %s does not implement.', - get_debug_type($cacheDriver) - )); - } - - $result = $cacheDriver->flushAll(); - $message = $result ? 'Successfully flushed cache entries.' : $message; - } + $message = $cache->clear() ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; $ui->success($message); diff --git a/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php deleted file mode 100644 index df0d42dc702..00000000000 --- a/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ /dev/null @@ -1,190 +0,0 @@ -entityGenerator === null) { - $this->entityGenerator = new EntityGenerator(); - } - - return $this->entityGenerator; - } - - /** @return void */ - public function setEntityGenerator(EntityGenerator $entityGenerator) - { - $this->entityGenerator = $entityGenerator; - } - - /** @return ClassMetadataExporter */ - public function getMetadataExporter() - { - if ($this->metadataExporter === null) { - $this->metadataExporter = new ClassMetadataExporter(); - } - - return $this->metadataExporter; - } - - /** @return void */ - public function setMetadataExporter(ClassMetadataExporter $metadataExporter) - { - $this->metadataExporter = $metadataExporter; - } - - /** @return void */ - protected function configure() - { - $this->setName('orm:convert-d1-schema') - ->setAliases(['orm:convert:d1-schema']) - ->setDescription('Converts Doctrine 1.x schema into a Doctrine 2.x schema') - ->addArgument('from-path', InputArgument::REQUIRED, 'The path of Doctrine 1.X schema information.') - ->addArgument('to-type', InputArgument::REQUIRED, 'The destination Doctrine 2.X mapping type.') - ->addArgument('dest-path', InputArgument::REQUIRED, 'The path to generate your Doctrine 2.X mapping information.') - ->addOption('from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Optional paths of Doctrine 1.X schema information.', []) - ->addOption('extend', null, InputOption::VALUE_OPTIONAL, 'Defines a base class to be extended by generated entity classes.') - ->addOption('num-spaces', null, InputOption::VALUE_OPTIONAL, 'Defines the number of indentation spaces', 4) - ->setHelp('Converts Doctrine 1.x schema into a Doctrine 2.x schema.'); - } - - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $ui = new SymfonyStyle($input, $output); - $ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); - - // Process source directories - $fromPaths = array_merge([$input->getArgument('from-path')], $input->getOption('from')); - - // Process destination directory - $destPath = realpath($input->getArgument('dest-path')); - - $toType = $input->getArgument('to-type'); - $extend = $input->getOption('extend'); - $numSpaces = (int) $input->getOption('num-spaces'); - - $this->convertDoctrine1Schema($fromPaths, $destPath, $toType, $numSpaces, $extend, $output); - - return 0; - } - - /** - * @param mixed[] $fromPaths - * @param string $destPath - * @param string $toType - * @param int $numSpaces - * @param string|null $extend - * - * @return void - * - * @throws InvalidArgumentException - */ - public function convertDoctrine1Schema(array $fromPaths, $destPath, $toType, $numSpaces, $extend, OutputInterface $output) - { - foreach ($fromPaths as &$dirName) { - $dirName = realpath($dirName); - - if (! file_exists($dirName)) { - throw new InvalidArgumentException( - sprintf("Doctrine 1.X schema directory '%s' does not exist.", $dirName) - ); - } - - if (! is_readable($dirName)) { - throw new InvalidArgumentException( - sprintf("Doctrine 1.X schema directory '%s' does not have read permissions.", $dirName) - ); - } - } - - if (! file_exists($destPath)) { - throw new InvalidArgumentException( - sprintf("Doctrine 2.X mapping destination directory '%s' does not exist.", $destPath) - ); - } - - if (! is_writable($destPath)) { - throw new InvalidArgumentException( - sprintf("Doctrine 2.X mapping destination directory '%s' does not have write permissions.", $destPath) - ); - } - - $cme = $this->getMetadataExporter(); - $exporter = $cme->getExporter($toType, $destPath); - - if ($exporter instanceof AnnotationExporter) { - $entityGenerator = $this->getEntityGenerator(); - $exporter->setEntityGenerator($entityGenerator); - - $entityGenerator->setNumSpaces($numSpaces); - - if ($extend !== null) { - $entityGenerator->setClassToExtend($extend); - } - } - - $converter = new ConvertDoctrine1Schema($fromPaths); - $metadata = $converter->getMetadata(); - - if ($metadata) { - $output->writeln(''); - - foreach ($metadata as $class) { - $output->writeln(sprintf('Processing entity "%s"', $class->name)); - } - - $exporter->setMetadata($metadata); - $exporter->export(); - - $output->writeln(PHP_EOL . sprintf( - 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', - $toType, - $destPath - )); - } else { - $output->writeln('No Metadata Classes to process.'); - } - } -} diff --git a/src/Tools/Console/Command/ConvertMappingCommand.php b/src/Tools/Console/Command/ConvertMappingCommand.php deleted file mode 100644 index f3bdea25f14..00000000000 --- a/src/Tools/Console/Command/ConvertMappingCommand.php +++ /dev/null @@ -1,193 +0,0 @@ -setName('orm:convert-mapping') - ->setAliases(['orm:convert:mapping']) - ->setDescription('Convert mapping information between supported formats') - ->addArgument('to-type', InputArgument::REQUIRED, 'The mapping type to be converted.') - ->addArgument('dest-path', InputArgument::REQUIRED, 'The path to generate your entities classes.') - ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on') - ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.') - ->addOption('force', 'f', InputOption::VALUE_NONE, 'Force to overwrite existing mapping files.') - ->addOption('from-database', null, null, 'Whether or not to convert mapping information from existing database.') - ->addOption('extend', null, InputOption::VALUE_OPTIONAL, 'Defines a base class to be extended by generated entity classes.') - ->addOption('num-spaces', null, InputOption::VALUE_OPTIONAL, 'Defines the number of indentation spaces', 4) - ->addOption('namespace', null, InputOption::VALUE_OPTIONAL, 'Defines a namespace for the generated entity classes, if converted from database.') - ->setHelp(<<<'EOT' -Convert mapping information between supported formats. - -This is an execute one-time command. It should not be necessary for -you to call this method multiple times, especially when using the --from-database -flag. - -Converting an existing database schema into mapping files only solves about 70-80% -of the necessary mapping information. Additionally the detection from an existing -database cannot detect inverse associations, inheritance types, -entities with foreign keys as primary keys and many of the -semantical operations on associations such as cascade. - -Hint: There is no need to convert YAML or XML mapping files to annotations -every time you make changes. All mapping drivers are first class citizens -in Doctrine 2 and can be used as runtime mapping for the ORM. - -Hint: If you have a database with tables that should not be managed -by the ORM, you can use a DBAL functionality to filter the tables and sequences down -on a global level: - - $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool { - if ($assetName instanceof AbstractAsset) { - $assetName = $assetName->getName(); - } - - return !str_starts_with($assetName, 'audit_'); - }); -EOT - ); - } - - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $ui = new SymfonyStyle($input, $output); - $ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); - - $em = $this->getEntityManager($input); - - if ($input->getOption('from-database') === true) { - $databaseDriver = new DatabaseDriver( - method_exists(Connection::class, 'createSchemaManager') - ? $em->getConnection()->createSchemaManager() - : $em->getConnection()->getSchemaManager() - ); - - $em->getConfiguration()->setMetadataDriverImpl( - $databaseDriver - ); - - $namespace = $input->getOption('namespace'); - if ($namespace !== null) { - $databaseDriver->setNamespace($namespace); - } - } - - $cmf = new DisconnectedClassMetadataFactory(); - $cmf->setEntityManager($em); - $metadata = $cmf->getAllMetadata(); - $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); - - // Process destination directory - $destPath = $input->getArgument('dest-path'); - if (! is_dir($destPath)) { - mkdir($destPath, 0775, true); - } - - $destPath = realpath($destPath); - - if (! file_exists($destPath)) { - throw new InvalidArgumentException( - sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) - ); - } - - if (! is_writable($destPath)) { - throw new InvalidArgumentException( - sprintf("Mapping destination directory '%s' does not have write permissions.", $destPath) - ); - } - - $toType = strtolower($input->getArgument('to-type')); - - $exporter = $this->getExporter($toType, $destPath); - $exporter->setOverwriteExistingFiles($input->getOption('force')); - - if ($exporter instanceof AnnotationExporter) { - $entityGenerator = new EntityGenerator(); - $exporter->setEntityGenerator($entityGenerator); - - $entityGenerator->setNumSpaces((int) $input->getOption('num-spaces')); - - $extend = $input->getOption('extend'); - if ($extend !== null) { - $entityGenerator->setClassToExtend($extend); - } - } - - if (empty($metadata)) { - $ui->success('No Metadata Classes to process.'); - - return 0; - } - - foreach ($metadata as $class) { - $ui->text(sprintf('Processing entity "%s"', $class->name)); - } - - $exporter->setMetadata($metadata); - $exporter->export(); - - $ui->newLine(); - $ui->text( - sprintf( - 'Exporting "%s" mapping information to "%s"', - $toType, - $destPath - ) - ); - - return 0; - } - - /** - * @param string $toType - * @param string $destPath - * - * @return AbstractExporter - */ - protected function getExporter($toType, $destPath) - { - $cme = new ClassMetadataExporter(); - - return $cme->getExporter($toType, $destPath); - } -} diff --git a/src/Tools/Console/Command/EnsureProductionSettingsCommand.php b/src/Tools/Console/Command/EnsureProductionSettingsCommand.php deleted file mode 100644 index bb5df1637a4..00000000000 --- a/src/Tools/Console/Command/EnsureProductionSettingsCommand.php +++ /dev/null @@ -1,58 +0,0 @@ -setName('orm:ensure-production-settings') - ->setDescription('Verify that Doctrine is properly configured for a production environment') - ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on') - ->addOption('complete', null, InputOption::VALUE_NONE, 'Flag to also inspect database connection existence.') - ->setHelp('Verify that Doctrine is properly configured for a production environment.'); - } - - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); - $ui->warning('This console command has been deprecated and will be removed in a future version of Doctrine ORM.'); - - $em = $this->getEntityManager($input); - - try { - $em->getConfiguration()->ensureProductionSettings(); - - if ($input->getOption('complete') === true) { - $em->getConnection()->connect(); - } - } catch (Throwable $e) { - $ui->error($e->getMessage()); - - return 1; - } - - $ui->success('Environment is correctly configured for production.'); - - return 0; - } -} diff --git a/src/Tools/Console/Command/GenerateEntitiesCommand.php b/src/Tools/Console/Command/GenerateEntitiesCommand.php deleted file mode 100644 index 7fdcb98bc8e..00000000000 --- a/src/Tools/Console/Command/GenerateEntitiesCommand.php +++ /dev/null @@ -1,133 +0,0 @@ -setName('orm:generate-entities') - ->setAliases(['orm:generate:entities']) - ->setDescription('Generate entity classes and method stubs from your mapping information') - ->addArgument('dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.') - ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on') - ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.') - ->addOption('generate-annotations', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should generate annotation metadata on entities.', false) - ->addOption('generate-methods', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should generate stub methods on entities.', true) - ->addOption('regenerate-entities', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should regenerate entity if it exists.', false) - ->addOption('update-entities', null, InputOption::VALUE_OPTIONAL, 'Flag to define if generator should only update entity if it exists.', true) - ->addOption('extend', null, InputOption::VALUE_REQUIRED, 'Defines a base class to be extended by generated entity classes.') - ->addOption('num-spaces', null, InputOption::VALUE_REQUIRED, 'Defines the number of indentation spaces', 4) - ->addOption('no-backup', null, InputOption::VALUE_NONE, 'Flag to define if generator should avoid backuping existing entity file if it exists.') - ->setHelp(<<<'EOT' -Generate entity classes and method stubs from your mapping information. - -If you use the --update-entities or --regenerate-entities flags your existing -code gets overwritten. The EntityGenerator will only append new code to your -file and will not delete the old code. However this approach may still be prone -to error and we suggest you use code repositories such as GIT or SVN to make -backups of your code. - -It makes sense to generate the entity code if you are using entities as Data -Access Objects only and don't put much additional logic on them. If you are -however putting much more logic on the entities you should refrain from using -the entity-generator and code your entities manually. - -Important: Even if you specified Inheritance options in your -XML or YAML Mapping files the generator cannot generate the base and -child classes for you correctly, because it doesn't know which -class is supposed to extend which. You have to adjust the entity -code manually for inheritance to work! -EOT - ); - } - - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); - $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); - - $em = $this->getEntityManager($input); - - $cmf = new DisconnectedClassMetadataFactory(); - $cmf->setEntityManager($em); - $metadatas = $cmf->getAllMetadata(); - $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); - - // Process destination directory - $destPath = realpath($input->getArgument('dest-path')); - - if (! file_exists($destPath)) { - throw new InvalidArgumentException( - sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) - ); - } - - if (! is_writable($destPath)) { - throw new InvalidArgumentException( - sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) - ); - } - - if (empty($metadatas)) { - $ui->success('No Metadata Classes to process.'); - - return 0; - } - - $entityGenerator = new EntityGenerator(); - - $entityGenerator->setGenerateAnnotations($input->getOption('generate-annotations')); - $entityGenerator->setGenerateStubMethods($input->getOption('generate-methods')); - $entityGenerator->setRegenerateEntityIfExists($input->getOption('regenerate-entities')); - $entityGenerator->setUpdateEntityIfExists($input->getOption('update-entities')); - $entityGenerator->setNumSpaces((int) $input->getOption('num-spaces')); - $entityGenerator->setBackupExisting(! $input->getOption('no-backup')); - - $extend = $input->getOption('extend'); - if ($extend !== null) { - $entityGenerator->setClassToExtend($extend); - } - - foreach ($metadatas as $metadata) { - $ui->text(sprintf('Processing entity "%s"', $metadata->name)); - } - - // Generating Entities - $entityGenerator->generate($metadatas, $destPath); - - // Outputting information message - $ui->newLine(); - $ui->success(sprintf('Entity classes generated to "%s"', $destPath)); - - return 0; - } -} diff --git a/src/Tools/Console/Command/GenerateProxiesCommand.php b/src/Tools/Console/Command/GenerateProxiesCommand.php index 6a602869857..5a407de6ce6 100644 --- a/src/Tools/Console/Command/GenerateProxiesCommand.php +++ b/src/Tools/Console/Command/GenerateProxiesCommand.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Tools\Console\Command; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\Console\MetadataFilter; use InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; @@ -27,10 +26,7 @@ */ class GenerateProxiesCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:generate-proxies') ->setAliases(['orm:generate:proxies']) @@ -41,7 +37,7 @@ protected function configure() ->setHelp('Generates proxy classes for entity classes.'); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -68,13 +64,13 @@ private function doExecute(InputInterface $input, OutputInterface $output): int if (! file_exists($destPath)) { throw new InvalidArgumentException( - sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()), ); } if (! is_writable($destPath)) { throw new InvalidArgumentException( - sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath) + sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath), ); } diff --git a/src/Tools/Console/Command/GenerateRepositoriesCommand.php b/src/Tools/Console/Command/GenerateRepositoriesCommand.php deleted file mode 100644 index 54379d5f6b7..00000000000 --- a/src/Tools/Console/Command/GenerateRepositoriesCommand.php +++ /dev/null @@ -1,105 +0,0 @@ -setName('orm:generate-repositories') - ->setAliases(['orm:generate:repositories']) - ->setDescription('Generate repository classes from your mapping information') - ->addArgument('dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.') - ->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on') - ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be processed.') - ->setHelp('Generate repository classes from your mapping information.'); - } - - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); - $ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.'); - - $em = $this->getEntityManager($input); - - $metadatas = $em->getMetadataFactory()->getAllMetadata(); - $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); - - $repositoryName = $em->getConfiguration()->getDefaultRepositoryClassName(); - - // Process destination directory - $destPath = realpath($input->getArgument('dest-path')); - - if (! file_exists($destPath)) { - throw new InvalidArgumentException( - sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) - ); - } - - if (! is_writable($destPath)) { - throw new InvalidArgumentException( - sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) - ); - } - - if (empty($metadatas)) { - $ui->success('No Metadata Classes to process.'); - - return 0; - } - - $numRepositories = 0; - $generator = new EntityRepositoryGenerator(); - - $generator->setDefaultRepositoryName($repositoryName); - - foreach ($metadatas as $metadata) { - if ($metadata->customRepositoryClassName) { - $ui->text(sprintf('Processing repository "%s"', $metadata->customRepositoryClassName)); - - $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); - - ++$numRepositories; - } - } - - if ($numRepositories === 0) { - $ui->text('No Repository classes were found to be processed.'); - - return 0; - } - - // Outputting information message - $ui->newLine(); - $ui->text(sprintf('Repository classes generated to "%s"', $destPath)); - - return 0; - } -} diff --git a/src/Tools/Console/Command/InfoCommand.php b/src/Tools/Console/Command/InfoCommand.php index 01f2fc2869a..deebb58a7b6 100644 --- a/src/Tools/Console/Command/InfoCommand.php +++ b/src/Tools/Console/Command/InfoCommand.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Tools\Console\Command; use Doctrine\ORM\Mapping\MappingException; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -21,10 +20,7 @@ */ class InfoCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:info') ->setDescription('Show basic information about all mapped entities') @@ -33,11 +29,10 @@ protected function configure() The %command.name% shows basic information about which entities exist and possibly if their mapping information contains errors or not. -EOT - ); +EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -52,7 +47,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int [ 'You do not have any mapped Doctrine ORM entities according to the current configuration.', 'If you have entities or mapping files you should check your mapping configuration for errors.', - ] + ], ); return 1; @@ -73,7 +68,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int sprintf('[FAIL] %s', $entityClassName), sprintf('%s', $e->getMessage()), '', - ] + ], ); $failure = true; diff --git a/src/Tools/Console/Command/MappingDescribeCommand.php b/src/Tools/Console/Command/MappingDescribeCommand.php index c6d91aadfdb..a0c69535346 100644 --- a/src/Tools/Console/Command/MappingDescribeCommand.php +++ b/src/Tools/Console/Command/MappingDescribeCommand.php @@ -5,7 +5,9 @@ namespace Doctrine\ORM\Tools\Console\Command; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\FieldMapping; use Doctrine\Persistence\Mapping\MappingException; use InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; @@ -32,6 +34,7 @@ use function sprintf; use const JSON_PRETTY_PRINT; +use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; @@ -39,9 +42,6 @@ * Show information about mapped entities. * * @link www.doctrine-project.org - * - * @phpstan-import-type AssociationMapping from ClassMetadata - * @phpstan-import-type FieldMapping from ClassMetadata */ final class MappingDescribeCommand extends AbstractEntityManagerCommand { @@ -59,8 +59,7 @@ protected function configure(): void Or: %command.full_name% MyEntity -EOT - ); +EOT); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -82,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function displayEntity( string $entityName, EntityManagerInterface $entityManager, - SymfonyStyle $ui + SymfonyStyle $ui, ): void { $metadata = $this->getClassMetadata($entityName, $entityManager); @@ -99,9 +98,6 @@ private function displayEntity( $this->formatField('Parent classes', $metadata->parentClasses), $this->formatField('Sub classes', $metadata->subClasses), $this->formatField('Embedded classes', $metadata->subClasses), - $this->formatField('Named queries', $metadata->namedQueries), - $this->formatField('Named native queries', $metadata->namedNativeQueries), - $this->formatField('SQL result set mappings', $metadata->sqlResultSetMappings), $this->formatField('Identifier', $metadata->identifier), $this->formatField('Inheritance type', $metadata->inheritanceType), $this->formatField('Discriminator column', $metadata->discriminatorColumn), @@ -123,8 +119,8 @@ private function displayEntity( [$this->formatField('Association mappings:', '')], $this->formatMappings($metadata->associationMappings), [$this->formatField('Field mappings:', '')], - $this->formatMappings($metadata->fieldMappings) - ) + $this->formatMappings($metadata->fieldMappings), + ), ); } @@ -142,7 +138,7 @@ private function getMappedEntities(EntityManagerInterface $entityManager): array if (! $entityClassNames) { throw new InvalidArgumentException( 'You do not have any mapped Doctrine ORM entities according to the current configuration. ' . - 'If you have entities or mapping files you should check your mapping configuration for errors.' + 'If you have entities or mapping files you should check your mapping configuration for errors.', ); } @@ -157,24 +153,22 @@ private function getMappedEntities(EntityManagerInterface $entityManager): array */ private function getClassMetadata( string $entityName, - EntityManagerInterface $entityManager + EntityManagerInterface $entityManager, ): ClassMetadata { try { return $entityManager->getClassMetadata($entityName); - } catch (MappingException $e) { + } catch (MappingException) { } $matches = array_filter( $this->getMappedEntities($entityManager), - static function ($mappedEntity) use ($entityName) { - return preg_match('{' . preg_quote($entityName) . '}', $mappedEntity); - } + static fn ($mappedEntity) => preg_match('{' . preg_quote($entityName) . '}', $mappedEntity) ); if (! $matches) { throw new InvalidArgumentException(sprintf( 'Could not find any mapped Entity classes matching "%s"', - $entityName + $entityName, )); } @@ -182,7 +176,7 @@ static function ($mappedEntity) use ($entityName) { throw new InvalidArgumentException(sprintf( 'Entity name "%s" is ambiguous, possible matches: "%s"', $entityName, - implode(', ', $matches) + implode(', ', $matches), )); } @@ -191,10 +185,8 @@ static function ($mappedEntity) use ($entityName) { /** * Format the given value for console output - * - * @param mixed $value */ - private function formatValue($value): string + private function formatValue(mixed $value): string { if ($value === '') { return ''; @@ -213,7 +205,10 @@ private function formatValue($value): string } if (is_array($value)) { - return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + return json_encode( + $value, + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR, + ); } if (is_object($value)) { @@ -236,7 +231,7 @@ private function formatValue($value): string * @return string[] * @phpstan-return array{0: string, 1: string} */ - private function formatField(string $label, $value): array + private function formatField(string $label, mixed $value): array { if ($value === null) { $value = 'None'; @@ -260,7 +255,7 @@ private function formatMappings(array $propertyMappings): array foreach ($propertyMappings as $propertyName => $mapping) { $output[] = $this->formatField(sprintf(' %s', $propertyName), ''); - foreach ($mapping as $field => $value) { + foreach ((array) $mapping as $field => $value) { $output[] = $this->formatField(sprintf(' %s', $field), $this->formatValue($value)); } } diff --git a/src/Tools/Console/Command/RunDqlCommand.php b/src/Tools/Console/Command/RunDqlCommand.php index 61f1eef78f1..252151ece23 100644 --- a/src/Tools/Console/Command/RunDqlCommand.php +++ b/src/Tools/Console/Command/RunDqlCommand.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Tools\Console\Command; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\Debug; use LogicException; use RuntimeException; @@ -28,10 +27,7 @@ */ class RunDqlCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:run-dql') ->setDescription('Executes arbitrary DQL directly from the command line') @@ -43,25 +39,24 @@ protected function configure() ->addOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of Entity graph.', 7) ->addOption('show-sql', null, InputOption::VALUE_NONE, 'Dump generated SQL instead of executing query') ->setHelp(<<<'EOT' -The %command.name% command executes the given DQL query and -outputs the results: + The %command.name% command executes the given DQL query and + outputs the results: -php %command.full_name% "SELECT u FROM App\Entity\User u" + php %command.full_name% "SELECT u FROM App\Entity\User u" -You can also optionally specify some additional options like what type of -hydration to use when executing the query: + You can also optionally specify some additional options like what type of + hydration to use when executing the query: -php %command.full_name% "SELECT u FROM App\Entity\User u" --hydrate=array + php %command.full_name% "SELECT u FROM App\Entity\User u" --hydrate=array -Additionally you can specify the first result and maximum amount of results to -show: + Additionally you can specify the first result and maximum amount of results to + show: -php %command.full_name% "SELECT u FROM App\Entity\User u" --first-result=0 --max-result=30 -EOT - ); + php %command.full_name% "SELECT u FROM App\Entity\User u" --first-result=0 --max-result=30 + EOT); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = new SymfonyStyle($input, $output); @@ -84,7 +79,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int if (! defined($hydrationMode)) { throw new RuntimeException(sprintf( "Hydration mode '%s' does not exist. It should be either: object. array, scalar or single-scalar.", - $hydrationModeName + $hydrationModeName, )); } diff --git a/src/Tools/Console/Command/SchemaTool/AbstractCommand.php b/src/Tools/Console/Command/SchemaTool/AbstractCommand.php index cabf90759a7..b1e44606ec9 100644 --- a/src/Tools/Console/Command/SchemaTool/AbstractCommand.php +++ b/src/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\SchemaTool; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -18,16 +17,10 @@ */ abstract class AbstractCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; + /** @param mixed[] $metadatas */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int; - /** - * @param mixed[] $metadatas - * - * @return int|null Null or 0 if everything went fine, or an error code. - */ - abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui); - - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = new SymfonyStyle($input, $output); diff --git a/src/Tools/Console/Command/SchemaTool/CreateCommand.php b/src/Tools/Console/Command/SchemaTool/CreateCommand.php index 2438d4109f0..69e20c60e95 100644 --- a/src/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/src/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -19,8 +19,7 @@ */ class CreateCommand extends AbstractCommand { - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:schema-tool:create') ->setDescription('Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output') @@ -40,14 +39,13 @@ protected function configure() return !str_starts_with($assetName, 'audit_'); }); -EOT - ); +EOT); } /** * {@inheritDoc} */ - protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui) + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int { $dumpSql = $input->getOption('dump-sql') === true; diff --git a/src/Tools/Console/Command/SchemaTool/DropCommand.php b/src/Tools/Console/Command/SchemaTool/DropCommand.php index c312675392d..5c8253bed30 100644 --- a/src/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/src/Tools/Console/Command/SchemaTool/DropCommand.php @@ -20,8 +20,7 @@ */ class DropCommand extends AbstractCommand { - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:schema-tool:drop') ->setDescription('Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output') @@ -44,14 +43,13 @@ protected function configure() return !str_starts_with($assetName, 'audit_'); }); -EOT - ); +EOT); } /** * {@inheritDoc} */ - protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui) + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int { $isFullDatabaseDrop = $input->getOption('full-database'); $dumpSql = $input->getOption('dump-sql') === true; @@ -110,7 +108,7 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ '', sprintf(' %s --force to execute the command', $this->getName()), sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName()), - ] + ], ); return 1; diff --git a/src/Tools/Console/Command/SchemaTool/UpdateCommand.php b/src/Tools/Console/Command/SchemaTool/UpdateCommand.php index 3d2631ec0f7..f35fc384536 100644 --- a/src/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/src/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -4,6 +4,7 @@ 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; @@ -21,16 +22,14 @@ */ class UpdateCommand extends AbstractCommand { - /** @var string */ - protected $name = 'orm:schema-tool:update'; + protected string $name = 'orm:schema-tool:update'; - /** @return void */ - protected function configure() + protected function configure(): void { $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, 'If defined, all assets of the database which are not relevant to the current metadata will be dropped.') + ->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' @@ -51,11 +50,10 @@ protected function configure() %command.name% --dump-sql --force -Finally, be aware that if the --complete option is passed, this -task will drop all database assets (e.g. tables, etc) that are *not* described -by the current metadata. In other words, without this option, this task leaves -untouched any "extra" tables that exist in the database, but which aren't -described by any metadata. Not passing that option is deprecated. +Finally, be aware that this task will drop all database assets (e.g. tables, +etc) that are *not* described by the current metadata. In other words, without +this option, this task leaves untouched any "extra" tables that exist in the +database, but which aren't described by any metadata. Hint: If you have a database with tables that should not be managed by the ORM, you can use a DBAL functionality to filter the tables and sequences down @@ -68,28 +66,26 @@ protected function configure() return !str_starts_with($assetName, 'audit_'); }); -EOT - ); +EOT); } /** * {@inheritDoc} */ - protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui) + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int { $notificationUi = $ui->getErrorStyle(); - // Defining if update is complete or not (--complete not defined means $saveMode = true) - $saveMode = ! $input->getOption('complete'); - - if ($saveMode) { - $notificationUi->warning(sprintf( - 'Not passing the "--complete" option to "%s" is deprecated and will not be supported when using doctrine/dbal 4', - $this->getName() ?? $this->name - )); + 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, $saveMode); + $sqls = $schemaTool->getUpdateSchemaSql($metadatas); if (empty($sqls)) { $notificationUi->success('Nothing to update - your database is already in sync with the current entity metadata.'); @@ -114,7 +110,7 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ $notificationUi->text('Updating database schema...'); $notificationUi->newLine(); - $schemaTool->updateSchema($metadatas, $saveMode); + $schemaTool->updateSchema($metadatas); $pluralization = count($sqls) === 1 ? 'query was' : 'queries were'; @@ -132,7 +128,7 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ '', 'Use the incremental update to detect changes during development and use', 'the SQL DDL provided to manually update your database in production.', - ] + ], ); $notificationUi->text( @@ -143,7 +139,7 @@ protected function executeSchemaCommand(InputInterface $input, OutputInterface $ '', sprintf(' %s --force to execute the command', $this->getName()), sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName()), - ] + ], ); return 1; diff --git a/src/Tools/Console/Command/ValidateSchemaCommand.php b/src/Tools/Console/Command/ValidateSchemaCommand.php index 3807a81d58d..cffb4ce4358 100644 --- a/src/Tools/Console/Command/ValidateSchemaCommand.php +++ b/src/Tools/Console/Command/ValidateSchemaCommand.php @@ -4,7 +4,6 @@ namespace Doctrine\ORM\Tools\Console\Command; -use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\SchemaValidator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -21,10 +20,7 @@ */ class ValidateSchemaCommand extends AbstractEntityManagerCommand { - use CommandCompatibility; - - /** @return void */ - protected function configure() + protected function configure(): void { $this->setName('orm:validate-schema') ->setDescription('Validate the mapping files') @@ -35,7 +31,7 @@ protected function configure() ->setHelp('Validate that the mapping files are correct and in sync with the database.'); } - private function doExecute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $ui = (new SymfonyStyle($input, $output))->getErrorStyle(); @@ -54,8 +50,8 @@ private function doExecute(InputInterface $input, OutputInterface $output): int $ui->text( sprintf( '[FAIL] The entity-class %s mapping is invalid:', - $className - ) + $className, + ), ); $ui->listing($errorMessages); diff --git a/src/Tools/Console/CommandCompatibility.php b/src/Tools/Console/CommandCompatibility.php deleted file mode 100644 index 4e16698f5d2..00000000000 --- a/src/Tools/Console/CommandCompatibility.php +++ /dev/null @@ -1,35 +0,0 @@ -hasReturnType()) { - /** @internal */ - trait CommandCompatibility - { - protected function execute(InputInterface $input, OutputInterface $output): int - { - return $this->doExecute($input, $output); - } - } -} else { - /** @internal */ - trait CommandCompatibility - { - /** - * {@inheritDoc} - * - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - return $this->doExecute($input, $output); - } - } -} diff --git a/src/Tools/Console/ConsoleRunner.php b/src/Tools/Console/ConsoleRunner.php index f45dc2163c3..0a00483244e 100644 --- a/src/Tools/Console/ConsoleRunner.php +++ b/src/Tools/Console/ConsoleRunner.php @@ -6,14 +6,10 @@ use Composer\InstalledVersions; use Doctrine\DBAL\Tools\Console as DBALConsole; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\Console\EntityManagerProvider\ConnectionFromManagerProvider; -use Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider; -use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; use OutOfBoundsException; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command as SymfonyCommand; -use Symfony\Component\Console\Helper\HelperSet; use function assert; use function class_exists; @@ -23,31 +19,14 @@ */ final class ConsoleRunner { - /** - * Create a Symfony Console HelperSet - * - * @deprecated This method will be removed in ORM 3.0 without replacement. - */ - public static function createHelperSet(EntityManagerInterface $entityManager): HelperSet - { - $helpers = ['em' => new EntityManagerHelper($entityManager)]; - - if (class_exists(DBALConsole\Helper\ConnectionHelper::class)) { - $helpers['db'] = new DBALConsole\Helper\ConnectionHelper($entityManager->getConnection()); - } - - return new HelperSet($helpers); - } - /** * Runs console with the given helper set. * - * @param HelperSet|EntityManagerProvider $helperSetOrProvider - * @param SymfonyCommand[] $commands + * @param SymfonyCommand[] $commands */ - public static function run($helperSetOrProvider, array $commands = []): void + public static function run(EntityManagerProvider $entityManagerProvider, array $commands = []): void { - $cli = self::createApplication($helperSetOrProvider, $commands); + $cli = self::createApplication($entityManagerProvider, $commands); $cli->run(); } @@ -55,50 +34,37 @@ public static function run($helperSetOrProvider, array $commands = []): void * Creates a console application with the given helperset and * optional commands. * - * @param HelperSet|EntityManagerProvider $helperSetOrProvider - * @param SymfonyCommand[] $commands + * @param SymfonyCommand[] $commands * * @throws OutOfBoundsException */ - public static function createApplication($helperSetOrProvider, array $commands = []): Application - { + public static function createApplication( + EntityManagerProvider $entityManagerProvider, + array $commands = [], + ): Application { $version = InstalledVersions::getVersion('doctrine/orm'); assert($version !== null); $cli = new Application('Doctrine Command Line Interface', $version); $cli->setCatchExceptions(true); - if ($helperSetOrProvider instanceof HelperSet) { - $cli->setHelperSet($helperSetOrProvider); - - // @phpstan-ignore new.deprecated - $helperSetOrProvider = new HelperSetManagerProvider($helperSetOrProvider); - } - - self::addCommands($cli, $helperSetOrProvider); + self::addCommands($cli, $entityManagerProvider); $cli->addCommands($commands); return $cli; } - public static function addCommands(Application $cli, ?EntityManagerProvider $entityManagerProvider = null): void + public static function addCommands(Application $cli, EntityManagerProvider $entityManagerProvider): void { - if ($entityManagerProvider === null) { - // @phpstan-ignore new.deprecated - $entityManagerProvider = new HelperSetManagerProvider($cli->getHelperSet()); - } - $connectionProvider = new ConnectionFromManagerProvider($entityManagerProvider); - if (class_exists(DBALConsole\Command\ImportCommand::class)) { - $cli->add(new DBALConsole\Command\ImportCommand()); + if (class_exists(DBALConsole\Command\ReservedWordsCommand::class)) { + $cli->add(new DBALConsole\Command\ReservedWordsCommand($connectionProvider)); } $cli->addCommands( [ // DBAL Commands - // @phpstan-ignore new.deprecated - new DBALConsole\Command\ReservedWordsCommand($connectionProvider), new DBALConsole\Command\RunSqlCommand($connectionProvider), // ORM Commands @@ -111,44 +77,12 @@ public static function addCommands(Application $cli, ?EntityManagerProvider $ent new Command\SchemaTool\CreateCommand($entityManagerProvider), new Command\SchemaTool\UpdateCommand($entityManagerProvider), new Command\SchemaTool\DropCommand($entityManagerProvider), - // @phpstan-ignore new.deprecated - new Command\EnsureProductionSettingsCommand($entityManagerProvider), - // @phpstan-ignore new.deprecated - new Command\ConvertDoctrine1SchemaCommand(), - // @phpstan-ignore new.deprecated - new Command\GenerateRepositoriesCommand($entityManagerProvider), - // @phpstan-ignore new.deprecated - new Command\GenerateEntitiesCommand($entityManagerProvider), new Command\GenerateProxiesCommand($entityManagerProvider), - // @phpstan-ignore new.deprecated - new Command\ConvertMappingCommand($entityManagerProvider), new Command\RunDqlCommand($entityManagerProvider), new Command\ValidateSchemaCommand($entityManagerProvider), new Command\InfoCommand($entityManagerProvider), new Command\MappingDescribeCommand($entityManagerProvider), - ] + ], ); } - - /** @deprecated This method will be removed in ORM 3.0 without replacement. */ - public static function printCliConfigTemplate(): void - { - echo <<<'HELP' -You are missing a "cli-config.php" or "config/cli-config.php" file in your -project, which is required to get the Doctrine Console working. You can use the -following sample as a template: - -entityManagerProvider = $entityManagerProvider; } public function getDefaultConnection(): Connection diff --git a/src/Tools/Console/EntityManagerProvider/HelperSetManagerProvider.php b/src/Tools/Console/EntityManagerProvider/HelperSetManagerProvider.php deleted file mode 100644 index 022f29024a4..00000000000 --- a/src/Tools/Console/EntityManagerProvider/HelperSetManagerProvider.php +++ /dev/null @@ -1,49 +0,0 @@ -helperSet = $helperSet; - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8327', - 'Use of a HelperSet and the HelperSetManagerProvider is deprecated and will be removed in ORM 3.0' - ); - } - - public function getManager(string $name): EntityManagerInterface - { - if ($name !== 'default') { - throw UnknownManagerException::unknownManager($name, ['default']); - } - - return $this->getDefaultManager(); - } - - public function getDefaultManager(): EntityManagerInterface - { - $helper = $this->helperSet->get('entityManager'); - - assert($helper instanceof EntityManagerHelper); - - return $helper->getEntityManager(); - } -} diff --git a/src/Tools/Console/EntityManagerProvider/SingleManagerProvider.php b/src/Tools/Console/EntityManagerProvider/SingleManagerProvider.php index 80da77f8664..ebe60c913cd 100644 --- a/src/Tools/Console/EntityManagerProvider/SingleManagerProvider.php +++ b/src/Tools/Console/EntityManagerProvider/SingleManagerProvider.php @@ -9,16 +9,10 @@ final class SingleManagerProvider implements EntityManagerProvider { - /** @var EntityManagerInterface */ - private $entityManager; - - /** @var string */ - private $defaultManagerName; - - public function __construct(EntityManagerInterface $entityManager, string $defaultManagerName = 'default') - { - $this->entityManager = $entityManager; - $this->defaultManagerName = $defaultManagerName; + public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly string $defaultManagerName = 'default', + ) { } public function getDefaultManager(): EntityManagerInterface diff --git a/src/Tools/Console/EntityManagerProvider/UnknownManagerException.php b/src/Tools/Console/EntityManagerProvider/UnknownManagerException.php index 54d4086dbf6..46e842ce976 100644 --- a/src/Tools/Console/EntityManagerProvider/UnknownManagerException.php +++ b/src/Tools/Console/EntityManagerProvider/UnknownManagerException.php @@ -17,7 +17,7 @@ public static function unknownManager(string $unknownManager, array $knownManage return new self(sprintf( 'Requested unknown entity manager: %s, known managers: %s', $unknownManager, - implode(', ', $knownManagers) + implode(', ', $knownManagers), )); } } diff --git a/src/Tools/Console/Helper/EntityManagerHelper.php b/src/Tools/Console/Helper/EntityManagerHelper.php deleted file mode 100644 index cf4f9d2aec5..00000000000 --- a/src/Tools/Console/Helper/EntityManagerHelper.php +++ /dev/null @@ -1,75 +0,0 @@ -hasReturnType()) { - /** @internal */ - trait EntityManagerHelperCompatibility - { - public function getName(): string - { - return 'entityManager'; - } - } -} else { - /** @internal */ - trait EntityManagerHelperCompatibility - { - /** - * {@inheritDoc} - * - * @return string - */ - public function getName() - { - return 'entityManager'; - } - } -} - -/** - * Doctrine CLI Connection Helper. - * - * @deprecated This class will be removed in ORM 3.0 without replacement. - */ -class EntityManagerHelper extends Helper -{ - use EntityManagerHelperCompatibility; - - /** - * Doctrine ORM EntityManagerInterface. - * - * @var EntityManagerInterface - */ - protected $_em; - - public function __construct(EntityManagerInterface $em) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9641', - 'The %s class is deprecated and will be removed in ORM 3.0', - self::class - ); - - $this->_em = $em; - } - - /** - * Retrieves Doctrine ORM EntityManager. - * - * @return EntityManagerInterface - */ - public function getEntityManager() - { - return $this->_em; - } -} diff --git a/src/Tools/Console/MetadataFilter.php b/src/Tools/Console/MetadataFilter.php index 18bdf34437e..05e248cea67 100644 --- a/src/Tools/Console/MetadataFilter.php +++ b/src/Tools/Console/MetadataFilter.php @@ -8,7 +8,6 @@ use Countable; use Doctrine\Persistence\Mapping\ClassMetadata; use FilterIterator; -use ReturnTypeWillChange; use RuntimeException; use function assert; @@ -25,7 +24,7 @@ class MetadataFilter extends FilterIterator implements Countable { /** @var mixed[] */ - private $filter = []; + private array $filter = []; /** * Filter Metadatas by one or more filter options. @@ -35,7 +34,7 @@ class MetadataFilter extends FilterIterator implements Countable * * @return ClassMetadata[] */ - public static function filter(array $metadatas, $filter) + public static function filter(array $metadatas, array|string $filter): array { $metadatas = new MetadataFilter(new ArrayIterator($metadatas), $filter); @@ -43,16 +42,14 @@ public static function filter(array $metadatas, $filter) } /** @param mixed[]|string $filter */ - public function __construct(ArrayIterator $metadata, $filter) + public function __construct(ArrayIterator $metadata, array|string $filter) { $this->filter = (array) $filter; parent::__construct($metadata); } - /** @return bool */ - #[ReturnTypeWillChange] - public function accept() + public function accept(): bool { if (count($this->filter) === 0) { return true; @@ -66,7 +63,7 @@ public function accept() if ($pregResult === false) { throw new RuntimeException( - sprintf("Error while evaluating regex '/%s/'.", $filter) + sprintf("Error while evaluating regex '/%s/'.", $filter), ); } @@ -79,8 +76,7 @@ public function accept() } /** @return ArrayIterator */ - #[ReturnTypeWillChange] - public function getInnerIterator() + public function getInnerIterator(): ArrayIterator { $innerIterator = parent::getInnerIterator(); @@ -89,9 +85,7 @@ public function getInnerIterator() return $innerIterator; } - /** @return int */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { return count($this->getInnerIterator()); } diff --git a/src/Tools/ConvertDoctrine1Schema.php b/src/Tools/ConvertDoctrine1Schema.php deleted file mode 100644 index 1145608762c..00000000000 --- a/src/Tools/ConvertDoctrine1Schema.php +++ /dev/null @@ -1,336 +0,0 @@ - */ - private $legacyTypeMap = [ - // TODO: This list may need to be updated - 'clob' => 'text', - 'timestamp' => 'datetime', - 'enum' => 'string', - ]; - - /** - * Constructor passes the directory or array of directories - * to convert the Doctrine 1 schema files from. - * - * @param string[]|string $from - * @phpstan-param list|string $from - */ - public function __construct($from) - { - $this->from = (array) $from; - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8458', - '%s is deprecated with no replacement', - self::class - ); - } - - /** - * Gets an array of ClassMetadataInfo instances from the passed - * Doctrine 1 schema. - * - * @return ClassMetadataInfo[] An array of ClassMetadataInfo instances - * @phpstan-return list - */ - public function getMetadata() - { - $schema = []; - foreach ($this->from as $path) { - if (is_dir($path)) { - $files = glob($path . '/*.yml'); - foreach ($files as $file) { - $schema = array_merge($schema, (array) Yaml::parse(file_get_contents($file))); - } - } else { - $schema = array_merge($schema, (array) Yaml::parse(file_get_contents($path))); - } - } - - $metadatas = []; - foreach ($schema as $className => $mappingInformation) { - $metadatas[] = $this->convertToClassMetadataInfo($className, $mappingInformation); - } - - return $metadatas; - } - - /** - * @param class-string $className - * @param mixed[] $mappingInformation - */ - private function convertToClassMetadataInfo( - string $className, - array $mappingInformation - ): ClassMetadataInfo { - $metadata = new ClassMetadataInfo($className); - - $this->convertTableName($className, $mappingInformation, $metadata); - $this->convertColumns($className, $mappingInformation, $metadata); - $this->convertIndexes($className, $mappingInformation, $metadata); - $this->convertRelations($className, $mappingInformation, $metadata); - - return $metadata; - } - - /** @param mixed[] $model */ - private function convertTableName(string $className, array $model, ClassMetadataInfo $metadata): void - { - if (isset($model['tableName']) && $model['tableName']) { - $e = explode('.', $model['tableName']); - - if (count($e) > 1) { - $metadata->table['schema'] = $e[0]; - $metadata->table['name'] = $e[1]; - } else { - $metadata->table['name'] = $e[0]; - } - } - } - - /** @param mixed[] $model */ - private function convertColumns( - string $className, - array $model, - ClassMetadataInfo $metadata - ): void { - $id = false; - - if (isset($model['columns']) && $model['columns']) { - foreach ($model['columns'] as $name => $column) { - $fieldMapping = $this->convertColumn($className, $name, $column, $metadata); - - if (isset($fieldMapping['id']) && $fieldMapping['id']) { - $id = true; - } - } - } - - if (! $id) { - $fieldMapping = [ - 'fieldName' => 'id', - 'columnName' => 'id', - 'type' => 'integer', - 'id' => true, - ]; - $metadata->mapField($fieldMapping); - $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); - } - } - - /** - * @param string|mixed[] $column - * - * @return mixed[] - * - * @throws ToolsException - */ - private function convertColumn( - string $className, - string $name, - $column, - ClassMetadataInfo $metadata - ): array { - if (is_string($column)) { - $string = $column; - $column = []; - $column['type'] = $string; - } - - if (! isset($column['name'])) { - $column['name'] = $name; - } - - // check if a column alias was used (column_name as field_name) - if (preg_match('/(\w+)\sas\s(\w+)/i', $column['name'], $matches)) { - $name = $matches[1]; - $column['name'] = $name; - $column['alias'] = $matches[2]; - } - - if (preg_match('/([a-zA-Z]+)\(([0-9]+)\)/', $column['type'], $matches)) { - $column['type'] = $matches[1]; - $column['length'] = $matches[2]; - } - - $column['type'] = strtolower($column['type']); - // check if legacy column type (1.x) needs to be mapped to a 2.0 one - if (isset($this->legacyTypeMap[$column['type']])) { - $column['type'] = $this->legacyTypeMap[$column['type']]; - } - - if (! Type::hasType($column['type'])) { - throw ToolsException::couldNotMapDoctrine1Type($column['type']); - } - - $fieldMapping = [ - 'nullable' => ! ($column['notnull'] ?? true), // Doctrine 1 columns are nullable by default - ]; - - if (isset($column['primary'])) { - $fieldMapping['id'] = true; - } - - $fieldMapping['fieldName'] = $column['alias'] ?? $name; - $fieldMapping['columnName'] = $column['name']; - $fieldMapping['type'] = $column['type']; - - if (isset($column['length'])) { - $fieldMapping['length'] = $column['length']; - } - - $allowed = ['precision', 'scale', 'unique', 'options', 'version']; - - foreach ($column as $key => $value) { - if (in_array($key, $allowed, true)) { - $fieldMapping[$key] = $value; - } - } - - $metadata->mapField($fieldMapping); - - if (isset($column['autoincrement'])) { - $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); - } elseif (isset($column['sequence'])) { - $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); - - $definition = [ - 'sequenceName' => (string) (is_array($column['sequence']) ? $column['sequence']['name'] : $column['sequence']), - ]; - - if (isset($column['sequence']['size'])) { - $definition['allocationSize'] = (int) $column['sequence']['size']; - } - - if (isset($column['sequence']['value'])) { - $definition['initialValue'] = (int) $column['sequence']['value']; - } - - $metadata->setSequenceGeneratorDefinition($definition); - } - - return $fieldMapping; - } - - /** @param mixed[] $model */ - private function convertIndexes( - string $className, - array $model, - ClassMetadataInfo $metadata - ): void { - if (empty($model['indexes'])) { - return; - } - - foreach ($model['indexes'] as $name => $index) { - $type = isset($index['type']) && $index['type'] === 'unique' - ? 'uniqueConstraints' : 'indexes'; - - $metadata->table[$type][$name] = [ - 'columns' => $index['fields'], - ]; - } - } - - /** @param mixed[] $model */ - private function convertRelations( - string $className, - array $model, - ClassMetadataInfo $metadata - ): void { - if (empty($model['relations'])) { - return; - } - - $inflector = InflectorFactory::create()->build(); - - foreach ($model['relations'] as $name => $relation) { - if (! isset($relation['alias'])) { - $relation['alias'] = $name; - } - - if (! isset($relation['class'])) { - $relation['class'] = $name; - } - - if (! isset($relation['local'])) { - $relation['local'] = $inflector->tableize($relation['class']); - } - - if (! isset($relation['foreign'])) { - $relation['foreign'] = 'id'; - } - - if (! isset($relation['foreignAlias'])) { - $relation['foreignAlias'] = $className; - } - - if (isset($relation['refClass'])) { - $type = 'many'; - $foreignType = 'many'; - $joinColumns = []; - } else { - $type = $relation['type'] ?? 'one'; - $foreignType = $relation['foreignType'] ?? 'many'; - $joinColumns = [ - [ - 'name' => $relation['local'], - 'referencedColumnName' => $relation['foreign'], - 'onDelete' => $relation['onDelete'] ?? null, - ], - ]; - } - - if ($type === 'one' && $foreignType === 'one') { - $method = 'mapOneToOne'; - } elseif ($type === 'many' && $foreignType === 'many') { - $method = 'mapManyToMany'; - } else { - $method = 'mapOneToMany'; - } - - $associationMapping = []; - $associationMapping['fieldName'] = $relation['alias']; - $associationMapping['targetEntity'] = $relation['class']; - $associationMapping['mappedBy'] = $relation['foreignAlias']; - $associationMapping['joinColumns'] = $joinColumns; - - $metadata->$method($associationMapping); - } - } -} diff --git a/src/Tools/Debug.php b/src/Tools/Debug.php index 153abac4a44..8521e53decc 100644 --- a/src/Tools/Debug.php +++ b/src/Tools/Debug.php @@ -17,7 +17,6 @@ use function end; use function explode; use function extension_loaded; -use function get_class; use function html_entity_decode; use function ini_get; use function ini_set; @@ -53,7 +52,7 @@ private function __construct() * @param mixed $var The variable to dump. * @param int $maxDepth The maximum nesting level for object properties. */ - public static function dump($var, int $maxDepth = 2): string + public static function dump(mixed $var, int $maxDepth = 2): string { $html = ini_get('html_errors'); @@ -88,19 +87,14 @@ public static function dump($var, int $maxDepth = 2): string return $dumpText; } - /** - * @param mixed $var - * - * @return mixed - */ - public static function export($var, int $maxDepth) + public static function export(mixed $var, int $maxDepth): mixed { if ($var instanceof Collection) { $var = $var->toArray(); } if (! $maxDepth) { - return is_object($var) ? get_class($var) + return is_object($var) ? $var::class : (is_array($var) ? 'Array(' . count($var) . ')' : $var); } @@ -120,7 +114,7 @@ public static function export($var, int $maxDepth) $return = new stdClass(); if ($var instanceof DateTimeInterface) { - $return->__CLASS__ = get_class($var); + $return->__CLASS__ = $var::class; $return->date = $var->format('c'); $return->timezone = $var->getTimezone()->getName(); @@ -144,12 +138,8 @@ public static function export($var, int $maxDepth) /** * Fill the $return variable with class attributes * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} - * - * @param object $var - * - * @return mixed */ - private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth) + private static function fillReturnWithClassAttributes(object $var, stdClass $return, int $maxDepth): stdClass { $clone = (array) $var; diff --git a/src/Tools/DebugUnitOfWorkListener.php b/src/Tools/DebugUnitOfWorkListener.php index 2b01d80af50..71059f7d271 100644 --- a/src/Tools/DebugUnitOfWorkListener.php +++ b/src/Tools/DebugUnitOfWorkListener.php @@ -6,7 +6,6 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\OnFlushEventArgs; -use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\UnitOfWork; use ReflectionObject; @@ -25,38 +24,26 @@ */ class DebugUnitOfWorkListener { - /** @var string */ - private $file; - - /** @var string */ - private $context; - /** * Pass a stream and context information for the debugging session. * * The stream can be php://output to print to the screen. - * - * @param string $file - * @param string $context */ - public function __construct($file = 'php://output', $context = '') - { - $this->file = $file; - $this->context = $context; + public function __construct( + private readonly string $file = 'php://output', + private readonly string $context = '', + ) { } - /** @return void */ - public function onFlush(OnFlushEventArgs $args) + public function onFlush(OnFlushEventArgs $args): void { $this->dumpIdentityMap($args->getObjectManager()); } /** * Dumps the contents of the identity map into a stream. - * - * @return void */ - public function dumpIdentityMap(EntityManagerInterface $em) + public function dumpIdentityMap(EntityManagerInterface $em): void { $uow = $em->getUnitOfWork(); $identityMap = $uow->getIdentityMap(); @@ -82,7 +69,7 @@ public function dumpIdentityMap(EntityManagerInterface $em) fwrite($fh, ' ' . $field . ' '); $value = $cm->getFieldValue($entity, $field); - if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc->isToOne()) { if ($value === null) { fwrite($fh, " NULL\n"); } else { @@ -116,8 +103,7 @@ public function dumpIdentityMap(EntityManagerInterface $em) fclose($fh); } - /** @param mixed $var */ - private function getType($var): string + private function getType(mixed $var): string { if (is_object($var)) { $refl = new ReflectionObject($var); @@ -128,8 +114,7 @@ private function getType($var): string return gettype($var); } - /** @param object $entity */ - private function getIdString($entity, UnitOfWork $uow): string + private function getIdString(object $entity, UnitOfWork $uow): string { if ($uow->isInIdentityMap($entity)) { $ids = $uow->getEntityIdentifier($entity); diff --git a/src/Tools/DisconnectedClassMetadataFactory.php b/src/Tools/DisconnectedClassMetadataFactory.php deleted file mode 100644 index 19865c2ee3d..00000000000 --- a/src/Tools/DisconnectedClassMetadataFactory.php +++ /dev/null @@ -1,27 +0,0 @@ -getClassMetadataFactory()->getAllMetadata(); - * - * $generator = new \Doctrine\ORM\Tools\EntityGenerator(); - * $generator->setGenerateAnnotations(true); - * $generator->setGenerateStubMethods(true); - * $generator->setRegenerateEntityIfExists(false); - * $generator->setUpdateEntityIfExists(true); - * $generator->generate($classes, '/path/to/generate/entities'); - * - * @deprecated 2.7 This class is being removed from the ORM and won't have any replacement - * - * @link www.doctrine-project.org - */ -class EntityGenerator -{ - /** - * Specifies class fields should be protected. - */ - public const FIELD_VISIBLE_PROTECTED = 'protected'; - - /** - * Specifies class fields should be private. - */ - public const FIELD_VISIBLE_PRIVATE = 'private'; - - /** @var bool */ - protected $backupExisting = true; - - /** - * The extension to use for written php files. - * - * @var string - */ - protected $extension = '.php'; - - /** - * Whether or not the current ClassMetadataInfo instance is new or old. - * - * @var bool - */ - protected $isNew = true; - - /** @var mixed[] */ - protected $staticReflection = []; - - /** - * Number of spaces to use for indention in generated code. - * - * @var int - */ - protected $numSpaces = 4; - - /** - * The actual spaces to use for indention. - * - * @var string - */ - protected $spaces = ' '; - - /** - * The class all generated entities should extend. - * - * @var string - */ - protected $classToExtend; - - /** - * Whether or not to generation annotations. - * - * @var bool - */ - protected $generateAnnotations = false; - - /** @var string */ - protected $annotationsPrefix = ''; - - /** - * Whether or not to generate sub methods. - * - * @var bool - */ - protected $generateEntityStubMethods = false; - - /** - * Whether or not to update the entity class if it exists already. - * - * @var bool - */ - protected $updateEntityIfExists = false; - - /** - * Whether or not to re-generate entity class if it exists already. - * - * @var bool - */ - protected $regenerateEntityIfExists = false; - - /** - * Visibility of the field - * - * @var string - */ - protected $fieldVisibility = 'private'; - - /** - * Whether or not to make generated embeddables immutable. - * - * @var bool - */ - protected $embeddablesImmutable = false; - - /** - * Hash-map for handle types. - * - * @phpstan-var array - */ - protected $typeAlias = [ - Types::DATETIMETZ_MUTABLE => '\DateTime', - Types::DATETIME_MUTABLE => '\DateTime', - Types::DATE_MUTABLE => '\DateTime', - Types::TIME_MUTABLE => '\DateTime', - Types::OBJECT => '\stdClass', - Types::INTEGER => 'int', - Types::BIGINT => 'int', - Types::SMALLINT => 'int', - Types::TEXT => 'string', - Types::BLOB => 'string', - Types::DECIMAL => 'string', - Types::GUID => 'string', - 'json_array' => 'array', - Types::JSON => 'array', - Types::SIMPLE_ARRAY => 'array', - Types::BOOLEAN => 'bool', - ]; - - /** - * Hash-map to handle generator types string. - * - * @phpstan-var array - */ - protected static $generatorStrategyMap = [ - ClassMetadataInfo::GENERATOR_TYPE_AUTO => 'AUTO', - ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE => 'SEQUENCE', - ClassMetadataInfo::GENERATOR_TYPE_IDENTITY => 'IDENTITY', - ClassMetadataInfo::GENERATOR_TYPE_NONE => 'NONE', - ClassMetadataInfo::GENERATOR_TYPE_UUID => 'UUID', - ClassMetadataInfo::GENERATOR_TYPE_CUSTOM => 'CUSTOM', - ]; - - /** - * Hash-map to handle the change tracking policy string. - * - * @phpstan-var array - */ - protected static $changeTrackingPolicyMap = [ - ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT => 'DEFERRED_IMPLICIT', - ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT => 'DEFERRED_EXPLICIT', - ClassMetadataInfo::CHANGETRACKING_NOTIFY => 'NOTIFY', - ]; - - /** - * Hash-map to handle the inheritance type string. - * - * @phpstan-var array - */ - protected static $inheritanceTypeMap = [ - ClassMetadataInfo::INHERITANCE_TYPE_NONE => 'NONE', - ClassMetadataInfo::INHERITANCE_TYPE_JOINED => 'JOINED', - ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE => 'SINGLE_TABLE', - ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS', - ]; - - /** @var string */ - protected static $classTemplate = - ' - - - -{ - -} -'; - - /** @var string */ - protected static $getMethodTemplate = - '/** - * - * - * @return - */ -public function () -{ -return $this->; -}'; - - /** @var string */ - protected static $setMethodTemplate = - '/** - * - * - * @param $ - * - * @return - */ -public function ($) -{ -$this-> = $; - -return $this; -}'; - - /** @var string */ - protected static $addMethodTemplate = - '/** - * - * - * @param $ - * - * @return - */ -public function ($) -{ -$this->[] = $; - -return $this; -}'; - - /** @var string */ - protected static $removeMethodTemplate = - '/** - * - * - * @param $ - * - * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. - */ -public function ($) -{ -return $this->->removeElement($); -}'; - - /** @var string */ - protected static $lifecycleCallbackMethodTemplate = - '/** - * @ - */ -public function () -{ -// Add your code here -}'; - - /** @var string */ - protected static $constructorMethodTemplate = - '/** - * Constructor - */ -public function __construct() -{ - -} -'; - - /** @var string */ - protected static $embeddableConstructorMethodTemplate = - '/** - * Constructor - * - * - */ -public function __construct() -{ - -} -'; - - /** @var Inflector */ - protected $inflector; - - public function __construct() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8458', - '%s is deprecated with no replacement', - self::class - ); - - $this->annotationsPrefix = 'ORM\\'; - $this->inflector = InflectorFactory::create()->build(); - } - - /** - * Generates and writes entity classes for the given array of ClassMetadataInfo instances. - * - * @param string $outputDirectory - * @phpstan-param list $metadatas - * - * @return void - */ - public function generate(array $metadatas, $outputDirectory) - { - foreach ($metadatas as $metadata) { - $this->writeEntityClass($metadata, $outputDirectory); - } - } - - /** - * Generates and writes entity class to disk for the given ClassMetadataInfo instance. - * - * @param string $outputDirectory - * - * @return void - * - * @throws RuntimeException - */ - public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory) - { - $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension; - $dir = dirname($path); - - if (! is_dir($dir)) { - mkdir($dir, 0775, true); - } - - $this->isNew = ! file_exists($path) || $this->regenerateEntityIfExists; - - if (! $this->isNew) { - $this->parseTokensInEntityFile(file_get_contents($path)); - } else { - $this->staticReflection[$metadata->name] = ['properties' => [], 'methods' => []]; - } - - if ($this->backupExisting && file_exists($path)) { - $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . '~'; - if (! copy($path, $backupPath)) { - throw new RuntimeException('Attempt to backup overwritten entity file but copy operation failed.'); - } - } - - // If entity doesn't exist or we're re-generating the entities entirely - if ($this->isNew) { - file_put_contents($path, $this->generateEntityClass($metadata)); - // If entity exists and we're allowed to update the entity class - } elseif ($this->updateEntityIfExists) { - file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); - } - - chmod($path, 0664); - } - - /** - * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance. - * - * @return string - */ - public function generateEntityClass(ClassMetadataInfo $metadata) - { - $placeHolders = [ - '', - '', - '', - '', - '', - ]; - - $replacements = [ - $this->generateEntityNamespace($metadata), - $this->generateEntityUse(), - $this->generateEntityDocBlock($metadata), - $this->generateEntityClassName($metadata), - $this->generateEntityBody($metadata), - ]; - - $code = str_replace($placeHolders, $replacements, static::$classTemplate); - - return str_replace('', $this->spaces, $code); - } - - /** - * Generates the updated code for the given ClassMetadataInfo and entity at path. - * - * @param string $path - * - * @return string - */ - public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path) - { - $currentCode = file_get_contents($path); - - $body = $this->generateEntityBody($metadata); - $body = str_replace('', $this->spaces, $body); - $last = strrpos($currentCode, '}'); - - return substr($currentCode, 0, $last) . $body . ($body ? "\n" : '') . "}\n"; - } - - /** - * Sets the number of spaces the exported class should have. - * - * @param int $numSpaces - * - * @return void - */ - public function setNumSpaces($numSpaces) - { - $this->spaces = str_repeat(' ', $numSpaces); - $this->numSpaces = $numSpaces; - } - - /** - * Sets the extension to use when writing php files to disk. - * - * @param string $extension - * - * @return void - */ - public function setExtension($extension) - { - $this->extension = $extension; - } - - /** - * Sets the name of the class the generated classes should extend from. - * - * @param string $classToExtend - * - * @return void - */ - public function setClassToExtend($classToExtend) - { - $this->classToExtend = $classToExtend; - } - - /** - * Sets whether or not to generate annotations for the entity. - * - * @param bool $bool - * - * @return void - */ - public function setGenerateAnnotations($bool) - { - $this->generateAnnotations = $bool; - } - - /** - * Sets the class fields visibility for the entity (can either be private or protected). - * - * @param string $visibility - * - * @return void - * - * @throws InvalidArgumentException - * - * @phpstan-assert self::FIELD_VISIBLE_* $visibility - */ - public function setFieldVisibility($visibility) - { - if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) { - throw new InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); - } - - $this->fieldVisibility = $visibility; - } - - /** - * Sets whether or not to generate immutable embeddables. - * - * @param bool $embeddablesImmutable - * - * @return void - */ - public function setEmbeddablesImmutable($embeddablesImmutable) - { - $this->embeddablesImmutable = (bool) $embeddablesImmutable; - } - - /** - * Sets an annotation prefix. - * - * @param string $prefix - * - * @return void - */ - public function setAnnotationPrefix($prefix) - { - $this->annotationsPrefix = $prefix; - } - - /** - * Sets whether or not to try and update the entity if it already exists. - * - * @param bool $bool - * - * @return void - */ - public function setUpdateEntityIfExists($bool) - { - $this->updateEntityIfExists = $bool; - } - - /** - * Sets whether or not to regenerate the entity if it exists. - * - * @param bool $bool - * - * @return void - */ - public function setRegenerateEntityIfExists($bool) - { - $this->regenerateEntityIfExists = $bool; - } - - /** - * Sets whether or not to generate stub methods for the entity. - * - * @param bool $bool - * - * @return void - */ - public function setGenerateStubMethods($bool) - { - $this->generateEntityStubMethods = $bool; - } - - /** - * Should an existing entity be backed up if it already exists? - * - * @param bool $bool - * - * @return void - */ - public function setBackupExisting($bool) - { - $this->backupExisting = $bool; - } - - public function setInflector(Inflector $inflector): void - { - $this->inflector = $inflector; - } - - /** - * @param string $type - * - * @return string - */ - protected function getType($type) - { - if (isset($this->typeAlias[$type])) { - return $this->typeAlias[$type]; - } - - return $type; - } - - /** @return string */ - protected function generateEntityNamespace(ClassMetadataInfo $metadata) - { - if (! $this->hasNamespace($metadata)) { - return ''; - } - - return 'namespace ' . $this->getNamespace($metadata) . ';'; - } - - /** @return string */ - protected function generateEntityUse() - { - if (! $this->generateAnnotations) { - return ''; - } - - return "\n" . 'use Doctrine\ORM\Mapping as ORM;' . "\n"; - } - - /** @return string */ - protected function generateEntityClassName(ClassMetadataInfo $metadata) - { - return 'class ' . $this->getClassName($metadata) . - ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); - } - - /** @return string */ - protected function generateEntityBody(ClassMetadataInfo $metadata) - { - $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata); - $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata); - $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata); - $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null; - $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata); - - $code = []; - - if ($fieldMappingProperties) { - $code[] = $fieldMappingProperties; - } - - if ($embeddedProperties) { - $code[] = $embeddedProperties; - } - - if ($associationMappingProperties) { - $code[] = $associationMappingProperties; - } - - $code[] = $this->generateEntityConstructor($metadata); - - if ($stubMethods) { - $code[] = $stubMethods; - } - - if ($lifecycleCallbackMethods) { - $code[] = $lifecycleCallbackMethods; - } - - return implode("\n", $code); - } - - /** @return string */ - protected function generateEntityConstructor(ClassMetadataInfo $metadata) - { - if ($this->hasMethod('__construct', $metadata)) { - return ''; - } - - if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) { - return $this->generateEmbeddableConstructor($metadata); - } - - $collections = []; - - foreach ($metadata->associationMappings as $mapping) { - if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { - $collections[] = '$this->' . $mapping['fieldName'] . ' = new \Doctrine\Common\Collections\ArrayCollection();'; - } - } - - if ($collections) { - return $this->prefixCodeWithSpaces(str_replace('', implode("\n" . $this->spaces, $collections), static::$constructorMethodTemplate)); - } - - return ''; - } - - private function generateEmbeddableConstructor(ClassMetadataInfo $metadata): string - { - $paramTypes = []; - $paramVariables = []; - $params = []; - $fields = []; - - // Resort fields to put optional fields at the end of the method signature. - $requiredFields = []; - $optionalFields = []; - - foreach ($metadata->fieldMappings as $fieldMapping) { - if (empty($fieldMapping['nullable'])) { - $requiredFields[] = $fieldMapping; - - continue; - } - - $optionalFields[] = $fieldMapping; - } - - $fieldMappings = array_merge($requiredFields, $optionalFields); - - foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { - $paramType = '\\' . ltrim($embeddedClass['class'], '\\'); - $paramVariable = '$' . $fieldName; - - $paramTypes[] = $paramType; - $paramVariables[] = $paramVariable; - $params[] = $paramType . ' ' . $paramVariable; - $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';'; - } - - foreach ($fieldMappings as $fieldMapping) { - if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) { - continue; - } - - $paramTypes[] = $this->getType($fieldMapping['type']) . (! empty($fieldMapping['nullable']) ? '|null' : ''); - $param = '$' . $fieldMapping['fieldName']; - $paramVariables[] = $param; - - if ($fieldMapping['type'] === 'datetime') { - $param = $this->getType($fieldMapping['type']) . ' ' . $param; - if (! empty($fieldMapping['nullable'])) { - $param = '?' . $param; - } - } - - if (! empty($fieldMapping['nullable'])) { - $param .= ' = null'; - } - - $params[] = $param; - - $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';'; - } - - $maxParamTypeLength = max(array_map('strlen', $paramTypes)); - $paramTags = array_map( - static function ($type, $variable) use ($maxParamTypeLength) { - return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable; - }, - $paramTypes, - $paramVariables - ); - - // Generate multi line constructor if the signature exceeds 120 characters. - if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) { - $delimiter = "\n" . $this->spaces; - $params = $delimiter . implode(',' . $delimiter, $params) . "\n"; - } else { - $params = implode(', ', $params); - } - - $replacements = [ - '' => implode("\n * ", $paramTags), - '' => $params, - '' => implode("\n" . $this->spaces, $fields), - ]; - - $constructor = str_replace( - array_keys($replacements), - array_values($replacements), - static::$embeddableConstructorMethodTemplate - ); - - return $this->prefixCodeWithSpaces($constructor); - } - - /** - * @param string $src - * - * @return void - * - * @todo this won't work if there is a namespace in brackets and a class outside of it. - */ - protected function parseTokensInEntityFile($src) - { - $tokens = token_get_all($src); - $tokensCount = count($tokens); - $lastSeenNamespace = ''; - $lastSeenClass = false; - - $inNamespace = false; - $inClass = false; - - for ($i = 0; $i < $tokensCount; $i++) { - $token = $tokens[$i]; - if (in_array($token[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)) { - continue; - } - - if ($inNamespace) { - if (in_array($token[0], [T_NS_SEPARATOR, T_STRING], true)) { - $lastSeenNamespace .= $token[1]; - } elseif (PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)) { - $lastSeenNamespace .= $token[1]; - } elseif (is_string($token) && in_array($token, [';', '{'], true)) { - $inNamespace = false; - } - } - - if ($inClass) { - $inClass = false; - $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1]; - $this->staticReflection[$lastSeenClass]['properties'] = []; - $this->staticReflection[$lastSeenClass]['methods'] = []; - } - - if ($token[0] === T_NAMESPACE) { - $lastSeenNamespace = ''; - $inNamespace = true; - } elseif ($token[0] === T_CLASS && $tokens[$i - 1][0] !== T_DOUBLE_COLON) { - $inClass = true; - } elseif ($token[0] === T_FUNCTION) { - if ($tokens[$i + 2][0] === T_STRING) { - $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i + 2][1]); - } elseif ($tokens[$i + 2] === '&' && $tokens[$i + 3][0] === T_STRING) { - $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i + 3][1]); - } - } elseif (in_array($token[0], [T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED], true) && $tokens[$i + 2][0] !== T_FUNCTION) { - $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i + 2][1], 1); - } - } - } - - /** - * @param string $property - * - * @return bool - */ - protected function hasProperty($property, ClassMetadataInfo $metadata) - { - if ($this->extendsClass() || (! $this->isNew && class_exists($metadata->name))) { - // don't generate property if its already on the base class. - $reflClass = new ReflectionClass($this->getClassToExtend() ?: $metadata->name); - if ($reflClass->hasProperty($property)) { - return true; - } - } - - // check traits for existing property - foreach ($this->getTraits($metadata) as $trait) { - if ($trait->hasProperty($property)) { - return true; - } - } - - return isset($this->staticReflection[$metadata->name]) && - in_array($property, $this->staticReflection[$metadata->name]['properties'], true); - } - - /** - * @param string $method - * - * @return bool - */ - protected function hasMethod($method, ClassMetadataInfo $metadata) - { - if ($this->extendsClass() || (! $this->isNew && class_exists($metadata->name))) { - // don't generate method if its already on the base class. - $reflClass = new ReflectionClass($this->getClassToExtend() ?: $metadata->name); - - if ($reflClass->hasMethod($method)) { - return true; - } - } - - // check traits for existing method - foreach ($this->getTraits($metadata) as $trait) { - if ($trait->hasMethod($method)) { - return true; - } - } - - return isset($this->staticReflection[$metadata->name]) && - in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods'], true); - } - - /** - * @return ReflectionClass[] - * @phpstan-return array> - * - * @throws ReflectionException - */ - protected function getTraits(ClassMetadataInfo $metadata) - { - if (! ($metadata->reflClass !== null || class_exists($metadata->name))) { - return []; - } - - $reflClass = $metadata->reflClass ?? new ReflectionClass($metadata->name); - - $traits = []; - - while ($reflClass !== false) { - $traits = array_merge($traits, $reflClass->getTraits()); - - $reflClass = $reflClass->getParentClass(); - } - - return $traits; - } - - /** @return bool */ - protected function hasNamespace(ClassMetadataInfo $metadata) - { - return str_contains($metadata->name, '\\'); - } - - /** @return bool */ - protected function extendsClass() - { - return (bool) $this->classToExtend; - } - - /** @return string */ - protected function getClassToExtend() - { - return $this->classToExtend; - } - - /** @return string */ - protected function getClassToExtendName() - { - $refl = new ReflectionClass($this->getClassToExtend()); - - return '\\' . $refl->name; - } - - /** @return string */ - protected function getClassName(ClassMetadataInfo $metadata) - { - return ($pos = strrpos($metadata->name, '\\')) - ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; - } - - /** @return string */ - protected function getNamespace(ClassMetadataInfo $metadata) - { - return substr($metadata->name, 0, strrpos($metadata->name, '\\')); - } - - /** @return string */ - protected function generateEntityDocBlock(ClassMetadataInfo $metadata) - { - $lines = []; - $lines[] = '/**'; - $lines[] = ' * ' . $this->getClassName($metadata); - - if ($this->generateAnnotations) { - $lines[] = ' *'; - - $methods = [ - 'generateTableAnnotation', - 'generateInheritanceAnnotation', - 'generateDiscriminatorColumnAnnotation', - 'generateDiscriminatorMapAnnotation', - 'generateEntityAnnotation', - 'generateEntityListenerAnnotation', - ]; - - foreach ($methods as $method) { - $code = $this->$method($metadata); - if ($code) { - $lines[] = ' * ' . $code; - } - } - - if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { - $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks'; - } - } - - $lines[] = ' */'; - - return implode("\n", $lines); - } - - /** @return string */ - protected function generateEntityAnnotation(ClassMetadataInfo $metadata) - { - $prefix = '@' . $this->annotationsPrefix; - - if ($metadata->isEmbeddedClass) { - return $prefix . 'Embeddable'; - } - - $customRepository = $metadata->customRepositoryClassName - ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")' - : ''; - - return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository; - } - - /** @return string */ - protected function generateTableAnnotation(ClassMetadataInfo $metadata) - { - if ($metadata->isEmbeddedClass) { - return ''; - } - - $table = []; - - if (isset($metadata->table['schema'])) { - $table[] = 'schema="' . $metadata->table['schema'] . '"'; - } - - if (isset($metadata->table['name'])) { - $table[] = 'name="' . $metadata->table['name'] . '"'; - } - - if (isset($metadata->table['options']) && $metadata->table['options']) { - $table[] = 'options={' . $this->exportTableOptions($metadata->table['options']) . '}'; - } - - if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) { - $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']); - $table[] = 'uniqueConstraints={' . $constraints . '}'; - } - - if (isset($metadata->table['indexes']) && $metadata->table['indexes']) { - $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']); - $table[] = 'indexes={' . $constraints . '}'; - } - - return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')'; - } - - /** - * @param string $constraintName - * @phpstan-param array> $constraints - * - * @return string - */ - protected function generateTableConstraints($constraintName, array $constraints) - { - $annotations = []; - foreach ($constraints as $name => $constraint) { - $columns = []; - foreach ($constraint['columns'] as $column) { - $columns[] = '"' . $column . '"'; - } - - $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})'; - } - - return implode(', ', $annotations); - } - - /** @return string */ - protected function generateInheritanceAnnotation(ClassMetadataInfo $metadata) - { - if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - return ''; - } - - return '@' . $this->annotationsPrefix . 'InheritanceType("' . $this->getInheritanceTypeString($metadata->inheritanceType) . '")'; - } - - /** @return string */ - protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $metadata) - { - if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - return ''; - } - - $discrColumn = $metadata->discriminatorColumn; - if ($discrColumn === null) { - return ''; - } - - $columnDefinition = sprintf('name="%s", type="%s"', $discrColumn['name'], $discrColumn['type']); - if (isset($discrColumn['length'])) { - $columnDefinition .= ', length=' . $discrColumn['length']; - } - - return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; - } - - /** @return string|null */ - protected function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata) - { - if ($metadata->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - return null; - } - - $inheritanceClassMap = []; - - foreach ($metadata->discriminatorMap as $type => $class) { - $inheritanceClassMap[] = '"' . $type . '" = "' . $class . '"'; - } - - return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; - } - - /** @return string */ - protected function generateEntityStubMethods(ClassMetadataInfo $metadata) - { - $methods = []; - - foreach ($metadata->fieldMappings as $fieldMapping) { - if (isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']])) { - continue; - } - - $nullableField = $this->nullableFieldExpression($fieldMapping); - - if ( - (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) - && (! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_NONE) - ) { - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'set', - $fieldMapping['fieldName'], - $fieldMapping['type'], - $nullableField - ); - } - - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'get', - $fieldMapping['fieldName'], - $fieldMapping['type'], - $nullableField - ); - } - - foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { - if (isset($embeddedClass['declaredField'])) { - continue; - } - - if (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) { - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'set', - $fieldName, - $embeddedClass['class'] - ); - } - - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'get', - $fieldName, - $embeddedClass['class'] - ); - } - - foreach ($metadata->associationMappings as $associationMapping) { - if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { - $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null; - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'set', - $associationMapping['fieldName'], - $associationMapping['targetEntity'], - $nullable - ); - - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'get', - $associationMapping['fieldName'], - $associationMapping['targetEntity'], - $nullable - ); - } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'add', - $associationMapping['fieldName'], - $associationMapping['targetEntity'] - ); - - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'remove', - $associationMapping['fieldName'], - $associationMapping['targetEntity'] - ); - - $methods[] = $this->generateEntityStubMethod( - $metadata, - 'get', - $associationMapping['fieldName'], - Collection::class - ); - } - } - - return implode("\n\n", array_filter($methods)); - } - - /** - * @phpstan-param array $associationMapping - * - * @return bool - */ - protected function isAssociationIsNullable(array $associationMapping) - { - if (isset($associationMapping['id']) && $associationMapping['id']) { - return false; - } - - if (isset($associationMapping['joinColumns'])) { - $joinColumns = $associationMapping['joinColumns']; - } else { - //@todo there is no way to retrieve targetEntity metadata - $joinColumns = []; - } - - foreach ($joinColumns as $joinColumn) { - if (isset($joinColumn['nullable']) && ! $joinColumn['nullable']) { - return false; - } - } - - return true; - } - - /** @return string */ - protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata) - { - if (empty($metadata->lifecycleCallbacks)) { - return ''; - } - - $methods = []; - - $lifecycleEventsByCallback = []; - foreach ($metadata->lifecycleCallbacks as $event => $callbacks) { - foreach ($callbacks as $callback) { - $callbackCaseInsensitive = $callback; - foreach (array_keys($lifecycleEventsByCallback) as $key) { - if (strtolower($key) === strtolower($callback)) { - $callbackCaseInsensitive = $key; - break; - } - } - - $lifecycleEventsByCallback[$callbackCaseInsensitive][] = $event; - } - } - - foreach ($lifecycleEventsByCallback as $callback => $events) { - $methods[] = $this->generateLifecycleCallbackMethod($events, $callback, $metadata); - } - - return implode("\n\n", array_filter($methods)); - } - - /** @return string */ - protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata) - { - $lines = []; - - foreach ($metadata->associationMappings as $associationMapping) { - if ($this->hasProperty($associationMapping['fieldName'], $metadata)) { - continue; - } - - $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); - $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName'] - . ($associationMapping['type'] === ClassMetadataInfo::MANY_TO_MANY ? ' = array()' : null) . ";\n"; - } - - return implode("\n", $lines); - } - - /** @return string */ - protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) - { - $lines = []; - - foreach ($metadata->fieldMappings as $fieldMapping) { - if ( - isset($fieldMapping['declaredField'], $metadata->embeddedClasses[$fieldMapping['declaredField']]) || - $this->hasProperty($fieldMapping['fieldName'], $metadata) || - $metadata->isInheritedField($fieldMapping['fieldName']) - ) { - continue; - } - - $defaultValue = ''; - if (isset($fieldMapping['options']['default'])) { - if ($fieldMapping['type'] === 'boolean' && $fieldMapping['options']['default'] === '1') { - $defaultValue = ' = true'; - } elseif (($fieldMapping['type'] === 'integer' || $fieldMapping['type'] === 'float') && ! empty($fieldMapping['options']['default'])) { - $defaultValue = ' = ' . (string) $fieldMapping['options']['default']; - } else { - $defaultValue = ' = ' . var_export($fieldMapping['options']['default'], true); - } - } - - $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); - $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName'] . $defaultValue . ";\n"; - } - - return implode("\n", $lines); - } - - /** @return string */ - protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata) - { - $lines = []; - - foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { - if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) { - continue; - } - - $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass); - $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n"; - } - - return implode("\n", $lines); - } - - /** - * @param string $type - * @param string $fieldName - * @param string|null $typeHint - * @param string|null $defaultValue - * - * @return string - */ - protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) - { - $methodName = $type . $this->inflector->classify($fieldName); - $variableName = $this->inflector->camelize($fieldName); - - if (in_array($type, ['add', 'remove'], true)) { - $methodName = $this->inflector->singularize($methodName); - $variableName = $this->inflector->singularize($variableName); - } - - if ($this->hasMethod($methodName, $metadata)) { - return ''; - } - - $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName); - - $var = sprintf('%sMethodTemplate', $type); - $template = (string) static::$$var; - - $methodTypeHint = ''; - $types = Type::getTypesMap(); - $variableType = $typeHint ? $this->getType($typeHint) : null; - - if ($typeHint && ! isset($types[$typeHint])) { - $variableType = '\\' . ltrim($variableType, '\\'); - $methodTypeHint = '\\' . $typeHint . ' '; - if ($defaultValue === 'null') { - $methodTypeHint = '?' . $methodTypeHint; - } - } - - $replacements = [ - '' => ucfirst($type) . ' ' . $variableName . '.', - '' => $methodTypeHint, - '' => $variableType . ($defaultValue !== null ? '|' . $defaultValue : ''), - '' => $variableName, - '' => $methodName, - '' => $fieldName, - '' => $defaultValue !== null ? ' = ' . $defaultValue : '', - '' => $this->getClassName($metadata), - ]; - - $method = str_replace( - array_keys($replacements), - array_values($replacements), - $template - ); - - return $this->prefixCodeWithSpaces($method); - } - - /** - * @param string|string[] $name - * @param string $methodName - * - * @return string - */ - protected function generateLifecycleCallbackMethod($name, $methodName, ClassMetadataInfo $metadata) - { - if ($this->hasMethod($methodName, $metadata)) { - return ''; - } - - $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName); - - $eventAnnotations = array_map( - function ($event) { - return $this->annotationsPrefix . ucfirst($event); - }, - is_array($name) ? $name : [$name] - ); - $replacements = [ - '' => implode("\n * @", $eventAnnotations), - '' => $methodName, - ]; - - $method = str_replace( - array_keys($replacements), - array_values($replacements), - static::$lifecycleCallbackMethodTemplate - ); - - return $this->prefixCodeWithSpaces($method); - } - - /** - * @phpstan-param array $joinColumn - * - * @return string - */ - protected function generateJoinColumnAnnotation(array $joinColumn) - { - $joinColumnAnnot = []; - - if (isset($joinColumn['name'])) { - $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; - } - - if (isset($joinColumn['referencedColumnName'])) { - $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; - } - - if (isset($joinColumn['unique']) && $joinColumn['unique']) { - $joinColumnAnnot[] = 'unique=true'; - } - - if (isset($joinColumn['nullable'])) { - $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); - } - - if (isset($joinColumn['onDelete'])) { - $joinColumnAnnot[] = 'onDelete="' . $joinColumn['onDelete'] . '"'; - } - - if (isset($joinColumn['columnDefinition'])) { - $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"'; - } - - return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; - } - - /** - * @param mixed[] $associationMapping - * - * @return string - */ - protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) - { - $lines = []; - $lines[] = $this->spaces . '/**'; - - if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { - $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection'; - } else { - $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\'); - } - - if ($this->generateAnnotations) { - $lines[] = $this->spaces . ' *'; - - if (isset($associationMapping['id']) && $associationMapping['id']) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; - - $generatorType = $this->getIdGeneratorTypeString($metadata->generatorType); - if ($generatorType) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; - } - } - - $type = null; - switch ($associationMapping['type']) { - case ClassMetadataInfo::ONE_TO_ONE: - $type = 'OneToOne'; - break; - case ClassMetadataInfo::MANY_TO_ONE: - $type = 'ManyToOne'; - break; - case ClassMetadataInfo::ONE_TO_MANY: - $type = 'OneToMany'; - break; - case ClassMetadataInfo::MANY_TO_MANY: - $type = 'ManyToMany'; - break; - } - - $typeOptions = []; - - if (isset($associationMapping['targetEntity'])) { - $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; - } - - if (isset($associationMapping['inversedBy'])) { - $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; - } - - if (isset($associationMapping['mappedBy'])) { - $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; - } - - if ($associationMapping['cascade']) { - $cascades = []; - - if ($associationMapping['isCascadePersist']) { - $cascades[] = '"persist"'; - } - - if ($associationMapping['isCascadeRemove']) { - $cascades[] = '"remove"'; - } - - if ($associationMapping['isCascadeDetach']) { - $cascades[] = '"detach"'; - } - - if ($associationMapping['isCascadeMerge']) { - $cascades[] = '"merge"'; - } - - if ($associationMapping['isCascadeRefresh']) { - $cascades[] = '"refresh"'; - } - - if (count($cascades) === 5) { - $cascades = ['"all"']; - } - - $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; - } - - if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { - $typeOptions[] = 'orphanRemoval=true'; - } - - if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) { - $fetchMap = [ - ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY', - ClassMetadataInfo::FETCH_EAGER => 'EAGER', - ]; - - $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"'; - } - - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; - - if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({'; - - $joinColumnsLines = []; - - foreach ($associationMapping['joinColumns'] as $joinColumn) { - $joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn); - if ($joinColumnAnnot) { - $joinColumnsLines[] = $this->spaces . ' * ' . $joinColumnAnnot; - } - } - - $lines[] = implode(",\n", $joinColumnsLines); - $lines[] = $this->spaces . ' * })'; - } - - if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { - $joinTable = []; - $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; - - if (isset($associationMapping['joinTable']['schema'])) { - $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; - } - - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ','; - $lines[] = $this->spaces . ' * joinColumns={'; - - $joinColumnsLines = []; - - foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { - $joinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); - } - - $lines[] = implode(',' . PHP_EOL, $joinColumnsLines); - $lines[] = $this->spaces . ' * },'; - $lines[] = $this->spaces . ' * inverseJoinColumns={'; - - $inverseJoinColumnsLines = []; - - foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { - $inverseJoinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); - } - - $lines[] = implode(',' . PHP_EOL, $inverseJoinColumnsLines); - $lines[] = $this->spaces . ' * }'; - $lines[] = $this->spaces . ' * )'; - } - - if (isset($associationMapping['orderBy'])) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({'; - - foreach ($associationMapping['orderBy'] as $name => $direction) { - $lines[] = $this->spaces . ' * "' . $name . '"="' . $direction . '",'; - } - - $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1); - $lines[] = $this->spaces . ' * })'; - } - } - - $lines[] = $this->spaces . ' */'; - - return implode("\n", $lines); - } - - /** - * @param mixed[] $fieldMapping - * - * @return string - */ - protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) - { - $lines = []; - $lines[] = $this->spaces . '/**'; - $lines[] = $this->spaces . ' * @var ' - . $this->getType($fieldMapping['type']) - . ($this->nullableFieldExpression($fieldMapping) ? '|null' : ''); - - if ($this->generateAnnotations) { - $lines[] = $this->spaces . ' *'; - - $column = []; - if (isset($fieldMapping['columnName'])) { - $column[] = 'name="' . $fieldMapping['columnName'] . '"'; - } - - if (isset($fieldMapping['type'])) { - $column[] = 'type="' . $fieldMapping['type'] . '"'; - } - - if (isset($fieldMapping['length'])) { - $column[] = 'length=' . $fieldMapping['length']; - } - - if (isset($fieldMapping['precision'])) { - $column[] = 'precision=' . $fieldMapping['precision']; - } - - if (isset($fieldMapping['scale'])) { - $column[] = 'scale=' . $fieldMapping['scale']; - } - - if (isset($fieldMapping['nullable'])) { - $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); - } - - $options = []; - - if (isset($fieldMapping['options']['default']) && $fieldMapping['options']['default']) { - $options[] = '"default"="' . $fieldMapping['options']['default'] . '"'; - } - - if (isset($fieldMapping['options']['unsigned']) && $fieldMapping['options']['unsigned']) { - $options[] = '"unsigned"=true'; - } - - if (isset($fieldMapping['options']['fixed']) && $fieldMapping['options']['fixed']) { - $options[] = '"fixed"=true'; - } - - if (isset($fieldMapping['options']['comment']) && $fieldMapping['options']['comment']) { - $options[] = '"comment"="' . str_replace('"', '""', (string) $fieldMapping['options']['comment']) . '"'; - } - - if (isset($fieldMapping['options']['collation']) && $fieldMapping['options']['collation']) { - $options[] = '"collation"="' . $fieldMapping['options']['collation'] . '"'; - } - - if (isset($fieldMapping['options']['check']) && $fieldMapping['options']['check']) { - $options[] = '"check"="' . $fieldMapping['options']['check'] . '"'; - } - - if ($options) { - $column[] = 'options={' . implode(',', $options) . '}'; - } - - if (isset($fieldMapping['columnDefinition'])) { - $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; - } - - if (isset($fieldMapping['unique'])) { - $column[] = 'unique=' . var_export($fieldMapping['unique'], true); - } - - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')'; - - if (isset($fieldMapping['id']) && $fieldMapping['id']) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; - - $generatorType = $this->getIdGeneratorTypeString($metadata->generatorType); - if ($generatorType) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; - } - - if ($metadata->sequenceGeneratorDefinition) { - $sequenceGenerator = []; - - if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { - $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; - } - - if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { - $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize']; - } - - if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { - $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue']; - } - - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; - } - } - - if (isset($fieldMapping['version']) && $fieldMapping['version']) { - $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version'; - } - } - - $lines[] = $this->spaces . ' */'; - - return implode("\n", $lines); - } - - /** - * @phpstan-param array $embeddedClass - * - * @return string - */ - protected function generateEmbeddedPropertyDocBlock(array $embeddedClass) - { - $lines = []; - $lines[] = $this->spaces . '/**'; - $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\'); - - if ($this->generateAnnotations) { - $lines[] = $this->spaces . ' *'; - - $embedded = ['class="' . $embeddedClass['class'] . '"']; - - if (isset($embeddedClass['columnPrefix'])) { - if (is_string($embeddedClass['columnPrefix'])) { - $embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"'; - } else { - $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true); - } - } - - $lines[] = $this->spaces . ' * @' . - $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')'; - } - - $lines[] = $this->spaces . ' */'; - - return implode("\n", $lines); - } - - private function generateEntityListenerAnnotation(ClassMetadataInfo $metadata): string - { - if (count($metadata->entityListeners) === 0) { - return ''; - } - - $processedClasses = []; - foreach ($metadata->entityListeners as $event => $eventListeners) { - foreach ($eventListeners as $eventListener) { - $processedClasses[] = '"' . $eventListener['class'] . '"'; - } - } - - return sprintf( - '%s%s({%s})', - '@' . $this->annotationsPrefix, - 'EntityListeners', - implode(',', array_unique($processedClasses)) - ); - } - - /** - * @param string $code - * @param int $num - * - * @return string - */ - protected function prefixCodeWithSpaces($code, $num = 1) - { - $lines = explode("\n", $code); - - foreach ($lines as $key => $value) { - if ($value !== '') { - $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key]; - } - } - - return implode("\n", $lines); - } - - /** - * @param int $type The inheritance type used by the class and its subclasses. - * - * @return string The literal string for the inheritance type. - * - * @throws InvalidArgumentException When the inheritance type does not exist. - */ - protected function getInheritanceTypeString($type) - { - if (! isset(static::$inheritanceTypeMap[$type])) { - throw new InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); - } - - return static::$inheritanceTypeMap[$type]; - } - - /** - * @param int $type The policy used for change-tracking for the mapped class. - * - * @return string The literal string for the change-tracking type. - * - * @throws InvalidArgumentException When the change-tracking type does not exist. - */ - protected function getChangeTrackingPolicyString($type) - { - if (! isset(static::$changeTrackingPolicyMap[$type])) { - throw new InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); - } - - return static::$changeTrackingPolicyMap[$type]; - } - - /** - * @param int $type The generator to use for the mapped class. - * - * @return string The literal string for the generator type. - * - * @throws InvalidArgumentException When the generator type does not exist. - */ - protected function getIdGeneratorTypeString($type) - { - if (! isset(static::$generatorStrategyMap[$type])) { - throw new InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); - } - - return static::$generatorStrategyMap[$type]; - } - - /** @phpstan-param array $fieldMapping */ - private function nullableFieldExpression(array $fieldMapping): ?string - { - if (isset($fieldMapping['nullable']) && $fieldMapping['nullable'] === true) { - return 'null'; - } - - return null; - } - - /** - * Exports (nested) option elements. - * - * @phpstan-param array $options - */ - private function exportTableOptions(array $options): string - { - $optionsStr = []; - - foreach ($options as $name => $option) { - if (is_array($option)) { - $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}'; - } else { - $optionsStr[] = '"' . $name . '"="' . (string) $option . '"'; - } - } - - return implode(',', $optionsStr); - } -} diff --git a/src/Tools/EntityRepositoryGenerator.php b/src/Tools/EntityRepositoryGenerator.php deleted file mode 100644 index 3e37ac8d008..00000000000 --- a/src/Tools/EntityRepositoryGenerator.php +++ /dev/null @@ -1,168 +0,0 @@ - - -/** - * - * - * This class was generated by the Doctrine ORM. Add your own custom - * repository methods below. - */ -class extends -{ -} -'; - - public function __construct() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8458', - '%s is deprecated and will be removed in Doctrine ORM 3.0', - self::class - ); - } - - /** - * @param string $fullClassName - * - * @return string - */ - public function generateEntityRepositoryClass($fullClassName) - { - $variables = [ - '' => $this->generateEntityRepositoryNamespace($fullClassName), - '' => $this->generateEntityRepositoryName($fullClassName), - '' => $this->generateClassName($fullClassName), - ]; - - return str_replace(array_keys($variables), array_values($variables), self::$_template); - } - - /** - * Generates the namespace, if class do not have namespace, return empty string instead. - * - * @param class-string $fullClassName - */ - private function getClassNamespace(string $fullClassName): string - { - return substr($fullClassName, 0, (int) strrpos($fullClassName, '\\')); - } - - /** - * Generates the class name - * - * @param class-string $fullClassName - */ - private function generateClassName(string $fullClassName): string - { - $namespace = $this->getClassNamespace($fullClassName); - - $className = $fullClassName; - - if ($namespace) { - $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); - } - - return $className; - } - - /** - * Generates the namespace statement, if class do not have namespace, return empty string instead. - * - * @param class-string $fullClassName The full repository class name. - */ - private function generateEntityRepositoryNamespace(string $fullClassName): string - { - $namespace = $this->getClassNamespace($fullClassName); - - return $namespace ? 'namespace ' . $namespace . ';' : ''; - } - - private function generateEntityRepositoryName(string $fullClassName): string - { - $namespace = $this->getClassNamespace($fullClassName); - - $repositoryName = $this->repositoryName ?: EntityRepository::class; - - if ($namespace && $repositoryName[0] !== '\\') { - $repositoryName = '\\' . $repositoryName; - } - - return $repositoryName; - } - - /** - * @param string $fullClassName - * @param string $outputDirectory - * - * @return void - */ - public function writeEntityRepositoryClass($fullClassName, $outputDirectory) - { - $code = $this->generateEntityRepositoryClass($fullClassName); - - $path = $outputDirectory . DIRECTORY_SEPARATOR - . str_replace('\\', DIRECTORY_SEPARATOR, $fullClassName) . '.php'; - $dir = dirname($path); - - if (! is_dir($dir)) { - mkdir($dir, 0775, true); - } - - if (! file_exists($path)) { - file_put_contents($path, $code); - chmod($path, 0664); - } - } - - /** - * @param string $repositoryName - * - * @return $this - */ - public function setDefaultRepositoryName($repositoryName) - { - $this->repositoryName = $repositoryName; - - return $this; - } -} diff --git a/src/Tools/Event/GenerateSchemaEventArgs.php b/src/Tools/Event/GenerateSchemaEventArgs.php index 7785420df78..3b0993e6510 100644 --- a/src/Tools/Event/GenerateSchemaEventArgs.php +++ b/src/Tools/Event/GenerateSchemaEventArgs.php @@ -15,26 +15,18 @@ */ class GenerateSchemaEventArgs extends EventArgs { - /** @var EntityManagerInterface */ - private $em; - - /** @var Schema */ - private $schema; - - public function __construct(EntityManagerInterface $em, Schema $schema) - { - $this->em = $em; - $this->schema = $schema; + public function __construct( + private readonly EntityManagerInterface $em, + private readonly Schema $schema, + ) { } - /** @return EntityManagerInterface */ - public function getEntityManager() + public function getEntityManager(): EntityManagerInterface { return $this->em; } - /** @return Schema */ - public function getSchema() + public function getSchema(): Schema { return $this->schema; } diff --git a/src/Tools/Event/GenerateSchemaTableEventArgs.php b/src/Tools/Event/GenerateSchemaTableEventArgs.php index 97decced501..a09aaae8e2c 100644 --- a/src/Tools/Event/GenerateSchemaTableEventArgs.php +++ b/src/Tools/Event/GenerateSchemaTableEventArgs.php @@ -16,36 +16,24 @@ */ class GenerateSchemaTableEventArgs extends EventArgs { - /** @var ClassMetadata */ - private $classMetadata; - - /** @var Schema */ - private $schema; - - /** @var Table */ - private $classTable; - - public function __construct(ClassMetadata $classMetadata, Schema $schema, Table $classTable) - { - $this->classMetadata = $classMetadata; - $this->schema = $schema; - $this->classTable = $classTable; + public function __construct( + private readonly ClassMetadata $classMetadata, + private readonly Schema $schema, + private readonly Table $classTable, + ) { } - /** @return ClassMetadata */ - public function getClassMetadata() + public function getClassMetadata(): ClassMetadata { return $this->classMetadata; } - /** @return Schema */ - public function getSchema() + public function getSchema(): Schema { return $this->schema; } - /** @return Table */ - public function getClassTable() + public function getClassTable(): Table { return $this->classTable; } diff --git a/src/Tools/Exception/MissingColumnException.php b/src/Tools/Exception/MissingColumnException.php index a1b58a2f5e5..764721e045b 100644 --- a/src/Tools/Exception/MissingColumnException.php +++ b/src/Tools/Exception/MissingColumnException.php @@ -5,10 +5,11 @@ namespace Doctrine\ORM\Tools\Exception; use Doctrine\ORM\Exception\ORMException; +use LogicException; use function sprintf; -final class MissingColumnException extends ORMException +final class MissingColumnException extends LogicException implements ORMException { public static function fromColumnSourceAndTarget(string $column, string $source, string $target): self { @@ -16,7 +17,7 @@ public static function fromColumnSourceAndTarget(string $column, string $source, 'Column name "%s" referenced for relation from %s towards %s does not exist.', $column, $source, - $target + $target, )); } } diff --git a/src/Tools/Exception/NotSupported.php b/src/Tools/Exception/NotSupported.php index 637762bc9a8..af619fd8420 100644 --- a/src/Tools/Exception/NotSupported.php +++ b/src/Tools/Exception/NotSupported.php @@ -4,10 +4,10 @@ namespace Doctrine\ORM\Tools\Exception; -use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\SchemaToolException; +use LogicException; -final class NotSupported extends ORMException implements SchemaToolException +final class NotSupported extends LogicException implements SchemaToolException { public static function create(): self { diff --git a/src/Tools/Export/ClassMetadataExporter.php b/src/Tools/Export/ClassMetadataExporter.php deleted file mode 100644 index 6b73461a913..00000000000 --- a/src/Tools/Export/ClassMetadataExporter.php +++ /dev/null @@ -1,71 +0,0 @@ - */ - private static $_exporterDrivers = [ - 'xml' => Driver\XmlExporter::class, - 'yaml' => Driver\YamlExporter::class, - 'yml' => Driver\YamlExporter::class, - 'php' => Driver\PhpExporter::class, - 'annotation' => Driver\AnnotationExporter::class, - ]; - - public function __construct() - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8458', - '%s is deprecated with no replacement', - self::class - ); - } - - /** - * Registers a new exporter driver class under a specified name. - * - * @param string $name - * @param string $class - * - * @return void - */ - public static function registerExportDriver($name, $class) - { - self::$_exporterDrivers[$name] = $class; - } - - /** - * Gets an exporter driver instance. - * - * @param string $type The type to get (yml, xml, etc.). - * @param string|null $dest The directory where the exporter will export to. - * - * @return Driver\AbstractExporter - * - * @throws ExportException - */ - public function getExporter($type, $dest = null) - { - if (! isset(self::$_exporterDrivers[$type])) { - throw ExportException::invalidExporterDriverType($type); - } - - $class = self::$_exporterDrivers[$type]; - - return new $class($dest); - } -} diff --git a/src/Tools/Export/Driver/AbstractExporter.php b/src/Tools/Export/Driver/AbstractExporter.php deleted file mode 100644 index be53f852b08..00000000000 --- a/src/Tools/Export/Driver/AbstractExporter.php +++ /dev/null @@ -1,261 +0,0 @@ -_outputDir = $dir; - } - - /** - * @param bool $overwrite - * - * @return void - */ - public function setOverwriteExistingFiles($overwrite) - { - $this->_overwriteExistingFiles = $overwrite; - } - - /** - * Converts a single ClassMetadata instance to the exported format - * and returns it. - * - * @return string - */ - abstract public function exportClassMetadata(ClassMetadataInfo $metadata); - - /** - * Sets the array of ClassMetadata instances to export. - * - * @phpstan-param list $metadata - * - * @return void - */ - public function setMetadata(array $metadata) - { - $this->_metadata = $metadata; - } - - /** - * Gets the extension used to generated the path to a class. - * - * @return string|null - */ - public function getExtension() - { - return $this->_extension; - } - - /** - * Sets the directory to output the mapping files to. - * - * [php] - * $exporter = new YamlExporter($metadata); - * $exporter->setOutputDir(__DIR__ . '/yaml'); - * $exporter->export(); - * - * @param string $dir - * - * @return void - */ - public function setOutputDir($dir) - { - $this->_outputDir = $dir; - } - - /** - * Exports each ClassMetadata instance to a single Doctrine Mapping file - * named after the entity. - * - * @return void - * - * @throws ExportException - */ - public function export() - { - if (! is_dir($this->_outputDir)) { - mkdir($this->_outputDir, 0775, true); - } - - foreach ($this->_metadata as $metadata) { - // In case output is returned, write it to a file, skip otherwise - $output = $this->exportClassMetadata($metadata); - if ($output) { - $path = $this->_generateOutputPath($metadata); - $dir = dirname($path); - if (! is_dir($dir)) { - mkdir($dir, 0775, true); - } - - if (file_exists($path) && ! $this->_overwriteExistingFiles) { - throw ExportException::attemptOverwriteExistingFile($path); - } - - file_put_contents($path, $output); - chmod($path, 0664); - } - } - } - - /** - * Generates the path to write the class for the given ClassMetadataInfo instance. - * - * @return string - */ - protected function _generateOutputPath(ClassMetadataInfo $metadata) - { - return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; - } - - /** - * Sets the directory to output the mapping files to. - * - * [php] - * $exporter = new YamlExporter($metadata, __DIR__ . '/yaml'); - * $exporter->setExtension('.yml'); - * $exporter->export(); - * - * @param string $extension - * - * @return void - */ - public function setExtension($extension) - { - $this->_extension = $extension; - } - - /** - * @param int $type - * @phpstan-param ClassMetadataInfo::INHERITANCE_TYPE_* $type - * - * @return string - */ - protected function _getInheritanceTypeString($type) - { - switch ($type) { - case ClassMetadataInfo::INHERITANCE_TYPE_NONE: - return 'NONE'; - - case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: - return 'JOINED'; - - case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: - return 'SINGLE_TABLE'; - - case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: - return 'PER_CLASS'; - } - } - - /** - * @param int $mode - * @phpstan-param ClassMetadataInfo::FETCH_* $mode - * - * @return string - */ - protected function _getFetchModeString($mode) - { - switch ($mode) { - case ClassMetadataInfo::FETCH_EAGER: - return 'EAGER'; - - case ClassMetadataInfo::FETCH_EXTRA_LAZY: - return 'EXTRA_LAZY'; - - case ClassMetadataInfo::FETCH_LAZY: - return 'LAZY'; - } - } - - /** - * @param int $policy - * @phpstan-param ClassMetadataInfo::CHANGETRACKING_* $policy - * - * @return string - */ - protected function _getChangeTrackingPolicyString($policy) - { - switch ($policy) { - case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: - return 'DEFERRED_IMPLICIT'; - - case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: - return 'DEFERRED_EXPLICIT'; - - case ClassMetadataInfo::CHANGETRACKING_NOTIFY: - return 'NOTIFY'; - } - } - - /** - * @param int $type - * @phpstan-param ClassMetadataInfo::GENERATOR_TYPE_* $type - * - * @return string - */ - protected function _getIdGeneratorTypeString($type) - { - switch ($type) { - case ClassMetadataInfo::GENERATOR_TYPE_AUTO: - return 'AUTO'; - - case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: - return 'SEQUENCE'; - - case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: - return 'IDENTITY'; - - case ClassMetadataInfo::GENERATOR_TYPE_UUID: - return 'UUID'; - - case ClassMetadataInfo::GENERATOR_TYPE_CUSTOM: - return 'CUSTOM'; - } - } -} diff --git a/src/Tools/Export/Driver/AnnotationExporter.php b/src/Tools/Export/Driver/AnnotationExporter.php deleted file mode 100644 index 1facd6735db..00000000000 --- a/src/Tools/Export/Driver/AnnotationExporter.php +++ /dev/null @@ -1,58 +0,0 @@ -entityGenerator) { - throw new RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.'); - } - - $this->entityGenerator->setGenerateAnnotations(true); - $this->entityGenerator->setGenerateStubMethods(false); - $this->entityGenerator->setRegenerateEntityIfExists(false); - $this->entityGenerator->setUpdateEntityIfExists(false); - - return $this->entityGenerator->generateEntityClass($metadata); - } - - /** @return string */ - protected function _generateOutputPath(ClassMetadataInfo $metadata) - { - return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; - } - - /** @return void */ - public function setEntityGenerator(EntityGenerator $entityGenerator) - { - $this->entityGenerator = $entityGenerator; - } -} diff --git a/src/Tools/Export/Driver/PhpExporter.php b/src/Tools/Export/Driver/PhpExporter.php deleted file mode 100644 index 8f6ee390b63..00000000000 --- a/src/Tools/Export/Driver/PhpExporter.php +++ /dev/null @@ -1,203 +0,0 @@ -isMappedSuperclass) { - $lines[] = '$metadata->isMappedSuperclass = true;'; - } - - $lines[] = '$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_' . $this->_getInheritanceTypeString($metadata->inheritanceType) . ');'; - - if ($metadata->customRepositoryClassName) { - $lines[] = "\$metadata->customRepositoryClassName = '" . $metadata->customRepositoryClassName . "';"; - } - - if ($metadata->table) { - $lines[] = '$metadata->setPrimaryTable(' . $this->_varExport($metadata->table) . ');'; - } - - if ($metadata->discriminatorColumn) { - $lines[] = '$metadata->setDiscriminatorColumn(' . $this->_varExport($metadata->discriminatorColumn) . ');'; - } - - if ($metadata->discriminatorMap) { - $lines[] = '$metadata->setDiscriminatorMap(' . $this->_varExport($metadata->discriminatorMap) . ');'; - } - - if ($metadata->changeTrackingPolicy) { - $lines[] = '$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_' . $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . ');'; - } - - if ($metadata->lifecycleCallbacks) { - foreach ($metadata->lifecycleCallbacks as $event => $callbacks) { - foreach ($callbacks as $callback) { - $lines[] = sprintf("\$metadata->addLifecycleCallback('%s', '%s');", $callback, $event); - } - } - } - - $lines = array_merge($lines, $this->processEntityListeners($metadata)); - - foreach ($metadata->fieldMappings as $fieldMapping) { - $lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');'; - } - - if (! $metadata->isIdentifierComposite) { - $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType); - if ($generatorType) { - $lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');'; - } - } - - foreach ($metadata->associationMappings as $associationMapping) { - $cascade = ['remove', 'persist', 'refresh', 'merge', 'detach']; - foreach ($cascade as $key => $value) { - if (! $associationMapping['isCascade' . ucfirst($value)]) { - unset($cascade[$key]); - } - } - - if (count($cascade) === 5) { - $cascade = ['all']; - } - - $method = null; - $associationMappingArray = [ - 'fieldName' => $associationMapping['fieldName'], - 'targetEntity' => $associationMapping['targetEntity'], - 'cascade' => $cascade, - ]; - - if (isset($associationMapping['fetch'])) { - $associationMappingArray['fetch'] = $associationMapping['fetch']; - } - - if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { - $method = 'mapOneToOne'; - $oneToOneMappingArray = [ - 'mappedBy' => $associationMapping['mappedBy'], - 'inversedBy' => $associationMapping['inversedBy'], - 'joinColumns' => $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : [], - 'orphanRemoval' => $associationMapping['orphanRemoval'], - ]; - - $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); - } elseif ($associationMapping['type'] === ClassMetadataInfo::ONE_TO_MANY) { - $method = 'mapOneToMany'; - $potentialAssociationMappingIndexes = [ - 'mappedBy', - 'orphanRemoval', - 'orderBy', - ]; - $oneToManyMappingArray = []; - foreach ($potentialAssociationMappingIndexes as $index) { - if (isset($associationMapping[$index])) { - $oneToManyMappingArray[$index] = $associationMapping[$index]; - } - } - - $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); - } elseif ($associationMapping['type'] === ClassMetadataInfo::MANY_TO_MANY) { - $method = 'mapManyToMany'; - $potentialAssociationMappingIndexes = [ - 'mappedBy', - 'joinTable', - 'orderBy', - ]; - $manyToManyMappingArray = []; - foreach ($potentialAssociationMappingIndexes as $index) { - if (isset($associationMapping[$index])) { - $manyToManyMappingArray[$index] = $associationMapping[$index]; - } - } - - $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); - } - - $lines[] = '$metadata->' . $method . '(' . $this->_varExport($associationMappingArray) . ');'; - } - - return implode("\n", $lines); - } - - /** - * @param mixed $var - * - * @return string - */ - protected function _varExport($var) - { - $export = var_export($var, true); - $export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export); - $export = str_replace(' ', ' ', $export); - $export = str_replace('array (', 'array(', $export); - $export = str_replace('array( ', 'array(', $export); - $export = str_replace(',)', ')', $export); - $export = str_replace(', )', ')', $export); - $export = str_replace(' ', ' ', $export); - - return $export; - } - - /** - * @return string[] - * @phpstan-return list - */ - private function processEntityListeners(ClassMetadataInfo $metadata): array - { - $lines = []; - - foreach ($metadata->entityListeners as $event => $entityListenerConfig) { - foreach ($entityListenerConfig as $entityListener) { - $lines[] = sprintf( - '$metadata->addEntityListener(%s, %s, %s);', - var_export($event, true), - var_export($entityListener['class'], true), - var_export($entityListener['method'], true) - ); - } - } - - return $lines; - } -} diff --git a/src/Tools/Export/Driver/XmlExporter.php b/src/Tools/Export/Driver/XmlExporter.php deleted file mode 100644 index e6b7a35c72d..00000000000 --- a/src/Tools/Export/Driver/XmlExporter.php +++ /dev/null @@ -1,503 +0,0 @@ -'); - - if ($metadata->isMappedSuperclass) { - $root = $xml->addChild('mapped-superclass'); - } else { - $root = $xml->addChild('entity'); - } - - if ($metadata->customRepositoryClassName) { - $root->addAttribute('repository-class', $metadata->customRepositoryClassName); - } - - $root->addAttribute('name', $metadata->name); - - if (isset($metadata->table['name'])) { - $root->addAttribute('table', $metadata->table['name']); - } - - if (isset($metadata->table['schema'])) { - $root->addAttribute('schema', $metadata->table['schema']); - } - - if ($metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - $root->addAttribute('inheritance-type', $this->_getInheritanceTypeString($metadata->inheritanceType)); - } - - if (isset($metadata->table['options'])) { - $optionsXml = $root->addChild('options'); - - $this->exportTableOptions($optionsXml, $metadata->table['options']); - } - - if ($metadata->discriminatorColumn) { - $discriminatorColumnXml = $root->addChild('discriminator-column'); - $discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']); - $discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']); - - if (isset($metadata->discriminatorColumn['length'])) { - $discriminatorColumnXml->addAttribute('length', (string) $metadata->discriminatorColumn['length']); - } - } - - if ($metadata->discriminatorMap) { - $discriminatorMapXml = $root->addChild('discriminator-map'); - - foreach ($metadata->discriminatorMap as $value => $className) { - $discriminatorMappingXml = $discriminatorMapXml->addChild('discriminator-mapping'); - $discriminatorMappingXml->addAttribute('value', (string) $value); - $discriminatorMappingXml->addAttribute('class', $className); - } - } - - $trackingPolicy = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); - - if ($trackingPolicy !== 'DEFERRED_IMPLICIT') { - $root->addAttribute('change-tracking-policy', $trackingPolicy); - } - - if (isset($metadata->table['indexes'])) { - $indexesXml = $root->addChild('indexes'); - - foreach ($metadata->table['indexes'] as $name => $index) { - $indexXml = $indexesXml->addChild('index'); - $indexXml->addAttribute('name', $name); - $indexXml->addAttribute('columns', implode(',', $index['columns'])); - if (isset($index['flags'])) { - $indexXml->addAttribute('flags', implode(',', $index['flags'])); - } - } - } - - if (isset($metadata->table['uniqueConstraints'])) { - $uniqueConstraintsXml = $root->addChild('unique-constraints'); - - foreach ($metadata->table['uniqueConstraints'] as $name => $unique) { - $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); - $uniqueConstraintXml->addAttribute('name', $name); - $uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns'])); - } - } - - $fields = $metadata->fieldMappings; - - $id = []; - foreach ($fields as $name => $field) { - if (isset($field['id']) && $field['id']) { - $id[$name] = $field; - unset($fields[$name]); - } - } - - foreach ($metadata->associationMappings as $name => $assoc) { - if (isset($assoc['id']) && $assoc['id']) { - $id[$name] = [ - 'fieldName' => $name, - 'associationKey' => true, - ]; - } - } - - if (! $metadata->isIdentifierComposite) { - $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType); - if ($idGeneratorType) { - $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; - } - } - - if ($id) { - foreach ($id as $field) { - $idXml = $root->addChild('id'); - $idXml->addAttribute('name', $field['fieldName']); - - if (isset($field['type'])) { - $idXml->addAttribute('type', $field['type']); - } - - if (isset($field['columnName'])) { - $idXml->addAttribute('column', $field['columnName']); - } - - if (isset($field['length'])) { - $idXml->addAttribute('length', (string) $field['length']); - } - - if (isset($field['associationKey']) && $field['associationKey']) { - $idXml->addAttribute('association-key', 'true'); - } - - $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType); - if ($idGeneratorType) { - $generatorXml = $idXml->addChild('generator'); - $generatorXml->addAttribute('strategy', $idGeneratorType); - - $this->exportSequenceInformation($idXml, $metadata); - } - } - } - - if ($fields) { - foreach ($fields as $field) { - $fieldXml = $root->addChild('field'); - $fieldXml->addAttribute('name', $field['fieldName']); - $fieldXml->addAttribute('type', $field['type']); - $fieldXml->addAttribute('column', $field['columnName']); - - if (isset($field['length'])) { - $fieldXml->addAttribute('length', (string) $field['length']); - } - - if (isset($field['precision'])) { - $fieldXml->addAttribute('precision', (string) $field['precision']); - } - - if (isset($field['scale'])) { - $fieldXml->addAttribute('scale', (string) $field['scale']); - } - - if (isset($field['unique']) && $field['unique']) { - $fieldXml->addAttribute('unique', 'true'); - } - - if (isset($field['options'])) { - $optionsXml = $fieldXml->addChild('options'); - foreach ($field['options'] as $key => $value) { - $optionXml = $optionsXml->addChild('option', (string) $value); - $optionXml->addAttribute('name', $key); - } - } - - if (isset($field['version'])) { - $fieldXml->addAttribute('version', $field['version']); - } - - if (isset($field['columnDefinition'])) { - $fieldXml->addAttribute('column-definition', $field['columnDefinition']); - } - - if (isset($field['nullable'])) { - $fieldXml->addAttribute('nullable', $field['nullable'] ? 'true' : 'false'); - } - - if (isset($field['notInsertable'])) { - $fieldXml->addAttribute('insertable', 'false'); - } - - if (isset($field['notUpdatable'])) { - $fieldXml->addAttribute('updatable', 'false'); - } - } - } - - $orderMap = [ - ClassMetadataInfo::ONE_TO_ONE, - ClassMetadataInfo::ONE_TO_MANY, - ClassMetadataInfo::MANY_TO_ONE, - ClassMetadataInfo::MANY_TO_MANY, - ]; - - uasort($metadata->associationMappings, static function ($m1, $m2) use (&$orderMap) { - $a1 = array_search($m1['type'], $orderMap, true); - $a2 = array_search($m2['type'], $orderMap, true); - - return strcmp((string) $a1, (string) $a2); - }); - - foreach ($metadata->associationMappings as $associationMapping) { - $associationMappingXml = null; - if ($associationMapping['type'] === ClassMetadataInfo::ONE_TO_ONE) { - $associationMappingXml = $root->addChild('one-to-one'); - } elseif ($associationMapping['type'] === ClassMetadataInfo::MANY_TO_ONE) { - $associationMappingXml = $root->addChild('many-to-one'); - } elseif ($associationMapping['type'] === ClassMetadataInfo::ONE_TO_MANY) { - $associationMappingXml = $root->addChild('one-to-many'); - } elseif ($associationMapping['type'] === ClassMetadataInfo::MANY_TO_MANY) { - $associationMappingXml = $root->addChild('many-to-many'); - } - - $associationMappingXml->addAttribute('field', $associationMapping['fieldName']); - $associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']); - - if (isset($associationMapping['mappedBy'])) { - $associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']); - } - - if (isset($associationMapping['inversedBy'])) { - $associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']); - } - - if (isset($associationMapping['indexBy'])) { - $associationMappingXml->addAttribute('index-by', $associationMapping['indexBy']); - } - - if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval'] !== false) { - $associationMappingXml->addAttribute('orphan-removal', 'true'); - } - - if (isset($associationMapping['fetch'])) { - $associationMappingXml->addAttribute('fetch', $this->_getFetchModeString($associationMapping['fetch'])); - } - - $cascade = []; - if ($associationMapping['isCascadeRemove']) { - $cascade[] = 'cascade-remove'; - } - - if ($associationMapping['isCascadePersist']) { - $cascade[] = 'cascade-persist'; - } - - if ($associationMapping['isCascadeRefresh']) { - $cascade[] = 'cascade-refresh'; - } - - if ($associationMapping['isCascadeMerge']) { - $cascade[] = 'cascade-merge'; - } - - if ($associationMapping['isCascadeDetach']) { - $cascade[] = 'cascade-detach'; - } - - if (count($cascade) === 5) { - $cascade = ['cascade-all']; - } - - if ($cascade) { - $cascadeXml = $associationMappingXml->addChild('cascade'); - - foreach ($cascade as $type) { - $cascadeXml->addChild($type); - } - } - - if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { - $joinTableXml = $associationMappingXml->addChild('join-table'); - $joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']); - $joinColumnsXml = $joinTableXml->addChild('join-columns'); - - foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { - $joinColumnXml = $joinColumnsXml->addChild('join-column'); - $joinColumnXml->addAttribute('name', $joinColumn['name']); - $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); - - if (isset($joinColumn['onDelete'])) { - $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); - } - } - - $inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns'); - - foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { - $inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column'); - $inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']); - $inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']); - - if (isset($inverseJoinColumn['onDelete'])) { - $inverseJoinColumnXml->addAttribute('on-delete', $inverseJoinColumn['onDelete']); - } - - if (isset($inverseJoinColumn['columnDefinition'])) { - $inverseJoinColumnXml->addAttribute('column-definition', $inverseJoinColumn['columnDefinition']); - } - - if (isset($inverseJoinColumn['nullable'])) { - $inverseJoinColumnXml->addAttribute('nullable', $inverseJoinColumn['nullable'] ? 'true' : 'false'); - } - - if (isset($inverseJoinColumn['orderBy'])) { - $inverseJoinColumnXml->addAttribute('order-by', $inverseJoinColumn['orderBy']); - } - } - } - - if (isset($associationMapping['joinColumns'])) { - $joinColumnsXml = $associationMappingXml->addChild('join-columns'); - - foreach ($associationMapping['joinColumns'] as $joinColumn) { - $joinColumnXml = $joinColumnsXml->addChild('join-column'); - $joinColumnXml->addAttribute('name', $joinColumn['name']); - $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); - - if (isset($joinColumn['onDelete'])) { - $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); - } - - if (isset($joinColumn['columnDefinition'])) { - $joinColumnXml->addAttribute('column-definition', $joinColumn['columnDefinition']); - } - - if (isset($joinColumn['nullable'])) { - $joinColumnXml->addAttribute('nullable', $joinColumn['nullable'] ? 'true' : 'false'); - } - } - } - - if (isset($associationMapping['orderBy'])) { - $orderByXml = $associationMappingXml->addChild('order-by'); - - foreach ($associationMapping['orderBy'] as $name => $direction) { - $orderByFieldXml = $orderByXml->addChild('order-by-field'); - $orderByFieldXml->addAttribute('name', $name); - $orderByFieldXml->addAttribute('direction', $direction); - } - } - } - - if (isset($metadata->lifecycleCallbacks) && count($metadata->lifecycleCallbacks) > 0) { - $lifecycleCallbacksXml = $root->addChild('lifecycle-callbacks'); - - foreach ($metadata->lifecycleCallbacks as $name => $methods) { - foreach ($methods as $method) { - $lifecycleCallbackXml = $lifecycleCallbacksXml->addChild('lifecycle-callback'); - $lifecycleCallbackXml->addAttribute('type', $name); - $lifecycleCallbackXml->addAttribute('method', $method); - } - } - } - - $this->processEntityListeners($metadata, $root); - - return $this->asXml($xml); - } - - /** - * Exports (nested) option elements. - * - * @param mixed[] $options - */ - private function exportTableOptions(SimpleXMLElement $parentXml, array $options): void - { - foreach ($options as $name => $option) { - $isArray = is_array($option); - $optionXml = $isArray - ? $parentXml->addChild('option') - : $parentXml->addChild('option', (string) $option); - - $optionXml->addAttribute('name', (string) $name); - - if ($isArray) { - $this->exportTableOptions($optionXml, $option); - } - } - } - - /** - * Export sequence information (if available/configured) into the current identifier XML node - */ - private function exportSequenceInformation(SimpleXMLElement $identifierXmlNode, ClassMetadataInfo $metadata): void - { - $sequenceDefinition = $metadata->sequenceGeneratorDefinition; - - if (! ($metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE && $sequenceDefinition)) { - return; - } - - $sequenceGeneratorXml = $identifierXmlNode->addChild('sequence-generator'); - - $sequenceGeneratorXml->addAttribute('sequence-name', $sequenceDefinition['sequenceName']); - $sequenceGeneratorXml->addAttribute('allocation-size', $sequenceDefinition['allocationSize']); - $sequenceGeneratorXml->addAttribute('initial-value', $sequenceDefinition['initialValue']); - } - - private function asXml(SimpleXMLElement $simpleXml): string - { - $dom = new DOMDocument('1.0', 'UTF-8'); - $dom->loadXML($simpleXml->asXML()); - $dom->formatOutput = true; - - return $dom->saveXML(); - } - - private function processEntityListeners(ClassMetadataInfo $metadata, SimpleXMLElement $root): void - { - if (count($metadata->entityListeners) === 0) { - return; - } - - $entityListenersXml = $root->addChild('entity-listeners'); - $entityListenersXmlMap = []; - - $this->generateEntityListenerXml($metadata, $entityListenersXmlMap, $entityListenersXml); - } - - /** @param mixed[] $entityListenersXmlMap */ - private function generateEntityListenerXml( - ClassMetadataInfo $metadata, - array $entityListenersXmlMap, - SimpleXMLElement $entityListenersXml - ): void { - foreach ($metadata->entityListeners as $event => $entityListenerConfig) { - foreach ($entityListenerConfig as $entityListener) { - $entityListenerXml = $this->addClassToMapIfExists( - $entityListenersXmlMap, - $entityListener, - $entityListenersXml - ); - - $entityListenerCallbackXml = $entityListenerXml->addChild('lifecycle-callback'); - $entityListenerCallbackXml->addAttribute('type', $event); - $entityListenerCallbackXml->addAttribute('method', $entityListener['method']); - } - } - } - - /** - * @param mixed[] $entityListenersXmlMap - * @param mixed[] $entityListener - */ - private function addClassToMapIfExists( - array $entityListenersXmlMap, - array $entityListener, - SimpleXMLElement $entityListenersXml - ): SimpleXMLElement { - if (isset($entityListenersXmlMap[$entityListener['class']])) { - return $entityListenersXmlMap[$entityListener['class']]; - } - - $entityListenerXml = $entityListenersXml->addChild('entity-listener'); - $entityListenerXml->addAttribute('class', $entityListener['class']); - $entityListenersXmlMap[$entityListener['class']] = $entityListenerXml; - - return $entityListenerXml; - } -} diff --git a/src/Tools/Export/Driver/YamlExporter.php b/src/Tools/Export/Driver/YamlExporter.php deleted file mode 100644 index 4fbf6b69523..00000000000 --- a/src/Tools/Export/Driver/YamlExporter.php +++ /dev/null @@ -1,273 +0,0 @@ -isMappedSuperclass) { - $array['type'] = 'mappedSuperclass'; - } else { - $array['type'] = 'entity'; - } - - $metadataTable = $metadata->table ?? ['name' => null]; - - $array['table'] = $metadataTable['name']; - - if (isset($metadataTable['schema'])) { - $array['schema'] = $metadataTable['schema']; - } - - $inheritanceType = $metadata->inheritanceType; - - if ($inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - $array['inheritanceType'] = $this->_getInheritanceTypeString($inheritanceType); - } - - $column = $metadata->discriminatorColumn; - if ($column) { - $array['discriminatorColumn'] = $column; - } - - $map = $metadata->discriminatorMap; - if ($map) { - $array['discriminatorMap'] = $map; - } - - if ($metadata->changeTrackingPolicy !== ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT) { - $array['changeTrackingPolicy'] = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); - } - - if (isset($metadataTable['indexes'])) { - $array['indexes'] = $metadataTable['indexes']; - } - - if ($metadata->customRepositoryClassName) { - $array['repositoryClass'] = $metadata->customRepositoryClassName; - } - - if (isset($metadataTable['uniqueConstraints'])) { - $array['uniqueConstraints'] = $metadataTable['uniqueConstraints']; - } - - if (isset($metadataTable['options'])) { - $array['options'] = $metadataTable['options']; - } - - $fieldMappings = $metadata->fieldMappings; - - $ids = []; - foreach ($fieldMappings as $name => $fieldMapping) { - $fieldMapping['column'] = $fieldMapping['columnName']; - - unset($fieldMapping['columnName'], $fieldMapping['fieldName']); - - if ($fieldMapping['column'] === $name) { - unset($fieldMapping['column']); - } - - if (isset($fieldMapping['id']) && $fieldMapping['id']) { - $ids[$name] = $fieldMapping; - unset($fieldMappings[$name]); - continue; - } - - $fieldMappings[$name] = $fieldMapping; - } - - if (! $metadata->isIdentifierComposite) { - $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType); - if ($idGeneratorType) { - $ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; - } - } - - $array['id'] = $ids; - - if ($fieldMappings) { - $array['fields'] = $fieldMappings; - } - - foreach ($metadata->associationMappings as $name => $associationMapping) { - $cascade = []; - - if ($associationMapping['isCascadeRemove']) { - $cascade[] = 'remove'; - } - - if ($associationMapping['isCascadePersist']) { - $cascade[] = 'persist'; - } - - if ($associationMapping['isCascadeRefresh']) { - $cascade[] = 'refresh'; - } - - if ($associationMapping['isCascadeMerge']) { - $cascade[] = 'merge'; - } - - if ($associationMapping['isCascadeDetach']) { - $cascade[] = 'detach'; - } - - if (count($cascade) === 5) { - $cascade = ['all']; - } - - $associationMappingArray = [ - 'targetEntity' => $associationMapping['targetEntity'], - 'cascade' => $cascade, - ]; - - if (isset($associationMapping['fetch'])) { - $associationMappingArray['fetch'] = $this->_getFetchModeString($associationMapping['fetch']); - } - - if (isset($associationMapping['id']) && $associationMapping['id'] === true) { - $array['id'][$name]['associationKey'] = true; - } - - if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { - $joinColumns = $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : []; - $newJoinColumns = []; - - foreach ($joinColumns as $joinColumn) { - $newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName']; - - if (isset($joinColumn['onDelete'])) { - $newJoinColumns[$joinColumn['name']]['onDelete'] = $joinColumn['onDelete']; - } - } - - $oneToOneMappingArray = [ - 'mappedBy' => $associationMapping['mappedBy'], - 'inversedBy' => $associationMapping['inversedBy'], - 'joinColumns' => $newJoinColumns, - 'orphanRemoval' => $associationMapping['orphanRemoval'], - ]; - - $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); - - if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) { - $array['oneToOne'][$name] = $associationMappingArray; - } else { - $array['manyToOne'][$name] = $associationMappingArray; - } - } elseif ($associationMapping['type'] === ClassMetadataInfo::ONE_TO_MANY) { - $oneToManyMappingArray = [ - 'mappedBy' => $associationMapping['mappedBy'], - 'inversedBy' => $associationMapping['inversedBy'], - 'orphanRemoval' => $associationMapping['orphanRemoval'], - 'orderBy' => $associationMapping['orderBy'] ?? null, - ]; - - $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); - $array['oneToMany'][$name] = $associationMappingArray; - } elseif ($associationMapping['type'] === ClassMetadataInfo::MANY_TO_MANY) { - $manyToManyMappingArray = [ - 'mappedBy' => $associationMapping['mappedBy'], - 'inversedBy' => $associationMapping['inversedBy'], - 'joinTable' => $associationMapping['joinTable'] ?? null, - 'orderBy' => $associationMapping['orderBy'] ?? null, - ]; - - $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); - $array['manyToMany'][$name] = $associationMappingArray; - } - } - - if (isset($metadata->lifecycleCallbacks)) { - $array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks; - } - - $array = $this->processEntityListeners($metadata, $array); - - return $this->yamlDump([$metadata->name => $array], 10); - } - - /** - * Dumps a PHP array to a YAML string. - * - * The yamlDump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. - * - * @param mixed[] $array PHP array - * @param int $inline [optional] The level where you switch to inline YAML - * - * @return string A YAML string representing the original PHP array - */ - protected function yamlDump($array, $inline = 2) - { - return Yaml::dump($array, $inline); - } - - /** - * @phpstan-param array $array - * - * @phpstan-return array&array{entityListeners: array>} - */ - private function processEntityListeners(ClassMetadataInfo $metadata, array $array): array - { - if (count($metadata->entityListeners) === 0) { - return $array; - } - - $array['entityListeners'] = []; - - foreach ($metadata->entityListeners as $event => $entityListenerConfig) { - $array = $this->processEntityListenerConfig($array, $entityListenerConfig, $event); - } - - return $array; - } - - /** - * @phpstan-param array{entityListeners: array>} $array - * @phpstan-param list $entityListenerConfig - * - * @phpstan-return array{entityListeners: array>} - */ - private function processEntityListenerConfig( - array $array, - array $entityListenerConfig, - string $event - ): array { - foreach ($entityListenerConfig as $entityListener) { - if (! isset($array['entityListeners'][$entityListener['class']])) { - $array['entityListeners'][$entityListener['class']] = []; - } - - $array['entityListeners'][$entityListener['class']][$event] = [$entityListener['method']]; - } - - return $array; - } -} diff --git a/src/Tools/Export/ExportException.php b/src/Tools/Export/ExportException.php deleted file mode 100644 index b28b70acdc2..00000000000 --- a/src/Tools/Export/ExportException.php +++ /dev/null @@ -1,49 +0,0 @@ - $queryComponents + * {@inheritDoc} */ - public function __construct($query, $parserResult, array $queryComponents) + public function __construct(Query $query, ParserResult $parserResult, array $queryComponents) { $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); $this->rsm = $parserResult->getResultSetMapping(); @@ -62,18 +53,18 @@ public function __construct($query, $parserResult, array $queryComponents) parent::__construct($query, $parserResult, $queryComponents); } - protected function createSqlForFinalizer(SelectStatement $AST): string + protected function createSqlForFinalizer(SelectStatement $selectStatement): string { if ($this->platform instanceof SQLServerPlatform) { - $AST->orderByClause = null; + $selectStatement->orderByClause = null; } - $sql = parent::createSqlForFinalizer($AST); + $sql = parent::createSqlForFinalizer($selectStatement); - if ($AST->groupByClause) { + if ($selectStatement->groupByClause) { return sprintf( 'SELECT COUNT(*) AS dctrn_count FROM (%s) dctrn_table', - $sql + $sql, ); } @@ -83,7 +74,7 @@ protected function createSqlForFinalizer(SelectStatement $AST): string // so for now, It's not supported. // Get the root entity and alias from the AST fromClause - $from = $AST->fromClause->identificationVariableDeclarations; + $from = $selectStatement->fromClause->identificationVariableDeclarations; if (count($from) > 1) { throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); } @@ -105,7 +96,9 @@ protected function createSqlForFinalizer(SelectStatement $AST): string } if (isset($rootClass->associationMappings[$property])) { - $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + $association = $rootClass->associationMappings[$property]; + assert($association->isToOneOwningSide()); + $joinColumn = $association->joinColumns[0]->name; foreach (array_keys($this->rsm->metaMappings, $joinColumn, true) as $alias) { if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) { @@ -118,7 +111,7 @@ protected function createSqlForFinalizer(SelectStatement $AST): string if (count($rootIdentifier) !== count($sqlIdentifier)) { throw new RuntimeException(sprintf( 'Not all identifier properties can be found in the ResultSetMapping: %s', - implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))), )); } @@ -126,7 +119,7 @@ protected function createSqlForFinalizer(SelectStatement $AST): string return sprintf( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table', implode(', ', $sqlIdentifier), - $sql + $sql, ); } } diff --git a/src/Tools/Pagination/CountWalker.php b/src/Tools/Pagination/CountWalker.php index 12c5efe32fb..f11b25d6821 100644 --- a/src/Tools/Pagination/CountWalker.php +++ b/src/Tools/Pagination/CountWalker.php @@ -24,45 +24,49 @@ class CountWalker extends TreeWalkerAdapter */ public const HINT_DISTINCT = 'doctrine_paginator.distinct'; - public function walkSelectStatement(SelectStatement $AST) + public function walkSelectStatement(SelectStatement $selectStatement): void { - if ($AST->havingClause) { + if ($selectStatement->havingClause) { throw new RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); } // Get the root entity and alias from the AST fromClause - $from = $AST->fromClause->identificationVariableDeclarations; + $from = $selectStatement->fromClause->identificationVariableDeclarations; if (count($from) > 1) { throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); } - $fromRoot = reset($from); - $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; - $rootClass = $this->getMetadataForDqlAlias($rootAlias); - $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT); - $pathType = PathExpression::TYPE_STATE_FIELD; - if (isset($rootClass->associationMappings[$identifierFieldName])) { - $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; - } + $countPathExpressionOrLiteral = '*'; + if ($distinct) { + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->getMetadataForDqlAlias($rootAlias); + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($rootClass->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } - $pathExpression = new PathExpression( - PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, - $rootAlias, - $identifierFieldName - ); - $pathExpression->type = $pathType; + $countPathExpressionOrLiteral = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $rootAlias, + $identifierFieldName, + ); + $countPathExpressionOrLiteral->type = $pathType; + } - $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT); - $AST->selectClause->selectExpressions = [ + $selectStatement->selectClause->selectExpressions = [ new SelectExpression( - new AggregateExpression('count', $pathExpression, $distinct), - null + new AggregateExpression('count', $countPathExpressionOrLiteral, $distinct), + null, ), ]; // ORDER BY is not needed, only increases query execution through unnecessary sorting. - $AST->orderByClause = null; + $selectStatement->orderByClause = null; } } diff --git a/src/Tools/Pagination/Exception/RowNumberOverFunctionNotEnabled.php b/src/Tools/Pagination/Exception/RowNumberOverFunctionNotEnabled.php index 8f8c79a4604..0e3da9393d1 100644 --- a/src/Tools/Pagination/Exception/RowNumberOverFunctionNotEnabled.php +++ b/src/Tools/Pagination/Exception/RowNumberOverFunctionNotEnabled.php @@ -5,8 +5,9 @@ namespace Doctrine\ORM\Tools\Pagination\Exception; use Doctrine\ORM\Exception\ORMException; +use LogicException; -final class RowNumberOverFunctionNotEnabled extends ORMException +final class RowNumberOverFunctionNotEnabled extends LogicException implements ORMException { public static function create(): self { diff --git a/src/Tools/Pagination/LimitSubqueryOutputWalker.php b/src/Tools/Pagination/LimitSubqueryOutputWalker.php index 95b9066db2b..a7450257d32 100644 --- a/src/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/src/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -8,18 +8,17 @@ use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SQLAnywherePlatform; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\QuoteStrategy; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\Query; -use Doctrine\ORM\Query\AST\DeleteStatement; +use Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\AST\OrderByClause; use Doctrine\ORM\Query\AST\PathExpression; use Doctrine\ORM\Query\AST\SelectExpression; use Doctrine\ORM\Query\AST\SelectStatement; -use Doctrine\ORM\Query\AST\UpdateStatement; +use Doctrine\ORM\Query\AST\Subselect; use Doctrine\ORM\Query\Exec\SingleSelectSqlFinalizer; use Doctrine\ORM\Query\Exec\SqlFinalizer; use Doctrine\ORM\Query\Parser; @@ -59,63 +58,55 @@ class LimitSubqueryOutputWalker extends SqlOutputWalker { private const ORDER_BY_PATH_EXPRESSION = '/(? */ - private $orderByPathExpressions = []; + private array $orderByPathExpressions = []; /** - * @var bool We don't want to add path expressions from sub-selects into the select clause of the containing query. - * This state flag simply keeps track on whether we are walking on a subquery or not + * We don't want to add path expressions from sub-selects into the select clause of the containing query. + * This state flag simply keeps track on whether we are walking on a subquery or not */ - private $inSubSelect = false; + private bool $inSubSelect = false; /** * Stores various parameters that are otherwise unavailable * because Doctrine\ORM\Query\SqlWalker keeps everything private without * accessors. * - * @param Query $query - * @param ParserResult $parserResult - * @param mixed[] $queryComponents - * @phpstan-param array $queryComponents + * {@inheritDoc} */ - public function __construct($query, $parserResult, array $queryComponents) - { + public function __construct( + Query $query, + ParserResult $parserResult, + array $queryComponents, + ) { $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); $this->rsm = $parserResult->getResultSetMapping(); - $query = clone $query; + $cloneQuery = clone $query; + + $cloneQuery->setParameters(clone $query->getParameters()); + $cloneQuery->setCacheable(false); + + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } // Reset limit and offset - $this->firstResult = $query->getFirstResult(); - $this->maxResults = $query->getMaxResults(); - $query->setFirstResult(0)->setMaxResults(null); + $this->firstResult = $cloneQuery->getFirstResult(); + $this->maxResults = $cloneQuery->getMaxResults(); + $cloneQuery->setFirstResult(0)->setMaxResults(null); - $this->em = $query->getEntityManager(); + $this->em = $cloneQuery->getEntityManager(); $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); - parent::__construct($query, $parserResult, $queryComponents); + parent::__construct($cloneQuery, $parserResult, $queryComponents); } /** @@ -126,7 +117,6 @@ private function platformSupportsRowNumber(): bool return $this->platform instanceof PostgreSQLPlatform || $this->platform instanceof SQLServerPlatform || $this->platform instanceof OraclePlatform - || $this->platform instanceof SQLAnywherePlatform || $this->platform instanceof DB2Platform || (method_exists($this->platform, 'supportsRowNumberFunction') && $this->platform->supportsRowNumberFunction()); @@ -160,12 +150,9 @@ private function rebuildOrderByForRowNumber(SelectStatement $AST): void $AST->orderByClause = null; } - /** - * {@inheritDoc} - */ - public function walkSelectStatement(SelectStatement $AST) + public function walkSelectStatement(SelectStatement $selectStatement): string { - $sqlFinalizer = $this->getFinalizer($AST); + $sqlFinalizer = $this->getFinalizer($selectStatement); $query = $this->getQuery(); @@ -174,12 +161,7 @@ public function walkSelectStatement(SelectStatement $AST) return $abstractSqlExecutor->getSqlStatements(); } - /** - * @param DeleteStatement|UpdateStatement|SelectStatement $AST - * - * @return SingleSelectSqlFinalizer - */ - public function getFinalizer($AST): SqlFinalizer + public function getFinalizer(AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST): SqlFinalizer { if (! $AST instanceof SelectStatement) { throw new LogicException(self::class . ' is to be used on SelectStatements only'); @@ -198,17 +180,15 @@ public function getFinalizer($AST): SqlFinalizer * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. * This method is for use with platforms which support ROW_NUMBER. * - * @return string - * * @throws RuntimeException */ - public function walkSelectStatementWithRowNumber(SelectStatement $AST) + public function walkSelectStatementWithRowNumber(SelectStatement $AST): string { // Apply the limit and offset. return $this->platform->modifyLimitQuery( $this->createSqlWithRowNumber($AST), $this->maxResults, - $this->firstResult + $this->firstResult, ); } @@ -235,7 +215,7 @@ private function createSqlWithRowNumber(SelectStatement $AST): string $sql = sprintf( 'SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), - $innerSql + $innerSql, ); if ($hasOrderBy) { @@ -257,19 +237,15 @@ private function createSqlWithRowNumber(SelectStatement $AST): string * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. * This method is for platforms which DO NOT support ROW_NUMBER. * - * @param bool $addMissingItemsFromOrderByToSelect - * - * @return string - * * @throws RuntimeException */ - public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, $addMissingItemsFromOrderByToSelect = true) + public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, bool $addMissingItemsFromOrderByToSelect = true): string { // Apply the limit and offset. return $this->platform->modifyLimitQuery( $this->createSqlWithoutRowNumber($AST, $addMissingItemsFromOrderByToSelect), $this->maxResults, - $this->firstResult + $this->firstResult, ); } @@ -296,7 +272,7 @@ private function createSqlWithoutRowNumber(SelectStatement $AST, bool $addMissin $sql = sprintf( 'SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), - $innerSql + $innerSql, ); // https://github.com/doctrine/orm/issues/2630 @@ -382,7 +358,7 @@ private function preserveSqlOrdering( array $sqlIdentifier, string $innerSql, string $sql, - ?OrderByClause $orderByClause + OrderByClause|null $orderByClause, ): string { // If the sql statement has an order by clause, we need to wrap it in a new select distinct statement if (! $orderByClause) { @@ -393,7 +369,7 @@ private function preserveSqlOrdering( return sprintf( 'SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), - $this->recreateInnerSql($orderByClause, $sqlIdentifier, $innerSql) + $this->recreateInnerSql($orderByClause, $sqlIdentifier, $innerSql), ); } @@ -405,7 +381,7 @@ private function preserveSqlOrdering( private function recreateInnerSql( OrderByClause $orderByClause, array $identifiers, - string $innerSql + string $innerSql, ): string { [$searchPatterns, $replacements] = $this->generateSqlAliasReplacements(); $orderByItems = []; @@ -416,7 +392,7 @@ private function recreateInnerSql( $orderByItemString = preg_replace( $searchPatterns, $replacements, - $this->walkOrderByItem($orderByItem) + $this->walkOrderByItem($orderByItem), ); $orderByItems[] = $orderByItemString; @@ -431,7 +407,7 @@ private function recreateInnerSql( 'SELECT DISTINCT %s FROM (%s) dctrn_result_inner ORDER BY %s', implode(', ', $identifiers), $innerSql, - implode(', ', $orderByItems) + implode(', ', $orderByItems), ); } @@ -465,16 +441,16 @@ private function generateSqlAliasReplacements(): array $columnName = $this->quoteStrategy->getColumnName( $fieldName, $metadataList[$dqlAliasForFieldAlias], - $this->em->getConnection()->getDatabasePlatform() + $this->em->getConnection()->getDatabasePlatform(), ); // Get the SQL table alias for the entity and field $sqlTableAliasForFieldAlias = $aliasMap[$dqlAliasForFieldAlias]; - if (isset($fieldMapping['declared']) && $fieldMapping['declared'] !== $class->name) { + if (isset($fieldMapping->declared) && $fieldMapping->declared !== $class->name) { // Field was declared in a parent class, so we need to get the proper SQL table alias // for the joined parent table. - $otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']); + $otherClassMetadata = $this->em->getClassMetadata($fieldMapping->declared); if (! $otherClassMetadata->isMappedSuperclass) { $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias); @@ -494,7 +470,7 @@ private function generateSqlAliasReplacements(): array * * @return list */ - public function getOrderByPathExpressions() + public function getOrderByPathExpressions(): array { return $this->orderByPathExpressions; } @@ -556,7 +532,9 @@ private function getSQLIdentifier(SelectStatement $AST): array } if (isset($rootClass->associationMappings[$property])) { - $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + $association = $rootClass->associationMappings[$property]; + assert($association->isToOneOwningSide()); + $joinColumn = $association->joinColumns[0]->name; foreach (array_keys($this->rsm->metaMappings, $joinColumn, true) as $alias) { if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) { @@ -573,17 +551,14 @@ private function getSQLIdentifier(SelectStatement $AST): array if (count($rootIdentifier) !== count($sqlIdentifier)) { throw new RuntimeException(sprintf( 'Not all identifier properties can be found in the ResultSetMapping: %s', - implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))), )); } return $sqlIdentifier; } - /** - * {@inheritDoc} - */ - public function walkPathExpression($pathExpr) + public function walkPathExpression(PathExpression $pathExpr): string { if (! $this->inSubSelect && ! $this->platformSupportsRowNumber() && ! in_array($pathExpr, $this->orderByPathExpressions, true)) { $this->orderByPathExpressions[] = $pathExpr; @@ -592,10 +567,7 @@ public function walkPathExpression($pathExpr) return parent::walkPathExpression($pathExpr); } - /** - * {@inheritDoc} - */ - public function walkSubSelect($subselect) + public function walkSubSelect(Subselect $subselect): string { $this->inSubSelect = true; diff --git a/src/Tools/Pagination/LimitSubqueryWalker.php b/src/Tools/Pagination/LimitSubqueryWalker.php index 173e65236b3..3fb0eeed5fc 100644 --- a/src/Tools/Pagination/LimitSubqueryWalker.php +++ b/src/Tools/Pagination/LimitSubqueryWalker.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Tools\Pagination; use Doctrine\DBAL\Types\Type; -use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\AST\Functions\IdentityFunction; use Doctrine\ORM\Query\AST\Node; @@ -30,20 +29,18 @@ class LimitSubqueryWalker extends TreeWalkerAdapter /** * Counter for generating unique order column aliases. - * - * @var int */ - private $aliasCounter = 0; + private int $aliasCounter = 0; - public function walkSelectStatement(SelectStatement $AST) + public function walkSelectStatement(SelectStatement $selectStatement): void { // Get the root entity and alias from the AST fromClause - $from = $AST->fromClause->identificationVariableDeclarations; + $from = $selectStatement->fromClause->identificationVariableDeclarations; $fromRoot = reset($from); $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; $rootClass = $this->getMetadataForDqlAlias($rootAlias); - $this->validate($AST); + $this->validate($selectStatement); $identifier = $rootClass->getSingleIdentifierFieldName(); if (isset($rootClass->associationMappings[$identifier])) { @@ -54,7 +51,7 @@ public function walkSelectStatement(SelectStatement $AST) $query->setHint( self::IDENTIFIER_TYPE, - Type::getType($rootClass->fieldMappings[$identifier]['type']) + Type::getType($rootClass->fieldMappings[$identifier]->type), ); $query->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true); @@ -62,25 +59,24 @@ public function walkSelectStatement(SelectStatement $AST) $pathExpression = new PathExpression( PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, - $identifier + $identifier, ); $pathExpression->type = PathExpression::TYPE_STATE_FIELD; - $AST->selectClause->selectExpressions = [new SelectExpression($pathExpression, '_dctrn_id')]; - $AST->selectClause->isDistinct = ($query->getHints()[Paginator::HINT_ENABLE_DISTINCT] ?? true) === true; + $selectStatement->selectClause->selectExpressions = [new SelectExpression($pathExpression, '_dctrn_id')]; + $selectStatement->selectClause->isDistinct = ($query->getHints()[Paginator::HINT_ENABLE_DISTINCT] ?? true) === true; - if (! isset($AST->orderByClause)) { + if (! isset($selectStatement->orderByClause)) { return; } - // @phpstan-ignore method.deprecated - $queryComponents = $this->_getQueryComponents(); - foreach ($AST->orderByClause->orderByItems as $item) { + $queryComponents = $this->getQueryComponents(); + foreach ($selectStatement->orderByClause->orderByItems as $item) { if ($item->expression instanceof PathExpression) { - $AST->selectClause->selectExpressions[] = new SelectExpression( + $selectStatement->selectClause->selectExpressions[] = new SelectExpression( $this->createSelectExpressionItem($item->expression), - '_dctrn_ord' . $this->aliasCounter++ + '_dctrn_ord' . $this->aliasCounter++, ); continue; @@ -90,9 +86,9 @@ public function walkSelectStatement(SelectStatement $AST) $qComp = $queryComponents[$item->expression]; if (isset($qComp['resultVariable'])) { - $AST->selectClause->selectExpressions[] = new SelectExpression( + $selectStatement->selectClause->selectExpressions[] = new SelectExpression( $qComp['resultVariable'], - $item->expression + $item->expression, ); } } @@ -130,7 +126,7 @@ private function validate(SelectStatement $AST): void if ( isset($queryComponent['parent']) && isset($queryComponent['relation']) - && $queryComponent['relation']['type'] & ClassMetadata::TO_MANY + && $queryComponent['relation']->isToMany() ) { throw new RuntimeException('Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers.'); } diff --git a/src/Tools/Pagination/Paginator.php b/src/Tools/Pagination/Paginator.php index a0ac0bfef4d..d74ab6c63f8 100644 --- a/src/Tools/Pagination/Paginator.php +++ b/src/Tools/Pagination/Paginator.php @@ -15,7 +15,6 @@ use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\QueryBuilder; use IteratorAggregate; -use ReturnTypeWillChange; use Traversable; use function array_key_exists; @@ -36,38 +35,26 @@ class Paginator implements Countable, IteratorAggregate public const HINT_ENABLE_DISTINCT = 'paginator.distinct.enable'; - /** @var Query */ - private $query; + private readonly Query $query; + private bool|null $useOutputWalkers = null; + private int|null $count = null; - /** @var bool */ - private $fetchJoinCollection; - - /** @var bool|null */ - private $useOutputWalkers; - - /** @var int|null */ - private $count; - - /** - * @param Query|QueryBuilder $query A Doctrine ORM query or query builder. - * @param bool $fetchJoinCollection Whether the query joins a collection (true by default). - */ - public function __construct($query, $fetchJoinCollection = true) - { + /** @param bool $fetchJoinCollection Whether the query joins a collection (true by default). */ + public function __construct( + Query|QueryBuilder $query, + private readonly bool $fetchJoinCollection = true, + ) { if ($query instanceof QueryBuilder) { $query = $query->getQuery(); } - $this->query = $query; - $this->fetchJoinCollection = (bool) $fetchJoinCollection; + $this->query = $query; } /** * Returns the query. - * - * @return Query */ - public function getQuery() + public function getQuery(): Query { return $this->query; } @@ -77,17 +64,15 @@ public function getQuery() * * @return bool Whether the query joins a collection. */ - public function getFetchJoinCollection() + public function getFetchJoinCollection(): bool { return $this->fetchJoinCollection; } /** * Returns whether the paginator will use an output walker. - * - * @return bool|null */ - public function getUseOutputWalkers() + public function getUseOutputWalkers(): bool|null { return $this->useOutputWalkers; } @@ -95,30 +80,21 @@ public function getUseOutputWalkers() /** * Sets whether the paginator will use an output walker. * - * @param bool|null $useOutputWalkers - * * @return $this - * @phpstan-return static */ - public function setUseOutputWalkers($useOutputWalkers) + public function setUseOutputWalkers(bool|null $useOutputWalkers): static { $this->useOutputWalkers = $useOutputWalkers; return $this; } - /** - * {@inheritDoc} - * - * @return int - */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { if ($this->count === null) { try { $this->count = (int) array_sum(array_map('current', $this->getCountQuery()->getScalarResult())); - } catch (NoResultException $e) { + } catch (NoResultException) { $this->count = 0; } } @@ -129,11 +105,9 @@ public function count() /** * {@inheritDoc} * - * @return Traversable * @phpstan-return Traversable */ - #[ReturnTypeWillChange] - public function getIterator() + public function getIterator(): Traversable { $offset = $this->query->getFirstResult(); $length = $this->query->getMaxResults(); @@ -284,8 +258,6 @@ private function convertWhereInIdentifiersToDatabaseValues(array $identifiers): $type = $query->getSQL(); assert(is_string($type)); - return array_map(static function ($id) use ($connection, $type) { - return $connection->convertToDatabaseValue($id, $type); - }, $identifiers); + return array_map(static fn ($id): mixed => $connection->convertToDatabaseValue($id, $type), $identifiers); } } diff --git a/src/Tools/Pagination/RootTypeWalker.php b/src/Tools/Pagination/RootTypeWalker.php index ec45dbd6fc1..82d52c2f4c4 100644 --- a/src/Tools/Pagination/RootTypeWalker.php +++ b/src/Tools/Pagination/RootTypeWalker.php @@ -27,10 +27,10 @@ */ final class RootTypeWalker extends SqlOutputWalker { - public function walkSelectStatement(AST\SelectStatement $AST): string + public function walkSelectStatement(AST\SelectStatement $selectStatement): string { // Get the root entity and alias from the AST fromClause - $from = $AST->fromClause->identificationVariableDeclarations; + $from = $selectStatement->fromClause->identificationVariableDeclarations; if (count($from) > 1) { throw new RuntimeException('Can only process queries that select only one FROM component'); @@ -45,11 +45,11 @@ public function walkSelectStatement(AST\SelectStatement $AST): string $identifierFieldName, $rootClass, $this->getQuery() - ->getEntityManager() + ->getEntityManager(), )[0]; } - public function getFinalizer($AST): SqlFinalizer + public function getFinalizer(AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST): SqlFinalizer { if (! $AST instanceof AST\SelectStatement) { throw new RuntimeException(self::class . ' is to be used on SelectStatements only'); diff --git a/src/Tools/Pagination/RowNumberOverFunction.php b/src/Tools/Pagination/RowNumberOverFunction.php index 80e9e83ac05..a0fdd012dbc 100644 --- a/src/Tools/Pagination/RowNumberOverFunction.php +++ b/src/Tools/Pagination/RowNumberOverFunction.php @@ -19,23 +19,21 @@ */ class RowNumberOverFunction extends FunctionNode { - /** @var OrderByClause */ - public $orderByClause; + public OrderByClause $orderByClause; - /** @inheritDoc */ - public function getSql(SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker): string { return 'ROW_NUMBER() OVER(' . trim($sqlWalker->walkOrderByClause( - $this->orderByClause + $this->orderByClause, )) . ')'; } /** * @throws RowNumberOverFunctionNotEnabled * - * @inheritDoc + * @inheritdoc */ - public function parse(Parser $parser) + public function parse(Parser $parser): void { throw RowNumberOverFunctionNotEnabled::create(); } diff --git a/src/Tools/Pagination/WhereInWalker.php b/src/Tools/Pagination/WhereInWalker.php index 0e88dad6ef0..01741cab7dc 100644 --- a/src/Tools/Pagination/WhereInWalker.php +++ b/src/Tools/Pagination/WhereInWalker.php @@ -42,10 +42,10 @@ class WhereInWalker extends TreeWalkerAdapter */ public const PAGINATOR_ID_ALIAS = 'dpid'; - public function walkSelectStatement(SelectStatement $AST) + public function walkSelectStatement(SelectStatement $selectStatement): void { // Get the root entity and alias from the AST fromClause - $from = $AST->fromClause->identificationVariableDeclarations; + $from = $selectStatement->fromClause->identificationVariableDeclarations; if (count($from) > 1) { throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); @@ -69,11 +69,11 @@ public function walkSelectStatement(SelectStatement $AST) if ($hasIds) { $arithmeticExpression = new ArithmeticExpression(); $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression( - [$pathExpression] + [$pathExpression], ); $expression = new InListExpression( $arithmeticExpression, - [new InputParameter(':' . self::PAGINATOR_ID_ALIAS)] + [new InputParameter(':' . self::PAGINATOR_ID_ALIAS)], ); } else { $expression = new NullComparisonExpression($pathExpression); @@ -81,35 +81,35 @@ public function walkSelectStatement(SelectStatement $AST) $conditionalPrimary = new ConditionalPrimary(); $conditionalPrimary->simpleConditionalExpression = $expression; - if ($AST->whereClause) { - if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) { - $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary; - } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) { - $AST->whereClause->conditionalExpression = new ConditionalExpression( + if ($selectStatement->whereClause) { + if ($selectStatement->whereClause->conditionalExpression instanceof ConditionalTerm) { + $selectStatement->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary; + } elseif ($selectStatement->whereClause->conditionalExpression instanceof ConditionalPrimary) { + $selectStatement->whereClause->conditionalExpression = new ConditionalExpression( [ new ConditionalTerm( [ - $AST->whereClause->conditionalExpression, + $selectStatement->whereClause->conditionalExpression, $conditionalPrimary, - ] + ], ), - ] + ], ); } else { - $tmpPrimary = new ConditionalPrimary(); - $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression; - $AST->whereClause->conditionalExpression = new ConditionalTerm( + $tmpPrimary = new ConditionalPrimary(); + $tmpPrimary->conditionalExpression = $selectStatement->whereClause->conditionalExpression; + $selectStatement->whereClause->conditionalExpression = new ConditionalTerm( [ $tmpPrimary, $conditionalPrimary, - ] + ], ); } } else { - $AST->whereClause = new WhereClause( + $selectStatement->whereClause = new WhereClause( new ConditionalExpression( - [new ConditionalTerm([$conditionalPrimary])] - ) + [new ConditionalTerm([$conditionalPrimary])], + ), ); } } diff --git a/src/Tools/ResolveTargetEntityListener.php b/src/Tools/ResolveTargetEntityListener.php index b6b650ef123..9760abf00cf 100644 --- a/src/Tools/ResolveTargetEntityListener.php +++ b/src/Tools/ResolveTargetEntityListener.php @@ -8,6 +8,7 @@ use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use function array_key_exists; @@ -19,18 +20,16 @@ * * Mechanism to overwrite interfaces or classes specified as association * targets. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ class ResolveTargetEntityListener implements EventSubscriber { /** @var mixed[][] indexed by original entity name */ - private $resolveTargetEntities = []; + private array $resolveTargetEntities = []; /** * {@inheritDoc} */ - public function getSubscribedEvents() + public function getSubscribedEvents(): array { return [ Events::loadClassMetadata, @@ -41,30 +40,22 @@ public function getSubscribedEvents() /** * Adds a target-entity class name to resolve to a new class name. * - * @param string $originalEntity - * @param string $newEntity * @phpstan-param array $mapping - * - * @return void */ - public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping) + public function addResolveTargetEntity(string $originalEntity, string $newEntity, array $mapping): void { $mapping['targetEntity'] = ltrim($newEntity, '\\'); $this->resolveTargetEntities[ltrim($originalEntity, '\\')] = $mapping; } - /** - * @internal this is an event callback, and should not be called directly - * - * @return void - */ - public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) + /** @internal this is an event callback, and should not be called directly */ + public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args): void { if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { $args->setFoundMetadata( $args ->getObjectManager() - ->getClassMetadata($this->resolveTargetEntities[$args->getClassName()]['targetEntity']) + ->getClassMetadata($this->resolveTargetEntities[$args->getClassName()]['targetEntity']), ); } } @@ -73,15 +64,13 @@ public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) * Processes event and resolves new target entity names. * * @internal this is an event callback, and should not be called directly - * - * @return void */ - public function loadClassMetadata(LoadClassMetadataEventArgs $args) + public function loadClassMetadata(LoadClassMetadataEventArgs $args): void { $cm = $args->getClassMetadata(); foreach ($cm->associationMappings as $mapping) { - if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + if (isset($this->resolveTargetEntities[$mapping->targetEntity])) { $this->remapAssociation($cm, $mapping); } } @@ -99,16 +88,18 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $args) } } - /** @param AssociationMapping $mapping */ - private function remapAssociation(ClassMetadata $classMetadata, array $mapping): void + private function remapAssociation(ClassMetadata $classMetadata, AssociationMapping $mapping): void { - $newMapping = $this->resolveTargetEntities[$mapping['targetEntity']]; - $newMapping = array_replace_recursive($mapping, $newMapping); - $newMapping['fieldName'] = $mapping['fieldName']; + $newMapping = $this->resolveTargetEntities[$mapping->targetEntity]; + $newMapping = array_replace_recursive( + $mapping->toArray(), + $newMapping, + ); + $newMapping['fieldName'] = $mapping->fieldName; - unset($classMetadata->associationMappings[$mapping['fieldName']]); + unset($classMetadata->associationMappings[$mapping->fieldName]); - switch ($mapping['type']) { + switch ($mapping->type()) { case ClassMetadata::MANY_TO_MANY: $classMetadata->mapManyToMany($newMapping); break; diff --git a/src/Tools/SchemaTool.php b/src/Tools/SchemaTool.php index 5112b2f962b..5057d1866a8 100644 --- a/src/Tools/SchemaTool.php +++ b/src/Tools/SchemaTool.php @@ -5,19 +5,19 @@ namespace Doctrine\ORM\Tools; use BackedEnum; -use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\AbstractAsset; use Doctrine\DBAL\Schema\AbstractSchemaManager; -use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\DiscriminatorColumnMapping; +use Doctrine\ORM\Mapping\FieldMapping; +use Doctrine\ORM\Mapping\JoinColumnMapping; +use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping; use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\Mapping\QuoteStrategy; use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; @@ -31,13 +31,12 @@ use function array_filter; use function array_flip; use function array_intersect_key; +use function assert; use function count; use function current; -use function func_num_args; use function implode; use function in_array; use function is_numeric; -use function method_exists; use function strtolower; /** @@ -45,45 +44,24 @@ * ClassMetadata class descriptors. * * @link www.doctrine-project.org - * - * @phpstan-import-type AssociationMapping from ClassMetadata - * @phpstan-import-type DiscriminatorColumnMapping from ClassMetadata - * @phpstan-import-type FieldMapping from ClassMetadata - * @phpstan-import-type JoinColumnData from ClassMetadata */ class SchemaTool { - private const KNOWN_COLUMN_OPTIONS = ['comment', 'unsigned', 'fixed', 'default']; - - /** @var EntityManagerInterface */ - private $em; - - /** @var AbstractPlatform */ - private $platform; - - /** - * The quote strategy. - * - * @var QuoteStrategy - */ - private $quoteStrategy; + private const KNOWN_COLUMN_OPTIONS = ['comment', 'unsigned', 'fixed', 'default', 'values']; - /** @var AbstractSchemaManager */ - private $schemaManager; + private readonly AbstractPlatform $platform; + private readonly QuoteStrategy $quoteStrategy; + private readonly AbstractSchemaManager $schemaManager; /** * Initializes a new SchemaTool instance that uses the connection of the * provided EntityManager. */ - public function __construct(EntityManagerInterface $em) + public function __construct(private readonly EntityManagerInterface $em) { - $this->em = $em; $this->platform = $em->getConnection()->getDatabasePlatform(); $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); - $this->schemaManager = method_exists(Connection::class, 'createSchemaManager') - ? $em->getConnection()->createSchemaManager() - // @phpstan-ignore method.deprecated - : $em->getConnection()->getSchemaManager(); + $this->schemaManager = $em->getConnection()->createSchemaManager(); } /** @@ -91,11 +69,9 @@ public function __construct(EntityManagerInterface $em) * * @phpstan-param list $classes * - * @return void - * * @throws ToolsException */ - public function createSchema(array $classes) + public function createSchema(array $classes): void { $createSchemaSql = $this->getCreateSchemaSql($classes); $conn = $this->em->getConnection(); @@ -117,7 +93,7 @@ public function createSchema(array $classes) * * @return list The SQL statements needed to create the schema for the classes. */ - public function getCreateSchemaSql(array $classes) + public function getCreateSchemaSql(array $classes): array { $schema = $this->getSchemaFromMetadata($classes); @@ -131,7 +107,7 @@ public function getCreateSchemaSql(array $classes) */ private function processingNotRequired( ClassMetadata $class, - array $processedClasses + array $processedClasses, ): bool { return isset($processedClasses[$class->name]) || $class->isMappedSuperclass || @@ -159,8 +135,8 @@ private function getIndexColumns(ClassMetadata $class, array $indexData): array ) ) { throw MappingException::invalidIndexConfiguration( - $class, - $indexData['name'] ?? 'unnamed' + (string) $class, + $indexData['name'] ?? 'unnamed', ); } @@ -173,7 +149,9 @@ private function getIndexColumns(ClassMetadata $class, array $indexData): array if ($class->hasField($fieldName)) { $columns[] = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); } elseif ($class->hasAssociation($fieldName)) { - foreach ($class->getAssociationMapping($fieldName)['joinColumns'] as $joinColumn) { + $assoc = $class->getAssociationMapping($fieldName); + assert($assoc->isToOneOwningSide()); + foreach ($assoc->joinColumns as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } } @@ -188,11 +166,9 @@ private function getIndexColumns(ClassMetadata $class, array $indexData): array * * @phpstan-param list $classes * - * @return Schema - * * @throws NotSupported */ - public function getSchemaFromMetadata(array $classes) + public function getSchemaFromMetadata(array $classes): Schema { // Reminder for processed classes, used for hierarchies $processedClasses = []; @@ -233,7 +209,7 @@ public function getSchemaFromMetadata(array $classes) } elseif ($class->isInheritanceTypeJoined()) { // Add all non-inherited fields as columns foreach ($class->fieldMappings as $fieldName => $mapping) { - if (! isset($mapping['inherited'])) { + if (! isset($mapping->inherited)) { $this->gatherColumn($class, $mapping, $table); } } @@ -249,13 +225,13 @@ public function getSchemaFromMetadata(array $classes) $inheritedKeyColumns = []; foreach ($class->identifier as $identifierField) { - if (isset($class->fieldMappings[$identifierField]['inherited'])) { + if (isset($class->fieldMappings[$identifierField]->inherited)) { $idMapping = $class->fieldMappings[$identifierField]; $this->gatherColumn($class, $idMapping, $table); $columnName = $this->quoteStrategy->getColumnName( $identifierField, $class, - $this->platform + $this->platform, ); // TODO: This seems rather hackish, can we optimize it? $table->getColumn($columnName)->setAutoincrement(false); @@ -266,24 +242,23 @@ public function getSchemaFromMetadata(array $classes) continue; } - if (isset($class->associationMappings[$identifierField]['inherited'])) { + if (isset($class->associationMappings[$identifierField]->inherited)) { $idMapping = $class->associationMappings[$identifierField]; + assert($idMapping->isToOneOwningSide()); $targetEntity = current( array_filter( $classes, - static function (ClassMetadata $class) use ($idMapping): bool { - return $class->name === $idMapping['targetEntity']; - } - ) + static fn (ClassMetadata $class): bool => $class->name === $idMapping->targetEntity, + ), ); - foreach ($idMapping['joinColumns'] as $joinColumn) { - if (isset($targetEntity->fieldMappings[$joinColumn['referencedColumnName']])) { + foreach ($idMapping->joinColumns as $joinColumn) { + if (isset($targetEntity->fieldMappings[$joinColumn->referencedColumnName])) { $columnName = $this->quoteStrategy->getJoinColumnName( $joinColumn, $class, - $this->platform + $this->platform, ); $pkColumns[] = $columnName; @@ -298,11 +273,11 @@ static function (ClassMetadata $class) use ($idMapping): bool { $table->addForeignKeyConstraint( $this->quoteStrategy->getTableName( $this->em->getClassMetadata($class->rootEntityName), - $this->platform + $this->platform, ), $inheritedKeyColumns, $inheritedKeyColumns, - ['onDelete' => 'CASCADE'] + ['onDelete' => 'CASCADE'], ); } @@ -310,9 +285,6 @@ static function (ClassMetadata $class) use ($idMapping): bool { $table->setPrimaryKey($pkColumns); } } - // @phpstan-ignore method.deprecated - } elseif ($class->isInheritanceTypeTablePerClass()) { - throw NotSupported::create(); } else { $this->gatherColumns($class, $table); $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); @@ -325,8 +297,9 @@ static function (ClassMetadata $class) use ($idMapping): bool { $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform); } elseif (isset($class->associationMappings[$identifierField])) { $assoc = $class->associationMappings[$identifierField]; + assert($assoc->isToOneOwningSide()); - foreach ($assoc['joinColumns'] as $joinColumn) { + foreach ($assoc->joinColumns as $joinColumn) { $pkColumns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } } @@ -357,7 +330,7 @@ static function (ClassMetadata $class) use ($idMapping): bool { $this->getIndexColumns($class, $indexData), is_numeric($indexName) ? null : $indexName, (array) $indexData['flags'], - $indexData['options'] ?? [] + $indexData['options'] ?? [], ); } } @@ -367,8 +340,7 @@ static function (ClassMetadata $class) use ($idMapping): bool { $uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []); foreach ($table->getIndexes() as $tableIndexName => $tableIndex) { - $method = method_exists($tableIndex, 'isFulfilledBy') ? 'isFulfilledBy' : 'isFullfilledBy'; - if ($tableIndex->$method($uniqIndex)) { + if ($tableIndex->isFulfilledBy($uniqIndex)) { $table->dropIndex($tableIndexName); break; } @@ -393,7 +365,7 @@ static function (ClassMetadata $class) use ($idMapping): bool { $schema->createSequence( $quotedName, (int) $seqDef['allocationSize'], - (int) $seqDef['initialValue'] + (int) $seqDef['initialValue'], ); } } @@ -401,27 +373,15 @@ static function (ClassMetadata $class) use ($idMapping): bool { if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { $eventManager->dispatchEvent( ToolEvents::postGenerateSchemaTable, - new GenerateSchemaTableEventArgs($class, $schema, $table) + new GenerateSchemaTableEventArgs($class, $schema, $table), ); } } - if (! $this->platform->supportsSchemas()) { - $filter = /** @param Sequence|Table $asset */ static function ($asset) use ($schema): bool { - return ! $asset->isInDefaultNamespace($schema->getName()); - }; - - // @phpstan-ignore method.deprecated - if (array_filter($schema->getSequences() + $schema->getTables(), $filter) && ! $this->platform->canEmulateSchemas()) { - // @phpstan-ignore method.deprecated, new.deprecated - $schema->visit(new RemoveNamespacedAssets()); - } - } - if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { $eventManager->dispatchEvent( ToolEvents::postGenerateSchema, - new GenerateSchemaEventArgs($this->em, $schema) + new GenerateSchemaEventArgs($this->em, $schema), ); } @@ -435,26 +395,24 @@ static function (ClassMetadata $class) use ($idMapping): bool { private function addDiscriminatorColumnDefinition(ClassMetadata $class, Table $table): void { $discrColumn = $class->discriminatorColumn; + assert($discrColumn !== null); - if ( - ! isset($discrColumn['type']) || - (strtolower($discrColumn['type']) === 'string' && ! isset($discrColumn['length'])) - ) { - $discrColumn['type'] = 'string'; - $discrColumn['length'] = 255; + if (strtolower($discrColumn->type) === 'string' && ! isset($discrColumn->length)) { + $discrColumn->type = 'string'; + $discrColumn->length = 255; } $options = [ - 'length' => $discrColumn['length'] ?? null, + 'length' => $discrColumn->length ?? null, 'notnull' => true, ]; - if (isset($discrColumn['columnDefinition'])) { - $options['columnDefinition'] = $discrColumn['columnDefinition']; + if (isset($discrColumn->columnDefinition)) { + $options['columnDefinition'] = $discrColumn->columnDefinition; } $options = $this->gatherColumnOptions($discrColumn) + $options; - $table->addColumn($discrColumn['name'], $discrColumn['type'], $options); + $table->addColumn($discrColumn->name, $discrColumn->type, $options); } /** @@ -463,18 +421,12 @@ private function addDiscriminatorColumnDefinition(ClassMetadata $class, Table $t */ private function gatherColumns(ClassMetadata $class, Table $table): void { - $pkColumns = []; - foreach ($class->fieldMappings as $mapping) { - if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping->inherited)) { continue; } $this->gatherColumn($class, $mapping, $table); - - if ($class->isIdentifier($mapping['fieldName'])) { - $pkColumns[] = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); - } } } @@ -486,46 +438,46 @@ private function gatherColumns(ClassMetadata $class, Table $table): void */ private function gatherColumn( ClassMetadata $class, - array $mapping, - Table $table + FieldMapping $mapping, + Table $table, ): void { - $columnName = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); - $columnType = $mapping['type']; + $columnName = $this->quoteStrategy->getColumnName($mapping->fieldName, $class, $this->platform); + $columnType = $mapping->type; $options = []; - $options['length'] = $mapping['length'] ?? null; - $options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; + $options['length'] = $mapping->length ?? null; + $options['notnull'] = isset($mapping->nullable) ? ! $mapping->nullable : true; if ($class->isInheritanceTypeSingleTable() && $class->parentClasses) { $options['notnull'] = false; } $options['platformOptions'] = []; - $options['platformOptions']['version'] = $class->isVersioned && $class->versionField === $mapping['fieldName']; + $options['platformOptions']['version'] = $class->isVersioned && $class->versionField === $mapping->fieldName; if (strtolower($columnType) === 'string' && $options['length'] === null) { $options['length'] = 255; } - if (isset($mapping['precision'])) { - $options['precision'] = $mapping['precision']; + if (isset($mapping->precision)) { + $options['precision'] = $mapping->precision; } - if (isset($mapping['scale'])) { - $options['scale'] = $mapping['scale']; + if (isset($mapping->scale)) { + $options['scale'] = $mapping->scale; } - if (isset($mapping['default'])) { - $options['default'] = $mapping['default']; + if (isset($mapping->default)) { + $options['default'] = $mapping->default; } - if (isset($mapping['columnDefinition'])) { - $options['columnDefinition'] = $mapping['columnDefinition']; + if (isset($mapping->columnDefinition)) { + $options['columnDefinition'] = $mapping->columnDefinition; } // the 'default' option can be overwritten here $options = $this->gatherColumnOptions($mapping) + $options; - if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() === [$mapping['fieldName']]) { + if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() === [$mapping->fieldName]) { $options['autoincrement'] = true; } @@ -534,14 +486,13 @@ private function gatherColumn( } if ($table->hasColumn($columnName)) { - $method = method_exists($table, 'modifyColumn') ? 'modifyColumn' : 'changeColumn'; // required in some inheritance scenarios - $table->$method($columnName, $options); + $table->modifyColumn($columnName, $options); } else { $table->addColumn($columnName, $columnType, $options); } - $isUnique = $mapping['unique'] ?? false; + $isUnique = $mapping->unique ?? false; if ($isUnique) { $table->addUniqueIndex([$columnName]); } @@ -564,66 +515,61 @@ private function gatherRelationsSql( Table $table, Schema $schema, array &$addedFks, - array &$blacklistedFks + array &$blacklistedFks, ): void { foreach ($class->associationMappings as $id => $mapping) { - if (isset($mapping['inherited']) && ! in_array($id, $class->identifier, true)) { + if (isset($mapping->inherited) && ! in_array($id, $class->identifier, true)) { continue; } - $foreignClass = $this->em->getClassMetadata($mapping['targetEntity']); + $foreignClass = $this->em->getClassMetadata($mapping->targetEntity); - if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + if ($mapping->isToOneOwningSide()) { $primaryKeyColumns = []; // PK is unnecessary for this relation-type $this->gatherRelationJoinColumns( - $mapping['joinColumns'], + $mapping->joinColumns, $table, $foreignClass, $mapping, $primaryKeyColumns, $addedFks, - $blacklistedFks + $blacklistedFks, ); - } elseif ($mapping['type'] === ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { - //... create join table, one-many through join table supported later - throw NotSupported::create(); - } elseif ($mapping['type'] === ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { + } elseif ($mapping instanceof ManyToManyOwningSideMapping) { // create join table - $joinTable = $mapping['joinTable']; + $joinTable = $mapping->joinTable; $theJoinTable = $schema->createTable( - $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform) + $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform), ); - if (isset($joinTable['options'])) { - foreach ($joinTable['options'] as $key => $val) { - $theJoinTable->addOption($key, $val); - } + foreach ($joinTable->options as $key => $val) { + $theJoinTable->addOption($key, $val); } $primaryKeyColumns = []; // Build first FK constraint (relation table => source table) $this->gatherRelationJoinColumns( - $joinTable['joinColumns'], + $joinTable->joinColumns, $theJoinTable, $class, $mapping, $primaryKeyColumns, $addedFks, - $blacklistedFks + $blacklistedFks, ); // Build second FK constraint (relation table => target table) $this->gatherRelationJoinColumns( - $joinTable['inverseJoinColumns'], + $joinTable->inverseJoinColumns, $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $addedFks, - $blacklistedFks + $blacklistedFks, ); $theJoinTable->setPrimaryKey($primaryKeyColumns); @@ -642,7 +588,7 @@ private function gatherRelationsSql( * * @phpstan-return array{ClassMetadata, string}|null */ - private function getDefiningClass(ClassMetadata $class, string $referencedColumnName): ?array + private function getDefiningClass(ClassMetadata $class, string $referencedColumnName): array|null { $referencedFieldName = $class->getFieldName($referencedColumnName); @@ -658,8 +604,8 @@ private function getDefiningClass(ClassMetadata $class, string $referencedColumn && $class->getSingleAssociationJoinColumnName($fieldName) === $referencedColumnName ) { return $this->getDefiningClass( - $this->em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']), - $class->getSingleAssociationReferencedJoinColumnName($fieldName) + $this->em->getClassMetadata($class->associationMappings[$fieldName]->targetEntity), + $class->getSingleAssociationReferencedJoinColumnName($fieldName), ); } } @@ -671,8 +617,7 @@ private function getDefiningClass(ClassMetadata $class, string $referencedColumn /** * Gathers columns and fk constraints that are required for one part of relationship. * - * @phpstan-param array $joinColumns - * @phpstan-param AssociationMapping $mapping + * @phpstan-param list $joinColumns * @phpstan-param list $primaryKeyColumns * @phpstan-param arraygetDefiningClass( $class, - $joinColumn['referencedColumnName'] + $joinColumn->referencedColumnName, ); if (! $definingClass) { throw MissingColumnException::fromColumnSourceAndTarget( - $joinColumn['referencedColumnName'], - $mapping['sourceEntity'], - $mapping['targetEntity'] + $joinColumn->referencedColumnName, + $mapping->sourceEntity, + $mapping->targetEntity, ); } @@ -715,7 +660,7 @@ private function gatherRelationJoinColumns( $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( $joinColumn, $class, - $this->platform + $this->platform, ); $primaryKeyColumns[] = $quotedColumnName; @@ -731,38 +676,38 @@ private function gatherRelationJoinColumns( $columnOptions = ['notnull' => false]; - if (isset($joinColumn['columnDefinition'])) { - $columnOptions['columnDefinition'] = $joinColumn['columnDefinition']; - } elseif (isset($fieldMapping['columnDefinition'])) { - $columnOptions['columnDefinition'] = $fieldMapping['columnDefinition']; + if (isset($joinColumn->columnDefinition)) { + $columnOptions['columnDefinition'] = $joinColumn->columnDefinition; + } elseif (isset($fieldMapping->columnDefinition)) { + $columnOptions['columnDefinition'] = $fieldMapping->columnDefinition; } - if (isset($joinColumn['nullable'])) { - $columnOptions['notnull'] = ! $joinColumn['nullable']; + if (isset($joinColumn->nullable)) { + $columnOptions['notnull'] = ! $joinColumn->nullable; } $columnOptions += $this->gatherColumnOptions($fieldMapping); - if (isset($fieldMapping['length'])) { - $columnOptions['length'] = $fieldMapping['length']; + if (isset($fieldMapping->length)) { + $columnOptions['length'] = $fieldMapping->length; } - if ($fieldMapping['type'] === 'decimal') { - $columnOptions['scale'] = $fieldMapping['scale']; - $columnOptions['precision'] = $fieldMapping['precision']; + if ($fieldMapping->type === 'decimal') { + $columnOptions['scale'] = $fieldMapping->scale; + $columnOptions['precision'] = $fieldMapping->precision; } $columnOptions = $this->gatherColumnOptions($joinColumn) + $columnOptions; - $theJoinTable->addColumn($quotedColumnName, $fieldMapping['type'], $columnOptions); + $theJoinTable->addColumn($quotedColumnName, $fieldMapping->type, $columnOptions); } - if (isset($joinColumn['unique']) && $joinColumn['unique'] === true) { + if (isset($joinColumn->unique) && $joinColumn->unique === true) { $uniqueConstraints[] = ['columns' => [$quotedColumnName]]; } - if (isset($joinColumn['onDelete'])) { - $fkOptions['onDelete'] = $joinColumn['onDelete']; + if (isset($joinColumn->onDelete)) { + $fkOptions['onDelete'] = $joinColumn->onDelete; } } @@ -796,22 +741,18 @@ private function gatherRelationJoinColumns( $foreignTableName, $localColumns, $foreignColumns, - $fkOptions + $fkOptions, ); } } - /** - * @phpstan-param JoinColumnData|FieldMapping|DiscriminatorColumnMapping $mapping - * - * @return mixed[] - */ - private function gatherColumnOptions(array $mapping): array + /** @return mixed[] */ + private function gatherColumnOptions(JoinColumnMapping|FieldMapping|DiscriminatorColumnMapping $mapping): array { - $mappingOptions = $mapping['options'] ?? []; + $mappingOptions = $mapping->options ?? []; - if (isset($mapping['enumType'])) { - $mappingOptions['enumType'] = $mapping['enumType']; + if (isset($mapping->enumType)) { + $mappingOptions['enumType'] = $mapping->enumType; } if (($mappingOptions['default'] ?? null) instanceof BackedEnum) { @@ -835,10 +776,8 @@ private function gatherColumnOptions(array $mapping): array * issued for all classes of the schema and some probably just don't exist. * * @phpstan-param list $classes - * - * @return void */ - public function dropSchema(array $classes) + public function dropSchema(array $classes): void { $dropSchemaSql = $this->getDropSchemaSQL($classes); $conn = $this->em->getConnection(); @@ -846,7 +785,7 @@ public function dropSchema(array $classes) foreach ($dropSchemaSql as $sql) { try { $conn->executeStatement($sql); - } catch (Throwable $e) { + } catch (Throwable) { // ignored } } @@ -854,10 +793,8 @@ public function dropSchema(array $classes) /** * Drops all elements in the database of the current connection. - * - * @return void */ - public function dropDatabase() + public function dropDatabase(): void { $dropSchemaSql = $this->getDropDatabaseSQL(); $conn = $this->em->getConnection(); @@ -872,14 +809,10 @@ public function dropDatabase() * * @return list */ - public function getDropDatabaseSQL() + public function getDropDatabaseSQL(): array { - $method = method_exists(AbstractSchemaManager::class, 'introspectSchema') ? - 'introspectSchema' : - 'createSchema'; - return $this->schemaManager - ->$method() + ->introspectSchema() ->toDropSql($this->platform); } @@ -890,11 +823,11 @@ public function getDropDatabaseSQL() * * @return list */ - public function getDropSchemaSQL(array $classes) + public function getDropSchemaSQL(array $classes): array { $schema = $this->getSchemaFromMetadata($classes); - $deployedSchema = $this->introspectSchema(); + $deployedSchema = $this->schemaManager->introspectSchema(); foreach ($schema->getTables() as $table) { if (! $deployedSchema->hasTable($table->getName())) { @@ -933,26 +866,12 @@ public function getDropSchemaSQL(array $classes) * instances to the current database schema that is inspected. * * @param mixed[] $classes - * @param bool $saveMode If TRUE, only performs a partial update - * without dropping assets which are scheduled for deletion. - * - * @return void */ - public function updateSchema(array $classes, $saveMode = false) + public function updateSchema(array $classes): void { - if (func_num_args() > 1) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10153', - 'Passing $saveMode to %s() is deprecated and will not be possible in Doctrine ORM 3.0.', - __METHOD__ - ); - } - - $updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode); - $conn = $this->em->getConnection(); + $conn = $this->em->getConnection(); - foreach ($updateSchemaSql as $sql) { + foreach ($this->getUpdateSchemaSql($classes) as $sql) { $conn->executeStatement($sql); } } @@ -961,44 +880,17 @@ public function updateSchema(array $classes, $saveMode = false) * Gets the sequence of SQL statements that need to be performed in order * to bring the given class mappings in-synch with the relational schema. * - * @param bool $saveMode If TRUE, only generates SQL for a partial update - * that does not include SQL for dropping assets which are scheduled for deletion. - * @param list $classes The classes to consider. + * @param list $classes The classes to consider. * * @return list The sequence of SQL statements. */ - public function getUpdateSchemaSql(array $classes, $saveMode = false) + public function getUpdateSchemaSql(array $classes): array { - if (func_num_args() > 1) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10153', - 'Passing $saveMode to %s() is deprecated and will not be possible in Doctrine ORM 3.0.', - __METHOD__ - ); - } - $toSchema = $this->getSchemaFromMetadata($classes); $fromSchema = $this->createSchemaForComparison($toSchema); - - if (method_exists($this->schemaManager, 'createComparator')) { - $comparator = $this->schemaManager->createComparator(); - } else { - $comparator = new Comparator(); - } - + $comparator = $this->schemaManager->createComparator(); $schemaDiff = $comparator->compareSchemas($fromSchema, $toSchema); - if ($saveMode) { - // @phpstan-ignore method.deprecated - return $schemaDiff->toSaveSql($this->platform); - } - - if (! method_exists(AbstractPlatform::class, 'getAlterSchemaSQL')) { - // @phpstan-ignore method.deprecated - return $schemaDiff->toSql($this->platform); - } - return $this->platform->getAlterSchemaSQL($schemaDiff); } @@ -1014,7 +906,7 @@ private function createSchemaForComparison(Schema $toSchema): Schema $previousFilter = $config->getSchemaAssetsFilter(); if ($previousFilter === null) { - return $this->introspectSchema(); + return $this->schemaManager->introspectSchema(); } // whitelist assets we already know about in $toSchema, use the existing filter otherwise @@ -1025,19 +917,10 @@ private function createSchemaForComparison(Schema $toSchema): Schema }); try { - return $this->introspectSchema(); + return $this->schemaManager->introspectSchema(); } finally { // restore schema assets filter $config->setSchemaAssetsFilter($previousFilter); } } - - private function introspectSchema(): Schema - { - $method = method_exists($this->schemaManager, 'introspectSchema') - ? 'introspectSchema' - : 'createSchema'; - - return $this->schemaManager->$method(); - } } diff --git a/src/Tools/SchemaValidator.php b/src/Tools/SchemaValidator.php index 34705f8b170..f602f69b4d4 100644 --- a/src/Tools/SchemaValidator.php +++ b/src/Tools/SchemaValidator.php @@ -18,10 +18,9 @@ use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\TextType; use Doctrine\DBAL\Types\Type; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\FieldMapping; use ReflectionEnum; use ReflectionNamedType; @@ -36,30 +35,19 @@ use function class_exists; use function class_parents; use function count; -use function get_class; use function implode; use function in_array; use function interface_exists; use function is_a; use function sprintf; -use const PHP_VERSION_ID; - /** * Performs strict validation of the mapping schema * * @link www.doctrine-project.com - * - * @phpstan-import-type FieldMapping from ClassMetadata */ class SchemaValidator { - /** @var EntityManagerInterface */ - private $em; - - /** @var bool */ - private $validatePropertyTypes; - /** * It maps built-in Doctrine types to PHP types */ @@ -78,10 +66,10 @@ class SchemaValidator TextType::class => ['string'], ]; - public function __construct(EntityManagerInterface $em, bool $validatePropertyTypes = true) - { - $this->em = $em; - $this->validatePropertyTypes = $validatePropertyTypes; + public function __construct( + private readonly EntityManagerInterface $em, + private readonly bool $validatePropertyTypes = true, + ) { } /** @@ -96,7 +84,7 @@ public function __construct(EntityManagerInterface $em, bool $validatePropertyTy * * @phpstan-return array> */ - public function validateMapping() + public function validateMapping(): array { $errors = []; $cmf = $this->em->getMetadataFactory(); @@ -118,170 +106,148 @@ public function validateMapping() * @return string[] * @phpstan-return list */ - public function validateClass(ClassMetadataInfo $class) + public function validateClass(ClassMetadata $class): array { - if (! $class instanceof ClassMetadata) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/249', - 'Passing an instance of %s to %s is deprecated, please pass a ClassMetadata instance instead.', - get_class($class), - __METHOD__, - ClassMetadata::class - ); - } - $ce = []; $cmf = $this->em->getMetadataFactory(); foreach ($class->fieldMappings as $fieldName => $mapping) { - if (! Type::hasType($mapping['type'])) { - $ce[] = "The field '" . $class->name . '#' . $fieldName . "' uses a non-existent type '" . $mapping['type'] . "'."; + if (! Type::hasType($mapping->type)) { + $ce[] = "The field '" . $class->name . '#' . $fieldName . "' uses a non-existent type '" . $mapping->type . "'."; } } - // PHP 7.4 introduces the ability to type properties, so we can't validate them in previous versions - if (PHP_VERSION_ID >= 70400 && $this->validatePropertyTypes) { + if ($this->validatePropertyTypes) { array_push($ce, ...$this->validatePropertiesTypes($class)); } - if ($class->isEmbeddedClass && count($class->associationMappings) > 0) { - $ce[] = "Embeddable '" . $class->name . "' does not support associations"; - - return $ce; - } - foreach ($class->associationMappings as $fieldName => $assoc) { - if (! class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) { - $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; + if (! class_exists($assoc->targetEntity) || $cmf->isTransient($assoc->targetEntity)) { + $ce[] = "The target entity '" . $assoc->targetEntity . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; return $ce; } - $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']); + $targetMetadata = $cmf->getMetadataFor($assoc->targetEntity); if ($targetMetadata->isMappedSuperclass) { - $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is a mapped superclass. This is not possible since there is no table that a foreign key could refer to.'; + $ce[] = "The target entity '" . $assoc->targetEntity . "' specified on " . $class->name . '#' . $fieldName . ' is a mapped superclass. This is not possible since there is no table that a foreign key could refer to.'; return $ce; } - if ($assoc['mappedBy'] && $assoc['inversedBy']) { - $ce[] = 'The association ' . $class . '#' . $fieldName . ' cannot be defined as both inverse and owning.'; - } - - if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) { + if (isset($assoc->id) && $targetMetadata->containsForeignIdentifier) { $ce[] = "Cannot map association '" . $class->name . '#' . $fieldName . ' as identifier, because ' . "the target entity '" . $targetMetadata->name . "' also maps an association as identifier."; } - if ($assoc['mappedBy']) { - if ($targetMetadata->hasField($assoc['mappedBy'])) { + if (! $assoc->isOwningSide()) { + if ($targetMetadata->hasField($assoc->mappedBy)) { $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' . - 'field ' . $assoc['targetEntity'] . '#' . $assoc['mappedBy'] . ' which is not defined as association, but as field.'; + 'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which is not defined as association, but as field.'; } - if (! $targetMetadata->hasAssociation($assoc['mappedBy'])) { + if (! $targetMetadata->hasAssociation($assoc->mappedBy)) { $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' . - 'field ' . $assoc['targetEntity'] . '#' . $assoc['mappedBy'] . ' which does not exist.'; - } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] === null) { + 'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which does not exist.'; + } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy === null) { $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the inverse side of a ' . 'bi-directional relationship, but the specified mappedBy association on the target-entity ' . - $assoc['targetEntity'] . '#' . $assoc['mappedBy'] . ' does not contain the required ' . + $assoc->targetEntity . '#' . $assoc->mappedBy . ' does not contain the required ' . "'inversedBy=\"" . $fieldName . "\"' attribute."; - } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] !== $fieldName) { + } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy !== $fieldName) { $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' . - $assoc['targetEntity'] . '#' . $assoc['mappedBy'] . ' are ' . + $assoc->targetEntity . '#' . $assoc->mappedBy . ' are ' . 'inconsistent with each other.'; } } - if ($assoc['inversedBy']) { - if ($targetMetadata->hasField($assoc['inversedBy'])) { + if ($assoc->isOwningSide() && $assoc->inversedBy !== null) { + if ($targetMetadata->hasField($assoc->inversedBy)) { $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' . - 'field ' . $assoc['targetEntity'] . '#' . $assoc['inversedBy'] . ' which is not defined as association.'; + 'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which is not defined as association.'; } - if (! $targetMetadata->hasAssociation($assoc['inversedBy'])) { + if (! $targetMetadata->hasAssociation($assoc->inversedBy)) { $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' . - 'field ' . $assoc['targetEntity'] . '#' . $assoc['inversedBy'] . ' which does not exist.'; - } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] === null) { + 'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which does not exist.'; + } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->isOwningSide()) { $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the owning side of a ' . 'bi-directional relationship, but the specified inversedBy association on the target-entity ' . - $assoc['targetEntity'] . '#' . $assoc['inversedBy'] . ' does not contain the required ' . + $assoc->targetEntity . '#' . $assoc->inversedBy . ' does not contain the required ' . "'mappedBy=\"" . $fieldName . "\"' attribute."; - } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] !== $fieldName) { + } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy !== $fieldName) { $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' . - $assoc['targetEntity'] . '#' . $assoc['inversedBy'] . ' are ' . + $assoc->targetEntity . '#' . $assoc->inversedBy . ' are ' . 'inconsistent with each other.'; } // Verify inverse side/owning side match each other - if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) { - $targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']]; - if ($assoc['type'] === ClassMetadata::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadata::ONE_TO_ONE) { + if (array_key_exists($assoc->inversedBy, $targetMetadata->associationMappings)) { + $targetAssoc = $targetMetadata->associationMappings[$assoc->inversedBy]; + if ($assoc->isOneToOne() && ! $targetAssoc->isOneToOne()) { $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is one-to-one, then the inversed ' . - 'side ' . $targetMetadata->name . '#' . $assoc['inversedBy'] . ' has to be one-to-one as well.'; - } elseif ($assoc['type'] === ClassMetadata::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadata::ONE_TO_MANY) { + 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-one as well.'; + } elseif ($assoc->isManyToOne() && ! $targetAssoc->isOneToMany()) { $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-one, then the inversed ' . - 'side ' . $targetMetadata->name . '#' . $assoc['inversedBy'] . ' has to be one-to-many.'; - } elseif ($assoc['type'] === ClassMetadata::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadata::MANY_TO_MANY) { + 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-many.'; + } elseif ($assoc->isManyToMany() && ! $targetAssoc->isManyToMany()) { $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-many, then the inversed ' . - 'side ' . $targetMetadata->name . '#' . $assoc['inversedBy'] . ' has to be many-to-many as well.'; + 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be many-to-many as well.'; } } } - if ($assoc['isOwningSide']) { - if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { + if ($assoc->isOwningSide()) { + if ($assoc->isManyToManyOwningSide()) { $identifierColumns = $class->getIdentifierColumnNames(); - foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { - if (! in_array($joinColumn['referencedColumnName'], $identifierColumns, true)) { - $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + foreach ($assoc->joinTable->joinColumns as $joinColumn) { + if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) { + $ce[] = "The referenced column name '" . $joinColumn->referencedColumnName . "' " . "has to be a primary key column on the target entity class '" . $class->name . "'."; break; } } $identifierColumns = $targetMetadata->getIdentifierColumnNames(); - foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { - if (! in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns, true)) { - $ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " . + foreach ($assoc->joinTable->inverseJoinColumns as $inverseJoinColumn) { + if (! in_array($inverseJoinColumn->referencedColumnName, $identifierColumns, true)) { + $ce[] = "The referenced column name '" . $inverseJoinColumn->referencedColumnName . "' " . "has to be a primary key column on the target entity class '" . $targetMetadata->name . "'."; break; } } - if (count($targetMetadata->getIdentifierColumnNames()) !== count($assoc['joinTable']['inverseJoinColumns'])) { - $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + if (count($targetMetadata->getIdentifierColumnNames()) !== count($assoc->joinTable->inverseJoinColumns)) { + $ce[] = "The inverse join columns of the many-to-many table '" . $assoc->joinTable->name . "' " . "have to contain to ALL identifier columns of the target entity '" . $targetMetadata->name . "', " . - "however '" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) . + "however '" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc->relationToTargetKeyColumns))) . "' are missing."; } - if (count($class->getIdentifierColumnNames()) !== count($assoc['joinTable']['joinColumns'])) { - $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + if (count($class->getIdentifierColumnNames()) !== count($assoc->joinTable->joinColumns)) { + $ce[] = "The join columns of the many-to-many table '" . $assoc->joinTable->name . "' " . "have to contain to ALL identifier columns of the source entity '" . $class->name . "', " . - "however '" . implode(', ', array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) . + "however '" . implode(', ', array_diff($class->getIdentifierColumnNames(), array_values($assoc->relationToSourceKeyColumns))) . "' are missing."; } - } elseif ($assoc['type'] & ClassMetadata::TO_ONE) { + } elseif ($assoc->isToOneOwningSide()) { $identifierColumns = $targetMetadata->getIdentifierColumnNames(); - foreach ($assoc['joinColumns'] as $joinColumn) { - if (! in_array($joinColumn['referencedColumnName'], $identifierColumns, true)) { - $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + foreach ($assoc->joinColumns as $joinColumn) { + if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) { + $ce[] = "The referenced column name '" . $joinColumn->referencedColumnName . "' " . "has to be a primary key column on the target entity class '" . $targetMetadata->name . "'."; } } - if (count($identifierColumns) !== count($assoc['joinColumns'])) { + if (count($identifierColumns) !== count($assoc->joinColumns)) { $ids = []; - foreach ($assoc['joinColumns'] as $joinColumn) { - $ids[] = $joinColumn['name']; + foreach ($assoc->joinColumns as $joinColumn) { + $ids[] = $joinColumn->name; } - $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " . + $ce[] = "The join columns of the association '" . $assoc->fieldName . "' " . "have to match to ALL identifier columns of the target entity '" . $targetMetadata->name . "', " . "however '" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) . "' are missing."; @@ -289,8 +255,8 @@ public function validateClass(ClassMetadataInfo $class) } } - if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) { - foreach ($assoc['orderBy'] as $orderField => $orientation) { + if ($assoc->isOrdered()) { + foreach ($assoc->orderBy() as $orderField => $orientation) { if (! $targetMetadata->hasField($orderField) && ! $targetMetadata->hasAssociation($orderField)) { $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a foreign field ' . $orderField . ' that is not a field on the target entity ' . $targetMetadata->name . '.'; @@ -336,10 +302,8 @@ public function validateClass(ClassMetadataInfo $class) /** * Checks if the Database Schema is in sync with the current metadata state. - * - * @return bool */ - public function schemaInSyncWithMetadata() + public function schemaInSyncWithMetadata(): bool { return count($this->getUpdateSchemaList()) === 0; } @@ -355,23 +319,22 @@ public function getUpdateSchemaList(): array $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); - return $schemaTool->getUpdateSchemaSql($allMetadata, true); + return $schemaTool->getUpdateSchemaSql($allMetadata); } /** @return list containing the found issues */ - private function validatePropertiesTypes(ClassMetadataInfo $class): array + private function validatePropertiesTypes(ClassMetadata $class): array { return array_values( array_filter( array_map( - /** @param FieldMapping $fieldMapping */ - function (array $fieldMapping) use ($class): ?string { - $fieldName = $fieldMapping['fieldName']; + function (FieldMapping $fieldMapping) use ($class): string|null { + $fieldName = $fieldMapping->fieldName; assert(isset($class->reflFields[$fieldName])); $propertyType = $class->reflFields[$fieldName]->getType(); // If the field type is not a built-in type, we cannot check it - if (! Type::hasType($fieldMapping['type'])) { + if (! Type::hasType($fieldMapping->type)) { return null; } @@ -380,7 +343,7 @@ function (array $fieldMapping) use ($class): ?string { return null; } - $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping['type'])); + $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping->type)); //If the metadata field type is not a mapped built-in type, we cannot check it if ($metadataFieldType === null) { @@ -404,11 +367,11 @@ function (array $fieldMapping) use ($class): ?string { $fieldName, $propertyType, $backingType, - implode('|', $metadataFieldType) + implode('|', $metadataFieldType), ); } - if (! isset($fieldMapping['enumType']) || $propertyType === $fieldMapping['enumType']) { + if (! isset($fieldMapping->enumType) || $propertyType === $fieldMapping->enumType) { return null; } @@ -417,17 +380,17 @@ function (array $fieldMapping) use ($class): ?string { $class->name, $fieldName, $propertyType, - $fieldMapping['enumType'] + $fieldMapping->enumType, ); } if ( - isset($fieldMapping['enumType']) - && $propertyType !== $fieldMapping['enumType'] + isset($fieldMapping->enumType) + && $propertyType !== $fieldMapping->enumType && interface_exists($propertyType) - && is_a($fieldMapping['enumType'], $propertyType, true) + && is_a($fieldMapping->enumType, $propertyType, true) ) { - $backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType(); + $backingType = (string) (new ReflectionEnum($fieldMapping->enumType))->getBackingType(); if (in_array($backingType, $metadataFieldType, true)) { return null; @@ -437,14 +400,14 @@ function (array $fieldMapping) use ($class): ?string { "The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.", $class->name, $fieldName, - $fieldMapping['enumType'], + $fieldMapping->enumType, $backingType, - implode('|', $metadataFieldType) + implode('|', $metadataFieldType), ); } if ( - $fieldMapping['type'] === 'json' + $fieldMapping->type === 'json' && in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true) ) { return null; @@ -456,12 +419,12 @@ function (array $fieldMapping) use ($class): ?string { $fieldName, $propertyType, implode('|', $metadataFieldType), - $fieldMapping['type'] + $fieldMapping->type, ); }, - $class->fieldMappings - ) - ) + $class->fieldMappings, + ), + ), ); } @@ -471,9 +434,9 @@ function (array $fieldMapping) use ($class): ?string { * * @return list|null */ - private function findBuiltInType(Type $type): ?array + private function findBuiltInType(Type $type): array|null { - $typeName = get_class($type); + $typeName = $type::class; return self::BUILTIN_TYPES_MAP[$typeName] ?? null; } diff --git a/src/Tools/Setup.php b/src/Tools/Setup.php deleted file mode 100644 index f07053f7198..00000000000 --- a/src/Tools/Setup.php +++ /dev/null @@ -1,268 +0,0 @@ -register(); - - $loader = new ClassLoader('Symfony\Component', $directory . '/Doctrine'); - $loader->register(); - } - - /** - * Creates a configuration with an annotation metadata driver. - * - * @param string[] $paths - * @param bool $isDevMode - * @param string|null $proxyDir - * @param bool $useSimpleAnnotationReader - * - * @return Configuration - */ - public static function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, ?Cache $cache = null, $useSimpleAnnotationReader = true) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9443', - '%s is deprecated and will be removed in Doctrine 3.0, please use %s instead.', - self::class, - ORMSetup::class - ); - - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths, $useSimpleAnnotationReader)); - - return $config; - } - - /** - * Creates a configuration with an attribute metadata driver. - * - * @param string[] $paths - * @param bool $isDevMode - * @param string|null $proxyDir - */ - public static function createAttributeMetadataConfiguration( - array $paths, - $isDevMode = false, - $proxyDir = null, - ?Cache $cache = null - ): Configuration { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9443', - '%s is deprecated and will be removed in Doctrine 3.0, please use %s instead.', - self::class, - ORMSetup::class - ); - - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(new AttributeDriver($paths)); - - return $config; - } - - /** - * Creates a configuration with an XML metadata driver. - * - * @param string[] $paths - * @param bool $isDevMode - * @param string|null $proxyDir - * - * @return Configuration - */ - public static function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, ?Cache $cache = null) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9443', - '%s is deprecated and will be removed in Doctrine 3.0, please use %s instead.', - self::class, - ORMSetup::class - ); - - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(new XmlDriver($paths)); - - return $config; - } - - /** - * Creates a configuration with a YAML metadata driver. - * - * @deprecated YAML metadata mapping is deprecated and will be removed in 3.0 - * - * @param string[] $paths - * @param bool $isDevMode - * @param string|null $proxyDir - * - * @return Configuration - */ - public static function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, ?Cache $cache = null) - { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8465', - 'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to attribute or XML driver.' - ); - - $config = self::createConfiguration($isDevMode, $proxyDir, $cache); - $config->setMetadataDriverImpl(new YamlDriver($paths)); - - return $config; - } - - /** - * Creates a configuration without a metadata driver. - * - * @param bool $isDevMode - * @param string|null $proxyDir - * - * @return Configuration - */ - public static function createConfiguration($isDevMode = false, $proxyDir = null, ?Cache $cache = null) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/9443', - '%s is deprecated and will be removed in Doctrine 3.0, please use %s instead.', - self::class, - ORMSetup::class - ); - - $proxyDir = $proxyDir ?: sys_get_temp_dir(); - - $cache = self::createCacheConfiguration($isDevMode, $proxyDir, $cache); - - $config = new Configuration(); - - $config->setMetadataCache(CacheAdapter::wrap($cache)); - $config->setQueryCache(CacheAdapter::wrap($cache)); - $config->setResultCache(CacheAdapter::wrap($cache)); - $config->setProxyDir($proxyDir); - $config->setProxyNamespace('DoctrineProxies'); - $config->setAutoGenerateProxyClasses($isDevMode); - - return $config; - } - - private static function createCacheConfiguration(bool $isDevMode, string $proxyDir, ?Cache $cache): Cache - { - $cache = self::createCacheInstance($isDevMode, $cache); - - if (! $cache instanceof CacheProvider) { - return $cache; - } - - $namespace = $cache->getNamespace(); - - if ($namespace !== '') { - $namespace .= ':'; - } - - $cache->setNamespace($namespace . 'dc2_' . md5($proxyDir) . '_'); // to avoid collisions - - return $cache; - } - - private static function createCacheInstance(bool $isDevMode, ?Cache $cache): Cache - { - if ($cache !== null) { - return $cache; - } - - if (! class_exists(ArrayCache::class) && ! class_exists(ArrayAdapter::class)) { - throw new RuntimeException('Setup tool cannot configure caches without doctrine/cache 1.11 or symfony/cache. Please add an explicit dependency to either library.'); - } - - if ($isDevMode === true) { - $cache = class_exists(ArrayCache::class) ? new ArrayCache() : new ArrayAdapter(); - } elseif (extension_loaded('apcu') && apcu_enabled()) { - $cache = class_exists(ApcuCache::class) ? new ApcuCache() : new ApcuAdapter(); - } elseif (extension_loaded('memcached') && (class_exists(MemcachedCache::class) || MemcachedAdapter::isSupported())) { - $memcached = new Memcached(); - $memcached->addServer('127.0.0.1', 11211); - - if (class_exists(MemcachedCache::class)) { - $cache = new MemcachedCache(); - $cache->setMemcached($memcached); - } else { - $cache = new MemcachedAdapter($memcached); - } - } elseif (extension_loaded('redis')) { - $redis = new Redis(); - $redis->connect('127.0.0.1'); - - if (class_exists(RedisCache::class)) { - $cache = new RedisCache(); - $cache->setRedis($redis); - } else { - $cache = new RedisAdapter($redis); - } - } else { - $cache = class_exists(ArrayCache::class) ? new ArrayCache() : new ArrayAdapter(); - } - - return $cache instanceof Cache ? $cache : DoctrineProvider::wrap($cache); - } -} diff --git a/src/Tools/ToolsException.php b/src/Tools/ToolsException.php index da4db1d0829..e5cb973b430 100644 --- a/src/Tools/ToolsException.php +++ b/src/Tools/ToolsException.php @@ -5,31 +5,20 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\Exception\ORMException; +use RuntimeException; use Throwable; -use function sprintf; - /** * Tools related Exceptions. */ -class ToolsException extends ORMException +class ToolsException extends RuntimeException implements ORMException { public static function schemaToolFailure(string $sql, Throwable $e): self { return new self( "Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, 0, - $e + $e, ); } - - /** - * @param string $type - * - * @return ToolsException - */ - public static function couldNotMapDoctrine1Type($type) - { - return new self(sprintf("Could not map doctrine 1 type '%s'!", $type)); - } } diff --git a/src/TransactionRequiredException.php b/src/TransactionRequiredException.php index ad3a9f83b29..611454478a8 100644 --- a/src/TransactionRequiredException.php +++ b/src/TransactionRequiredException.php @@ -5,16 +5,16 @@ namespace Doctrine\ORM; use Doctrine\ORM\Exception\ORMException; +use LogicException; /** * Is thrown when a transaction is required for the current operation, but there is none open. * * @link www.doctrine-project.com */ -class TransactionRequiredException extends ORMException +class TransactionRequiredException extends LogicException implements ORMException { - /** @return TransactionRequiredException */ - public static function transactionRequired() + public static function transactionRequired(): self { return new self('An open transaction is required for this operation.'); } diff --git a/src/UnexpectedResultException.php b/src/UnexpectedResultException.php index e0b724d32b0..8d8c9661a15 100644 --- a/src/UnexpectedResultException.php +++ b/src/UnexpectedResultException.php @@ -5,10 +5,11 @@ namespace Doctrine\ORM; use Doctrine\ORM\Exception\ORMException; +use RuntimeException; /** * Exception for a unexpected query result. */ -class UnexpectedResultException extends ORMException +class UnexpectedResultException extends RuntimeException implements ORMException { } diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index ff30f14f6d8..9d86c34dbde 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -9,11 +9,12 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\EventManager; +use Doctrine\DBAL; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Cache\Persister\CachedPersister; use Doctrine\ORM\Event\ListenersInvoker; +use Doctrine\ORM\Event\OnClearEventArgs; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Event\PostFlushEventArgs; use Doctrine\ORM\Event\PostPersistEventArgs; @@ -30,9 +31,10 @@ use Doctrine\ORM\Internal\HydrationCompleteHandler; use Doctrine\ORM\Internal\StronglyConnectedComponents; use Doctrine\ORM\Internal\TopologicalSort; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException; -use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter; +use Doctrine\ORM\Mapping\ToManyInverseSideMapping; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\ORM\Persisters\Collection\ManyToManyPersister; use Doctrine\ORM\Persisters\Collection\OneToManyPersister; @@ -42,13 +44,11 @@ use Doctrine\ORM\Persisters\Entity\SingleTablePersister; use Doctrine\ORM\Proxy\InternalProxy; use Doctrine\ORM\Utility\IdentifierFlattener; -use Doctrine\Persistence\Mapping\RuntimeReflectionService; -use Doctrine\Persistence\NotifyPropertyChanged; -use Doctrine\Persistence\ObjectManagerAware; use Doctrine\Persistence\PropertyChangedListener; use Exception; use InvalidArgumentException; use RuntimeException; +use Stringable; use UnexpectedValueException; use function array_chunk; @@ -57,20 +57,15 @@ use function array_filter; use function array_key_exists; use function array_map; -use function array_merge; use function array_sum; use function array_values; use function assert; use function current; -use function func_get_arg; -use function func_num_args; -use function get_class; use function get_debug_type; use function implode; use function in_array; use function is_array; use function is_object; -use function method_exists; use function reset; use function spl_object_id; use function sprintf; @@ -82,8 +77,6 @@ * in the correct order. * * Internal note: This class contains highly performance-sensitive code. - * - * @phpstan-import-type AssociationMapping from ClassMetadata */ class UnitOfWork implements PropertyChangedListener { @@ -127,16 +120,15 @@ class UnitOfWork implements PropertyChangedListener * * @var array> */ - private $identityMap = []; + private array $identityMap = []; /** * Map of all identifiers of managed entities. * Keys are object ids (spl_object_id). * - * @var mixed[] * @phpstan-var array> */ - private $entityIdentifiers = []; + private array $entityIdentifiers = []; /** * Map of the original entity data of managed entities. @@ -149,7 +141,7 @@ class UnitOfWork implements PropertyChangedListener * * @phpstan-var array> */ - private $originalEntityData = []; + private array $originalEntityData = []; /** * Map of entity changes. Keys are object ids (spl_object_id). @@ -157,7 +149,7 @@ class UnitOfWork implements PropertyChangedListener * * @phpstan-var array> */ - private $entityChangeSets = []; + private array $entityChangeSets = []; /** * The (cached) states of any known entities. @@ -165,7 +157,7 @@ class UnitOfWork implements PropertyChangedListener * * @phpstan-var array */ - private $entityStates = []; + private array $entityStates = []; /** * Map of entities that are scheduled for dirty checking at commit time. @@ -174,35 +166,35 @@ class UnitOfWork implements PropertyChangedListener * * @var array> */ - private $scheduledForSynchronization = []; + private array $scheduledForSynchronization = []; /** * A list of all pending entity insertions. * * @phpstan-var array */ - private $entityInsertions = []; + private array $entityInsertions = []; /** * A list of all pending entity updates. * * @phpstan-var array */ - private $entityUpdates = []; + private array $entityUpdates = []; /** * Any pending extra updates that have been scheduled by persisters. * * @phpstan-var array}> */ - private $extraUpdates = []; + private array $extraUpdates = []; /** * A list of all pending entity deletions. * * @phpstan-var array */ - private $entityDeletions = []; + private array $entityDeletions = []; /** * New entities that were discovered through relationships that were not @@ -215,21 +207,21 @@ class UnitOfWork implements PropertyChangedListener * * @var array indexed by respective object spl_object_id() */ - private $nonCascadedNewDetectedEntities = []; + private array $nonCascadedNewDetectedEntities = []; /** * All pending collection deletions. * * @phpstan-var array> */ - private $collectionDeletions = []; + private array $collectionDeletions = []; /** * All pending collection updates. * * @phpstan-var array> */ - private $collectionUpdates = []; + private array $collectionUpdates = []; /** * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. @@ -238,7 +230,7 @@ class UnitOfWork implements PropertyChangedListener * * @phpstan-var array> */ - private $visitedCollections = []; + private array $visitedCollections = []; /** * List of collections visited during the changeset calculation that contain to-be-removed @@ -249,99 +241,81 @@ class UnitOfWork implements PropertyChangedListener * * @phpstan-var array> */ - private $pendingCollectionElementRemovals = []; - - /** - * The EntityManager that "owns" this UnitOfWork instance. - * - * @var EntityManagerInterface - */ - private $em; + private array $pendingCollectionElementRemovals = []; /** * The entity persister instances used to persist entity instances. * * @phpstan-var array */ - private $persisters = []; + private array $persisters = []; /** * The collection persister instances used to persist collections. * * @phpstan-var array */ - private $collectionPersisters = []; + private array $collectionPersisters = []; /** * The EventManager used for dispatching events. - * - * @var EventManager */ - private $evm; + private readonly EventManager $evm; /** * The ListenersInvoker used for dispatching events. - * - * @var ListenersInvoker */ - private $listenersInvoker; + private readonly ListenersInvoker $listenersInvoker; /** * The IdentifierFlattener used for manipulating identifiers - * - * @var IdentifierFlattener */ - private $identifierFlattener; + private readonly IdentifierFlattener $identifierFlattener; /** * Orphaned entities that are scheduled for removal. * * @phpstan-var array */ - private $orphanRemovals = []; + private array $orphanRemovals = []; /** * Read-Only objects are never evaluated * * @var array */ - private $readOnlyObjects = []; + private array $readOnlyObjects = []; /** * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. * * @var array> */ - private $eagerLoadingEntities = []; + private array $eagerLoadingEntities = []; /** @var array> */ - private $eagerLoadingCollections = []; + private array $eagerLoadingCollections = []; - /** @var bool */ - protected $hasCache = false; + protected bool $hasCache = false; /** * Helper for handling completion of hydration - * - * @var HydrationCompleteHandler */ - private $hydrationCompleteHandler; - - /** @var ReflectionPropertiesGetter */ - private $reflectionPropertiesGetter; + private readonly HydrationCompleteHandler $hydrationCompleteHandler; /** * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param EntityManagerInterface $em The EntityManager that "owns" this UnitOfWork instance. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; - $this->evm = $em->getEventManager(); - $this->listenersInvoker = new ListenersInvoker($em); - $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); - $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); - $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); - $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService()); + public function __construct( + private readonly EntityManagerInterface $em, + ) { + $this->evm = $em->getEventManager(); + $this->listenersInvoker = new ListenersInvoker($em); + $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); + $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); + $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); } /** @@ -357,23 +331,10 @@ public function __construct(EntityManagerInterface $em) * 4) All collection updates * 5) All entity deletions * - * @param object|mixed[]|null $entity - * - * @return void - * * @throws Exception */ - public function commit($entity = null) + public function commit(): void { - if ($entity !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8459', - 'Calling %s() with any arguments to commit specific entities is deprecated and will not be supported in Doctrine ORM 3.0.', - __METHOD__ - ); - } - $connection = $this->em->getConnection(); if ($connection instanceof PrimaryReadReplicaConnection) { @@ -386,15 +347,7 @@ public function commit($entity = null) } // Compute changes done since last commit. - if ($entity === null) { - $this->computeChangeSets(); - } elseif (is_object($entity)) { - $this->computeSingleEntityChangeSet($entity); - } elseif (is_array($entity)) { - foreach ($entity as $object) { - $this->computeSingleEntityChangeSet($object); - } - } + $this->computeChangeSets(); if ( ! ($this->entityInsertions || @@ -407,7 +360,7 @@ public function commit($entity = null) $this->dispatchOnFlushEvent(); $this->dispatchPostFlushEvent(); - $this->postCommitCleanup($entity); + $this->postCommitCleanup(); return; // Nothing to do. } @@ -433,7 +386,7 @@ public function commit($entity = null) // Deferred explicit tracked collections can be removed only when owning relation was persisted $owner = $collectionToDelete->getOwner(); - if ($this->em->getClassMetadata(get_class($owner))->isChangeTrackingDeferredImplicit() || $this->isScheduledForDirtyCheck($owner)) { + if ($this->em->getClassMetadata($owner::class)->isChangeTrackingDeferredImplicit() || $this->isScheduledForDirtyCheck($owner)) { $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete); } } @@ -472,11 +425,17 @@ public function commit($entity = null) $this->executeDeletions(); } - // Commit failed silently - if ($conn->commit() === false) { - $object = is_object($entity) ? $entity : null; + $commitFailed = false; + try { + if ($conn->commit() === false) { + $commitFailed = true; + } + } catch (DBAL\Exception $e) { + $commitFailed = true; + } - throw new OptimisticLockException('Commit failed', $object); + if ($commitFailed) { + throw new OptimisticLockException('Commit failed', null, $e ?? null); } $successful = true; @@ -508,11 +467,10 @@ public function commit($entity = null) $this->dispatchPostFlushEvent(); - $this->postCommitCleanup($entity); + $this->postCommitCleanup(); } - /** @param object|object[]|null $entity */ - private function postCommitCleanup($entity): void + private function postCommitCleanup(): void { $this->entityInsertions = $this->entityUpdates = @@ -523,25 +481,9 @@ private function postCommitCleanup($entity): void $this->collectionDeletions = $this->pendingCollectionElementRemovals = $this->visitedCollections = - $this->orphanRemovals = []; - - if ($entity === null) { - $this->entityChangeSets = $this->scheduledForSynchronization = []; - - return; - } - - $entities = is_object($entity) - ? [$entity] - : $entity; - - foreach ($entities as $object) { - $oid = spl_object_id($object); - - $this->clearEntityChangeSet($oid); - - unset($this->scheduledForSynchronization[$this->em->getClassMetadata(get_class($object))->rootEntityName][$oid]); - } + $this->orphanRemovals = + $this->entityChangeSets = + $this->scheduledForSynchronization = []; } /** @@ -550,54 +492,8 @@ private function postCommitCleanup($entity): void private function computeScheduleInsertsChangeSets(): void { foreach ($this->entityInsertions as $entity) { - $class = $this->em->getClassMetadata(get_class($entity)); - - $this->computeChangeSet($class, $entity); - } - } - - /** - * Only flushes the given entity according to a ruleset that keeps the UoW consistent. - * - * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well! - * 2. Read Only entities are skipped. - * 3. Proxies are skipped. - * 4. Only if entity is properly managed. - * - * @param object $entity - * - * @throws InvalidArgumentException - */ - private function computeSingleEntityChangeSet($entity): void - { - $state = $this->getEntityState($entity); - - if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) { - throw new InvalidArgumentException('Entity has to be managed or scheduled for removal for single computation ' . self::objToStr($entity)); - } - - $class = $this->em->getClassMetadata(get_class($entity)); - - if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) { - $this->persist($entity); - } - - // Compute changes for INSERTed entities first. This must always happen even in this case. - $this->computeScheduleInsertsChangeSets(); + $class = $this->em->getClassMetadata($entity::class); - if ($class->isReadOnly) { - return; - } - - // Ignore uninitialized proxy objects - if ($this->isUninitializedObject($entity)) { - return; - } - - // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here. - $oid = spl_object_id($entity); - - if (! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) { $this->computeChangeSet($class, $entity); } } @@ -611,7 +507,7 @@ private function executeExtraUpdates(): void [$entity, $changeset] = $update; $this->entityChangeSets[$oid] = $changeset; - $this->getEntityPersister(get_class($entity))->update($entity); + $this->getEntityPersister($entity::class)->update($entity); } $this->extraUpdates = []; @@ -620,12 +516,10 @@ private function executeExtraUpdates(): void /** * Gets the changeset for an entity. * - * @param object $entity - * * @return mixed[][] * @phpstan-return array */ - public function & getEntityChangeSet($entity) + public function & getEntityChangeSet(object $entity): array { $oid = spl_object_id($entity); $data = []; @@ -667,13 +561,11 @@ public function & getEntityChangeSet($entity) * @phpstan-param ClassMetadata $class * @phpstan-param T $entity * - * @return void - * * @template T of object * * @ignore */ - public function computeChangeSet(ClassMetadata $class, $entity) + public function computeChangeSet(ClassMetadata $class, object $entity): void { $oid = spl_object_id($entity); @@ -682,7 +574,7 @@ public function computeChangeSet(ClassMetadata $class, $entity) } if (! $class->isInheritanceTypeNone()) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); } $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER; @@ -712,12 +604,13 @@ public function computeChangeSet(ClassMetadata $class, $entity) } $assoc = $class->associationMappings[$name]; + assert($assoc->isToMany()); // Inject PersistentCollection $value = new PersistentCollection( $this->em, - $this->em->getClassMetadata($assoc['targetEntity']), - $value + $this->em->getClassMetadata($assoc->targetEntity), + $value, ); $value->setOwner($entity, $assoc); $value->setDirty(! $value->isEmpty()); @@ -749,7 +642,7 @@ public function computeChangeSet(ClassMetadata $class, $entity) $assoc = $class->associationMappings[$propName]; - if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc->isToOneOwningSide()) { $changeSet[$propName] = [null, $actualValue]; } } @@ -758,11 +651,8 @@ public function computeChangeSet(ClassMetadata $class, $entity) } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data - $originalData = $this->originalEntityData[$oid]; - $isChangeTrackingNotify = $class->isChangeTrackingNotify(); - $changeSet = $isChangeTrackingNotify && isset($this->entityChangeSets[$oid]) - ? $this->entityChangeSets[$oid] - : []; + $originalData = $this->originalEntityData[$oid]; + $changeSet = []; foreach ($actualData as $propName => $actualValue) { // skip field, its a partially omitted one! @@ -772,7 +662,7 @@ public function computeChangeSet(ClassMetadata $class, $entity) $orgValue = $originalData[$propName]; - if (! empty($class->fieldMappings[$propName]['enumType'])) { + if (! empty($class->fieldMappings[$propName]->enumType)) { if (is_array($orgValue)) { foreach ($orgValue as $id => $val) { if ($val instanceof BackedEnum) { @@ -793,10 +683,6 @@ public function computeChangeSet(ClassMetadata $class, $entity) // if regular field if (! isset($class->associationMappings[$propName])) { - if ($isChangeTrackingNotify) { - continue; - } - $changeSet[$propName] = [$orgValue, $actualValue]; continue; @@ -808,6 +694,7 @@ public function computeChangeSet(ClassMetadata $class, $entity) // created one. This can only mean it was cloned and replaced // on another entity. if ($actualValue instanceof PersistentCollection) { + assert($assoc->isToMany()); $owner = $actualValue->getOwner(); if ($owner === null) { // cloned $actualValue->setOwner($entity, $assoc); @@ -836,12 +723,12 @@ public function computeChangeSet(ClassMetadata $class, $entity) continue; } - if ($assoc['type'] & ClassMetadata::TO_ONE) { - if ($assoc['isOwningSide']) { + if ($assoc->isToOne()) { + if ($assoc->isOwningSide()) { $changeSet[$propName] = [$orgValue, $actualValue]; } - if ($orgValue !== null && $assoc['orphanRemoval']) { + if ($orgValue !== null && $assoc->orphanRemoval) { assert(is_object($orgValue)); $this->scheduleOrphanRemoval($orgValue); } @@ -866,8 +753,7 @@ public function computeChangeSet(ClassMetadata $class, $entity) if ( ! isset($this->entityChangeSets[$oid]) && - $assoc['isOwningSide'] && - $assoc['type'] === ClassMetadata::MANY_TO_MANY && + $assoc->isManyToManyOwningSide() && $val instanceof PersistentCollection && $val->isDirty() ) { @@ -882,10 +768,8 @@ public function computeChangeSet(ClassMetadata $class, $entity) * Computes all the changes that have been done to entities and collections * since the last commit and stores these changes in the _entityChangeSet map * temporarily for access by the persisters, until the UoW commit is finished. - * - * @return void */ - public function computeChangeSets() + public function computeChangeSets(): void { // Compute changes for INSERTed entities first. This must always happen. $this->computeScheduleInsertsChangeSets(); @@ -899,20 +783,11 @@ public function computeChangeSets() continue; } - // If change tracking is explicit or happens through notification, then only compute - // changes on entities of that type that are explicitly marked for synchronization. - switch (true) { - case $class->isChangeTrackingDeferredImplicit(): - $entitiesToProcess = $entities; - break; - - case isset($this->scheduledForSynchronization[$className]): - $entitiesToProcess = $this->scheduledForSynchronization[$className]; - break; - - default: - $entitiesToProcess = []; - } + $entitiesToProcess = match (true) { + $class->isChangeTrackingDeferredImplicit() => $entities, + isset($this->scheduledForSynchronization[$className]) => $this->scheduledForSynchronization[$className], + default => [], + }; foreach ($entitiesToProcess as $entity) { // Ignore uninitialized proxy objects @@ -934,12 +809,11 @@ public function computeChangeSets() * Computes the changes of an association. * * @param mixed $value The value of the association. - * @phpstan-param AssociationMapping $assoc The association mapping. * * @throws ORMInvalidArgumentException * @throws ORMException */ - private function computeAssociationChanges(array $assoc, $value): void + private function computeAssociationChanges(AssociationMapping $assoc, mixed $value): void { if ($this->isUninitializedObject($value)) { return; @@ -956,8 +830,8 @@ private function computeAssociationChanges(array $assoc, $value): void // Look through the entities, and in any of their associations, // for transient (new) entities, recursively. ("Persistence by reachability") // Unwrap. Uninitialized collections will simply be empty. - $unwrappedValue = $assoc['type'] & ClassMetadata::TO_ONE ? [$value] : $value->unwrap(); - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $unwrappedValue = $assoc->isToOne() ? [$value] : $value->unwrap(); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); foreach ($unwrappedValue as $key => $entry) { if (! ($entry instanceof $targetClass->name)) { @@ -966,18 +840,18 @@ private function computeAssociationChanges(array $assoc, $value): void $state = $this->getEntityState($entry, self::STATE_NEW); - if (! ($entry instanceof $assoc['targetEntity'])) { + if (! ($entry instanceof $assoc->targetEntity)) { throw UnexpectedAssociationValue::create( - $assoc['sourceEntity'], - $assoc['fieldName'], + $assoc->sourceEntity, + $assoc->fieldName, get_debug_type($entry), - $assoc['targetEntity'] + $assoc->targetEntity, ); } switch ($state) { case self::STATE_NEW: - if (! $assoc['isCascadePersist']) { + if (! $assoc->isCascadePersist()) { /* * For now just record the details, because this may * not be an issue if we later discover another pathway @@ -997,7 +871,7 @@ private function computeAssociationChanges(array $assoc, $value): void case self::STATE_REMOVED: // Consume the $value as array (it's either an array or an ArrayAccess) // and remove the element from Collection. - if (! ($assoc['type'] & ClassMetadata::TO_MANY)) { + if (! $assoc->isToMany()) { break; } @@ -1024,13 +898,12 @@ private function computeAssociationChanges(array $assoc, $value): void } /** - * @param object $entity * @phpstan-param ClassMetadata $class * @phpstan-param T $entity * * @template T of object */ - private function persistNew(ClassMetadata $class, $entity): void + private function persistNew(ClassMetadata $class, object $entity): void { $oid = spl_object_id($entity); $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist); @@ -1088,14 +961,12 @@ private function hasMissingIdsWhichAreForeignKeys(ClassMetadata $class, array $i * @phpstan-param ClassMetadata $class * @phpstan-param T $entity * - * @return void - * * @throws ORMInvalidArgumentException If the passed entity is not MANAGED. * * @template T of object * @ignore */ - public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) + public function recomputeSingleEntityChangeSet(ClassMetadata $class, object $entity): void { $oid = spl_object_id($entity); @@ -1103,13 +974,8 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) throw ORMInvalidArgumentException::entityNotManaged($entity); } - // skip if change tracking is "NOTIFY" - if ($class->isChangeTrackingNotify()) { - return; - } - if (! $class->isInheritanceTypeNone()) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); } $actualData = []; @@ -1134,7 +1000,7 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) foreach ($actualData as $propName => $actualValue) { $orgValue = $originalData[$propName] ?? null; - if (isset($class->fieldMappings[$propName]['enumType'])) { + if (isset($class->fieldMappings[$propName]->enumType)) { if (is_array($orgValue)) { foreach ($orgValue as $id => $val) { if ($val instanceof BackedEnum) { @@ -1155,7 +1021,7 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) if ($changeSet) { if (isset($this->entityChangeSets[$oid])) { - $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + $this->entityChangeSets[$oid] = [...$this->entityChangeSets[$oid], ...$changeSet]; } elseif (! isset($this->entityInsertions[$oid])) { $this->entityChangeSets[$oid] = $changeSet; $this->entityUpdates[$oid] = $entity; @@ -1175,27 +1041,14 @@ private function executeInserts(): void foreach ($entities as $entity) { $oid = spl_object_id($entity); - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $persister = $this->getEntityPersister($class->name); $persister->addInsert($entity); unset($this->entityInsertions[$oid]); - $postInsertIds = $persister->executeInserts(); - - if (is_array($postInsertIds)) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10743/', - 'Returning post insert IDs from \Doctrine\ORM\Persisters\Entity\EntityPersister::executeInserts() is deprecated and will not be supported in Doctrine ORM 3.0. Make the persister call Doctrine\ORM\UnitOfWork::assignPostInsertId() instead.' - ); - - // Persister returned post-insert IDs - foreach ($postInsertIds as $postInsertId) { - $this->assignPostInsertId($postInsertId['entity'], $postInsertId['generatedId']); - } - } + $persister->executeInserts(); if (! isset($this->entityIdentifiers[$oid])) { //entity was not added to identity map because some identifiers are foreign keys to new entities. @@ -1218,13 +1071,12 @@ private function executeInserts(): void Events::postPersist, $event['entity'], new PostPersistEventArgs($event['entity'], $this->em), - $event['invoke'] + $event['invoke'], ); } } /** - * @param object $entity * @phpstan-param ClassMetadata $class * @phpstan-param T $entity * @@ -1233,7 +1085,7 @@ private function executeInserts(): void private function addToEntityIdentifiersAndEntityMap( ClassMetadata $class, int $oid, - $entity + object $entity, ): void { $identifier = []; @@ -1262,7 +1114,7 @@ private function addToEntityIdentifiersAndEntityMap( private function executeUpdates(): void { foreach ($this->entityUpdates as $oid => $entity) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $persister = $this->getEntityPersister($class->name); $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate); $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate); @@ -1297,7 +1149,7 @@ private function executeDeletions(): void $this->removeFromIdentityMap($entity); $oid = spl_object_id($entity); - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $persister = $this->getEntityPersister($class->name); $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove); @@ -1307,7 +1159,7 @@ private function executeDeletions(): void $this->entityDeletions[$oid], $this->entityIdentifiers[$oid], $this->originalEntityData[$oid], - $this->entityStates[$oid] + $this->entityStates[$oid], ); // Entity with this $oid after deletion treated as NEW, even if the $oid @@ -1329,7 +1181,7 @@ private function executeDeletions(): void Events::postRemove, $event['entity'], new PostRemoveEventArgs($event['entity'], $this->em), - $event['invoke'] + $event['invoke'], ); } } @@ -1346,18 +1198,18 @@ private function computeInsertExecutionOrder(): array // Now add edges foreach ($this->entityInsertions as $entity) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); foreach ($class->associationMappings as $assoc) { // We only need to consider the owning sides of to-one associations, // since many-to-many associations are persisted at a later step and // have no insertion order problems (all entities already in the database // at that time). - if (! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + if (! $assoc->isToOneOwningSide()) { continue; } - $targetEntity = $class->getFieldValue($entity, $assoc['fieldName']); + $targetEntity = $class->getFieldValue($entity, $assoc->fieldName); // If there is no entity that we need to refer to, or it is already in the // database (i. e. does not have to be inserted), no need to consider it. @@ -1379,9 +1231,8 @@ private function computeInsertExecutionOrder(): array // // Same in \Doctrine\ORM\Tools\EntityGenerator::isAssociationIsNullable or \Doctrine\ORM\Persisters\Entity\BasicEntityPersister::getJoinSQLForJoinColumns, // to give two examples. - assert(isset($assoc['joinColumns'])); - $joinColumns = reset($assoc['joinColumns']); - $isNullable = ! isset($joinColumns['nullable']) || $joinColumns['nullable']; + $joinColumns = reset($assoc->joinColumns); + $isNullable = ! isset($joinColumns->nullable) || $joinColumns->nullable; // Add dependency. The dependency direction implies that "$entity depends on $targetEntity". The // topological sort result will output the depended-upon nodes first, which means we can insert @@ -1410,28 +1261,27 @@ private function computeDeleteExecutionOrder(): array // we need to treat those groups like a single entity when performing delete // order topological sorting. foreach ($this->entityDeletions as $entity) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); foreach ($class->associationMappings as $assoc) { // We only need to consider the owning sides of to-one associations, // since many-to-many associations can always be (and have already been) // deleted in a preceding step. - if (! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + if (! $assoc->isToOneOwningSide()) { continue; } - assert(isset($assoc['joinColumns'])); - $joinColumns = reset($assoc['joinColumns']); - if (! isset($joinColumns['onDelete'])) { + $joinColumns = reset($assoc->joinColumns); + if (! isset($joinColumns->onDelete)) { continue; } - $onDeleteOption = strtolower($joinColumns['onDelete']); + $onDeleteOption = strtolower($joinColumns->onDelete); if ($onDeleteOption !== 'cascade') { continue; } - $targetEntity = $class->getFieldValue($entity, $assoc['fieldName']); + $targetEntity = $class->getFieldValue($entity, $assoc->fieldName); // If the association does not refer to another entity or that entity // is not to be deleted, there is no ordering problem and we can @@ -1448,7 +1298,7 @@ private function computeDeleteExecutionOrder(): array // Now do the actual topological sorting to find the delete order. foreach ($this->entityDeletions as $entity) { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); // Get the entities representing the SCC $entityComponent = $stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($entity); @@ -1466,7 +1316,7 @@ private function computeDeleteExecutionOrder(): array // We only need to consider the owning sides of to-one associations, // since many-to-many associations can always be (and have already been) // deleted in a preceding step. - if (! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + if (! $assoc->isToOneOwningSide()) { continue; } @@ -1474,16 +1324,15 @@ private function computeDeleteExecutionOrder(): array // we do not have to follow a particular order: If the referred-to entity is // deleted first, the DBMS will temporarily set the foreign key to NULL (SET NULL). // So, we can skip it in the computation. - assert(isset($assoc['joinColumns'])); - $joinColumns = reset($assoc['joinColumns']); - if (isset($joinColumns['onDelete'])) { - $onDeleteOption = strtolower($joinColumns['onDelete']); + $joinColumns = reset($assoc->joinColumns); + if (isset($joinColumns->onDelete)) { + $onDeleteOption = strtolower($joinColumns->onDelete); if ($onDeleteOption === 'set null') { continue; } } - $targetEntity = $class->getFieldValue($entity, $assoc['fieldName']); + $targetEntity = $class->getFieldValue($entity, $assoc->fieldName); // If the association does not refer to another entity or that entity // is not to be deleted, there is no ordering problem and we can @@ -1513,14 +1362,10 @@ private function computeDeleteExecutionOrder(): array * Schedules an entity for insertion into the database. * If the entity already has an identifier, it will be added to the identity map. * - * @param object $entity The entity to schedule for insertion. - * - * @return void - * * @throws ORMInvalidArgumentException * @throws InvalidArgumentException */ - public function scheduleForInsert($entity) + public function scheduleForInsert(object $entity): void { $oid = spl_object_id($entity); @@ -1545,20 +1390,12 @@ public function scheduleForInsert($entity) if (isset($this->entityIdentifiers[$oid])) { $this->addToIdentityMap($entity); } - - if ($entity instanceof NotifyPropertyChanged) { - $entity->addPropertyChangedListener($this); - } } /** * Checks whether an entity is scheduled for insertion. - * - * @param object $entity - * - * @return bool */ - public function isScheduledForInsert($entity) + public function isScheduledForInsert(object $entity): bool { return isset($this->entityInsertions[spl_object_id($entity)]); } @@ -1566,13 +1403,9 @@ public function isScheduledForInsert($entity) /** * Schedules an entity for being updated. * - * @param object $entity The entity to schedule for being updated. - * - * @return void - * * @throws ORMInvalidArgumentException */ - public function scheduleForUpdate($entity) + public function scheduleForUpdate(object $entity): void { $oid = spl_object_id($entity); @@ -1596,14 +1429,11 @@ public function scheduleForUpdate($entity) * * Extra updates for entities are stored as (entity, changeset) tuples. * - * @param object $entity The entity for which to schedule an extra update. * @phpstan-param array $changeset The changeset of the entity (what to update). * - * @return void - * * @ignore */ - public function scheduleExtraUpdate($entity, array $changeset) + public function scheduleExtraUpdate(object $entity, array $changeset): void { $oid = spl_object_id($entity); $extraUpdate = [$entity, $changeset]; @@ -1621,26 +1451,18 @@ public function scheduleExtraUpdate($entity, array $changeset) * Checks whether an entity is registered as dirty in the unit of work. * Note: Is not very useful currently as dirty entities are only registered * at commit time. - * - * @param object $entity - * - * @return bool */ - public function isScheduledForUpdate($entity) + public function isScheduledForUpdate(object $entity): bool { return isset($this->entityUpdates[spl_object_id($entity)]); } /** * Checks whether an entity is registered to be checked in the unit of work. - * - * @param object $entity - * - * @return bool */ - public function isScheduledForDirtyCheck($entity) + public function isScheduledForDirtyCheck(object $entity): bool { - $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + $rootEntityName = $this->em->getClassMetadata($entity::class)->rootEntityName; return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_id($entity)]); } @@ -1648,12 +1470,8 @@ public function isScheduledForDirtyCheck($entity) /** * INTERNAL: * Schedules an entity for deletion. - * - * @param object $entity - * - * @return void */ - public function scheduleForDelete($entity) + public function scheduleForDelete(object $entity): void { $oid = spl_object_id($entity); @@ -1682,24 +1500,16 @@ public function scheduleForDelete($entity) /** * Checks whether an entity is registered as removed/deleted with the unit * of work. - * - * @param object $entity - * - * @return bool */ - public function isScheduledForDelete($entity) + public function isScheduledForDelete(object $entity): bool { return isset($this->entityDeletions[spl_object_id($entity)]); } /** * Checks whether an entity is scheduled for insertion, update or deletion. - * - * @param object $entity - * - * @return bool */ - public function isEntityScheduled($entity) + public function isEntityScheduled(object $entity): bool { $oid = spl_object_id($entity); @@ -1714,8 +1524,6 @@ public function isEntityScheduled($entity) * Note that entities in a hierarchy are registered with the class name of * the root entity. * - * @param object $entity The entity to register. - * * @return bool TRUE if the registration was successful, FALSE if the identity of * the entity in question is already managed. * @@ -1724,46 +1532,15 @@ public function isEntityScheduled($entity) * * @ignore */ - public function addToIdentityMap($entity) + public function addToIdentityMap(object $entity): bool { - $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $classMetadata = $this->em->getClassMetadata($entity::class); $idHash = $this->getIdHashByEntity($entity); $className = $classMetadata->rootEntityName; if (isset($this->identityMap[$className][$idHash])) { if ($this->identityMap[$className][$idHash] !== $entity) { - if ($this->em->getConfiguration()->isRejectIdCollisionInIdentityMapEnabled()) { - throw EntityIdentityCollisionException::create($this->identityMap[$className][$idHash], $entity, $idHash); - } - - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/pull/10785', - <<<'EXCEPTION' -While adding an entity of class %s with an ID hash of "%s" to the identity map, -another object of class %s was already present for the same ID. This will trigger -an exception in ORM 3.0. - -IDs should uniquely map to entity object instances. This problem may occur if: - -- you use application-provided IDs and reuse ID values; -- database-provided IDs are reassigned after truncating the database without -clearing the EntityManager; -- you might have been using EntityManager#getReference() to create a reference -for a nonexistent ID that was subsequently (by the RDBMS) assigned to another -entity. - -Otherwise, it might be an ORM-internal inconsistency, please report it. - -To opt-in to the new exception, call -\Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap on the entity -manager's configuration. -EXCEPTION - , - get_class($entity), - $idHash, - get_class($this->identityMap[$className][$idHash]) - ); + throw EntityIdentityCollisionException::create($this->identityMap[$className][$idHash], $entity, $idHash); } return false; @@ -1791,7 +1568,7 @@ final public static function getIdHashByIdentifier(array $identifier): string return implode( ' ', - $identifier + $identifier, ); } @@ -1802,12 +1579,12 @@ final public static function getIdHashByIdentifier(array $identifier): string * * @return string The entity id hash. */ - public function getIdHashByEntity($entity): string + public function getIdHashByEntity(object $entity): string { $identifier = $this->entityIdentifiers[spl_object_id($entity)]; if (empty($identifier) || in_array(null, $identifier, true)) { - $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $classMetadata = $this->em->getClassMetadata($entity::class); throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity); } @@ -1818,17 +1595,15 @@ public function getIdHashByEntity($entity): string /** * Gets the state of an entity with regard to the current unit of work. * - * @param object $entity * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED). * This parameter can be set to improve performance of entity state detection * by potentially avoiding a database lookup if the distinction between NEW and DETACHED * is either known or does not matter for the caller of the method. * @phpstan-param self::STATE_*|null $assume * - * @return int The entity state. * @phpstan-return self::STATE_* */ - public function getEntityState($entity, $assume = null) + public function getEntityState(object $entity, int|null $assume = null): int { $oid = spl_object_id($entity); @@ -1844,7 +1619,7 @@ public function getEntityState($entity, $assume = null) // Note that you can not remember the NEW or DETACHED state in _entityStates since // the UoW does not hold references to such objects and the object hash can be reused. // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it. - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $id = $class->getIdentifierValues($entity); if (! $id) { @@ -1905,18 +1680,14 @@ public function getEntityState($entity, $assume = null) * Removes an entity from the identity map. This effectively detaches the * entity from the persistence management of Doctrine. * - * @param object $entity - * - * @return bool - * * @throws ORMInvalidArgumentException * * @ignore */ - public function removeFromIdentityMap($entity) + public function removeFromIdentityMap(object $entity): bool { $oid = spl_object_id($entity); - $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $classMetadata = $this->em->getClassMetadata($entity::class); $idHash = self::getIdHashByIdentifier($this->entityIdentifiers[$oid]); if ($idHash === '') { @@ -1940,14 +1711,9 @@ public function removeFromIdentityMap($entity) * INTERNAL: * Gets an entity in the identity map by its identifier hash. * - * @param string $idHash - * @param string $rootClassName - * - * @return object - * * @ignore */ - public function getByIdHash($idHash, $rootClassName) + public function getByIdHash(string $idHash, string $rootClassName): object|null { return $this->identityMap[$rootClassName][$idHash]; } @@ -1957,14 +1723,13 @@ public function getByIdHash($idHash, $rootClassName) * Tries to get an entity by its identifier hash. If no entity is found for * the given hash, FALSE is returned. * - * @param mixed $idHash (must be possible to cast it to string) - * @param string $rootClassName + * @param mixed $idHash (must be possible to cast it to string) * * @return false|object The found entity or FALSE. * * @ignore */ - public function tryGetByIdHash($idHash, $rootClassName) + public function tryGetByIdHash(mixed $idHash, string $rootClassName): object|false { $stringIdHash = (string) $idHash; @@ -1973,12 +1738,8 @@ public function tryGetByIdHash($idHash, $rootClassName) /** * Checks whether an entity is registered in the identity map of this UnitOfWork. - * - * @param object $entity - * - * @return bool */ - public function isInIdentityMap($entity) + public function isInIdentityMap(object $entity): bool { $oid = spl_object_id($entity); @@ -1986,36 +1747,16 @@ public function isInIdentityMap($entity) return false; } - $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $classMetadata = $this->em->getClassMetadata($entity::class); $idHash = self::getIdHashByIdentifier($this->entityIdentifiers[$oid]); return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]); } - /** - * INTERNAL: - * Checks whether an identifier hash exists in the identity map. - * - * @param string $idHash - * @param string $rootClassName - * - * @return bool - * - * @ignore - */ - public function containsIdHash($idHash, $rootClassName) - { - return isset($this->identityMap[$rootClassName][$idHash]); - } - /** * Persists an entity as part of the current unit of work. - * - * @param object $entity The entity to persist. - * - * @return void */ - public function persist($entity) + public function persist(object $entity): void { $visited = []; @@ -2028,13 +1769,12 @@ public function persist($entity) * This method is internally called during persist() cascades as it tracks * the already visited entities to prevent infinite recursions. * - * @param object $entity The entity to persist. * @phpstan-param array $visited The already visited entities. * * @throws ORMInvalidArgumentException * @throws UnexpectedValueException */ - private function doPersist($entity, array &$visited): void + private function doPersist(object $entity, array &$visited): void { $oid = spl_object_id($entity); @@ -2044,7 +1784,7 @@ private function doPersist($entity, array &$visited): void $visited[$oid] = $entity; // Mark visited - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation). // If we would detect DETACHED here we would throw an exception anyway with the same @@ -2086,7 +1826,7 @@ private function doPersist($entity, array &$visited): void throw new UnexpectedValueException(sprintf( 'Unexpected entity state: %s. %s', $entityState, - self::objToStr($entity) + self::objToStr($entity), )); } @@ -2095,12 +1835,8 @@ private function doPersist($entity, array &$visited): void /** * Deletes an entity as part of the current unit of work. - * - * @param object $entity The entity to remove. - * - * @return void */ - public function remove($entity) + public function remove(object $entity): void { $visited = []; @@ -2113,13 +1849,12 @@ public function remove($entity) * This method is internally called during delete() cascades as it tracks * the already visited entities to prevent infinite recursions. * - * @param object $entity The entity to delete. * @phpstan-param array $visited The map of the already visited entities. * * @throws ORMInvalidArgumentException If the instance is a detached entity. * @throws UnexpectedValueException */ - private function doRemove($entity, array &$visited): void + private function doRemove(object $entity, array &$visited): void { $oid = spl_object_id($entity); @@ -2133,7 +1868,7 @@ private function doRemove($entity, array &$visited): void // can cause problems when a lazy proxy has to be initialized for the cascade operation. $this->cascadeRemove($entity, $visited); - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $entityState = $this->getEntityState($entity); switch ($entityState) { @@ -2159,211 +1894,16 @@ private function doRemove($entity, array &$visited): void throw new UnexpectedValueException(sprintf( 'Unexpected entity state: %s. %s', $entityState, - self::objToStr($entity) + self::objToStr($entity), )); } } - /** - * Merges the state of the given detached entity into this UnitOfWork. - * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement - * - * @param object $entity - * - * @return object The managed copy of the entity. - * - * @throws OptimisticLockException If the entity uses optimistic locking through a version - * attribute and the version check against the managed copy fails. - */ - public function merge($entity) - { - $visited = []; - - return $this->doMerge($entity, $visited); - } - - /** - * Executes a merge operation on an entity. - * - * @param object $entity - * @phpstan-param AssociationMapping|null $assoc - * @phpstan-param array $visited - * - * @return object The managed copy of the entity. - * - * @throws OptimisticLockException If the entity uses optimistic locking through a version - * attribute and the version check against the managed copy fails. - * @throws ORMInvalidArgumentException If the entity instance is NEW. - * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided. - */ - private function doMerge( - $entity, - array &$visited, - $prevManagedCopy = null, - ?array $assoc = null - ) { - $oid = spl_object_id($entity); - - if (isset($visited[$oid])) { - $managedCopy = $visited[$oid]; - - if ($prevManagedCopy !== null) { - $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); - } - - return $managedCopy; - } - - $class = $this->em->getClassMetadata(get_class($entity)); - - // First we assume DETACHED, although it can still be NEW but we can avoid - // an extra db-roundtrip this way. If it is not MANAGED but has an identity, - // we need to fetch it from the db anyway in order to merge. - // MANAGED entities are ignored by the merge operation. - $managedCopy = $entity; - - if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { - // Try to look the entity up in the identity map. - $id = $class->getIdentifierValues($entity); - - // If there is no ID, it is actually NEW. - if (! $id) { - $managedCopy = $this->newInstance($class); - - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - $this->persistNew($class, $managedCopy); - } else { - $flatId = $class->containsForeignIdentifier || $class->containsEnumIdentifier - ? $this->identifierFlattener->flattenIdentifier($class, $id) - : $id; - - $managedCopy = $this->tryGetById($flatId, $class->rootEntityName); - - if ($managedCopy) { - // We have the entity in-memory already, just make sure its not removed. - if ($this->getEntityState($managedCopy) === self::STATE_REMOVED) { - throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, 'merge'); - } - } else { - // We need to fetch the managed copy in order to merge. - $managedCopy = $this->em->find($class->name, $flatId); - } - - if ($managedCopy === null) { - // If the identifier is ASSIGNED, it is NEW, otherwise an error - // since the managed entity was not found. - if (! $class->isIdentifierNatural()) { - throw EntityNotFoundException::fromClassNameAndIdentifier( - $class->getName(), - $this->identifierFlattener->flattenIdentifier($class, $id) - ); - } - - $managedCopy = $this->newInstance($class); - $class->setIdentifierValues($managedCopy, $id); - - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - $this->persistNew($class, $managedCopy); - } else { - $this->ensureVersionMatch($class, $entity, $managedCopy); - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - } - } - - $visited[$oid] = $managedCopy; // mark visited - - if ($class->isChangeTrackingDeferredExplicit()) { - $this->scheduleForDirtyCheck($entity); - } - } - - if ($prevManagedCopy !== null) { - $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); - } - - // Mark the managed copy visited as well - $visited[spl_object_id($managedCopy)] = $managedCopy; - - $this->cascadeMerge($entity, $managedCopy, $visited); - - return $managedCopy; - } - - /** - * @param object $entity - * @param object $managedCopy - * @phpstan-param ClassMetadata $class - * @phpstan-param T $entity - * @phpstan-param T $managedCopy - * - * @throws OptimisticLockException - * - * @template T of object - */ - private function ensureVersionMatch( - ClassMetadata $class, - $entity, - $managedCopy - ): void { - if (! ($class->isVersioned && ! $this->isUninitializedObject($managedCopy) && ! $this->isUninitializedObject($entity))) { - return; - } - - assert($class->versionField !== null); - $reflField = $class->reflFields[$class->versionField]; - $managedCopyVersion = $reflField->getValue($managedCopy); - $entityVersion = $reflField->getValue($entity); - - // Throw exception if versions don't match. - // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedEqualOperator - if ($managedCopyVersion == $entityVersion) { - return; - } - - throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); - } - - /** - * Sets/adds associated managed copies into the previous entity's association field - * - * @param object $entity - * @phpstan-param AssociationMapping $association - */ - private function updateAssociationWithMergedEntity( - $entity, - array $association, - $previousManagedCopy, - $managedCopy - ): void { - $assocField = $association['fieldName']; - $prevClass = $this->em->getClassMetadata(get_class($previousManagedCopy)); - - if ($association['type'] & ClassMetadata::TO_ONE) { - $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy); - - return; - } - - $value = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy); - $value[] = $managedCopy; - - if ($association['type'] === ClassMetadata::ONE_TO_MANY) { - $class = $this->em->getClassMetadata(get_class($entity)); - - $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy); - } - } - /** * Detaches an entity from the persistence management. It's persistence will * no longer be managed by Doctrine. - * - * @param object $entity The entity to detach. - * - * @return void */ - public function detach($entity) + public function detach(object $entity): void { $visited = []; @@ -2373,14 +1913,13 @@ public function detach($entity) /** * Executes a detach operation on the given entity. * - * @param object $entity * @param mixed[] $visited * @param bool $noCascade if true, don't cascade detach operation. */ private function doDetach( - $entity, + object $entity, array &$visited, - bool $noCascade = false + bool $noCascade = false, ): void { $oid = spl_object_id($entity); @@ -2402,7 +1941,7 @@ private function doDetach( $this->entityDeletions[$oid], $this->entityIdentifiers[$oid], $this->entityStates[$oid], - $this->originalEntityData[$oid] + $this->originalEntityData[$oid], ); break; case self::STATE_NEW: @@ -2419,37 +1958,28 @@ private function doDetach( * Refreshes the state of the given entity from the database, overwriting * any local, unpersisted changes. * - * @param object $entity The entity to refresh - * - * @return void + * @phpstan-param LockMode::*|null $lockMode * * @throws InvalidArgumentException If the entity is not MANAGED. * @throws TransactionRequiredException */ - public function refresh($entity) + public function refresh(object $entity, LockMode|int|null $lockMode = null): void { $visited = []; - $lockMode = null; - - if (func_num_args() > 1) { - $lockMode = func_get_arg(1); - } - $this->doRefresh($entity, $visited, $lockMode); } /** * Executes a refresh operation on an entity. * - * @param object $entity The entity to refresh. * @phpstan-param array $visited The already visited entities during cascades. * @phpstan-param LockMode::*|null $lockMode * * @throws ORMInvalidArgumentException If the entity is not MANAGED. * @throws TransactionRequiredException */ - private function doRefresh($entity, array &$visited, ?int $lockMode = null): void + private function doRefresh(object $entity, array &$visited, LockMode|int|null $lockMode = null): void { switch (true) { case $lockMode === LockMode::PESSIMISTIC_READ: @@ -2467,7 +1997,7 @@ private function doRefresh($entity, array &$visited, ?int $lockMode = null): voi $visited[$oid] = $entity; // mark visited - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); if ($this->getEntityState($entity) !== self::STATE_MANAGED) { throw ORMInvalidArgumentException::entityNotManaged($entity); @@ -2478,30 +2008,27 @@ private function doRefresh($entity, array &$visited, ?int $lockMode = null): voi $this->getEntityPersister($class->name)->refresh( array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), $entity, - $lockMode + $lockMode, ); } /** * Cascades a refresh operation to associated entities. * - * @param object $entity * @phpstan-param array $visited * @phpstan-param LockMode::*|null $lockMode */ - private function cascadeRefresh($entity, array &$visited, ?int $lockMode = null): void + private function cascadeRefresh(object $entity, array &$visited, LockMode|int|null $lockMode = null): void { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $associationMappings = array_filter( $class->associationMappings, - static function ($assoc) { - return $assoc['isCascadeRefresh']; - } + static fn (AssociationMapping $assoc): bool => $assoc->isCascadeRefresh() ); foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc->fieldName]->getValue($entity); switch (true) { case $relatedEntities instanceof PersistentCollection: @@ -2530,22 +2057,19 @@ static function ($assoc) { /** * Cascades a detach operation to associated entities. * - * @param object $entity * @param array $visited */ - private function cascadeDetach($entity, array &$visited): void + private function cascadeDetach(object $entity, array &$visited): void { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $associationMappings = array_filter( $class->associationMappings, - static function ($assoc) { - return $assoc['isCascadeDetach']; - } + static fn (AssociationMapping $assoc): bool => $assoc->isCascadeDetach() ); foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc->fieldName]->getValue($entity); switch (true) { case $relatedEntities instanceof PersistentCollection: @@ -2571,70 +2095,27 @@ static function ($assoc) { } } - /** - * Cascades a merge operation to associated entities. - * - * @param object $entity - * @param object $managedCopy - * @phpstan-param array $visited - */ - private function cascadeMerge($entity, $managedCopy, array &$visited): void - { - $class = $this->em->getClassMetadata(get_class($entity)); - - $associationMappings = array_filter( - $class->associationMappings, - static function ($assoc) { - return $assoc['isCascadeMerge']; - } - ); - - foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); - - if ($relatedEntities instanceof Collection) { - if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { - continue; - } - - if ($relatedEntities instanceof PersistentCollection) { - // Unwrap so that foreach() does not initialize - $relatedEntities = $relatedEntities->unwrap(); - } - - foreach ($relatedEntities as $relatedEntity) { - $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); - } - } elseif ($relatedEntities !== null) { - $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc); - } - } - } - /** * Cascades the save operation to associated entities. * - * @param object $entity * @phpstan-param array $visited */ - private function cascadePersist($entity, array &$visited): void + private function cascadePersist(object $entity, array &$visited): void { if ($this->isUninitializedObject($entity)) { // nothing to do - proxy is not initialized, therefore we don't do anything with it return; } - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $associationMappings = array_filter( $class->associationMappings, - static function ($assoc) { - return $assoc['isCascadePersist']; - } + static fn (AssociationMapping $assoc): bool => $assoc->isCascadePersist() ); foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc->fieldName]->getValue($entity); switch (true) { case $relatedEntities instanceof PersistentCollection: @@ -2644,11 +2125,11 @@ static function ($assoc) { case $relatedEntities instanceof Collection: case is_array($relatedEntities): - if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) { + if ($assoc->isToMany() <= 0) { throw ORMInvalidArgumentException::invalidAssociation( - $this->em->getClassMetadata($assoc['targetEntity']), + $this->em->getClassMetadata($assoc->targetEntity), $assoc, - $relatedEntities + $relatedEntities, ); } @@ -2659,11 +2140,11 @@ static function ($assoc) { break; case $relatedEntities !== null: - if (! $relatedEntities instanceof $assoc['targetEntity']) { + if (! $relatedEntities instanceof $assoc->targetEntity) { throw ORMInvalidArgumentException::invalidAssociation( - $this->em->getClassMetadata($assoc['targetEntity']), + $this->em->getClassMetadata($assoc->targetEntity), $assoc, - $relatedEntities + $relatedEntities, ); } @@ -2679,18 +2160,15 @@ static function ($assoc) { /** * Cascades the delete operation to associated entities. * - * @param object $entity * @phpstan-param array $visited */ - private function cascadeRemove($entity, array &$visited): void + private function cascadeRemove(object $entity, array &$visited): void { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $associationMappings = array_filter( $class->associationMappings, - static function ($assoc) { - return $assoc['isCascadeRemove']; - } + static fn (AssociationMapping $assoc): bool => $assoc->isCascadeRemove() ); if ($associationMappings) { @@ -2700,7 +2178,7 @@ static function ($assoc) { $entitiesToCascade = []; foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc->fieldName]->getValue($entity); switch (true) { case $relatedEntities instanceof Collection: @@ -2729,21 +2207,19 @@ static function ($assoc) { /** * Acquire a lock on the given entity. * - * @param object $entity - * @param int|DateTimeInterface|null $lockVersion * @phpstan-param LockMode::* $lockMode * * @throws ORMInvalidArgumentException * @throws TransactionRequiredException * @throws OptimisticLockException */ - public function lock($entity, int $lockMode, $lockVersion = null): void + public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void { if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { throw ORMInvalidArgumentException::entityNotManaged($entity); } - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); switch (true) { case $lockMode === LockMode::OPTIMISTIC: @@ -2778,7 +2254,7 @@ public function lock($entity, int $lockMode, $lockVersion = null): void $this->getEntityPersister($class->name)->lock( array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), - $lockMode + $lockMode, ); break; @@ -2789,49 +2265,31 @@ public function lock($entity, int $lockMode, $lockVersion = null): void /** * Clears the UnitOfWork. - * - * @param string|null $entityName if given, only entities of this type will get detached. - * - * @return void - * - * @throws ORMInvalidArgumentException if an invalid entity name is given. - */ - public function clear($entityName = null) - { - if ($entityName === null) { - $this->identityMap = - $this->entityIdentifiers = - $this->originalEntityData = - $this->entityChangeSets = - $this->entityStates = - $this->scheduledForSynchronization = - $this->entityInsertions = - $this->entityUpdates = - $this->entityDeletions = - $this->nonCascadedNewDetectedEntities = - $this->collectionDeletions = - $this->collectionUpdates = - $this->extraUpdates = - $this->readOnlyObjects = - $this->pendingCollectionElementRemovals = - $this->visitedCollections = - $this->eagerLoadingEntities = - $this->eagerLoadingCollections = - $this->orphanRemovals = []; - } else { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8460', - 'Calling %s() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.', - __METHOD__ - ); - - $this->clearIdentityMapForEntityName($entityName); - $this->clearEntityInsertionsForEntityName($entityName); - } + */ + public function clear(): void + { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForSynchronization = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->nonCascadedNewDetectedEntities = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->pendingCollectionElementRemovals = + $this->visitedCollections = + $this->eagerLoadingEntities = + $this->eagerLoadingCollections = + $this->orphanRemovals = []; if ($this->evm->hasListeners(Events::onClear)) { - $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); + $this->evm->dispatchEvent(Events::onClear, new OnClearEventArgs($this->em)); } } @@ -2841,13 +2299,9 @@ public function clear($entityName = null) * invoked on that entity at the beginning of the next commit of this * UnitOfWork. * - * @param object $entity - * - * @return void - * * @ignore */ - public function scheduleOrphanRemoval($entity) + public function scheduleOrphanRemoval(object $entity): void { $this->orphanRemovals[spl_object_id($entity)] = $entity; } @@ -2856,13 +2310,9 @@ public function scheduleOrphanRemoval($entity) * INTERNAL: * Cancels a previously scheduled orphan removal. * - * @param object $entity - * - * @return void - * * @ignore */ - public function cancelOrphanRemoval($entity) + public function cancelOrphanRemoval(object $entity): void { unset($this->orphanRemovals[spl_object_id($entity)]); } @@ -2870,10 +2320,8 @@ public function cancelOrphanRemoval($entity) /** * INTERNAL: * Schedules a complete collection for removal when this UnitOfWork commits. - * - * @return void */ - public function scheduleCollectionDeletion(PersistentCollection $coll) + public function scheduleCollectionDeletion(PersistentCollection $coll): void { $coid = spl_object_id($coll); @@ -2884,24 +2332,11 @@ public function scheduleCollectionDeletion(PersistentCollection $coll) $this->collectionDeletions[$coid] = $coll; } - /** @return bool */ - public function isCollectionScheduledForDeletion(PersistentCollection $coll) + public function isCollectionScheduledForDeletion(PersistentCollection $coll): bool { return isset($this->collectionDeletions[spl_object_id($coll)]); } - /** @return object */ - private function newInstance(ClassMetadata $class) - { - $entity = $class->newInstance(); - - if ($entity instanceof ObjectManagerAware) { - $entity->injectObjectManager($this->em, $class); - } - - return $entity; - } - /** * INTERNAL: * Creates an entity. Used for reconstitution of persistent entities. @@ -2917,7 +2352,7 @@ private function newInstance(ClassMetadata $class) * @ignore * @todo Rename: getOrCreateEntity */ - public function createEntity($className, array $data, &$hints = []) + public function createEntity(string $className, array $data, array &$hints = []): object { $class = $this->em->getClassMetadata($className); @@ -2953,18 +2388,9 @@ public function createEntity($className, array $data, &$hints = []) } } - // inject ObjectManager upon refresh. - if ($entity instanceof ObjectManagerAware) { - $entity->injectObjectManager($this->em, $class); - } - $this->originalEntityData[$oid] = $data; - - if ($entity instanceof NotifyPropertyChanged) { - $entity->addPropertyChangedListener($this); - } } else { - $entity = $this->newInstance($class); + $entity = $class->newInstance(); $oid = spl_object_id($entity); $this->registerManaged($entity, $id, $data); @@ -2986,18 +2412,6 @@ public function createEntity($className, array $data, &$hints = []) unset($this->eagerLoadingEntities[$class->rootEntityName]); } - // Properly initialize any unfetched associations, if partial objects are not allowed. - if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8471', - 'Partial Objects are deprecated (here entity %s)', - $className - ); - - return $entity; - } - foreach ($class->associationMappings as $field => $assoc) { // Check if the association is not among the fetch-joined associations already. if (isset($hints['fetchAlias'], $hints['fetched'][$hints['fetchAlias']][$field])) { @@ -3005,26 +2419,26 @@ public function createEntity($className, array $data, &$hints = []) } if (! isset($hints['fetchMode'][$class->name][$field])) { - $hints['fetchMode'][$class->name][$field] = $assoc['fetch']; + $hints['fetchMode'][$class->name][$field] = $assoc->fetch; } - $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetClass = $this->em->getClassMetadata($assoc->targetEntity); switch (true) { - case $assoc['type'] & ClassMetadata::TO_ONE: - if (! $assoc['isOwningSide']) { + case $assoc->isToOne(): + if (! $assoc->isOwningSide()) { // use the given entity association if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_id($data[$field])])) { $this->originalEntityData[$oid][$field] = $data[$field]; $class->reflFields[$field]->setValue($entity, $data[$field]); - $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity); + $targetClass->reflFields[$assoc->mappedBy]->setValue($data[$field], $entity); continue 2; } // Inverse side of x-to-one can never be lazy - $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc->targetEntity)->loadOneToOneEntity($assoc, $entity)); continue 2; } @@ -3039,8 +2453,9 @@ public function createEntity($className, array $data, &$hints = []) $associatedId = []; + assert($assoc->isToOneOwningSide()); // TODO: Is this even computed right in all cases of composite keys? - foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) { $joinColumnValue = $data[$srcColumn] ?? null; if ($joinColumnValue !== null) { @@ -3096,7 +2511,7 @@ public function createEntity($className, array $data, &$hints = []) // If it might be a subtype, it can not be lazy. There isn't even // a way to solve this with deferred eager loading, which means putting // an entity with subclasses at a *-to-one location is really bad! (performance-wise) - $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId); + $newValue = $this->getEntityPersister($assoc->targetEntity)->loadOneToOneEntity($assoc, $entity, $associatedId); break; default: @@ -3105,7 +2520,7 @@ public function createEntity($className, array $data, &$hints = []) switch (true) { // We are negating the condition here. Other cases will assume it is valid! case $hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER: - $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $normalizedAssociatedId); + $newValue = $this->em->getProxyFactory()->getProxy($assoc->targetEntity, $normalizedAssociatedId); $this->registerManaged($newValue, $associatedId, []); break; @@ -3116,13 +2531,13 @@ public function createEntity($className, array $data, &$hints = []) // TODO: Is there a faster approach? $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($normalizedAssociatedId); - $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $normalizedAssociatedId); + $newValue = $this->em->getProxyFactory()->getProxy($assoc->targetEntity, $normalizedAssociatedId); $this->registerManaged($newValue, $associatedId, []); break; default: // TODO: This is very imperformant, ignore it? - $newValue = $this->em->find($assoc['targetEntity'], $normalizedAssociatedId); + $newValue = $this->em->find($assoc->targetEntity, $normalizedAssociatedId); break; } } @@ -3130,14 +2545,15 @@ public function createEntity($className, array $data, &$hints = []) $this->originalEntityData[$oid][$field] = $newValue; $class->reflFields[$field]->setValue($entity, $newValue); - if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE && $newValue !== null) { - $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; - $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity); + if ($assoc->inversedBy !== null && $assoc->isOneToOne() && $newValue !== null) { + $inverseAssoc = $targetClass->associationMappings[$assoc->inversedBy]; + $targetClass->reflFields[$inverseAssoc->fieldName]->setValue($newValue, $entity); } break; default: + assert($assoc->isToMany()); // Ignore if its a cached collection if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) { break; @@ -3163,7 +2579,7 @@ public function createEntity($className, array $data, &$hints = []) if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) { $isIteration = isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION]; - if ($assoc['type'] === ClassMetadata::ONE_TO_MANY && ! $isIteration && ! $targetClass->isIdentifierComposite && ! isset($assoc['indexBy'])) { + if (! $isIteration && $assoc->isOneToMany() && ! $targetClass->isIdentifierComposite && ! $assoc->isIndexed()) { $this->scheduleCollectionForBatchLoading($pColl, $class); } else { $this->loadCollection($pColl); @@ -3182,8 +2598,7 @@ public function createEntity($className, array $data, &$hints = []) return $entity; } - /** @return void */ - public function triggerEagerLoads() + public function triggerEagerLoads(): void { if (! $this->eagerLoadingEntities && ! $this->eagerLoadingCollections) { return; @@ -3203,7 +2618,7 @@ public function triggerEagerLoads() foreach ($batches as $batchedIds) { $this->getEntityPersister($entityName)->loadAll( - array_combine($class->identifier, [$batchedIds]) + array_combine($class->identifier, [$batchedIds]), ); } } @@ -3220,20 +2635,12 @@ public function triggerEagerLoads() * Load all data into the given collections, according to the specified mapping * * @param PersistentCollection[] $collections - * @param array $mapping - * @phpstan-param array{ - * targetEntity: class-string, - * sourceEntity: class-string, - * mappedBy: string, - * indexBy: string|null, - * orderBy: array|null - * } $mapping */ - private function eagerLoadCollections(array $collections, array $mapping): void + private function eagerLoadCollections(array $collections, ToManyInverseSideMapping $mapping): void { - $targetEntity = $mapping['targetEntity']; - $class = $this->em->getClassMetadata($mapping['sourceEntity']); - $mappedBy = $mapping['mappedBy']; + $targetEntity = $mapping->targetEntity; + $class = $this->em->getClassMetadata($mapping->sourceEntity); + $mappedBy = $mapping->mappedBy; $batches = array_chunk($collections, $this->em->getConfiguration()->getEagerFetchBatchSize(), true); @@ -3244,22 +2651,23 @@ private function eagerLoadCollections(array $collections, array $mapping): void $entities[] = $collection->getOwner(); } - $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping['orderBy'] ?? null); + $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping->orderBy); $targetClass = $this->em->getClassMetadata($targetEntity); $targetProperty = $targetClass->getReflectionProperty($mappedBy); + assert($targetProperty !== null); foreach ($found as $targetValue) { $sourceEntity = $targetProperty->getValue($targetValue); - if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]['joinColumns'])) { + if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]->joinColumns)) { // case where the hydration $targetValue itself has not yet fully completed, for example // in case a bi-directional association is being hydrated and deferring eager loading is // not possible due to subclassing. $data = $this->getOriginalEntityData($targetValue); $id = []; - foreach ($targetClass->associationMappings[$mappedBy]['joinColumns'] as $joinColumn) { - $id[] = $data[$joinColumn['name']]; + foreach ($targetClass->associationMappings[$mappedBy]->joinColumns as $joinColumn) { + $id[] = $data[$joinColumn->name]; } } else { $id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity)); @@ -3267,8 +2675,9 @@ private function eagerLoadCollections(array $collections, array $mapping): void $idHash = implode(' ', $id); - if (isset($mapping['indexBy'])) { - $indexByProperty = $targetClass->getReflectionProperty($mapping['indexBy']); + if ($mapping->indexBy !== null) { + $indexByProperty = $targetClass->getReflectionProperty($mapping->indexBy); + assert($indexByProperty !== null); $collectionBatch[$idHash]->hydrateSet($indexByProperty->getValue($targetValue), $targetValue); } else { $collectionBatch[$idHash]->add($targetValue); @@ -3285,18 +2694,14 @@ private function eagerLoadCollections(array $collections, array $mapping): void /** * Initializes (loads) an uninitialized persistent collection of an entity. * - * @param PersistentCollection $collection The collection to initialize. - * - * @return void - * * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. */ - public function loadCollection(PersistentCollection $collection) + public function loadCollection(PersistentCollection $collection): void { $assoc = $collection->getMapping(); - $persister = $this->getEntityPersister($assoc['targetEntity']); + $persister = $this->getEntityPersister($assoc->targetEntity); - switch ($assoc['type']) { + switch ($assoc->type()) { case ClassMetadata::ONE_TO_MANY: $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection); break; @@ -3315,7 +2720,7 @@ public function loadCollection(PersistentCollection $collection) private function scheduleCollectionForBatchLoading(PersistentCollection $collection, ClassMetadata $sourceClass): void { $mapping = $collection->getMapping(); - $name = $mapping['sourceEntity'] . '#' . $mapping['fieldName']; + $name = $mapping->sourceEntity . '#' . $mapping->fieldName; if (! isset($this->eagerLoadingCollections[$name])) { $this->eagerLoadingCollections[$name] = [ @@ -3329,7 +2734,7 @@ private function scheduleCollectionForBatchLoading(PersistentCollection $collect $id = $this->identifierFlattener->flattenIdentifier( $sourceClass, - $sourceClass->getIdentifierValues($owner) + $sourceClass->getIdentifierValues($owner), ); $idHash = implode(' ', $id); @@ -3341,7 +2746,7 @@ private function scheduleCollectionForBatchLoading(PersistentCollection $collect * * @return array> */ - public function getIdentityMap() + public function getIdentityMap(): array { return $this->identityMap; } @@ -3350,12 +2755,9 @@ public function getIdentityMap() * Gets the original data of an entity. The original data is the data that was * present at the time the entity was reconstituted from the database. * - * @param object $entity - * - * @return mixed[] * @phpstan-return array */ - public function getOriginalEntityData($entity) + public function getOriginalEntityData(object $entity): array { $oid = spl_object_id($entity); @@ -3363,14 +2765,11 @@ public function getOriginalEntityData($entity) } /** - * @param object $entity * @param mixed[] $data * - * @return void - * * @ignore */ - public function setOriginalEntityData($entity, array $data) + public function setOriginalEntityData(object $entity, array $data): void { $this->originalEntityData[spl_object_id($entity)] = $data; } @@ -3379,15 +2778,9 @@ public function setOriginalEntityData($entity, array $data) * INTERNAL: * Sets a property value of the original data array of an entity. * - * @param int $oid - * @param string $property - * @param mixed $value - * - * @return void - * * @ignore */ - public function setOriginalEntityProperty($oid, $property, $value) + public function setOriginalEntityProperty(int $oid, string $property, mixed $value): void { $this->originalEntityData[$oid][$property] = $value; } @@ -3398,31 +2791,24 @@ public function setOriginalEntityProperty($oid, $property, $value) * has a composite identifier then the identifier values are in the same * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). * - * @param object $entity - * * @return mixed[] The identifier values. */ - public function getEntityIdentifier($entity) + public function getEntityIdentifier(object $entity): array { - if (! isset($this->entityIdentifiers[spl_object_id($entity)])) { - throw EntityNotFoundException::noIdentifierFound(get_debug_type($entity)); - } - - return $this->entityIdentifiers[spl_object_id($entity)]; + return $this->entityIdentifiers[spl_object_id($entity)] + ?? throw EntityNotFoundException::noIdentifierFound(get_debug_type($entity)); } /** * Processes an entity instance to extract their identifier values. * - * @param object $entity The entity instance. - * * @return mixed A scalar value. * * @throws ORMInvalidArgumentException */ - public function getSingleIdentifierValue($entity) + public function getSingleIdentifierValue(object $entity): mixed { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); if ($class->isIdentifierComposite) { throw ORMInvalidArgumentException::invalidCompositeIdentifier(); @@ -3445,7 +2831,7 @@ public function getSingleIdentifierValue($entity) * @return object|false Returns the entity with the specified identifier if it exists in * this UnitOfWork, FALSE otherwise. */ - public function tryGetById($id, $rootClassName) + public function tryGetById(mixed $id, string $rootClassName): object|false { $idHash = self::getIdHashByIdentifier((array) $id); @@ -3455,25 +2841,19 @@ public function tryGetById($id, $rootClassName) /** * Schedules an entity for dirty-checking at commit-time. * - * @param object $entity The entity to schedule for dirty-checking. - * - * @return void - * * @todo Rename: scheduleForSynchronization */ - public function scheduleForDirtyCheck($entity) + public function scheduleForDirtyCheck(object $entity): void { - $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + $rootClassName = $this->em->getClassMetadata($entity::class)->rootEntityName; $this->scheduledForSynchronization[$rootClassName][spl_object_id($entity)] = $entity; } /** * Checks whether the UnitOfWork has any pending insertions. - * - * @return bool TRUE if this UnitOfWork has pending insertions, FALSE otherwise. */ - public function hasPendingInsertions() + public function hasPendingInsertions(): bool { return ! empty($this->entityInsertions); } @@ -3481,10 +2861,8 @@ public function hasPendingInsertions() /** * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the * number of entities in the identity map. - * - * @return int */ - public function size() + public function size(): int { return array_sum(array_map('count', $this->identityMap)); } @@ -3493,10 +2871,8 @@ public function size() * Gets the EntityPersister for an Entity. * * @param class-string $entityName The name of the Entity. - * - * @return EntityPersister */ - public function getEntityPersister($entityName) + public function getEntityPersister(string $entityName): EntityPersister { if (isset($this->persisters[$entityName])) { return $this->persisters[$entityName]; @@ -3504,22 +2880,12 @@ public function getEntityPersister($entityName) $class = $this->em->getClassMetadata($entityName); - switch (true) { - case $class->isInheritanceTypeNone(): - $persister = new BasicEntityPersister($this->em, $class); - break; - - case $class->isInheritanceTypeSingleTable(): - $persister = new SingleTablePersister($this->em, $class); - break; - - case $class->isInheritanceTypeJoined(): - $persister = new JoinedSubclassPersister($this->em, $class); - break; - - default: - throw new RuntimeException('No persister found for entity.'); - } + $persister = match (true) { + $class->isInheritanceTypeNone() => new BasicEntityPersister($this->em, $class), + $class->isInheritanceTypeSingleTable() => new SingleTablePersister($this->em, $class), + $class->isInheritanceTypeJoined() => new JoinedSubclassPersister($this->em, $class), + default => throw new RuntimeException('No persister found for entity.'), + }; if ($this->hasCache && $class->cache !== null) { $persister = $this->em->getConfiguration() @@ -3533,28 +2899,22 @@ public function getEntityPersister($entityName) return $this->persisters[$entityName]; } - /** - * Gets a collection persister for a collection-valued association. - * - * @phpstan-param AssociationMapping $association - * - * @return CollectionPersister - */ - public function getCollectionPersister(array $association) + /** Gets a collection persister for a collection-valued association. */ + public function getCollectionPersister(AssociationMapping $association): CollectionPersister { - $role = isset($association['cache']) - ? $association['sourceEntity'] . '::' . $association['fieldName'] - : $association['type']; + $role = isset($association->cache) + ? $association->sourceEntity . '::' . $association->fieldName + : $association->type(); if (isset($this->collectionPersisters[$role])) { return $this->collectionPersisters[$role]; } - $persister = $association['type'] === ClassMetadata::ONE_TO_MANY + $persister = $association->type() === ClassMetadata::ONE_TO_MANY ? new OneToManyPersister($this->em) : new ManyToManyPersister($this->em); - if ($this->hasCache && isset($association['cache'])) { + if ($this->hasCache && isset($association->cache)) { $persister = $this->em->getConfiguration() ->getSecondLevelCacheConfiguration() ->getCacheFactory() @@ -3570,13 +2930,10 @@ public function getCollectionPersister(array $association) * INTERNAL: * Registers an entity as managed. * - * @param object $entity The entity. - * @param mixed[] $id The identifier values. - * @param mixed[] $data The original entity data. - * - * @return void + * @param mixed[] $id The identifier values. + * @param mixed[] $data The original entity data. */ - public function registerManaged($entity, array $id, array $data) + public function registerManaged(object $entity, array $id, array $data): void { $oid = spl_object_id($entity); @@ -3585,23 +2942,6 @@ public function registerManaged($entity, array $id, array $data) $this->originalEntityData[$oid] = $data; $this->addToIdentityMap($entity); - - if ($entity instanceof NotifyPropertyChanged && ! $this->isUninitializedObject($entity)) { - $entity->addPropertyChangedListener($this); - } - } - - /** - * INTERNAL: - * Clears the property changeset of the entity with the given OID. - * - * @param int $oid The entity's OID. - * - * @return void - */ - public function clearEntityChangeSet($oid) - { - unset($this->entityChangeSets[$oid]); } /* PropertyChangedListener implementation */ @@ -3609,17 +2949,12 @@ public function clearEntityChangeSet($oid) /** * Notifies this UnitOfWork of a property change in an entity. * - * @param object $sender The entity that owns the property. - * @param string $propertyName The name of the property that changed. - * @param mixed $oldValue The old value of the property. - * @param mixed $newValue The new value of the property. - * - * @return void + * {@inheritDoc} */ - public function propertyChanged($sender, $propertyName, $oldValue, $newValue) + public function propertyChanged(object $sender, string $propertyName, mixed $oldValue, mixed $newValue): void { $oid = spl_object_id($sender); - $class = $this->em->getClassMetadata(get_class($sender)); + $class = $this->em->getClassMetadata($sender::class); $isAssocField = isset($class->associationMappings[$propertyName]); @@ -3640,7 +2975,7 @@ public function propertyChanged($sender, $propertyName, $oldValue, $newValue) * * @phpstan-return array */ - public function getScheduledEntityInsertions() + public function getScheduledEntityInsertions(): array { return $this->entityInsertions; } @@ -3650,7 +2985,7 @@ public function getScheduledEntityInsertions() * * @phpstan-return array */ - public function getScheduledEntityUpdates() + public function getScheduledEntityUpdates(): array { return $this->entityUpdates; } @@ -3660,7 +2995,7 @@ public function getScheduledEntityUpdates() * * @phpstan-return array */ - public function getScheduledEntityDeletions() + public function getScheduledEntityDeletions(): array { return $this->entityDeletions; } @@ -3670,7 +3005,7 @@ public function getScheduledEntityDeletions() * * @phpstan-return array> */ - public function getScheduledCollectionDeletions() + public function getScheduledCollectionDeletions(): array { return $this->collectionDeletions; } @@ -3680,19 +3015,15 @@ public function getScheduledCollectionDeletions() * * @phpstan-return array> */ - public function getScheduledCollectionUpdates() + public function getScheduledCollectionUpdates(): array { return $this->collectionUpdates; } /** * Helper method to initialize a lazy loading proxy or persistent collection. - * - * @param object $obj - * - * @return void */ - public function initializeObject($obj) + public function initializeObject(object $obj): void { if ($obj instanceof InternalProxy) { $obj->__load(); @@ -3708,23 +3039,19 @@ public function initializeObject($obj) /** * Tests if a value is an uninitialized entity. * - * @param mixed $obj - * * @phpstan-assert-if-true InternalProxy $obj */ - public function isUninitializedObject($obj): bool + public function isUninitializedObject(mixed $obj): bool { return $obj instanceof InternalProxy && ! $obj->__isInitialized(); } /** * Helper method to show an object as string. - * - * @param object $obj */ - private static function objToStr($obj): string + private static function objToStr(object $obj): string { - return method_exists($obj, '__toString') ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj); + return $obj instanceof Stringable ? (string) $obj : get_debug_type($obj) . '@' . spl_object_id($obj); } /** @@ -3733,15 +3060,11 @@ private static function objToStr($obj): string * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information * on this object that might be necessary to perform a correct update. * - * @param object $object - * - * @return void - * * @throws ORMInvalidArgumentException */ - public function markReadOnly($object) + public function markReadOnly(object $object): void { - if (! is_object($object) || ! $this->isInIdentityMap($object)) { + if (! $this->isInIdentityMap($object)) { throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); } @@ -3751,18 +3074,10 @@ public function markReadOnly($object) /** * Is this entity read only? * - * @param object $object - * - * @return bool - * * @throws ORMInvalidArgumentException */ - public function isReadOnly($object) + public function isReadOnly(object $object): bool { - if (! is_object($object)) { - throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); - } - return isset($this->readOnlyObjects[spl_object_id($object)]); } @@ -3771,7 +3086,7 @@ public function isReadOnly($object) */ private function afterTransactionComplete(): void { - $this->performCallbackOnCachedPersister(static function (CachedPersister $persister) { + $this->performCallbackOnCachedPersister(static function (CachedPersister $persister): void { $persister->afterTransactionComplete(); }); } @@ -3781,7 +3096,7 @@ private function afterTransactionComplete(): void */ private function afterTransactionRolledBack(): void { - $this->performCallbackOnCachedPersister(static function (CachedPersister $persister) { + $this->performCallbackOnCachedPersister(static function (CachedPersister $persister): void { $persister->afterTransactionRolledBack(); }); } @@ -3795,7 +3110,7 @@ private function performCallbackOnCachedPersister(callable $callback): void return; } - foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) { + foreach ([...$this->persisters, ...$this->collectionPersisters] as $persister) { if ($persister instanceof CachedPersister) { $callback($persister); } @@ -3818,19 +3133,16 @@ private function dispatchPostFlushEvent(): void /** * Verifies if two given entities actually are the same based on identifier comparison - * - * @param object $entity1 - * @param object $entity2 */ - private function isIdentifierEquals($entity1, $entity2): bool + private function isIdentifierEquals(object $entity1, object $entity2): bool { if ($entity1 === $entity2) { return true; } - $class = $this->em->getClassMetadata(get_class($entity1)); + $class = $this->em->getClassMetadata($entity1::class); - if ($class !== $this->em->getClassMetadata(get_class($entity2))) { + if ($class !== $this->em->getClassMetadata($entity2::class)) { return false; } @@ -3852,170 +3164,28 @@ private function assertThatThereAreNoUnintentionallyNonPersistedAssociations(): if ($entitiesNeedingCascadePersist) { throw ORMInvalidArgumentException::newEntitiesFoundThroughRelationships( - array_values($entitiesNeedingCascadePersist) + array_values($entitiesNeedingCascadePersist), ); } } - /** - * @param object $entity - * @param object $managedCopy - * - * @throws ORMException - * @throws OptimisticLockException - * @throws TransactionRequiredException - */ - private function mergeEntityStateIntoManagedCopy($entity, $managedCopy): void - { - if ($this->isUninitializedObject($entity)) { - return; - } - - $this->initializeObject($managedCopy); - - $class = $this->em->getClassMetadata(get_class($entity)); - - foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) { - $name = $prop->name; - - $prop->setAccessible(true); - - if (! isset($class->associationMappings[$name])) { - if (! $class->isIdentifier($name)) { - $prop->setValue($managedCopy, $prop->getValue($entity)); - } - } else { - $assoc2 = $class->associationMappings[$name]; - - if ($assoc2['type'] & ClassMetadata::TO_ONE) { - $other = $prop->getValue($entity); - if ($other === null) { - $prop->setValue($managedCopy, null); - } else { - if ($this->isUninitializedObject($other)) { - // do not merge fields marked lazy that have not been fetched. - continue; - } - - if (! $assoc2['isCascadeMerge']) { - if ($this->getEntityState($other) === self::STATE_DETACHED) { - $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); - $relatedId = $targetClass->getIdentifierValues($other); - - $other = $this->tryGetById($relatedId, $targetClass->name); - - if (! $other) { - if ($targetClass->subClasses) { - $other = $this->em->find($targetClass->name, $relatedId); - } else { - $other = $this->em->getProxyFactory()->getProxy( - $assoc2['targetEntity'], - $relatedId - ); - $this->registerManaged($other, $relatedId, []); - } - } - } - - $prop->setValue($managedCopy, $other); - } - } - } else { - $mergeCol = $prop->getValue($entity); - - if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) { - // do not merge fields marked lazy that have not been fetched. - // keep the lazy persistent collection of the managed copy. - continue; - } - - $managedCol = $prop->getValue($managedCopy); - - if (! $managedCol) { - $managedCol = new PersistentCollection( - $this->em, - $this->em->getClassMetadata($assoc2['targetEntity']), - new ArrayCollection() - ); - $managedCol->setOwner($managedCopy, $assoc2); - $prop->setValue($managedCopy, $managedCol); - } - - if ($assoc2['isCascadeMerge']) { - $managedCol->initialize(); - - // clear and set dirty a managed collection if its not also the same collection to merge from. - if (! $managedCol->isEmpty() && $managedCol !== $mergeCol) { - $managedCol->unwrap()->clear(); - $managedCol->setDirty(true); - - if ( - $assoc2['isOwningSide'] - && $assoc2['type'] === ClassMetadata::MANY_TO_MANY - && $class->isChangeTrackingNotify() - ) { - $this->scheduleForDirtyCheck($managedCopy); - } - } - } - } - } - - if ($class->isChangeTrackingNotify()) { - // Just treat all properties as changed, there is no other choice. - $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); - } - } - } - /** * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle. * Unit of work able to fire deferred events, related to loading events here. * * @internal should be called internally from object hydrators - * - * @return void */ - public function hydrationComplete() + public function hydrationComplete(): void { $this->hydrationCompleteHandler->hydrationComplete(); } - private function clearIdentityMapForEntityName(string $entityName): void - { - if (! isset($this->identityMap[$entityName])) { - return; - } - - $visited = []; - - foreach ($this->identityMap[$entityName] as $entity) { - $this->doDetach($entity, $visited, false); - } - } - - private function clearEntityInsertionsForEntityName(string $entityName): void - { - foreach ($this->entityInsertions as $hash => $entity) { - // note: performance optimization - `instanceof` is much faster than a function call - if ($entity instanceof $entityName && get_class($entity) === $entityName) { - unset($this->entityInsertions[$hash]); - } - } - } - - /** - * @param mixed $identifierValue - * - * @return mixed the identifier after type conversion - * - * @throws MappingException if the entity has more than a single identifier. - */ - private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, $identifierValue) + /** @throws MappingException if the entity has more than a single identifier. */ + private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, mixed $identifierValue): mixed { return $this->em->getConnection()->convertToPHPValue( $identifierValue, - $class->getTypeOfField($class->getSingleIdentifierFieldName()) + $class->getTypeOfField($class->getSingleIdentifierFieldName()), ); } @@ -4049,8 +3219,8 @@ private function normalizeIdentifier(ClassMetadata $targetClass, array $flatIden $targetIdMetadata->getName(), $this->normalizeIdentifier( $targetIdMetadata, - [(string) reset($targetIdMetadata->identifier) => $flatIdentifier[$name]] - ) + [(string) reset($targetIdMetadata->identifier) => $flatIdentifier[$name]], + ), ); } @@ -4063,13 +3233,10 @@ private function normalizeIdentifier(ClassMetadata $targetClass, array $flatIden * This is used by EntityPersisters after they inserted entities into the database. * It will place the assigned ID values in the entity's fields and start tracking * the entity in the identity map. - * - * @param object $entity - * @param mixed $generatedId */ - final public function assignPostInsertId($entity, $generatedId): void + final public function assignPostInsertId(object $entity, mixed $generatedId): void { - $class = $this->em->getClassMetadata(get_class($entity)); + $class = $this->em->getClassMetadata($entity::class); $idField = $class->getSingleIdentifierFieldName(); $idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $generatedId); $oid = spl_object_id($entity); diff --git a/src/Utility/HierarchyDiscriminatorResolver.php b/src/Utility/HierarchyDiscriminatorResolver.php index ee97f1183cf..0949f7b12b8 100644 --- a/src/Utility/HierarchyDiscriminatorResolver.php +++ b/src/Utility/HierarchyDiscriminatorResolver.php @@ -23,7 +23,7 @@ private function __construct() */ public static function resolveDiscriminatorsForClass( ClassMetadata $rootClassMetadata, - EntityManagerInterface $entityManager + EntityManagerInterface $entityManager, ): array { $hierarchyClasses = $rootClassMetadata->subClasses; $hierarchyClasses[] = $rootClassMetadata->name; diff --git a/src/Utility/IdentifierFlattener.php b/src/Utility/IdentifierFlattener.php index 27ad79bd1f4..2f16e56e4ac 100644 --- a/src/Utility/IdentifierFlattener.php +++ b/src/Utility/IdentifierFlattener.php @@ -19,27 +19,19 @@ */ final class IdentifierFlattener { - /** - * The UnitOfWork used to coordinate object-level transactions. - * - * @var UnitOfWork - */ - private $unitOfWork; - - /** - * The metadata factory, used to retrieve the ORM metadata of entity classes. - * - * @var ClassMetadataFactory - */ - private $metadataFactory; - /** * Initializes a new IdentifierFlattener instance, bound to the given EntityManager. */ - public function __construct(UnitOfWork $unitOfWork, ClassMetadataFactory $metadataFactory) - { - $this->unitOfWork = $unitOfWork; - $this->metadataFactory = $metadataFactory; + public function __construct( + /** + * The UnitOfWork used to coordinate object-level transactions. + */ + private readonly UnitOfWork $unitOfWork, + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + */ + private readonly ClassMetadataFactory $metadataFactory, + ) { } /** @@ -55,9 +47,9 @@ public function flattenIdentifier(ClassMetadata $class, array $id): array $flatId = []; foreach ($class->identifier as $field) { - if (isset($class->associationMappings[$field]) && isset($id[$field]) && is_a($id[$field], $class->associationMappings[$field]['targetEntity'])) { + if (isset($class->associationMappings[$field]) && isset($id[$field]) && is_a($id[$field], $class->associationMappings[$field]->targetEntity)) { $targetClassMetadata = $this->metadataFactory->getMetadataFor( - $class->associationMappings[$field]['targetEntity'] + $class->associationMappings[$field]->targetEntity, ); assert($targetClassMetadata instanceof ClassMetadata); @@ -69,10 +61,11 @@ public function flattenIdentifier(ClassMetadata $class, array $id): array $flatId[$field] = implode(' ', $associatedId); } elseif (isset($class->associationMappings[$field])) { + assert($class->associationMappings[$field]->isToOneOwningSide()); $associatedId = []; - foreach ($class->associationMappings[$field]['joinColumns'] as $joinColumn) { - $associatedId[] = $id[$joinColumn['name']]; + foreach ($class->associationMappings[$field]->joinColumns as $joinColumn) { + $associatedId[] = $id[$joinColumn->name]; } $flatId[$field] = implode(' ', $associatedId); diff --git a/src/Utility/LockSqlHelper.php b/src/Utility/LockSqlHelper.php index 30d86a5ce5c..7d135eb8cb5 100644 --- a/src/Utility/LockSqlHelper.php +++ b/src/Utility/LockSqlHelper.php @@ -7,9 +7,8 @@ use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DB2Platform; -use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Platforms\SQLServerPlatform; /** @internal */ @@ -17,31 +16,20 @@ trait LockSqlHelper { private function getReadLockSQL(AbstractPlatform $platform): string { - if ($platform instanceof AbstractMySQLPlatform || $platform instanceof MySQLPlatform) { - return 'LOCK IN SHARE MODE'; - } - - if ($platform instanceof PostgreSQLPlatform) { - return 'FOR SHARE'; - } - - return $this->getWriteLockSQL($platform); + return match (true) { + $platform instanceof AbstractMySQLPlatform => 'LOCK IN SHARE MODE', + $platform instanceof PostgreSQLPlatform => 'FOR SHARE', + default => $this->getWriteLockSQL($platform), + }; } private function getWriteLockSQL(AbstractPlatform $platform): string { - if ($platform instanceof DB2Platform) { - return 'WITH RR USE AND KEEP UPDATE LOCKS'; - } - - if ($platform instanceof SqlitePlatform) { - return ''; - } - - if ($platform instanceof SQLServerPlatform) { - return ''; - } - - return 'FOR UPDATE'; + return match (true) { + $platform instanceof DB2Platform => 'WITH RR USE AND KEEP UPDATE LOCKS', + $platform instanceof SQLitePlatform, + $platform instanceof SQLServerPlatform => '', + default => 'FOR UPDATE', + }; } } diff --git a/src/Utility/PersisterHelper.php b/src/Utility/PersisterHelper.php index 479ed9c1122..76e92427828 100644 --- a/src/Utility/PersisterHelper.php +++ b/src/Utility/PersisterHelper.php @@ -20,16 +20,14 @@ class PersisterHelper { /** - * @param string $fieldName - * - * @return array + * @return list * * @throws QueryException */ - public static function getTypeOfField($fieldName, ClassMetadata $class, EntityManagerInterface $em) + public static function getTypeOfField(string $fieldName, ClassMetadata $class, EntityManagerInterface $em): array { if (isset($class->fieldMappings[$fieldName])) { - return [$class->fieldMappings[$fieldName]['type']]; + return [$class->fieldMappings[$fieldName]->type]; } if (! isset($class->associationMappings[$fieldName])) { @@ -38,53 +36,47 @@ public static function getTypeOfField($fieldName, ClassMetadata $class, EntityMa $assoc = $class->associationMappings[$fieldName]; - if (! $assoc['isOwningSide']) { - return self::getTypeOfField($assoc['mappedBy'], $em->getClassMetadata($assoc['targetEntity']), $em); + if (! $assoc->isOwningSide()) { + return self::getTypeOfField($assoc->mappedBy, $em->getClassMetadata($assoc->targetEntity), $em); } - if ($assoc['type'] & ClassMetadata::MANY_TO_MANY) { - $joinData = $assoc['joinTable']; + if ($assoc->isManyToManyOwningSide()) { + $joinData = $assoc->joinTable; } else { $joinData = $assoc; } $types = []; - $targetClass = $em->getClassMetadata($assoc['targetEntity']); + $targetClass = $em->getClassMetadata($assoc->targetEntity); - foreach ($joinData['joinColumns'] as $joinColumn) { - $types[] = self::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $em); + foreach ($joinData->joinColumns as $joinColumn) { + $types[] = self::getTypeOfColumn($joinColumn->referencedColumnName, $targetClass, $em); } return $types; } - /** - * @param string $columnName - * - * @return string - * - * @throws RuntimeException - */ - public static function getTypeOfColumn($columnName, ClassMetadata $class, EntityManagerInterface $em) + /** @throws RuntimeException */ + public static function getTypeOfColumn(string $columnName, ClassMetadata $class, EntityManagerInterface $em): string { if (isset($class->fieldNames[$columnName])) { $fieldName = $class->fieldNames[$columnName]; if (isset($class->fieldMappings[$fieldName])) { - return $class->fieldMappings[$fieldName]['type']; + return $class->fieldMappings[$fieldName]->type; } } // iterate over to-one association mappings foreach ($class->associationMappings as $assoc) { - if (! isset($assoc['joinColumns'])) { + if (! $assoc->isToOneOwningSide()) { continue; } - foreach ($assoc['joinColumns'] as $joinColumn) { - if ($joinColumn['name'] === $columnName) { - $targetColumnName = $joinColumn['referencedColumnName']; - $targetClass = $em->getClassMetadata($assoc['targetEntity']); + foreach ($assoc->joinColumns as $joinColumn) { + if ($joinColumn->name === $columnName) { + $targetColumnName = $joinColumn->referencedColumnName; + $targetClass = $em->getClassMetadata($assoc->targetEntity); return self::getTypeOfColumn($targetColumnName, $targetClass, $em); } @@ -93,14 +85,14 @@ public static function getTypeOfColumn($columnName, ClassMetadata $class, Entity // iterate over to-many association mappings foreach ($class->associationMappings as $assoc) { - if (! (isset($assoc['joinTable']) && isset($assoc['joinTable']['joinColumns']))) { + if (! $assoc->isManyToManyOwningSide()) { continue; } - foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { - if ($joinColumn['name'] === $columnName) { - $targetColumnName = $joinColumn['referencedColumnName']; - $targetClass = $em->getClassMetadata($assoc['targetEntity']); + foreach ($assoc->joinTable->joinColumns as $joinColumn) { + if ($joinColumn->name === $columnName) { + $targetColumnName = $joinColumn->referencedColumnName; + $targetClass = $em->getClassMetadata($assoc->targetEntity); return self::getTypeOfColumn($targetColumnName, $targetClass, $em); } @@ -110,7 +102,7 @@ public static function getTypeOfColumn($columnName, ClassMetadata $class, Entity throw new RuntimeException(sprintf( 'Could not resolve type of column "%s" of class "%s"', $columnName, - $class->getName() + $class->getName(), )); } } diff --git a/src/Version.php b/src/Version.php deleted file mode 100644 index 0b6758a8dc2..00000000000 --- a/src/Version.php +++ /dev/null @@ -1,40 +0,0 @@ -createSchemaForModels( LazyEagerCollectionUser::class, LazyEagerCollectionAddress::class, - LazyEagerCollectionPhone::class + LazyEagerCollectionPhone::class, ); } @@ -58,38 +59,24 @@ public function testRefreshRefreshesBothLazyAndEagerCollections(): void } } -/** - * @Entity - */ +#[Entity] class LazyEagerCollectionUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $data; - - /** - * @ORM\OneToMany(targetEntity="LazyEagerCollectionPhone", cascade={"refresh"}, fetch="EAGER", mappedBy="user") - * - * @var LazyEagerCollectionPhone[] - */ - public $phones; - - /** - * @ORM\OneToMany(targetEntity="LazyEagerCollectionAddress", cascade={"refresh"}, mappedBy="user") - * - * @var LazyEagerCollectionAddress[] - */ - public $addresses; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + public int $id; + + #[Column(type: 'string', length: 255)] + public string $data; + + /** @var Collection */ + #[ORM\OneToMany(targetEntity: 'LazyEagerCollectionPhone', cascade: ['refresh'], fetch: 'EAGER', mappedBy: 'user')] + public Collection $phones; + + /** @var Collection */ + #[ORM\OneToMany(targetEntity: 'LazyEagerCollectionAddress', cascade: ['refresh'], mappedBy: 'user')] + public Collection $addresses; public function __construct() { @@ -110,52 +97,32 @@ public function addAddress(LazyEagerCollectionAddress $address): void } } -/** @Entity */ +#[Entity] class LazyEagerCollectionPhone { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $data; - - /** - * @ORM\ManyToOne(targetEntity="LazyEagerCollectionUser", inversedBy="phones") - * - * @var LazyEagerCollectionUser - */ - public $user; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + public int $id; + + #[Column(type: 'string', length: 255)] + public string $data; + + #[ORM\ManyToOne(targetEntity: 'LazyEagerCollectionUser', inversedBy: 'phones')] + public LazyEagerCollectionUser $user; } -/** @Entity */ +#[Entity] class LazyEagerCollectionAddress { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $data; - - /** - * @ORM\ManyToOne(targetEntity="LazyEagerCollectionUser", inversedBy="addresses") - * - * @var LazyEagerCollectionUser - */ - public $user; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + public int $id; + + #[Column(type: 'string', length: 255)] + public string $data; + + #[ORM\ManyToOne(targetEntity: 'LazyEagerCollectionUser', inversedBy: 'addresses')] + public LazyEagerCollectionUser $user; } diff --git a/tests/Performance/ChangeSet/UnitOfWorkComputeChangesBench.php b/tests/Performance/ChangeSet/UnitOfWorkComputeChangesBench.php index 382ef850678..a4c21687bd2 100644 --- a/tests/Performance/ChangeSet/UnitOfWorkComputeChangesBench.php +++ b/tests/Performance/ChangeSet/UnitOfWorkComputeChangesBench.php @@ -16,10 +16,9 @@ final class UnitOfWorkComputeChangesBench { /** @var CmsUser[] */ - private $users; + private array|null $users = null; - /** @var UnitOfWork */ - private $unitOfWork; + private UnitOfWork|null $unitOfWork = null; public function init(): void { @@ -43,7 +42,7 @@ public function init(): void 'name' => $user->name, 'address' => $user->address, 'email' => $user->email, - ] + ], ); } diff --git a/tests/Performance/EntityManagerFactory.php b/tests/Performance/EntityManagerFactory.php index 6d2c79b5fa1..45b82405aca 100644 --- a/tests/Performance/EntityManagerFactory.php +++ b/tests/Performance/EntityManagerFactory.php @@ -13,10 +13,10 @@ use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\ORMSetup; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Tools\SchemaTool; -use Doctrine\Tests\Mocks\DriverResultMock; +use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\TestUtil; use function array_map; @@ -30,7 +30,7 @@ public static function getEntityManager(array $schemaClassNames): EntityManagerI TestUtil::configureProxies($config); $config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver([ + $config->setMetadataDriverImpl(new AttributeDriver([ realpath(__DIR__ . '/Models/Cache'), realpath(__DIR__ . '/Models/GeoNames'), ])); @@ -40,7 +40,7 @@ public static function getEntityManager(array $schemaClassNames): EntityManagerI 'driverClass' => Driver::class, 'memory' => true, ], $config), - $config + $config, ); (new SchemaTool($entityManager)) @@ -55,7 +55,7 @@ public static function makeEntityManagerWithNoResultsConnection(): EntityManager TestUtil::configureProxies($config); $config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver([ + $config->setMetadataDriverImpl(new AttributeDriver([ realpath(__DIR__ . '/Models/Cache'), realpath(__DIR__ . '/Models/Generic'), realpath(__DIR__ . '/Models/GeoNames'), @@ -65,9 +65,9 @@ public static function makeEntityManagerWithNoResultsConnection(): EntityManager $connection = new class ([], new Driver(), null, new EventManager()) extends Connection { /** {@inheritDoc} */ - public function executeQuery(string $sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): Result + public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile|null $qcp = null): Result { - return new Result(new DriverResultMock(), $this); + return ArrayResultFactory::createWrapperResultFromArray([], $this); } }; diff --git a/tests/Performance/Hydration/MixedQueryFetchJoinArrayHydrationPerformanceBench.php b/tests/Performance/Hydration/MixedQueryFetchJoinArrayHydrationPerformanceBench.php index baaf489bc13..b07c0876bcf 100644 --- a/tests/Performance/Hydration/MixedQueryFetchJoinArrayHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/MixedQueryFetchJoinArrayHydrationPerformanceBench.php @@ -16,14 +16,11 @@ /** @BeforeMethods({"init"}) */ final class MixedQueryFetchJoinArrayHydrationPerformanceBench { - /** @var ArrayHydrator */ - private $hydrator; + private ArrayHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -65,7 +62,7 @@ public function init(): void ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ArrayHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); diff --git a/tests/Performance/Hydration/MixedQueryFetchJoinFullObjectHydrationPerformanceBench.php b/tests/Performance/Hydration/MixedQueryFetchJoinFullObjectHydrationPerformanceBench.php index a21a7da801a..21fce4f4dff 100644 --- a/tests/Performance/Hydration/MixedQueryFetchJoinFullObjectHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/MixedQueryFetchJoinFullObjectHydrationPerformanceBench.php @@ -17,14 +17,11 @@ /** @BeforeMethods({"init"}) */ final class MixedQueryFetchJoinFullObjectHydrationPerformanceBench { - /** @var ObjectHydrator */ - private $hydrator; + private ObjectHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -52,7 +49,7 @@ public function init(): void ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); diff --git a/tests/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php b/tests/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php index 7f432fd9a78..0694e8fff6a 100644 --- a/tests/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/MixedQueryFetchJoinPartialObjectHydrationPerformanceBench.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Performance\EntityManagerFactory; use Doctrine\Tests\Mocks\ArrayResultFactory; +use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods; @@ -17,14 +18,11 @@ /** @BeforeMethods({"init"}) */ final class MixedQueryFetchJoinPartialObjectHydrationPerformanceBench { - /** @var ObjectHydrator */ - private $hydrator; + private ObjectHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -36,6 +34,7 @@ public function init(): void 'u__name' => 'Roman', 'sclr0' => 'ROMANB', 'p__phonenumber' => '42', + 'a__id' => '1', ], [ 'u__id' => '1', @@ -44,6 +43,7 @@ public function init(): void 'u__name' => 'Roman', 'sclr0' => 'ROMANB', 'p__phonenumber' => '43', + 'a__id' => '1', ], [ 'u__id' => '2', @@ -52,6 +52,7 @@ public function init(): void 'u__name' => 'Roman', 'sclr0' => 'JWAGE', 'p__phonenumber' => '91', + 'a__id' => '1', ], ]; @@ -63,10 +64,11 @@ public function init(): void 'u__name' => 'Jonathan', 'sclr0' => 'JWAGE' . $i, 'p__phonenumber' => '91', + 'a__id' => '1', ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); @@ -78,6 +80,8 @@ public function init(): void $this->rsm->addFieldResult('u', 'u__name', 'name'); $this->rsm->addScalarResult('sclr0', 'nameUpper'); $this->rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); + $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address'); + $this->rsm->addFieldResult('a', 'a__id', 'id'); } public function benchHydration(): void diff --git a/tests/Performance/Hydration/SimpleHydrationBench.php b/tests/Performance/Hydration/SimpleHydrationBench.php index 93d59f1d365..ab08f886e2e 100644 --- a/tests/Performance/Hydration/SimpleHydrationBench.php +++ b/tests/Performance/Hydration/SimpleHydrationBench.php @@ -7,32 +7,37 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Performance\EntityManagerFactory; -use Doctrine\Tests\Models\CMS; +use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsArticle; +use Doctrine\Tests\Models\CMS\CmsComment; +use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsGroup; +use Doctrine\Tests\Models\CMS\CmsPhonenumber; +use Doctrine\Tests\Models\CMS\CmsTag; +use Doctrine\Tests\Models\CMS\CmsUser; /** @BeforeMethods({"init"}) */ final class SimpleHydrationBench { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface|null $entityManager = null; - /** @var EntityRepository */ - private $repository; + private EntityRepository|null $repository = null; public function init(): void { $this->entityManager = EntityManagerFactory::getEntityManager([ - CMS\CmsUser::class, - CMS\CmsPhonenumber::class, - CMS\CmsAddress::class, - CMS\CmsEmail::class, - CMS\CmsGroup::class, - CMS\CmsTag::class, - CMS\CmsArticle::class, - CMS\CmsComment::class, + CmsUser::class, + CmsPhonenumber::class, + CmsAddress::class, + CmsEmail::class, + CmsGroup::class, + CmsTag::class, + CmsArticle::class, + CmsComment::class, ]); for ($i = 2; $i < 10000; ++$i) { - $user = new CMS\CmsUser(); + $user = new CmsUser(); $user->status = 'developer'; $user->username = 'jwage' . $i; @@ -44,7 +49,7 @@ public function init(): void $this->entityManager->flush(); $this->entityManager->clear(); - $this->repository = $this->entityManager->getRepository(CMS\CmsUser::class); + $this->repository = $this->entityManager->getRepository(CmsUser::class); } public function benchHydration(): void diff --git a/tests/Performance/Hydration/SimpleInsertPerformanceBench.php b/tests/Performance/Hydration/SimpleInsertPerformanceBench.php index 50d703c407d..fdc474d66f0 100644 --- a/tests/Performance/Hydration/SimpleInsertPerformanceBench.php +++ b/tests/Performance/Hydration/SimpleInsertPerformanceBench.php @@ -7,35 +7,41 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\Performance\EntityManagerFactory; use Doctrine\Tests\Models\CMS; +use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsArticle; +use Doctrine\Tests\Models\CMS\CmsComment; +use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsGroup; +use Doctrine\Tests\Models\CMS\CmsPhonenumber; +use Doctrine\Tests\Models\CMS\CmsTag; +use Doctrine\Tests\Models\CMS\CmsUser; use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods; /** @BeforeMethods({"init"}) */ final class SimpleInsertPerformanceBench { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface|null $entityManager = null; /** @var CMS\CmsUser[] */ - private $users; + private array|null $users = null; - /** @var string */ - private $tableName; + private string|null $tableName = null; public function init(): void { $this->entityManager = EntityManagerFactory::getEntityManager([ - CMS\CmsUser::class, - CMS\CmsPhonenumber::class, - CMS\CmsAddress::class, - CMS\CmsEmail::class, - CMS\CmsGroup::class, - CMS\CmsTag::class, - CMS\CmsArticle::class, - CMS\CmsComment::class, + CmsUser::class, + CmsPhonenumber::class, + CmsAddress::class, + CmsEmail::class, + CmsGroup::class, + CmsTag::class, + CmsArticle::class, + CmsComment::class, ]); for ($i = 1; $i <= 10000; ++$i) { - $user = new CMS\CmsUser(); + $user = new CmsUser(); $user->status = 'user'; $user->username = 'user' . $i; $user->name = 'Mr.Smith-' . $i; @@ -43,7 +49,7 @@ public function init(): void $this->users[$i] = $user; } - $this->tableName = $this->entityManager->getClassMetadata(CMS\CmsUser::class)->getTableName(); + $this->tableName = $this->entityManager->getClassMetadata(CmsUser::class)->getTableName(); } public function benchHydration(): void diff --git a/tests/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php b/tests/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php index d315df63b3f..e863107edf6 100644 --- a/tests/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php @@ -15,14 +15,11 @@ /** @BeforeMethods({"init"}) */ final class SimpleQueryArrayHydrationPerformanceBench { - /** @var ArrayHydrator */ - private $hydrator; + private ArrayHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -56,7 +53,7 @@ public function init(): void ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ArrayHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); diff --git a/tests/Performance/Hydration/SimpleQueryFullObjectHydrationPerformanceBench.php b/tests/Performance/Hydration/SimpleQueryFullObjectHydrationPerformanceBench.php index 6b3f059297b..8c1114ca07e 100644 --- a/tests/Performance/Hydration/SimpleQueryFullObjectHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/SimpleQueryFullObjectHydrationPerformanceBench.php @@ -16,14 +16,11 @@ /** @BeforeMethods({"init"}) */ final class SimpleQueryFullObjectHydrationPerformanceBench { - /** @var ObjectHydrator */ - private $hydrator; + private ObjectHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -47,7 +44,7 @@ public function init(): void ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); diff --git a/tests/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php b/tests/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php index 9cb902a540f..14ed606508c 100644 --- a/tests/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/SimpleQueryPartialObjectHydrationPerformanceBench.php @@ -10,20 +10,18 @@ use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Performance\EntityManagerFactory; use Doctrine\Tests\Mocks\ArrayResultFactory; +use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsUser; use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods; /** @BeforeMethods({"init"}) */ final class SimpleQueryPartialObjectHydrationPerformanceBench { - /** @var ObjectHydrator */ - private $hydrator; + private ObjectHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -33,18 +31,21 @@ public function init(): void 'u__status' => 'developer', 'u__username' => 'romanb', 'u__name' => 'Roman', + 'a__id' => '1', ], [ 'u__id' => '1', 'u__status' => 'developer', 'u__username' => 'romanb', 'u__name' => 'Roman', + 'a__id' => '1', ], [ 'u__id' => '2', 'u__status' => 'developer', 'u__username' => 'romanb', 'u__name' => 'Roman', + 'a__id' => '1', ], ]; @@ -54,10 +55,11 @@ public function init(): void 'u__status' => 'developer', 'u__username' => 'jwage', 'u__name' => 'Jonathan', + 'a__id' => '1', ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ObjectHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); @@ -66,6 +68,8 @@ public function init(): void $this->rsm->addFieldResult('u', 'u__status', 'status'); $this->rsm->addFieldResult('u', 'u__username', 'username'); $this->rsm->addFieldResult('u', 'u__name', 'name'); + $this->rsm->addJoinedEntityResult(CmsAddress::class, 'a', 'u', 'address'); + $this->rsm->addFieldResult('a', 'a__id', 'id'); } public function benchHydration(): void diff --git a/tests/Performance/Hydration/SimpleQueryScalarHydrationPerformanceBench.php b/tests/Performance/Hydration/SimpleQueryScalarHydrationPerformanceBench.php index d0b09392391..1761b9d3f80 100644 --- a/tests/Performance/Hydration/SimpleQueryScalarHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/SimpleQueryScalarHydrationPerformanceBench.php @@ -15,14 +15,11 @@ /** @BeforeMethods({"init"}) */ final class SimpleQueryScalarHydrationPerformanceBench { - /** @var ScalarHydrator */ - private $hydrator; + private ScalarHydrator|null $hydrator = null; - /** @var ResultSetMapping */ - private $rsm; + private ResultSetMapping|null $rsm = null; - /** @var Result */ - private $result; + private Result|null $result = null; public function init(): void { @@ -56,7 +53,7 @@ public function init(): void ]; } - $this->result = ArrayResultFactory::createFromArray($resultSet); + $this->result = ArrayResultFactory::createWrapperResultFromArray($resultSet); $this->hydrator = new ScalarHydrator(EntityManagerFactory::getEntityManager([])); $this->rsm = new ResultSetMapping(); diff --git a/tests/Performance/Hydration/SingleTableInheritanceHydrationPerformanceBench.php b/tests/Performance/Hydration/SingleTableInheritanceHydrationPerformanceBench.php index 4256c73a47e..4dc0e55c2fd 100644 --- a/tests/Performance/Hydration/SingleTableInheritanceHydrationPerformanceBench.php +++ b/tests/Performance/Hydration/SingleTableInheritanceHydrationPerformanceBench.php @@ -6,53 +6,60 @@ use Doctrine\ORM\EntityRepository; use Doctrine\Performance\EntityManagerFactory; -use Doctrine\Tests\Models\Company; +use Doctrine\Tests\Models\Company\CompanyAuction; +use Doctrine\Tests\Models\Company\CompanyCar; +use Doctrine\Tests\Models\Company\CompanyContract; +use Doctrine\Tests\Models\Company\CompanyEmployee; +use Doctrine\Tests\Models\Company\CompanyEvent; +use Doctrine\Tests\Models\Company\CompanyFixContract; +use Doctrine\Tests\Models\Company\CompanyFlexContract; +use Doctrine\Tests\Models\Company\CompanyFlexUltraContract; +use Doctrine\Tests\Models\Company\CompanyManager; +use Doctrine\Tests\Models\Company\CompanyOrganization; +use Doctrine\Tests\Models\Company\CompanyPerson; +use Doctrine\Tests\Models\Company\CompanyRaffle; use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods; /** @BeforeMethods({"init"}) */ final class SingleTableInheritanceHydrationPerformanceBench { - /** @var EntityRepository */ - private $contractsRepository; + private EntityRepository|null $contractsRepository = null; - /** @var EntityRepository */ - private $fixContractsRepository; + private EntityRepository|null $fixContractsRepository = null; - /** @var EntityRepository */ - private $flexContractRepository; + private EntityRepository|null $flexContractRepository = null; - /** @var EntityRepository */ - private $ultraContractRepository; + private EntityRepository|null $ultraContractRepository = null; public function init(): void { $entityManager = EntityManagerFactory::getEntityManager([ - Company\CompanyPerson::class, - Company\CompanyEmployee::class, - Company\CompanyManager::class, - Company\CompanyOrganization::class, - Company\CompanyEvent::class, - Company\CompanyAuction::class, - Company\CompanyRaffle::class, - Company\CompanyCar::class, - Company\CompanyContract::class, + CompanyPerson::class, + CompanyEmployee::class, + CompanyManager::class, + CompanyOrganization::class, + CompanyEvent::class, + CompanyAuction::class, + CompanyRaffle::class, + CompanyCar::class, + CompanyContract::class, ]); - $this->contractsRepository = $entityManager->getRepository(Company\CompanyContract::class); - $this->fixContractsRepository = $entityManager->getRepository(Company\CompanyFixContract::class); - $this->flexContractRepository = $entityManager->getRepository(Company\CompanyFlexContract::class); - $this->ultraContractRepository = $entityManager->getRepository(Company\CompanyFlexUltraContract::class); + $this->contractsRepository = $entityManager->getRepository(CompanyContract::class); + $this->fixContractsRepository = $entityManager->getRepository(CompanyFixContract::class); + $this->flexContractRepository = $entityManager->getRepository(CompanyFlexContract::class); + $this->ultraContractRepository = $entityManager->getRepository(CompanyFlexUltraContract::class); - $person = new Company\CompanyEmployee(); + $person = new CompanyEmployee(); $person->setName('Poor Sales Guy'); $person->setDepartment('Sales'); $person->setSalary(100); $entityManager->persist($person); for ($i = 0; $i < 33; $i++) { - $fixContract = new Company\CompanyFixContract(); - $flexContract = new Company\CompanyFlexContract(); - $ultraContract = new Company\CompanyFlexUltraContract(); + $fixContract = new CompanyFixContract(); + $flexContract = new CompanyFlexContract(); + $ultraContract = new CompanyFlexUltraContract(); $fixContract->setFixPrice(1000); $fixContract->setSalesPerson($person); diff --git a/tests/Performance/Hydration/SingleTableInheritanceInsertPerformanceBench.php b/tests/Performance/Hydration/SingleTableInheritanceInsertPerformanceBench.php index 05b5f367fd5..a9dac5224d8 100644 --- a/tests/Performance/Hydration/SingleTableInheritanceInsertPerformanceBench.php +++ b/tests/Performance/Hydration/SingleTableInheritanceInsertPerformanceBench.php @@ -7,6 +7,18 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\Performance\EntityManagerFactory; use Doctrine\Tests\Models\Company; +use Doctrine\Tests\Models\Company\CompanyAuction; +use Doctrine\Tests\Models\Company\CompanyCar; +use Doctrine\Tests\Models\Company\CompanyContract; +use Doctrine\Tests\Models\Company\CompanyEmployee; +use Doctrine\Tests\Models\Company\CompanyEvent; +use Doctrine\Tests\Models\Company\CompanyFixContract; +use Doctrine\Tests\Models\Company\CompanyFlexContract; +use Doctrine\Tests\Models\Company\CompanyFlexUltraContract; +use Doctrine\Tests\Models\Company\CompanyManager; +use Doctrine\Tests\Models\Company\CompanyOrganization; +use Doctrine\Tests\Models\Company\CompanyPerson; +use Doctrine\Tests\Models\Company\CompanyRaffle; use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods; use function array_map; @@ -14,51 +26,50 @@ /** @BeforeMethods({"init"}) */ final class SingleTableInheritanceInsertPerformanceBench { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface|null $entityManager = null; /** @var Company\CompanyFixContract[] */ - private $fixContracts = []; + private array $fixContracts = []; /** @var Company\CompanyFlexContract[] */ - private $flexContracts = []; + private array $flexContracts = []; /** @var Company\CompanyFlexUltraContract[] */ - private $ultraContracts = []; + private array $ultraContracts = []; public function init(): void { $this->entityManager = EntityManagerFactory::getEntityManager([ - Company\CompanyPerson::class, - Company\CompanyEmployee::class, - Company\CompanyManager::class, - Company\CompanyOrganization::class, - Company\CompanyEvent::class, - Company\CompanyAuction::class, - Company\CompanyRaffle::class, - Company\CompanyCar::class, - Company\CompanyContract::class, + CompanyPerson::class, + CompanyEmployee::class, + CompanyManager::class, + CompanyOrganization::class, + CompanyEvent::class, + CompanyAuction::class, + CompanyRaffle::class, + CompanyCar::class, + CompanyContract::class, ]); - $person = new Company\CompanyEmployee(); + $person = new CompanyEmployee(); $person->setName('Poor Sales Guy'); $person->setDepartment('Sales'); $person->setSalary(100); $this->entityManager->persist($person); for ($i = 0; $i < 33; $i++) { - $this->fixContracts[$i] = new Company\CompanyFixContract(); + $this->fixContracts[$i] = new CompanyFixContract(); $this->fixContracts[$i]->setFixPrice(1000); $this->fixContracts[$i]->setSalesPerson($person); $this->fixContracts[$i]->markCompleted(); - $this->flexContracts[$i] = new Company\CompanyFlexContract(); + $this->flexContracts[$i] = new CompanyFlexContract(); $this->flexContracts[$i]->setSalesPerson($person); $this->flexContracts[$i]->setHoursWorked(100); $this->flexContracts[$i]->setPricePerHour(100); $this->flexContracts[$i]->markCompleted(); - $this->ultraContracts[$i] = new Company\CompanyFlexUltraContract(); + $this->ultraContracts[$i] = new CompanyFlexUltraContract(); $this->ultraContracts[$i]->setSalesPerson($person); $this->ultraContracts[$i]->setHoursWorked(150); $this->ultraContracts[$i]->setPricePerHour(150); diff --git a/tests/Performance/LazyLoading/ProxyInitializationTimeBench.php b/tests/Performance/LazyLoading/ProxyInitializationTimeBench.php index 0befc9eb41e..68f987254c2 100644 --- a/tests/Performance/LazyLoading/ProxyInitializationTimeBench.php +++ b/tests/Performance/LazyLoading/ProxyInitializationTimeBench.php @@ -14,16 +14,16 @@ final class ProxyInitializationTimeBench { /** @var Proxy[] */ - private $cmsUsers; + private array|null $cmsUsers = null; /** @var Proxy[] */ - private $cmsEmployees; + private array|null $cmsEmployees = null; /** @var Proxy[] */ - private $initializedUsers; + private array|null $initializedUsers = null; /** @var Proxy[] */ - private $initializedEmployees; + private array|null $initializedEmployees = null; public function init(): void { diff --git a/tests/Performance/Mock/NonLoadingPersister.php b/tests/Performance/Mock/NonLoadingPersister.php index 55eef56bf27..bf487978c9f 100644 --- a/tests/Performance/Mock/NonLoadingPersister.php +++ b/tests/Performance/Mock/NonLoadingPersister.php @@ -4,6 +4,7 @@ namespace Doctrine\Performance\Mock; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; /** @@ -11,22 +12,14 @@ */ class NonLoadingPersister extends BasicEntityPersister { - public function __construct() - { + public function __construct( + ClassMetadata $class, + ) { + $this->class = $class; } - /** - * {@inheritDoc} - */ - public function load( - array $criteria, - $entity = null, - $assoc = null, - array $hints = [], - $lockMode = null, - $limit = null, - ?array $orderBy = null - ) { - return $entity; + public function loadById(array $identifier, object|null $entity = null): object|null + { + return $entity ?? new ($this->class->name)(); } } diff --git a/tests/Performance/Mock/NonProxyLoadingEntityManager.php b/tests/Performance/Mock/NonProxyLoadingEntityManager.php index 55e687ecaf4..bff330ab9be 100644 --- a/tests/Performance/Mock/NonProxyLoadingEntityManager.php +++ b/tests/Performance/Mock/NonProxyLoadingEntityManager.php @@ -4,27 +4,36 @@ namespace Doctrine\Performance\Mock; +use DateTimeInterface; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Internal\Hydration\AbstractHydrator; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\NativeQuery; use Doctrine\ORM\Proxy\ProxyFactory; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\FilterCollection; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\UnitOfWork; /** * An entity manager mock that prevents lazy-loading of proxies */ class NonProxyLoadingEntityManager implements EntityManagerInterface { - /** @var EntityManagerInterface */ - private $realEntityManager; - - public function __construct(EntityManagerInterface $realEntityManager) + public function __construct(private readonly EntityManagerInterface $realEntityManager) { - $this->realEntityManager = $realEntityManager; } - /** - * {@inheritDoc} - */ - public function getProxyFactory() + public function getProxyFactory(): ProxyFactory { $config = $this->realEntityManager->getConfiguration(); @@ -32,198 +41,101 @@ public function getProxyFactory() $this, $config->getProxyDir(), $config->getProxyNamespace(), - $config->getAutoGenerateProxyClasses() + $config->getAutoGenerateProxyClasses(), ); } - /** - * {@inheritDoc} - */ - public function getMetadataFactory() + public function getMetadataFactory(): ClassMetadataFactory { return $this->realEntityManager->getMetadataFactory(); } - /** - * {@inheritDoc} - */ - public function getClassMetadata($className) + public function getClassMetadata(string $className): ClassMetadata { return $this->realEntityManager->getClassMetadata($className); } - /** - * {@inheritDoc} - */ - public function getUnitOfWork() + public function getUnitOfWork(): UnitOfWork { - return new NonProxyLoadingUnitOfWork(); + return new NonProxyLoadingUnitOfWork($this); } - /** - * {@inheritDoc} - */ - public function getCache() + public function getCache(): Cache|null { return $this->realEntityManager->getCache(); } - /** - * {@inheritDoc} - */ - public function getConnection() + public function getConnection(): Connection { return $this->realEntityManager->getConnection(); } - /** - * {@inheritDoc} - */ - public function getExpressionBuilder() + public function getExpressionBuilder(): Expr { return $this->realEntityManager->getExpressionBuilder(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): void { $this->realEntityManager->beginTransaction(); } - /** - * {@inheritDoc} - */ - public function transactional($func) - { - return $this->realEntityManager->transactional($func); - } - - /** - * {@inheritDoc} - */ - public function wrapInTransaction(callable $func) + public function wrapInTransaction(callable $func): mixed { return $this->realEntityManager->wrapInTransaction($func); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { $this->realEntityManager->commit(); } - /** - * {@inheritDoc} - */ - public function rollback() + public function rollback(): void { $this->realEntityManager->rollback(); } - /** - * {@inheritDoc} - */ - public function createQuery($dql = '') + public function createQuery(string $dql = ''): Query { return $this->realEntityManager->createQuery($dql); } - /** - * {@inheritDoc} - */ - public function createNamedQuery($name) - { - return $this->realEntityManager->createNamedQuery($name); - } - - /** - * {@inheritDoc} - */ - public function createNativeQuery($sql, ResultSetMapping $rsm) + public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery { return $this->realEntityManager->createNativeQuery($sql, $rsm); } - /** - * {@inheritDoc} - */ - public function createNamedNativeQuery($name) - { - return $this->realEntityManager->createNamedNativeQuery($name); - } - - /** - * {@inheritDoc} - */ - public function createQueryBuilder() + public function createQueryBuilder(): QueryBuilder { return $this->realEntityManager->createQueryBuilder(); } - /** - * {@inheritDoc} - */ - public function getReference($entityName, $id) + public function getReference(string $entityName, mixed $id): object|null { return $this->realEntityManager->getReference($entityName, $id); } - /** - * {@inheritDoc} - */ - public function getPartialReference($entityName, $identifier) - { - return $this->realEntityManager->getPartialReference($entityName, $identifier); - } - - /** - * {@inheritDoc} - */ - public function close() + public function close(): void { $this->realEntityManager->close(); } - /** - * {@inheritDoc} - */ - public function copy($entity, $deep = false) - { - return $this->realEntityManager->copy($entity, $deep); - } - - /** - * {@inheritDoc} - */ - public function lock($entity, $lockMode, $lockVersion = null) + public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void { $this->realEntityManager->lock($entity, $lockMode, $lockVersion); } - /** - * {@inheritDoc} - */ - public function getEventManager() + public function getEventManager(): EventManager { return $this->realEntityManager->getEventManager(); } - /** - * {@inheritDoc} - */ - public function getConfiguration() + public function getConfiguration(): Configuration { return $this->realEntityManager->getConfiguration(); } - /** - * {@inheritDoc} - */ - public function isOpen() + public function isOpen(): bool { return $this->realEntityManager->isOpen(); } @@ -231,128 +143,81 @@ public function isOpen() /** * {@inheritDoc} */ - public function getHydrator($hydrationMode) - { - return $this->realEntityManager->getHydrator($hydrationMode); - } - - /** - * {@inheritDoc} - */ - public function newHydrator($hydrationMode) + public function newHydrator($hydrationMode): AbstractHydrator { return $this->realEntityManager->newHydrator($hydrationMode); } - /** - * {@inheritDoc} - */ - public function getFilters() + public function getFilters(): FilterCollection { return $this->realEntityManager->getFilters(); } - /** - * {@inheritDoc} - */ - public function isFiltersStateClean() + public function isFiltersStateClean(): bool { return $this->realEntityManager->isFiltersStateClean(); } - /** - * {@inheritDoc} - */ - public function hasFilters() + public function hasFilters(): bool { return $this->realEntityManager->hasFilters(); } - /** - * {@inheritDoc} - */ - public function find($className, $id) + public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null { - return $this->realEntityManager->find($className, $id); + return $this->realEntityManager->find($className, $id, $lockMode, $lockVersion); } - /** - * {@inheritDoc} - */ - public function persist($object) + public function persist(object $object): void { $this->realEntityManager->persist($object); } - /** - * {@inheritDoc} - */ - public function remove($object) + public function remove(object $object): void { $this->realEntityManager->remove($object); } - /** - * {@inheritDoc} - */ - public function merge($object) - { - return $this->realEntityManager->merge($object); - } - - /** - * {@inheritDoc} - */ - public function clear($objectName = null) + public function clear(): void { - $this->realEntityManager->clear($objectName); + $this->realEntityManager->clear(); } - /** - * {@inheritDoc} - */ - public function detach($object) + public function detach(object $object): void { $this->realEntityManager->detach($object); } - /** - * {@inheritDoc} - */ - public function refresh($object, ?int $lockMode = null) + public function refresh(object $object, LockMode|int|null $lockMode = null): void { $this->realEntityManager->refresh($object, $lockMode); } - /** - * {@inheritDoc} - */ - public function flush() + public function flush(): void { $this->realEntityManager->flush(); } - /** - * {@inheritDoc} - */ - public function getRepository($className) + public function getRepository(string $className): EntityRepository { return $this->realEntityManager->getRepository($className); } - /** - * {@inheritDoc} - */ - public function initializeObject($obj) + public function initializeObject(object $obj): void { $this->realEntityManager->initializeObject($obj); } + public function contains(object $object): bool + { + return $this->realEntityManager->contains($object); + } + /** * {@inheritDoc} */ - public function contains($object) + public function isUninitializedObject($value): bool { - return $this->realEntityManager->contains($object); + return $this->realEntityManager->isUninitializedObject($value); } } diff --git a/tests/Performance/Mock/NonProxyLoadingUnitOfWork.php b/tests/Performance/Mock/NonProxyLoadingUnitOfWork.php index 41c40b8b89d..68884a3f232 100644 --- a/tests/Performance/Mock/NonProxyLoadingUnitOfWork.php +++ b/tests/Performance/Mock/NonProxyLoadingUnitOfWork.php @@ -4,6 +4,7 @@ namespace Doctrine\Performance\Mock; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\UnitOfWork; /** @@ -11,19 +12,17 @@ */ class NonProxyLoadingUnitOfWork extends UnitOfWork { - /** @var NonLoadingPersister */ - private $entityPersister; + /** @var array */ + private array $entityPersisters = []; - public function __construct() - { - $this->entityPersister = new NonLoadingPersister(); + public function __construct( + private EntityManagerInterface $entityManager, + ) { } - /** - * {@inheritDoc} - */ - public function getEntityPersister($entityName) + public function getEntityPersister(string $entityName): NonLoadingPersister { - return $this->entityPersister; + return $this->entityPersisters[$entityName] + ??= new NonLoadingPersister($this->entityManager->getClassMetadata($entityName)); } } diff --git a/tests/Performance/Query/QueryBoundParameterProcessingBench.php b/tests/Performance/Query/QueryBoundParameterProcessingBench.php index 702065bdb78..8672ae62ca0 100644 --- a/tests/Performance/Query/QueryBoundParameterProcessingBench.php +++ b/tests/Performance/Query/QueryBoundParameterProcessingBench.php @@ -15,11 +15,9 @@ /** @BeforeMethods({"init"}) */ final class QueryBoundParameterProcessingBench { - /** @var Query */ - private $parsedQueryWithInferredParameterType; + private Query|null $parsedQueryWithInferredParameterType = null; - /** @var Query */ - private $parsedQueryWithDeclaredParameterType; + private Query|null $parsedQueryWithDeclaredParameterType = null; public function init(): void { diff --git a/tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php b/tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php index 6f2033b39ea..0211a605033 100644 --- a/tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php +++ b/tests/StaticAnalysis/Tools/Pagination/paginator-covariant.php @@ -6,33 +6,25 @@ use Doctrine\ORM\Tools\Pagination\Paginator; -/** - * @template-covariant T of object - */ +/** @template-covariant T of object */ abstract class PaginatorFactory { /** @var class-string */ private $class; - /** - * @param class-string $class - */ + /** @param class-string $class */ final public function __construct(string $class) { $this->class = $class; } - /** - * @return class-string - */ + /** @return class-string */ public function getClass(): string { return $this->class; } - /** - * @phpstan-return Paginator - */ + /** @phpstan-return Paginator */ abstract public function createPaginator(): Paginator; } @@ -44,10 +36,8 @@ class Cat implements Animal { } -/** - * @param Paginator $paginator - */ -function getFirstAnimal(Paginator $paginator): ?Animal +/** @param Paginator $paginator */ +function getFirstAnimal(Paginator $paginator): Animal|null { foreach ($paginator as $result) { return $result; @@ -56,10 +46,8 @@ function getFirstAnimal(Paginator $paginator): ?Animal return null; } -/** - * @param PaginatorFactory $catPaginatorFactory - */ -function test(PaginatorFactory $catPaginatorFactory): ?Animal +/** @param PaginatorFactory $catPaginatorFactory */ +function test(PaginatorFactory $catPaginatorFactory): Animal|null { return getFirstAnimal($catPaginatorFactory->createPaginator()); } diff --git a/tests/StaticAnalysis/get-metadata.php b/tests/StaticAnalysis/get-metadata.php index e55a438648c..a67d34fe459 100644 --- a/tests/StaticAnalysis/get-metadata.php +++ b/tests/StaticAnalysis/get-metadata.php @@ -16,7 +16,7 @@ abstract class GetMetadata { /** @param class-string|object $class */ - abstract public function getEntityManager($class): EntityManagerInterface; + abstract public function getEntityManager(string|object $class): EntityManagerInterface; /** * @param class-string $class diff --git a/tests/Tests/DbalExtensions/Connection.php b/tests/Tests/DbalExtensions/Connection.php index ce6d3982d2d..7d9ec33e3f8 100644 --- a/tests/Tests/DbalExtensions/Connection.php +++ b/tests/Tests/DbalExtensions/Connection.php @@ -10,23 +10,15 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Logging\Middleware as LoggingMiddleware; -use function class_exists; - class Connection extends BaseConnection { - /** @var QueryLog */ - public $queryLog; + public QueryLog $queryLog; - public function __construct(array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null) + public function __construct(array $params, Driver $driver, Configuration|null $config = null, EventManager|null $eventManager = null) { $this->queryLog = new QueryLog(); - if (class_exists(LoggingMiddleware::class)) { - $logging = new LoggingMiddleware(new SqlLogger($this->queryLog)); - $driver = $logging->wrap($driver); - } else { - $config = $config ?? new Configuration(); - $config->setSQLLogger(new LegacySqlLogger($this->queryLog)); - } + $logging = new LoggingMiddleware(new SqlLogger($this->queryLog)); + $driver = $logging->wrap($driver); parent::__construct($params, $driver, $config, $eventManager); } diff --git a/tests/Tests/DbalExtensions/LegacySqlLogger.php b/tests/Tests/DbalExtensions/LegacySqlLogger.php deleted file mode 100644 index b615b25f0a0..00000000000 --- a/tests/Tests/DbalExtensions/LegacySqlLogger.php +++ /dev/null @@ -1,27 +0,0 @@ -queryLog = $queryLog; - } - - public function startQuery($sql, ?array $params = null, ?array $types = null): void - { - $this->queryLog->logQuery($sql, $params, $types); - } - - public function stopQuery(): void - { - } -} diff --git a/tests/Tests/DbalExtensions/QueryLog.php b/tests/Tests/DbalExtensions/QueryLog.php index 066ee902c09..ca46b2fd113 100644 --- a/tests/Tests/DbalExtensions/QueryLog.php +++ b/tests/Tests/DbalExtensions/QueryLog.php @@ -7,12 +7,11 @@ final class QueryLog { /** @var list */ - public $queries = []; + public array $queries = []; - /** @var bool */ - private $enabled = false; + private bool $enabled = false; - public function logQuery(string $sql, ?array $params = null, ?array $types = null): void + public function logQuery(string $sql, array|null $params = null, array|null $types = null): void { if (! $this->enabled) { return; diff --git a/tests/Tests/DbalExtensions/SqlLogger.php b/tests/Tests/DbalExtensions/SqlLogger.php index 3bbfd67b2dd..2bde7406004 100644 --- a/tests/Tests/DbalExtensions/SqlLogger.php +++ b/tests/Tests/DbalExtensions/SqlLogger.php @@ -8,12 +8,8 @@ final class SqlLogger extends AbstractLogger { - /** @var QueryLog */ - private $queryLog; - - public function __construct(QueryLog $queryLog) + public function __construct(private readonly QueryLog $queryLog) { - $this->queryLog = $queryLog; } public function log($level, $message, array $context = []): void @@ -25,7 +21,7 @@ public function log($level, $message, array $context = []): void $this->queryLog->logQuery( $context['sql'], $context['params'] ?? null, - $context['types'] ?? null + $context['types'] ?? null, ); } } diff --git a/tests/Tests/DbalTypes/CustomIdObject.php b/tests/Tests/DbalTypes/CustomIdObject.php index 3a24a512059..8e7b6ddaa27 100644 --- a/tests/Tests/DbalTypes/CustomIdObject.php +++ b/tests/Tests/DbalTypes/CustomIdObject.php @@ -4,14 +4,12 @@ namespace Doctrine\Tests\DbalTypes; -class CustomIdObject -{ - /** @var string */ - public $id; +use Stringable; - public function __construct(string $id) +class CustomIdObject implements Stringable +{ + public function __construct(public string $id) { - $this->id = (string) $id; } public function __toString(): string diff --git a/tests/Tests/DbalTypes/CustomIdObjectType.php b/tests/Tests/DbalTypes/CustomIdObjectType.php index eb8c570407f..5cc893524ef 100644 --- a/tests/Tests/DbalTypes/CustomIdObjectType.php +++ b/tests/Tests/DbalTypes/CustomIdObjectType.php @@ -7,8 +7,6 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; -use function method_exists; - class CustomIdObjectType extends Type { public const NAME = 'CustomIdObject'; @@ -16,7 +14,7 @@ class CustomIdObjectType extends Type /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { return $value->id; } @@ -24,7 +22,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): CustomIdObject { return new CustomIdObject($value); } @@ -32,19 +30,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } diff --git a/tests/Tests/DbalTypes/CustomIntType.php b/tests/Tests/DbalTypes/CustomIntType.php index f501bf6fb7e..4ed60311e58 100644 --- a/tests/Tests/DbalTypes/CustomIntType.php +++ b/tests/Tests/DbalTypes/CustomIntType.php @@ -10,10 +10,7 @@ class CustomIntType extends IntegerType { public const NAME = 'custom_int_type'; - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } diff --git a/tests/Tests/DbalTypes/NegativeToPositiveType.php b/tests/Tests/DbalTypes/NegativeToPositiveType.php index 5121898f8aa..8f1896e9384 100644 --- a/tests/Tests/DbalTypes/NegativeToPositiveType.php +++ b/tests/Tests/DbalTypes/NegativeToPositiveType.php @@ -11,10 +11,7 @@ class NegativeToPositiveType extends Type { public const NAME = 'negative_to_positive'; - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } @@ -22,23 +19,15 @@ public function getName() /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) - { - return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); - } - - /** - * {@inheritDoc} - */ - public function canRequireSQLConversion() + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return true; + return $platform->getIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string { return 'ABS(' . $sqlExpr . ')'; } @@ -46,7 +35,7 @@ public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValueSQL($sqlExpr, $platform) + public function convertToPHPValueSQL($sqlExpr, $platform): string { return '-(' . $sqlExpr . ')'; } diff --git a/tests/Tests/DbalTypes/Rot13Type.php b/tests/Tests/DbalTypes/Rot13Type.php index 679eb3ed141..798b85b87ad 100644 --- a/tests/Tests/DbalTypes/Rot13Type.php +++ b/tests/Tests/DbalTypes/Rot13Type.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; -use function method_exists; use function str_rot13; /** @@ -18,12 +17,9 @@ class Rot13Type extends Type /** * {@inheritDoc} * - * @param string|null $value - * @param AbstractPlatform $platform - * - * @return string|null + * @param string|null $value */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string|null { if ($value === null) { return null; @@ -35,12 +31,9 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} * - * @param string|null $value - * @param AbstractPlatform $platform - * - * @return string|null + * @param string|null $value */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): string|null { if ($value === null) { return null; @@ -51,39 +44,13 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} - * - * @param array $fieldDeclaration - * @param AbstractPlatform $platform - * - * @return string - */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) - { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); - } - - /** - * {@inheritDoc} - * - * @param AbstractPlatform $platform - * - * @return int|null */ - public function getDefaultLength(AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getVarcharDefaultLength(); + return $platform->getStringTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - * - * @return string - */ - public function getName() + public function getName(): string { return 'rot13'; } diff --git a/tests/Tests/DbalTypes/UpperCaseStringType.php b/tests/Tests/DbalTypes/UpperCaseStringType.php index 251a689f1c7..24922110cd0 100644 --- a/tests/Tests/DbalTypes/UpperCaseStringType.php +++ b/tests/Tests/DbalTypes/UpperCaseStringType.php @@ -11,10 +11,7 @@ class UpperCaseStringType extends StringType { public const NAME = 'upper_case_string'; - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } @@ -22,15 +19,7 @@ public function getName() /** * {@inheritDoc} */ - public function canRequireSQLConversion() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string { return 'UPPER(' . $sqlExpr . ')'; } @@ -38,7 +27,7 @@ public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValueSQL($sqlExpr, $platform) + public function convertToPHPValueSQL($sqlExpr, $platform): string { return 'LOWER(' . $sqlExpr . ')'; } diff --git a/tests/Tests/DoctrineTestCase.php b/tests/Tests/DoctrineTestCase.php deleted file mode 100644 index 89a852f1573..00000000000 --- a/tests/Tests/DoctrineTestCase.php +++ /dev/null @@ -1,67 +0,0 @@ - */ - private static $phpunitMethodRenames = [ - 'assertMatchesRegularExpression' => 'assertRegExp', // can be removed when PHPUnit 9 is minimum - 'assertDoesNotMatchRegularExpression' => 'assertNotRegExp', // can be removed when PHPUnit 9 is minimum - 'assertFileDoesNotExist' => 'assertFileNotExists', // can be removed PHPUnit 9 is minimum - 'expectExceptionMessageMatches' => 'expectExceptionMessageRegExp', // can be removed when PHPUnit 8 is minimum - ]; - - /** - * @param array $arguments - * - * @return mixed - */ - public static function __callStatic(string $method, array $arguments) - { - if (isset(self::$phpunitMethodRenames[$method])) { - $method = self::$phpunitMethodRenames[$method]; - - return self::$method(...$arguments); - } - - throw new BadMethodCallException(sprintf('%s::%s does not exist', static::class, $method)); - } - - /** - * @param array $arguments - * - * @return mixed - */ - public function __call(string $method, array $arguments) - { - if ($method === 'createStub') { - return $this->getMockBuilder(...$arguments) - ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() - ->getMock(); - } - - if (isset(self::$phpunitMethodRenames[$method])) { - $method = self::$phpunitMethodRenames[$method]; - - return self::$method(...$arguments); - } - - throw new BadMethodCallException(sprintf('%s::%s does not exist', static::class, $method)); - } -} diff --git a/tests/Tests/EventListener/CacheMetadataListener.php b/tests/Tests/EventListener/CacheMetadataListener.php index 72d59d995a2..c666b04d2f9 100644 --- a/tests/Tests/EventListener/CacheMetadataListener.php +++ b/tests/Tests/EventListener/CacheMetadataListener.php @@ -65,11 +65,11 @@ protected function enableCaching(ClassMetadata $metadata, EntityManagerInterface // only enable association-caching when the target has already been // given caching settings foreach ($metadata->associationMappings as $mapping) { - $targetMeta = $em->getClassMetadata($mapping['targetEntity']); + $targetMeta = $em->getClassMetadata($mapping->targetEntity); $this->enableCaching($targetMeta, $em); if ($this->isVisited($targetMeta)) { - $metadata->enableAssociationCache($mapping['fieldName'], $cache); + $metadata->enableAssociationCache($mapping->fieldName, $cache); } } } diff --git a/tests/Tests/Mocks/ArrayResultFactory.php b/tests/Tests/Mocks/ArrayResultFactory.php index 591f41bd3d8..56a8110c613 100644 --- a/tests/Tests/Mocks/ArrayResultFactory.php +++ b/tests/Tests/Mocks/ArrayResultFactory.php @@ -4,20 +4,39 @@ namespace Doctrine\Tests\Mocks; +use Doctrine\DBAL\Cache\ArrayResult; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\PDO\SQLite\Driver; use Doctrine\DBAL\Result; +use ReflectionMethod; -use function interface_exists; +use function array_keys; +use function array_map; +use function array_values; final class ArrayResultFactory { - public static function createFromArray(array $resultSet): Result + /** @param list> $resultSet */ + public static function createDriverResultFromArray(array $resultSet): ArrayResult { - if (interface_exists(Result::class)) { - // DBAL 2 compatibility. - return new ResultMock($resultSet); + if ((new ReflectionMethod(ArrayResult::class, '__construct'))->getNumberOfRequiredParameters() < 2) { + // DBAL < 4.2 + return new ArrayResult($resultSet); } - return new Result(new DriverResultMock($resultSet), new Connection([], new DriverMock())); + // DBAL 4.2+ + return new ArrayResult( + array_keys($resultSet[0] ?? []), + array_map(array_values(...), $resultSet), + ); + } + + /** @param list> $resultSet */ + public static function createWrapperResultFromArray(array $resultSet, Connection|null $connection = null): Result + { + return new Result( + self::createDriverResultFromArray($resultSet), + $connection ?? new Connection([], new Driver()), + ); } } diff --git a/tests/Tests/Mocks/CacheKeyMock.php b/tests/Tests/Mocks/CacheKeyMock.php index 017ae23602d..4b1ad539482 100644 --- a/tests/Tests/Mocks/CacheKeyMock.php +++ b/tests/Tests/Mocks/CacheKeyMock.php @@ -13,9 +13,4 @@ */ class CacheKeyMock extends CacheKey { - /** @param string $hash The string hash that represents this cache key */ - public function __construct(string $hash) - { - parent::__construct($hash); - } } diff --git a/tests/Tests/Mocks/CacheRegionMock.php b/tests/Tests/Mocks/CacheRegionMock.php index d764ab561ba..a6968bff2f5 100644 --- a/tests/Tests/Mocks/CacheRegionMock.php +++ b/tests/Tests/Mocks/CacheRegionMock.php @@ -18,32 +18,25 @@ class CacheRegionMock implements Region { /** @var array>> */ - public $calls = []; + public array $calls = []; /** @var array */ - public $returns = []; + public array $returns = []; - /** @var string */ - public $name = 'mock'; + public string $name = 'mock'; /** * Queue a return value for a specific method invocation - * - * @param mixed $value */ - public function addReturn(string $method, $value): void + public function addReturn(string $method, mixed $value): void { $this->returns[$method][] = $value; } /** * Dequeue a value for a specific method invocation - * - * @param mixed $default - * - * @return mixed */ - private function getReturn(string $method, $default) + private function getReturn(string $method, mixed $default): mixed { if (isset($this->returns[$method]) && ! empty($this->returns[$method])) { return array_shift($this->returns[$method]); @@ -80,24 +73,21 @@ public function evictAll(): bool return $this->getReturn(__FUNCTION__, true); } - public function get(CacheKey $key): ?CacheEntry + public function get(CacheKey $key): CacheEntry|null { $this->calls[__FUNCTION__][] = ['key' => $key]; return $this->getReturn(__FUNCTION__, null); } - /** - * {@inheritDoc} - */ - public function getMultiple(CollectionCacheEntry $collection): ?array + public function getMultiple(CollectionCacheEntry $collection): array|null { $this->calls[__FUNCTION__][] = ['collection' => $collection]; return $this->getReturn(__FUNCTION__, null); } - public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null): bool + public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool { $this->calls[__FUNCTION__][] = ['key' => $key, 'entry' => $entry]; diff --git a/tests/Tests/Mocks/CompatibilityType.php b/tests/Tests/Mocks/CompatibilityType.php new file mode 100644 index 00000000000..41417068ab2 --- /dev/null +++ b/tests/Tests/Mocks/CompatibilityType.php @@ -0,0 +1,37 @@ +doGetBindingType(); + } + + private function doGetBindingType(): int|ParameterType + { + return parent::getBindingType(); + } + } +} else { + trait CompatibilityType + { + public function getBindingType(): ParameterType + { + return $this->doGetBindingType(); + } + + private function doGetBindingType(): int|ParameterType + { + return parent::getBindingType(); + } + } +} diff --git a/tests/Tests/Mocks/ConcurrentRegionMock.php b/tests/Tests/Mocks/ConcurrentRegionMock.php index 89ac562a2fc..2d5ff97b140 100644 --- a/tests/Tests/Mocks/ConcurrentRegionMock.php +++ b/tests/Tests/Mocks/ConcurrentRegionMock.php @@ -23,20 +23,17 @@ class ConcurrentRegionMock implements ConcurrentRegion { /** @phpstan-var array>> */ - public $calls = []; + public array $calls = []; /** @phpstan-var array> */ - public $exceptions = []; + public array $exceptions = []; /** @phpstan-var array */ - public $locks = []; + public array $locks = []; - /** @var Region */ - private $region; - - public function __construct(Region $region) - { - $this->region = $region; + public function __construct( + private readonly Region $region, + ) { } /** @@ -100,7 +97,7 @@ public function evictAll(): bool return $this->region->evictAll(); } - public function get(CacheKey $key): ?CacheEntry + public function get(CacheKey $key): CacheEntry|null { $this->calls[__FUNCTION__][] = ['key' => $key]; @@ -113,10 +110,7 @@ public function get(CacheKey $key): ?CacheEntry return $this->region->get($key); } - /** - * {@inheritDoc} - */ - public function getMultiple(CollectionCacheEntry $collection): ?array + public function getMultiple(CollectionCacheEntry $collection): array|null { $this->calls[__FUNCTION__][] = ['collection' => $collection]; @@ -134,7 +128,7 @@ public function getName(): string return $this->region->getName(); } - public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null): bool + public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool { $this->calls[__FUNCTION__][] = ['key' => $key, 'entry' => $entry]; @@ -151,7 +145,7 @@ public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null): bool return $this->region->put($key, $entry); } - public function lock(CacheKey $key): ?Lock + public function lock(CacheKey $key): Lock|null { $this->calls[__FUNCTION__][] = ['key' => $key]; diff --git a/tests/Tests/Mocks/ConnectionMock.php b/tests/Tests/Mocks/ConnectionMock.php deleted file mode 100644 index e25c8e0d5dc..00000000000 --- a/tests/Tests/Mocks/ConnectionMock.php +++ /dev/null @@ -1,132 +0,0 @@ -_platformMock = new DatabasePlatformMock(); - - parent::__construct($params, $driver ?? new DriverMock(), $config, $eventManager); - } - - public function getDatabase(): string - { - return 'mock'; - } - - /** - * {@inheritDoc} - */ - public function getDatabasePlatform() - { - return $this->_platformMock; - } - - /** - * {@inheritDoc} - */ - public function insert($tableName, array $data, array $types = []) - { - } - - /** - * {@inheritDoc} - */ - public function executeUpdate($query, array $params = [], array $types = []): int - { - throw new BadMethodCallException(sprintf('Call to deprecated method %s().', __METHOD__)); - } - - /** - * {@inheritDoc} - */ - public function executeStatement($sql, array $params = [], array $types = []): int - { - $this->_executeStatements[] = ['sql' => $sql, 'params' => $params, 'types' => $types]; - - return 1; - } - - /** - * {@inheritDoc} - */ - public function delete($table, array $criteria, array $types = []) - { - $this->_deletes[] = ['table' => $table, 'criteria' => $criteria, 'types' => $types]; - } - - /** - * {@inheritDoc} - */ - public function fetchColumn($statement, array $params = [], $colunm = 0, array $types = []) - { - throw new BadMethodCallException(sprintf( - 'Call to deprecated method %s().', - __METHOD__ - )); - } - - public function query(?string $sql = null): Result - { - throw new BadMethodCallException(sprintf( - 'Call to deprecated method %s().', - __METHOD__ - )); - } - - /** - * {@inheritDoc} - */ - public function quote($input, $type = null) - { - if (is_string($input)) { - return "'" . $input . "'"; - } - - return $input; - } - - public function setDatabasePlatform(AbstractPlatform $platform): void - { - $this->_platformMock = $platform; - } - - /** @return array */ - public function getExecuteStatements(): array - { - return $this->_executeStatements; - } - - /** @return array */ - public function getDeletes(): array - { - return $this->_deletes; - } -} diff --git a/tests/Tests/Mocks/CustomTreeWalkerJoin.php b/tests/Tests/Mocks/CustomTreeWalkerJoin.php index e15d332a479..94addb649c8 100644 --- a/tests/Tests/Mocks/CustomTreeWalkerJoin.php +++ b/tests/Tests/Mocks/CustomTreeWalkerJoin.php @@ -53,7 +53,7 @@ private function modifySelectStatement(SelectStatement $selectStatement, Identif 'map' => null, 'nestingLevel' => 0, 'token' => null, - ] + ], ); } } diff --git a/tests/Tests/Mocks/DatabasePlatformMock.php b/tests/Tests/Mocks/DatabasePlatformMock.php deleted file mode 100644 index aa87106d25f..00000000000 --- a/tests/Tests/Mocks/DatabasePlatformMock.php +++ /dev/null @@ -1,130 +0,0 @@ -resultMock = $resultMock; - } - - /** - * {@inheritDoc} - */ - public function prepare($prepareString): Statement - { - return new StatementMock(); - } - - public function query(?string $sql = null): Result - { - return $this->resultMock ?? new DriverResultMock(); - } - - /** - * {@inheritDoc} - */ - public function quote($input, $type = ParameterType::STRING) - { - } - - /** - * {@inheritDoc} - */ - public function exec($statement): int - { - } - - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) - { - } - - /** - * {@inheritDoc} - */ - public function beginTransaction() - { - } - - /** - * {@inheritDoc} - */ - public function commit() - { - } - - /** - * {@inheritDoc} - */ - public function rollBack() - { - } - - /** - * {@inheritDoc} - */ - public function errorCode() - { - } - - /** - * {@inheritDoc} - */ - public function errorInfo() - { - } -} diff --git a/tests/Tests/Mocks/DriverMock.php b/tests/Tests/Mocks/DriverMock.php deleted file mode 100644 index 04154e55f91..00000000000 --- a/tests/Tests/Mocks/DriverMock.php +++ /dev/null @@ -1,93 +0,0 @@ -_platformMock) { - $this->_platformMock = new DatabasePlatformMock(); - } - - return $this->_platformMock; - } - - public function getSchemaManager(Connection $conn, ?AbstractPlatform $platform = null): AbstractSchemaManager - { - return $this->_schemaManagerMock ?? new SchemaManagerMock($conn, $platform ?? new DatabasePlatformMock()); - } - - public function getExceptionConverter(): ExceptionConverter - { - return new ExceptionConverterMock(); - } - - /* MOCK API */ - - public function setDatabasePlatform(AbstractPlatform $platform): void - { - $this->_platformMock = $platform; - } - - public function setSchemaManager(AbstractSchemaManager $sm): void - { - $this->_schemaManagerMock = $sm; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - throw new BadMethodCallException(sprintf( - 'Call to deprecated method %s().', - __METHOD__ - )); - } - - /** - * {@inheritDoc} - */ - public function getDatabase(Connection $conn) - { - return 'not implemented'; - } - - public function convertExceptionCode(Exception $exception): int - { - return 0; - } -} diff --git a/tests/Tests/Mocks/DriverResultMock.php b/tests/Tests/Mocks/DriverResultMock.php deleted file mode 100644 index f4fd3fc56be..00000000000 --- a/tests/Tests/Mocks/DriverResultMock.php +++ /dev/null @@ -1,143 +0,0 @@ -> */ - private $resultSet; - - /** - * Creates a new mock statement that will serve the provided fake result set to clients. - * - * @param list> $resultSet The faked SQL result set. - */ - public function __construct(array $resultSet = []) - { - $this->resultSet = $resultSet; - } - - /** - * {@inheritDoc} - */ - public function fetchNumeric() - { - $row = $this->fetchAssociative(); - - return $row === false ? false : array_values($row); - } - - /** - * {@inheritDoc} - */ - public function fetchAssociative() - { - $current = current($this->resultSet); - next($this->resultSet); - - return $current; - } - - /** - * {@inheritDoc} - */ - public function fetchOne() - { - $row = $this->fetchNumeric(); - - return $row ? $row[0] : false; - } - - public function fetchAllNumeric(): array - { - $values = []; - while (($row = $this->fetchNumeric()) !== false) { - $values[] = $row; - } - - return $values; - } - - public function fetchAllAssociative(): array - { - $resultSet = $this->resultSet; - reset($resultSet); - - return $resultSet; - } - - public function fetchFirstColumn(): array - { - throw new BadMethodCallException('Not implemented'); - } - - public function rowCount(): int - { - return 0; - } - - public function columnCount(): int - { - $resultSet = $this->resultSet; - - return count(reset($resultSet) ?: []); - } - - public function free(): void - { - } - - public function closeCursor(): bool - { - $this->free(); - - return true; - } - - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null): void - { - } - - /** - * {@inheritDoc} - */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) - { - return $this->fetchAssociative(); - } - - /** - * {@inheritDoc} - */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null): array - { - return $this->fetchAllAssociative(); - } - - /** - * {@inheritDoc} - */ - public function fetchColumn($columnIndex = 0) - { - return $this->fetchOne(); - } - - public function getIterator(): Traversable - { - return new ArrayIterator($this->fetchAllAssociative()); - } -} diff --git a/tests/Tests/Mocks/EntityManagerMock.php b/tests/Tests/Mocks/EntityManagerMock.php index 1dfefe4f6a4..a594045b3f1 100644 --- a/tests/Tests/Mocks/EntityManagerMock.php +++ b/tests/Tests/Mocks/EntityManagerMock.php @@ -4,35 +4,29 @@ namespace Doctrine\Tests\Mocks; -use BadMethodCallException; use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; -use Doctrine\ORM\ORMSetup; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\TestUtil; -use function sprintf; - /** * Special EntityManager mock used for testing purposes. */ class EntityManagerMock extends EntityManager { - /** @var UnitOfWork|null */ - private $_uowMock; - - /** @var ProxyFactory|null */ - private $_proxyFactoryMock; + private UnitOfWork|null $_uowMock = null; + private ProxyFactory|null $_proxyFactoryMock = null; - public function __construct(Connection $conn, ?Configuration $config = null, ?EventManager $eventManager = null) + public function __construct(Connection $conn, Configuration|null $config = null, EventManager|null $eventManager = null) { if ($config === null) { $config = new Configuration(); TestUtil::configureProxies($config); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver()); + $config->setMetadataDriverImpl(new AttributeDriver([])); } parent::__construct($conn, $config, $eventManager); @@ -62,12 +56,4 @@ public function getProxyFactory(): ProxyFactory { return $this->_proxyFactoryMock ?? parent::getProxyFactory(); } - - /** - * {@inheritDoc} - */ - public static function create($connection, Configuration $config, ?EventManager $eventManager = null): self - { - throw new BadMethodCallException(sprintf('Call to deprecated method %s().', __METHOD__)); - } } diff --git a/tests/Tests/Mocks/EntityPersisterMock.php b/tests/Tests/Mocks/EntityPersisterMock.php index 1d44c2fd81d..e7e5ce85433 100644 --- a/tests/Tests/Mocks/EntityPersisterMock.php +++ b/tests/Tests/Mocks/EntityPersisterMock.php @@ -13,28 +13,18 @@ */ class EntityPersisterMock extends BasicEntityPersister { - /** @var array */ - private $inserts = []; - - /** @var array */ - private $updates = []; - - /** @var array */ - private $deletes = []; - - /** @var int */ - private $identityColumnValueCounter = 0; - - /** @var int|null */ - private $mockIdGeneratorType; + private array $inserts = []; + private array $updates = []; + private array $deletes = []; + private int $identityColumnValueCounter = 0; + private int|null $mockIdGeneratorType = null; /** @phpstan-var list */ - private $postInsertIds = []; + private array $postInsertIds = []; - /** @var bool */ - private $existsCalled = false; + private bool $existsCalled = false; - public function addInsert($entity): void + public function addInsert(object $entity): void { $this->inserts[] = $entity; if ($this->mockIdGeneratorType !== ClassMetadata::GENERATOR_TYPE_IDENTITY && ! $this->class->isIdGeneratorIdentity()) { @@ -48,10 +38,11 @@ public function addInsert($entity): void ]; } - /** @phpstan-return list */ - public function executeInserts(): array + public function executeInserts(): void { - return $this->postInsertIds; + foreach ($this->postInsertIds as $item) { + $this->em->getUnitOfWork()->assignPostInsertId($item['entity'], $item['generatedId']); + } } public function setMockIdGeneratorType(int $genType): void @@ -59,28 +50,19 @@ public function setMockIdGeneratorType(int $genType): void $this->mockIdGeneratorType = $genType; } - /** - * {@inheritDoc} - */ - public function update($entity): void + public function update(object $entity): void { $this->updates[] = $entity; } - /** - * {@inheritDoc} - */ - public function exists($entity, ?Criteria $extraConditions = null): bool + public function exists(object $entity, Criteria|null $extraConditions = null): bool { $this->existsCalled = true; return false; } - /** - * {@inheritDoc} - */ - public function delete($entity): bool + public function delete(object $entity): bool { $this->deletes[] = $entity; diff --git a/tests/Tests/Mocks/ExceptionConverterMock.php b/tests/Tests/Mocks/ExceptionConverterMock.php index 6bd69e97fcf..d6e91402f4f 100644 --- a/tests/Tests/Mocks/ExceptionConverterMock.php +++ b/tests/Tests/Mocks/ExceptionConverterMock.php @@ -11,7 +11,7 @@ class ExceptionConverterMock implements ExceptionConverter { - public function convert(Exception $exception, ?Query $query): DriverException + public function convert(Exception $exception, Query|null $query): DriverException { return new DriverException($exception, $query); } diff --git a/tests/Tests/Mocks/MetadataDriverMock.php b/tests/Tests/Mocks/MetadataDriverMock.php index b3be873c596..a2472cf8c46 100644 --- a/tests/Tests/Mocks/MetadataDriverMock.php +++ b/tests/Tests/Mocks/MetadataDriverMock.php @@ -15,14 +15,14 @@ class MetadataDriverMock implements MappingDriver /** * {@inheritDoc} */ - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadata $metadata): void { } /** * {@inheritDoc} */ - public function isTransient($className) + public function isTransient($className): bool { return false; } @@ -30,7 +30,7 @@ public function isTransient($className) /** * {@inheritDoc} */ - public function getAllClassNames() + public function getAllClassNames(): array { return []; } diff --git a/tests/Tests/Mocks/NullSqlWalker.php b/tests/Tests/Mocks/NullSqlWalker.php index 90cf0c6e926..f94d1705a60 100644 --- a/tests/Tests/Mocks/NullSqlWalker.php +++ b/tests/Tests/Mocks/NullSqlWalker.php @@ -14,24 +14,24 @@ /** * SqlWalker implementation that does not produce SQL. */ -class NullSqlWalker extends SqlOutputWalker +final class NullSqlWalker extends SqlOutputWalker { - public function walkSelectStatement(AST\SelectStatement $AST): string + public function walkSelectStatement(AST\SelectStatement $selectStatement): string { return ''; } - public function walkUpdateStatement(AST\UpdateStatement $AST): string + public function walkUpdateStatement(AST\UpdateStatement $updateStatement): string { return ''; } - public function walkDeleteStatement(AST\DeleteStatement $AST): string + public function walkDeleteStatement(AST\DeleteStatement $deleteStatement): string { return ''; } - public function getFinalizer($AST): SqlFinalizer + public function getFinalizer(AST\SelectStatement|AST\UpdateStatement|AST\DeleteStatement $statement): SqlFinalizer { return new PreparedExecutorFinalizer( new class extends AbstractSqlExecutor { @@ -39,7 +39,7 @@ public function execute(Connection $conn, array $params, array $types): int { return 0; } - } + }, ); } } diff --git a/tests/Tests/Mocks/ResultMock.php b/tests/Tests/Mocks/ResultMock.php deleted file mode 100644 index 15be2152b2a..00000000000 --- a/tests/Tests/Mocks/ResultMock.php +++ /dev/null @@ -1,129 +0,0 @@ -> $resultSet The faked SQL result set. - */ - public function __construct(array $resultSet) - { - $this->result = new DriverResultMock($resultSet); - } - - /** - * {@inheritDoc} - */ - public function fetchNumeric() - { - return $this->result->fetchNumeric(); - } - - /** - * {@inheritDoc} - */ - public function fetchAssociative() - { - return $this->result->fetchAssociative(); - } - - /** - * {@inheritDoc} - */ - public function fetchOne() - { - return $this->result->fetchOne(); - } - - /** - * {@inheritDoc} - */ - public function fetchAllAssociative(): array - { - return $this->result->fetchAllAssociative(); - } - - /** - * {@inheritDoc} - */ - public function fetchAllAssociativeIndexed(): array - { - throw new BadMethodCallException('Not implemented'); - } - - /** - * {@inheritDoc} - */ - public function fetchAllKeyValue(): array - { - throw new BadMethodCallException('Not implemented'); - } - - /** - * {@inheritDoc} - */ - public function fetchAllNumeric(): array - { - return $this->result->fetchAllNumeric(); - } - - /** - * {@inheritDoc} - */ - public function fetchFirstColumn(): array - { - return $this->result->fetchFirstColumn(); - } - - public function iterateKeyValue(): Traversable - { - throw new BadMethodCallException('Not implemented'); - } - - public function iterateColumn(): Traversable - { - throw new BadMethodCallException('Not implemented'); - } - - public function columnCount(): int - { - return $this->result->columnCount(); - } - - public function iterateNumeric(): Traversable - { - throw new BadMethodCallException('Not implemented'); - } - - public function iterateAssociative(): Traversable - { - throw new BadMethodCallException('Not implemented'); - } - - public function iterateAssociativeIndexed(): Traversable - { - throw new BadMethodCallException('Not implemented'); - } - - public function rowCount(): int - { - return $this->result->rowCount(); - } - - public function free(): void - { - } -} diff --git a/tests/Tests/Mocks/ResultStatement.php b/tests/Tests/Mocks/ResultStatement.php deleted file mode 100644 index 931262455e6..00000000000 --- a/tests/Tests/Mocks/ResultStatement.php +++ /dev/null @@ -1,20 +0,0 @@ -_persisterMock[$entityName] ?? parent::getEntityPersister($entityName); + return $this->persisterMock[$entityName] ?? parent::getEntityPersister($entityName); } /** * {@inheritDoc} */ - public function & getEntityChangeSet($entity) + public function & getEntityChangeSet(object $entity): array { $oid = spl_object_id($entity); - if (isset($this->_mockDataChangeSets[$oid])) { - return $this->_mockDataChangeSets[$oid]; + if (isset($this->mockDataChangeSets[$oid])) { + return $this->mockDataChangeSets[$oid]; } $data = parent::getEntityChangeSet($entity); @@ -52,13 +47,13 @@ public function & getEntityChangeSet($entity) */ public function setEntityPersister(string $entityName, BasicEntityPersister $persister): void { - $this->_persisterMock[$entityName] = $persister; + $this->persisterMock[$entityName] = $persister; } /** * {@inheritDoc} */ - public function setOriginalEntityData($entity, array $originalData) + public function setOriginalEntityData(object $entity, array $originalData): void { $this->_originalEntityData[spl_object_id($entity)] = $originalData; } diff --git a/tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php b/tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php index 5790cbe365d..6ef67359e99 100644 --- a/tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php +++ b/tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php @@ -8,37 +8,24 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - * @ORM\Table(name="abstract_fetch_eager_remote_control") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"mobile"="MobileRemoteControl"}) - */ +#[ORM\Entity] +#[ORM\Table(name: 'abstract_fetch_eager_remote_control')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap(['mobile' => 'MobileRemoteControl'])] abstract class AbstractRemoteControl { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public int $id; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $name; + #[ORM\Column(type: 'string')] + public string $name; - /** - * @ORM\OneToMany(targetEntity="User", mappedBy="remoteControl", fetch="EAGER") - * - * @var Collection - */ - public $users; + /** @var Collection */ + #[ORM\OneToMany(targetEntity: User::class, mappedBy: 'remoteControl', fetch: 'EAGER')] + public Collection $users; public function __construct(string $name) { diff --git a/tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php b/tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php index 50bdc25470b..c9246036741 100644 --- a/tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php +++ b/tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php @@ -6,9 +6,7 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - */ +#[ORM\Entity] class MobileRemoteControl extends AbstractRemoteControl { } diff --git a/tests/Tests/Models/AbstractFetchEager/User.php b/tests/Tests/Models/AbstractFetchEager/User.php index d8b7b2c012a..5755c359514 100644 --- a/tests/Tests/Models/AbstractFetchEager/User.php +++ b/tests/Tests/Models/AbstractFetchEager/User.php @@ -6,28 +6,18 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity() - * @ORM\Table(name="abstract_fetch_eager_user") - */ +#[ORM\Entity] +#[ORM\Table(name: 'abstract_fetch_eager_user')] class User { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public int $id; - /** - * @ORM\ManyToOne(targetEntity="AbstractRemoteControl", inversedBy="users") - * @ORM\JoinColumn(nullable=false) - * - * @var AbstractRemoteControl - */ - public $remoteControl; + #[ORM\ManyToOne(targetEntity: AbstractRemoteControl::class, inversedBy: 'users')] + #[ORM\JoinColumn(nullable: false)] + public AbstractRemoteControl $remoteControl; public function __construct(AbstractRemoteControl $control) { diff --git a/tests/Tests/Models/BigIntegers/BigIntegers.php b/tests/Tests/Models/BigIntegers/BigIntegers.php index 16b6f58dd0e..a79f6c2b05d 100644 --- a/tests/Tests/Models/BigIntegers/BigIntegers.php +++ b/tests/Tests/Models/BigIntegers/BigIntegers.php @@ -4,24 +4,23 @@ namespace Doctrine\Tests\Models\BigIntegers; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -/** @ORM\Entity */ +#[ORM\Entity] class BigIntegers { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - */ - public ?int $id = null; + #[ORM\Column] + #[ORM\Id] + #[ORM\GeneratedValue] + public int|null $id = null; - /** @ORM\Column(type="bigint") */ + #[ORM\Column(type: Types::BIGINT)] public int $one = 1; - /** @ORM\Column(type="bigint") */ + #[ORM\Column(type: Types::BIGINT)] public string $two = '2'; - /** @ORM\Column(type="bigint") */ + #[ORM\Column(type: Types::BIGINT)] public float $three = 3.0; } diff --git a/tests/Tests/Models/CMS/CmsAddress.php b/tests/Tests/Models/CMS/CmsAddress.php index b97dba337d7..b0aa6619fd6 100644 --- a/tests/Tests/Models/CMS/CmsAddress.php +++ b/tests/Tests/Models/CMS/CmsAddress.php @@ -8,108 +8,30 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\ColumnResult; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityListeners; -use Doctrine\ORM\Mapping\EntityResult; -use Doctrine\ORM\Mapping\FieldResult; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\NamedNativeQueries; -use Doctrine\ORM\Mapping\NamedNativeQuery; use Doctrine\ORM\Mapping\OneToOne; -use Doctrine\ORM\Mapping\SqlResultSetMapping; -use Doctrine\ORM\Mapping\SqlResultSetMappings; -use Doctrine\ORM\Mapping\Table; -/** - * CmsAddress - * - * @Entity - * @Table(name="cms_addresses") - * @NamedNativeQueries({ - * @NamedNativeQuery( - * name = "find-all", - * resultSetMapping = "mapping-find-all", - * query = "SELECT id, country, city FROM cms_addresses" - * ), - * @NamedNativeQuery( - * name = "find-by-id", - * resultClass = "CmsAddress", - * query = "SELECT * FROM cms_addresses WHERE id = ?" - * ), - * @NamedNativeQuery( - * name = "count", - * resultSetMapping= "mapping-count", - * query = "SELECT COUNT(*) AS count FROM cms_addresses" - * ) - * }) - * @SqlResultSetMappings({ - * @SqlResultSetMapping( - * name = "mapping-find-all", - * entities= { - * @EntityResult( - * entityClass = "CmsAddress", - * fields = { - * @FieldResult(name = "id", column="id"), - * @FieldResult(name = "city", column="city"), - * @FieldResult(name = "country", column="country") - * } - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mapping-without-fields", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__" - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mapping-count", - * columns = { - * @ColumnResult( - * name = "count" - * ) - * } - * ) - * }) - * @EntityListeners({"CmsAddressListener"}) - */ #[ORM\Entity] #[ORM\Table(name: 'cms_addresses')] #[ORM\EntityListeners(['CmsAddressListener'])] class CmsAddress { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] public $id; - /** - * @var string - * @Column(length=50) - */ + /** @var string */ + #[Column(length: 50)] public $country; - /** - * @var string - * @Column(length=50) - */ + /** @var string */ + #[Column(length: 50)] public $zip; - /** - * @var string - * @Column(length=50) - */ + /** @var string */ + #[Column(length: 50)] public $city; /** @@ -119,11 +41,9 @@ class CmsAddress */ public $street; - /** - * @var CmsUser - * @OneToOne(targetEntity="CmsUser", inversedBy="address") - * @JoinColumn(referencedColumnName="id") - */ + /** @var CmsUser */ + #[OneToOne(targetEntity: 'CmsUser', inversedBy: 'address')] + #[JoinColumn(referencedColumnName: 'id')] public $user; public function getId(): int @@ -162,7 +82,7 @@ public function setUser(CmsUser $user): void public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setPrimaryTable( - ['name' => 'company_person'] + ['name' => 'company_person'], ); $metadata->mapField( @@ -170,21 +90,21 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'zip', 'length' => 50, - ] + ], ); $metadata->mapField( [ 'fieldName' => 'city', 'length' => 50, - ] + ], ); $metadata->mapOneToOne( @@ -192,79 +112,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'user', 'targetEntity' => 'CmsUser', 'joinColumns' => [['referencedColumnName' => 'id']], - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT id, country, city FROM cms_addresses', - 'resultSetMapping' => 'mapping-find-all', - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'find-by-id', - 'query' => 'SELECT * FROM cms_addresses WHERE id = ?', - 'resultClass' => self::class, - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'count', - 'query' => 'SELECT COUNT(*) AS count FROM cms_addresses', - 'resultSetMapping' => 'mapping-count', - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-find-all', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'city', - 'column' => 'city', - ], - [ - 'name' => 'country', - 'column' => 'country', - ], - ], - 'entityClass' => self::class, - ], - ], - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-without-fields', - 'columns' => [], - 'entities' => [ - [ - 'entityClass' => self::class, - 'fields' => [], - ], - ], - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-count', - 'columns' => [ - ['name' => 'count'], - ], - ] + ], ); $metadata->addEntityListener(Events::postPersist, 'CmsAddressListener', 'postPersist'); diff --git a/tests/Tests/Models/CMS/CmsAddressDTO.php b/tests/Tests/Models/CMS/CmsAddressDTO.php index a2bdce4e308..502644ed25e 100644 --- a/tests/Tests/Models/CMS/CmsAddressDTO.php +++ b/tests/Tests/Models/CMS/CmsAddressDTO.php @@ -6,22 +6,7 @@ class CmsAddressDTO { - /** @var string|null */ - public $country; - - /** @var string|null */ - public $city; - - /** @var string|null */ - public $zip; - - public function __construct( - ?string $country = null, - ?string $city = null, - ?string $zip = null - ) { - $this->country = $country; - $this->city = $city; - $this->zip = $zip; + public function __construct(public string|null $country = null, public string|null $city = null, public string|null $zip = null, public CmsAddressDTO|string|null $address = null) + { } } diff --git a/tests/Tests/Models/CMS/CmsAddressDTONamedArgs.php b/tests/Tests/Models/CMS/CmsAddressDTONamedArgs.php new file mode 100644 index 00000000000..547c7fb0c31 --- /dev/null +++ b/tests/Tests/Models/CMS/CmsAddressDTONamedArgs.php @@ -0,0 +1,16 @@ + - * @OneToMany(targetEntity="CmsComment", mappedBy="article") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'CmsComment', mappedBy: 'article')] public $comments; - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; public function setAuthor(CmsUser $author): void diff --git a/tests/Tests/Models/CMS/CmsComment.php b/tests/Tests/Models/CMS/CmsComment.php index cce79925ada..e5d816283e9 100644 --- a/tests/Tests/Models/CMS/CmsComment.php +++ b/tests/Tests/Models/CMS/CmsComment.php @@ -11,38 +11,29 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; +use Stringable; -/** - * @Entity - * @Table(name="cms_comments") - */ -class CmsComment +#[Table(name: 'cms_comments')] +#[Entity] +class CmsComment implements Stringable { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $topic; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $text; - /** - * @var CmsArticle - * @ManyToOne(targetEntity="CmsArticle", inversedBy="comments") - * @JoinColumn(name="article_id", referencedColumnName="id") - */ + /** @var CmsArticle */ + #[ManyToOne(targetEntity: 'CmsArticle', inversedBy: 'comments')] + #[JoinColumn(name: 'article_id', referencedColumnName: 'id')] public $article; public function setArticle(CmsArticle $article): void diff --git a/tests/Tests/Models/CMS/CmsEmail.php b/tests/Tests/Models/CMS/CmsEmail.php index 8d30291802b..905b3624d9f 100644 --- a/tests/Tests/Models/CMS/CmsEmail.php +++ b/tests/Tests/Models/CMS/CmsEmail.php @@ -13,30 +13,23 @@ /** * CmsEmail - * - * @Entity - * @Table(name="cms_emails") */ +#[Table(name: 'cms_emails')] +#[Entity] class CmsEmail { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(length=250) - */ + /** @var string */ + #[Column(length: 250)] public $email; - /** - * @var CmsUser - * @OneToOne(targetEntity="CmsUser", mappedBy="email") - */ + /** @var CmsUser */ + #[OneToOne(targetEntity: 'CmsUser', mappedBy: 'email')] public $user; public function getId(): int diff --git a/tests/Tests/Models/CMS/CmsEmployee.php b/tests/Tests/Models/CMS/CmsEmployee.php index e79fd075b8f..e7c653f23ff 100644 --- a/tests/Tests/Models/CMS/CmsEmployee.php +++ b/tests/Tests/Models/CMS/CmsEmployee.php @@ -14,32 +14,22 @@ /** * Description of CmsEmployee - * - * @Entity - * @Table(name="cms_employees") */ +#[Table(name: 'cms_employees')] +#[Entity] class CmsEmployee { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @var CmsEmployee - * @OneToOne(targetEntity="CmsEmployee") - * @JoinColumn(name="spouse_id", referencedColumnName="id") - */ - private $spouse; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + #[Column] + private string $name; + + #[OneToOne(targetEntity: 'CmsEmployee')] + #[JoinColumn(name: 'spouse_id', referencedColumnName: 'id')] + private CmsEmployee $spouse; public function getId(): int { @@ -51,7 +41,7 @@ public function getName(): string return $this->name; } - public function getSpouse(): ?CmsEmployee + public function getSpouse(): CmsEmployee|null { return $this->spouse; } diff --git a/tests/Tests/Models/CMS/CmsGroup.php b/tests/Tests/Models/CMS/CmsGroup.php index b815afa58ed..d3b9329b51c 100644 --- a/tests/Tests/Models/CMS/CmsGroup.php +++ b/tests/Tests/Models/CMS/CmsGroup.php @@ -16,30 +16,23 @@ /** * Description of CmsGroup - * - * @Entity - * @Table(name="cms_groups") */ +#[Table(name: 'cms_groups')] +#[Entity] class CmsGroup implements IteratorAggregate { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(length=50) - */ + /** @var string */ + #[Column(length: 50)] public $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CmsUser", mappedBy="groups") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'CmsUser', mappedBy: 'groups')] public $users; public function __construct() diff --git a/tests/Tests/Models/CMS/CmsPhonenumber.php b/tests/Tests/Models/CMS/CmsPhonenumber.php index 5ffc16ba55e..1f23e4e0b60 100644 --- a/tests/Tests/Models/CMS/CmsPhonenumber.php +++ b/tests/Tests/Models/CMS/CmsPhonenumber.php @@ -11,24 +11,18 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="cms_phonenumbers") - */ +#[Table(name: 'cms_phonenumbers')] +#[Entity] class CmsPhonenumber { - /** - * @var string - * @Id - * @Column(length=50) - */ + /** @var string */ + #[Id] + #[Column(length: 50)] public $phonenumber; - /** - * @var CmsUser - * @ManyToOne(targetEntity="CmsUser", inversedBy="phonenumbers", cascade={"merge"}) - * @JoinColumn(name="user_id", referencedColumnName="id") - */ + /** @var CmsUser */ + #[ManyToOne(targetEntity: 'CmsUser', inversedBy: 'phonenumbers', cascade: [])] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] public $user; public function setUser(CmsUser $user): void @@ -36,7 +30,7 @@ public function setUser(CmsUser $user): void $this->user = $user; } - public function getUser(): ?CmsUser + public function getUser(): CmsUser|null { return $this->user; } diff --git a/tests/Tests/Models/CMS/CmsTag.php b/tests/Tests/Models/CMS/CmsTag.php index 9153b0a8d6c..b59f67cead5 100644 --- a/tests/Tests/Models/CMS/CmsTag.php +++ b/tests/Tests/Models/CMS/CmsTag.php @@ -14,30 +14,23 @@ /** * Description of CmsTag - * - * @Entity - * @Table(name="cms_tags") */ +#[Table(name: 'cms_tags')] +#[Entity] class CmsTag { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(length=50, name="tag_name", nullable=true) - */ + /** @var string */ + #[Column(length: 50, name: 'tag_name', nullable: true)] public $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CmsUser", mappedBy="tags") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'CmsUser', mappedBy: 'tags')] public $users; public function setName(string $name): void diff --git a/tests/Tests/Models/CMS/CmsUser.php b/tests/Tests/Models/CMS/CmsUser.php index 641f4b467dc..d94fa20adc3 100644 --- a/tests/Tests/Models/CMS/CmsUser.php +++ b/tests/Tests/Models/CMS/CmsUser.php @@ -8,208 +8,68 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\ColumnResult; use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityResult; -use Doctrine\ORM\Mapping\FieldResult; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; -use Doctrine\ORM\Mapping\NamedNativeQueries; -use Doctrine\ORM\Mapping\NamedNativeQuery; -use Doctrine\ORM\Mapping\NamedQueries; -use Doctrine\ORM\Mapping\NamedQuery; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; -use Doctrine\ORM\Mapping\SqlResultSetMapping; -use Doctrine\ORM\Mapping\SqlResultSetMappings; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="cms_users") - * @NamedQueries({ - * @NamedQuery(name="all", query="SELECT u FROM __CLASS__ u") - * }) - * @NamedNativeQueries({ - * @NamedNativeQuery( - * name = "fetchIdAndUsernameWithResultClass", - * resultClass = "CmsUser", - * query = "SELECT id, username FROM cms_users WHERE username = ?" - * ), - * @NamedNativeQuery( - * name = "fetchAllColumns", - * resultClass = "CmsUser", - * query = "SELECT * FROM cms_users WHERE username = ?" - * ), - * @NamedNativeQuery( - * name = "fetchJoinedAddress", - * resultSetMapping= "mappingJoinedAddress", - * query = "SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?" - * ), - * @NamedNativeQuery( - * name = "fetchJoinedPhonenumber", - * resultSetMapping= "mappingJoinedPhonenumber", - * query = "SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?" - * ), - * @NamedNativeQuery( - * name = "fetchUserPhonenumberCount", - * resultSetMapping= "mappingUserPhonenumberCount", - * query = "SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username" - * ), - * @NamedNativeQuery( - * name = "fetchMultipleJoinsEntityResults", - * resultSetMapping= "mappingMultipleJoinsEntityResults", - * query = "SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username" - * ), - * }) - * @SqlResultSetMappings({ - * @SqlResultSetMapping( - * name = "mappingJoinedAddress", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * fields = { - * @FieldResult(name = "id"), - * @FieldResult(name = "name"), - * @FieldResult(name = "status"), - * @FieldResult(name = "address.zip"), - * @FieldResult(name = "address.city"), - * @FieldResult(name = "address.country"), - * @FieldResult(name = "address.id", column = "a_id"), - * } - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mappingJoinedPhonenumber", - * entities= { - * @EntityResult( - * entityClass = "CmsUser", - * fields = { - * @FieldResult("id"), - * @FieldResult("name"), - * @FieldResult("status"), - * @FieldResult("phonenumbers.phonenumber" , column = "number"), - * } - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mappingUserPhonenumberCount", - * entities= { - * @EntityResult( - * entityClass = "CmsUser", - * fields = { - * @FieldResult(name = "id"), - * @FieldResult(name = "name"), - * @FieldResult(name = "status"), - * } - * ) - * }, - * columns = { - * @ColumnResult("numphones") - * } - * ), - * @SqlResultSetMapping( - * name = "mappingMultipleJoinsEntityResults", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * fields = { - * @FieldResult(name = "id", column="u_id"), - * @FieldResult(name = "name", column="u_name"), - * @FieldResult(name = "status", column="u_status"), - * } - * ), - * @EntityResult( - * entityClass = "CmsAddress", - * fields = { - * @FieldResult(name = "id", column="a_id"), - * @FieldResult(name = "zip", column="a_zip"), - * @FieldResult(name = "country", column="a_country"), - * } - * ) - * }, - * columns = { - * @ColumnResult("numphones") - * } - * ) - * }) - */ +#[Table(name: 'cms_users')] +#[Entity] class CmsUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=50, nullable=true) - */ + /** @var string */ + #[Column(type: 'string', length: 50, nullable: true)] public $status; - /** - * @var string - * @Column(type="string", length=255, unique=true) - */ + /** @var string */ + #[Column(type: 'string', length: 255, unique: true)] public $username; - /** - * @phpstan-var string|null - * @Column(type="string", length=255) - */ + /** @phpstan-var string|null */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'CmsPhonenumber', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] public $phonenumbers; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="CmsArticle", mappedBy="user", cascade={"detach"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'CmsArticle', mappedBy: 'user', cascade: ['detach'])] public $articles; - /** - * @var CmsAddress - * @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"persist"}, orphanRemoval=true) - */ + /** @var CmsAddress */ + #[OneToOne(targetEntity: 'CmsAddress', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] public $address; - /** - * @var CmsEmail - * @OneToOne(targetEntity="CmsEmail", inversedBy="user", cascade={"persist"}, orphanRemoval=true) - * @JoinColumn(referencedColumnName="id", nullable=true) - */ + /** @var CmsEmail */ + #[OneToOne(targetEntity: 'CmsEmail', inversedBy: 'user', cascade: ['persist'], orphanRemoval: true)] + #[JoinColumn(referencedColumnName: 'id', nullable: true)] public $email; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge", "detach"}) - * @JoinTable(name="cms_users_groups", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'cms_users_groups')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'CmsGroup', inversedBy: 'users', cascade: ['persist', 'detach'])] public $groups; - /** - * @var Collection - * @ManyToMany(targetEntity="CmsTag", inversedBy="users", cascade={"all"}) - * @JoinTable(name="cms_users_tags", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="tag_id", referencedColumnName="id")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'cms_users_tags')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'CmsTag', inversedBy: 'users', cascade: ['all'])] public $tags; /** @var mixed */ @@ -241,7 +101,7 @@ public function getUsername(): string return $this->username; } - public function getName(): ?string + public function getName(): string|null { return $this->name; } @@ -316,12 +176,12 @@ public function setAddress(CmsAddress $address): void } } - public function getEmail(): ?CmsEmail + public function getEmail(): CmsEmail|null { return $this->email; } - public function setEmail(?CmsEmail $email = null): void + public function setEmail(CmsEmail|null $email = null): void { if ($this->email !== $email) { $this->email = $email; @@ -335,206 +195,7 @@ public function setEmail(?CmsEmail $email = null): void public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setPrimaryTable( - ['name' => 'cms_users'] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchIdAndUsernameWithResultClass', - 'query' => 'SELECT id, username FROM cms_users WHERE username = ?', - 'resultClass' => self::class, - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllColumns', - 'query' => 'SELECT * FROM cms_users WHERE username = ?', - 'resultClass' => self::class, - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchJoinedAddress', - 'query' => 'SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', - 'resultSetMapping' => 'mappingJoinedAddress', - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchJoinedPhonenumber', - 'query' => 'SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', - 'resultSetMapping' => 'mappingJoinedPhonenumber', - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchUserPhonenumberCount', - 'query' => 'SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username', - 'resultSetMapping' => 'mappingUserPhonenumberCount', - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchMultipleJoinsEntityResults', - 'resultSetMapping' => 'mappingMultipleJoinsEntityResults', - 'query' => 'SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username', - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingJoinedAddress', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - [ - 'name' => 'address.zip', - 'column' => 'zip', - ], - [ - 'name' => 'address.city', - 'column' => 'city', - ], - [ - 'name' => 'address.country', - 'column' => 'country', - ], - [ - 'name' => 'address.id', - 'column' => 'a_id', - ], - ], - 'entityClass' => self::class, - 'discriminatorColumn' => null, - ], - ], - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingJoinedPhonenumber', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - [ - 'name' => 'phonenumbers.phonenumber', - 'column' => 'number', - ], - ], - 'entityClass' => self::class, - 'discriminatorColumn' => null, - ], - ], - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingUserPhonenumberCount', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - ], - 'entityClass' => self::class, - 'discriminatorColumn' => null, - ], - ], - 'columns' => [ - ['name' => 'numphones'], - ], - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingMultipleJoinsEntityResults', - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'u_id', - ], - [ - 'name' => 'name', - 'column' => 'u_name', - ], - [ - 'name' => 'status', - 'column' => 'u_status', - ], - ], - 'entityClass' => self::class, - 'discriminatorColumn' => null, - ], - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'a_id', - ], - [ - 'name' => 'zip', - 'column' => 'a_zip', - ], - [ - 'name' => 'country', - 'column' => 'a_country', - ], - ], - 'entityClass' => CmsAddress::class, - 'discriminatorColumn' => null, - ], - ], - 'columns' => [ - ['name' => 'numphones'], - ], - ] + ['name' => 'cms_users'], ); } } diff --git a/tests/Tests/Models/CMS/CmsUserDTO.php b/tests/Tests/Models/CMS/CmsUserDTO.php index 41d171407a1..f2dc43114db 100644 --- a/tests/Tests/Models/CMS/CmsUserDTO.php +++ b/tests/Tests/Models/CMS/CmsUserDTO.php @@ -6,27 +6,7 @@ class CmsUserDTO { - /** @var string|null */ - public $name; - - /** @var string|null */ - public $email; - - /** @var string|null */ - public $address; - - /** @var int|null */ - public $phonenumbers; - - public function __construct( - ?string $name = null, - ?string $email = null, - ?string $address = null, - ?int $phonenumbers = null - ) { - $this->name = $name; - $this->email = $email; - $this->address = $address; - $this->phonenumbers = $phonenumbers; + public function __construct(public string|null $name = null, public string|null $email = null, public CmsAddressDTO|string|null $address = null, public int|null $phonenumbers = null) + { } } diff --git a/tests/Tests/Models/CMS/CmsUserDTONamedArgs.php b/tests/Tests/Models/CMS/CmsUserDTONamedArgs.php new file mode 100644 index 00000000000..b9a7652911a --- /dev/null +++ b/tests/Tests/Models/CMS/CmsUserDTONamedArgs.php @@ -0,0 +1,18 @@ +name = $args['name'] ?? null; + $this->email = $args['email'] ?? null; + $this->phonenumbers = $args['phonenumbers'] ?? null; + $this->address = $args['address'] ?? null; + } +} diff --git a/tests/Tests/Models/Cache/Action.php b/tests/Tests/Models/Cache/Action.php index ffe80e63a04..88d8dfd3495 100644 --- a/tests/Tests/Models/Cache/Action.php +++ b/tests/Tests/Models/Cache/Action.php @@ -13,29 +13,20 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_action") - */ +#[Table('cache_action')] +#[Entity] class Action { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - public $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Token", cascade={"persist", "remove"}, mappedBy="action") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Token', cascade: ['persist', 'remove'], mappedBy: 'action')] public $tokens; - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + public string $name, + ) { $this->tokens = new ArrayCollection(); } diff --git a/tests/Tests/Models/Cache/Address.php b/tests/Tests/Models/Cache/Address.php index bfd8cbce90a..442457d957c 100644 --- a/tests/Tests/Models/Cache/Address.php +++ b/tests/Tests/Models/Cache/Address.php @@ -12,35 +12,24 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_client_address") - */ +#[Table('cache_client_address')] +#[Entity] class Address { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var Person - * @JoinColumn(name="person_id", referencedColumnName="id") - * @OneToOne(targetEntity="Person", inversedBy="address") - */ + /** @var Person */ + #[JoinColumn(name: 'person_id', referencedColumnName: 'id')] + #[OneToOne(targetEntity: 'Person', inversedBy: 'address')] public $person; - /** - * @var string - * @Column - */ - public $location; - - public function __construct(string $location) - { - $this->location = $location; + public function __construct( + #[Column] + public string $location, + ) { } } diff --git a/tests/Tests/Models/Cache/Attraction.php b/tests/Tests/Models/Cache/Attraction.php index 51fd5e6f729..04d9358ce39 100644 --- a/tests/Tests/Models/Cache/Attraction.php +++ b/tests/Tests/Models/Cache/Attraction.php @@ -18,52 +18,32 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Cache("NONSTRICT_READ_WRITE") - * @Entity - * @Table("cache_attraction") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({ - * 1 = "Restaurant", - * 2 = "Beach", - * 3 = "Bar" - * }) - */ +#[Table('cache_attraction')] +#[Cache('NONSTRICT_READ_WRITE')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap([1 => 'Restaurant', 2 => 'Beach', 3 => 'Bar'])] abstract class Attraction { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - protected $name; - - /** - * @var City - * @Cache - * @ManyToOne(targetEntity="City", inversedBy="attractions") - * @JoinColumn(name="city_id", referencedColumnName="id") - */ - protected $city; - - /** - * @phpstan-var Collection - * @Cache - * @OneToMany(targetEntity="AttractionInfo", mappedBy="attraction") - */ + /** @phpstan-var Collection */ + #[Cache] + #[OneToMany(targetEntity: 'AttractionInfo', mappedBy: 'attraction')] protected $infos; - public function __construct(string $name, City $city) - { - $this->name = $name; - $this->city = $city; + public function __construct( + #[Column(unique: true)] + protected string $name, + #[Cache] + #[ManyToOne(targetEntity: 'City', inversedBy: 'attractions')] + #[JoinColumn(name: 'city_id', referencedColumnName: 'id')] + protected City $city, + ) { $this->infos = new ArrayCollection(); } diff --git a/tests/Tests/Models/Cache/AttractionContactInfo.php b/tests/Tests/Models/Cache/AttractionContactInfo.php index 65e6a70e78f..70ac0565594 100644 --- a/tests/Tests/Models/Cache/AttractionContactInfo.php +++ b/tests/Tests/Models/Cache/AttractionContactInfo.php @@ -8,16 +8,12 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_attraction_contact_info") - */ +#[Table('cache_attraction_contact_info')] +#[Entity] class AttractionContactInfo extends AttractionInfo { - /** - * @var string - * @Column(unique=true) - */ + /** @var string */ + #[Column(unique: true)] protected $fone; public function __construct(string $fone, Attraction $attraction) diff --git a/tests/Tests/Models/Cache/AttractionInfo.php b/tests/Tests/Models/Cache/AttractionInfo.php index bc8e68185fe..6885565b30b 100644 --- a/tests/Tests/Models/Cache/AttractionInfo.php +++ b/tests/Tests/Models/Cache/AttractionInfo.php @@ -15,32 +15,23 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Cache - * @Entity - * @Table("cache_attraction_info") - * @InheritanceType("JOINED") - * @DiscriminatorMap({ - * 1 = "AttractionContactInfo", - * 2 = "AttractionLocationInfo", - * }) - */ +#[Table('cache_attraction_info')] +#[Cache] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap([1 => 'AttractionContactInfo', 2 => 'AttractionLocationInfo'])] abstract class AttractionInfo { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var Attraction - * @Cache - * @ManyToOne(targetEntity="Attraction", inversedBy="infos") - * @JoinColumn(name="attraction_id", referencedColumnName="id") - */ + /** @var Attraction */ + #[Cache] + #[ManyToOne(targetEntity: 'Attraction', inversedBy: 'infos')] + #[JoinColumn(name: 'attraction_id', referencedColumnName: 'id')] protected $attraction; public function getId(): int diff --git a/tests/Tests/Models/Cache/AttractionLocationInfo.php b/tests/Tests/Models/Cache/AttractionLocationInfo.php index daf48374e29..093df20f8bc 100644 --- a/tests/Tests/Models/Cache/AttractionLocationInfo.php +++ b/tests/Tests/Models/Cache/AttractionLocationInfo.php @@ -8,16 +8,12 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_attraction_location_info") - */ +#[Table('cache_attraction_location_info')] +#[Entity] class AttractionLocationInfo extends AttractionInfo { - /** - * @var string - * @Column(unique=true) - */ + /** @var string */ + #[Column(unique: true)] protected $address; public function __construct(string $address, Attraction $attraction) diff --git a/tests/Tests/Models/Cache/Bar.php b/tests/Tests/Models/Cache/Bar.php index e937370b609..19517107fa2 100644 --- a/tests/Tests/Models/Cache/Bar.php +++ b/tests/Tests/Models/Cache/Bar.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class Bar extends Attraction { } diff --git a/tests/Tests/Models/Cache/Beach.php b/tests/Tests/Models/Cache/Beach.php index 65fdad68827..e09baeab212 100644 --- a/tests/Tests/Models/Cache/Beach.php +++ b/tests/Tests/Models/Cache/Beach.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class Beach extends Attraction { } diff --git a/tests/Tests/Models/Cache/City.php b/tests/Tests/Models/Cache/City.php index 56a83e97e79..834ba0abf07 100644 --- a/tests/Tests/Models/Cache/City.php +++ b/tests/Tests/Models/Cache/City.php @@ -7,80 +7,37 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\Cache; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\ManyToMany; -use Doctrine\ORM\Mapping\ManyToOne; -use Doctrine\ORM\Mapping\OneToMany; -use Doctrine\ORM\Mapping\OrderBy; -use Doctrine\ORM\Mapping\Table; - -/** - * @Cache - * @Entity - * @Table("cache_city") - */ + #[ORM\Entity] #[ORM\Table(name: 'cache_city')] #[ORM\Cache] class City { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - #[ORM\Column(unique: true)] - protected $name; - - /** - * @var State|null - * @Cache - * @ManyToOne(targetEntity="State", inversedBy="cities") - * @JoinColumn(name="state_id", referencedColumnName="id") - */ - #[ORM\Cache] - #[ORM\ManyToOne(targetEntity: 'State', inversedBy: 'cities')] - #[ORM\JoinColumn(name: 'state_id', referencedColumnName: 'id')] - protected $state; - - /** - * @var Collection - * @ManyToMany(targetEntity="Travel", mappedBy="visitedCities") - */ + /** @var Collection */ #[ORM\ManyToMany(targetEntity: 'Travel', mappedBy: 'visitedCities')] public $travels; - /** - * @phpstan-var Collection - * @Cache - * @OrderBy({"name" = "ASC"}) - * @OneToMany(targetEntity="Attraction", mappedBy="city") - */ + /** @phpstan-var Collection */ #[ORM\Cache] #[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OneToMany(targetEntity: 'Attraction', mappedBy: 'city')] public $attractions; - public function __construct(string $name, ?State $state = null) - { - $this->name = $name; - $this->state = $state; + public function __construct( + #[ORM\Column(unique: true)] + protected string $name, + #[ORM\Cache] + #[ORM\ManyToOne(targetEntity: 'State', inversedBy: 'cities')] + #[ORM\JoinColumn(name: 'state_id', referencedColumnName: 'id')] + protected State|null $state = null, + ) { $this->travels = new ArrayCollection(); $this->attractions = new ArrayCollection(); } @@ -105,7 +62,7 @@ public function setName(string $name): void $this->name = $name; } - public function getState(): ?State + public function getState(): State|null { return $this->state; } @@ -139,6 +96,68 @@ public function getAttractions(): Collection public static function loadMetadata(ClassMetadata $metadata): void { - include __DIR__ . '/../../ORM/Mapping/php/Doctrine.Tests.Models.Cache.City.php'; + $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); + $metadata->setPrimaryTable(['name' => 'cache_city']); + $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); + $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); + + $metadata->enableCache( + [ + 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, + ], + ); + + $metadata->mapField( + [ + 'fieldName' => 'id', + 'type' => 'integer', + 'id' => true, + ], + ); + + $metadata->mapField( + [ + 'fieldName' => 'name', + 'type' => 'string', + ], + ); + + $metadata->mapOneToOne( + [ + 'fieldName' => 'state', + 'targetEntity' => State::class, + 'inversedBy' => 'cities', + 'joinColumns' => + [ + [ + 'name' => 'state_id', + 'referencedColumnName' => 'id', + ], + ], + ], + ); + $metadata->enableAssociationCache('state', [ + 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, + ]); + + $metadata->mapManyToMany( + [ + 'fieldName' => 'travels', + 'targetEntity' => Travel::class, + 'mappedBy' => 'visitedCities', + ], + ); + + $metadata->mapOneToMany( + [ + 'fieldName' => 'attractions', + 'targetEntity' => Attraction::class, + 'mappedBy' => 'city', + 'orderBy' => ['name' => 'ASC'], + ], + ); + $metadata->enableAssociationCache('attractions', [ + 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, + ]); } } diff --git a/tests/Tests/Models/Cache/Client.php b/tests/Tests/Models/Cache/Client.php index 1e763eadd2a..991fa1e8816 100644 --- a/tests/Tests/Models/Cache/Client.php +++ b/tests/Tests/Models/Cache/Client.php @@ -10,28 +10,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_client") - */ +#[Table('cache_client')] +#[Entity] class Client { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(unique=true) - */ - public $name; - - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Column(unique: true)] + public string $name, + ) { } } diff --git a/tests/Tests/Models/Cache/ComplexAction.php b/tests/Tests/Models/Cache/ComplexAction.php index a642ce571df..7e0da48add3 100644 --- a/tests/Tests/Models/Cache/ComplexAction.php +++ b/tests/Tests/Models/Cache/ComplexAction.php @@ -14,46 +14,29 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_complex_action") - */ +#[Table('cache_complex_action')] +#[Entity] class ComplexAction { - /** - * @var string - * @Column - */ - public $name; - - /** - * @var Action - * @Id - * @OneToOne(targetEntity="Action", cascade={"persist", "remove"}) - * @JoinColumn(name="action1_name", referencedColumnName="name") - */ - public $action1; - - /** - * @var Action - * @Id - * @OneToOne(targetEntity="Action", cascade={"persist", "remove"}) - * @JoinColumn(name="action2_name", referencedColumnName="name") - */ - public $action2; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Token", cascade={"persist", "remove"}, mappedBy="complexAction") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Token', cascade: ['persist', 'remove'], mappedBy: 'complexAction')] public $tokens; - public function __construct(Action $action1, Action $action2, string $name) - { - $this->name = $name; - $this->action1 = $action1; - $this->action2 = $action2; - $this->tokens = new ArrayCollection(); + public function __construct( + /** @var Action */ + #[Id] + #[OneToOne(targetEntity: 'Action', cascade: ['persist', 'remove'])] + #[JoinColumn(name: 'action1_name', referencedColumnName: 'name')] + public $action1, + /** @var Action */ + #[Id] + #[OneToOne(targetEntity: 'Action', cascade: ['persist', 'remove'])] + #[JoinColumn(name: 'action2_name', referencedColumnName: 'name')] + public $action2, + #[Column] + public string $name, + ) { + $this->tokens = new ArrayCollection(); } public function addToken(Token $token): void diff --git a/tests/Tests/Models/Cache/Country.php b/tests/Tests/Models/Cache/Country.php index 0a28060b69a..09b3b1c3595 100644 --- a/tests/Tests/Models/Cache/Country.php +++ b/tests/Tests/Models/Cache/Country.php @@ -11,30 +11,21 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Cache - * @Entity - * @Table("cache_country") - */ +#[Table('cache_country')] +#[Cache] +#[Entity] class Country { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - protected $name; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(unique: true)] + protected string $name, + ) { } public function getId(): int diff --git a/tests/Tests/Models/Cache/Flight.php b/tests/Tests/Models/Cache/Flight.php index fcdd9675ca0..e1b12aadfca 100644 --- a/tests/Tests/Models/Cache/Flight.php +++ b/tests/Tests/Models/Cache/Flight.php @@ -13,42 +13,28 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_flight") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table('cache_flight')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class Flight { - /** - * @var City - * @Id - * @Cache - * @ManyToOne(targetEntity="City") - * @JoinColumn(name="leaving_from_city_id", referencedColumnName="id") - */ - protected $leavingFrom; - - /** - * @var City - * @Id - * @Cache - * @ManyToOne(targetEntity="City") - * @JoinColumn(name="going_to_city_id", referencedColumnName="id") - */ - protected $goingTo; - - /** - * @var DateTime - * @Column(type="date") - */ + /** @var DateTime */ + #[Column(type: 'date')] protected $departure; - public function __construct(City $leavingFrom, City $goingTo) - { - $this->goingTo = $goingTo; - $this->leavingFrom = $leavingFrom; - $this->departure = new DateTime(); + public function __construct( + #[Id] + #[Cache] + #[ManyToOne(targetEntity: 'City')] + #[JoinColumn(name: 'leaving_from_city_id', referencedColumnName: 'id')] + protected City $leavingFrom, + #[Id] + #[Cache] + #[ManyToOne(targetEntity: 'City')] + #[JoinColumn(name: 'going_to_city_id', referencedColumnName: 'id')] + protected City $goingTo, + ) { + $this->departure = new DateTime(); } public function getLeavingFrom(): City diff --git a/tests/Tests/Models/Cache/Login.php b/tests/Tests/Models/Cache/Login.php index 4c0fc375b6c..52e522448e0 100644 --- a/tests/Tests/Models/Cache/Login.php +++ b/tests/Tests/Models/Cache/Login.php @@ -12,36 +12,25 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_login") - */ +#[Table('cache_login')] +#[Entity] class Login { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column - */ - public $name; - - /** - * @var Token - * @ManyToOne(targetEntity="Token", cascade={"persist", "remove"}, inversedBy="logins") - * @JoinColumn(name="token_id", referencedColumnName="token") - */ + /** @var Token */ + #[ManyToOne(targetEntity: 'Token', cascade: ['persist', 'remove'], inversedBy: 'logins')] + #[JoinColumn(name: 'token_id', referencedColumnName: 'token')] public $token; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column] + public string $name, + ) { } public function getToken(): Token diff --git a/tests/Tests/Models/Cache/Person.php b/tests/Tests/Models/Cache/Person.php index a31be87e057..40ff28d67ec 100644 --- a/tests/Tests/Models/Cache/Person.php +++ b/tests/Tests/Models/Cache/Person.php @@ -12,35 +12,24 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_person") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table('cache_person')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class Person { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(unique=true) - */ - public $name; - - /** - * @var Address - * @OneToOne(targetEntity="Address", mappedBy="person") - */ + /** @var Address */ + #[OneToOne(targetEntity: 'Address', mappedBy: 'person')] public $address; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(unique: true)] + public string $name, + ) { } } diff --git a/tests/Tests/Models/Cache/Restaurant.php b/tests/Tests/Models/Cache/Restaurant.php index 595b7e10de3..1eafb56b4e7 100644 --- a/tests/Tests/Models/Cache/Restaurant.php +++ b/tests/Tests/Models/Cache/Restaurant.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class Restaurant extends Attraction { } diff --git a/tests/Tests/Models/Cache/State.php b/tests/Tests/Models/Cache/State.php index c34b4244426..fafbf3656fd 100644 --- a/tests/Tests/Models/Cache/State.php +++ b/tests/Tests/Models/Cache/State.php @@ -16,47 +16,31 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_state") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table('cache_state')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class State { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - protected $name; - - /** - * @var Country|null - * @Cache - * @ManyToOne(targetEntity="Country") - * @JoinColumn(name="country_id", referencedColumnName="id") - */ - protected $country; - - /** - * @phpstan-var Collection - * @Cache("NONSTRICT_READ_WRITE") - * @OneToMany(targetEntity="City", mappedBy="state") - */ + /** @phpstan-var Collection */ + #[Cache('NONSTRICT_READ_WRITE')] + #[OneToMany(targetEntity: 'City', mappedBy: 'state')] protected $cities; - public function __construct(string $name, ?Country $country = null) - { - $this->name = $name; - $this->country = $country; - $this->cities = new ArrayCollection(); + public function __construct( + #[Column(unique: true)] + protected string $name, + #[Cache] + #[ManyToOne(targetEntity: 'Country')] + #[JoinColumn(name: 'country_id', referencedColumnName: 'id')] + protected Country|null $country = null, + ) { + $this->cities = new ArrayCollection(); } public function getId(): int @@ -79,7 +63,7 @@ public function setName(string $name): void $this->name = $name; } - public function getCountry(): ?Country + public function getCountry(): Country|null { return $this->country; } diff --git a/tests/Tests/Models/Cache/Token.php b/tests/Tests/Models/Cache/Token.php index 2af7a474223..b38f68ed58d 100644 --- a/tests/Tests/Models/Cache/Token.php +++ b/tests/Tests/Models/Cache/Token.php @@ -12,7 +12,6 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; @@ -21,60 +20,38 @@ use function date; use function strtotime; -/** - * @Entity - * @Cache("READ_ONLY") - * @Table("cache_token") - */ +#[Table('cache_token')] +#[Entity] +#[Cache('READ_ONLY')] class Token { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - public $token; - - /** - * @var DateTime - * @Column(type="date") - */ + /** @var DateTime */ + #[Column(type: 'date')] public $expiresAt; - /** - * @var Client|null - * @OneToOne(targetEntity="Client") - */ - public $client; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Login", cascade={"persist", "remove"}, mappedBy="token") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Login', cascade: ['persist', 'remove'], mappedBy: 'token')] public $logins; - /** - * @var Action - * @ManyToOne(targetEntity="Action", cascade={"persist", "remove"}, inversedBy="tokens") - * @JoinColumn(name="action_name", referencedColumnName="name") - */ + /** @var Action */ + #[ManyToOne(targetEntity: 'Action', cascade: ['persist', 'remove'], inversedBy: 'tokens')] + #[JoinColumn(name: 'action_name', referencedColumnName: 'name')] public $action; - /** - * @ManyToOne(targetEntity="ComplexAction", cascade={"persist", "remove"}, inversedBy="tokens") - * @JoinColumns({ - * @JoinColumn(name="complex_action1_name", referencedColumnName="action1_name"), - * @JoinColumn(name="complex_action2_name", referencedColumnName="action2_name") - * }) - * @var ComplexAction - */ + /** @var ComplexAction */ + #[JoinColumn(name: 'complex_action1_name', referencedColumnName: 'action1_name')] + #[JoinColumn(name: 'complex_action2_name', referencedColumnName: 'action2_name')] + #[ManyToOne(targetEntity: 'ComplexAction', cascade: ['persist', 'remove'], inversedBy: 'tokens')] public $complexAction; - public function __construct(string $token, ?Client $client = null) - { + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + public string $token, + #[OneToOne(targetEntity: 'Client')] + public Client|null $client = null, + ) { $this->logins = new ArrayCollection(); - $this->token = $token; - $this->client = $client; $this->expiresAt = new DateTime(date('Y-m-d H:i:s', strtotime('+7 day'))); } @@ -84,7 +61,7 @@ public function addLogin(Login $login): void $login->token = $this; } - public function getClient(): ?Client + public function getClient(): Client|null { return $this->client; } diff --git a/tests/Tests/Models/Cache/Travel.php b/tests/Tests/Models/Cache/Travel.php index 67957fdfd0c..167d75817aa 100644 --- a/tests/Tests/Models/Cache/Travel.php +++ b/tests/Tests/Models/Cache/Travel.php @@ -12,59 +12,42 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Cache - * @Entity - * @Table("cache_travel") - */ +#[Table('cache_travel')] +#[Cache] +#[Entity] class Travel { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var DateTime - * @Column(type="date") - */ + /** @var DateTime */ + #[Column(type: 'date')] protected $createdAt; - /** - * @var Traveler - * @Cache - * @ManyToOne(targetEntity="Traveler", inversedBy="travels") - * @JoinColumn(name="traveler_id", referencedColumnName="id") - */ - protected $traveler; - - /** - * @phpstan-var Collection - * @Cache - * @ManyToMany(targetEntity="City", inversedBy="travels", cascade={"persist", "remove"}) - * @JoinTable(name="cache_visited_cities", - * joinColumns={ - * @JoinColumn(name="travel_id", referencedColumnName="id") - * }, - * inverseJoinColumns={ - * @JoinColumn(name="city_id", referencedColumnName="id") - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'cache_visited_cities')] + #[JoinColumn(name: 'travel_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'city_id', referencedColumnName: 'id')] + #[Cache] + #[ManyToMany(targetEntity: 'City', inversedBy: 'travels', cascade: ['persist', 'remove'])] public $visitedCities; - public function __construct(Traveler $traveler) - { - $this->traveler = $traveler; + public function __construct( + #[Cache] + #[ManyToOne(targetEntity: 'Traveler', inversedBy: 'travels')] + #[JoinColumn(name: 'traveler_id', referencedColumnName: 'id')] + protected Traveler $traveler, + ) { $this->createdAt = new DateTime('now'); $this->visitedCities = new ArrayCollection(); } diff --git a/tests/Tests/Models/Cache/Traveler.php b/tests/Tests/Models/Cache/Traveler.php index b8c75e9f2d9..2e56f0fab84 100644 --- a/tests/Tests/Models/Cache/Traveler.php +++ b/tests/Tests/Models/Cache/Traveler.php @@ -15,44 +15,31 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Cache - * @Entity - * @Table("cache_traveler") - */ +#[Table('cache_traveler')] +#[Cache] +#[Entity] class Traveler { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column - */ - protected $name; - - /** - * @phpstan-var Collection - * @Cache("NONSTRICT_READ_WRITE") - * @OneToMany(targetEntity="Travel", mappedBy="traveler", cascade={"persist", "remove"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[Cache('NONSTRICT_READ_WRITE')] + #[OneToMany(targetEntity: 'Travel', mappedBy: 'traveler', cascade: ['persist', 'remove'], orphanRemoval: true)] public $travels; - /** - * @var TravelerProfile - * @Cache - * @OneToOne(targetEntity="TravelerProfile") - */ + /** @var TravelerProfile */ + #[Cache] + #[OneToOne(targetEntity: 'TravelerProfile')] protected $profile; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column] + protected string $name, + ) { $this->travels = new ArrayCollection(); } diff --git a/tests/Tests/Models/Cache/TravelerProfile.php b/tests/Tests/Models/Cache/TravelerProfile.php index 0ea8719a7cd..cb9cb996301 100644 --- a/tests/Tests/Models/Cache/TravelerProfile.php +++ b/tests/Tests/Models/Cache/TravelerProfile.php @@ -12,37 +12,25 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_traveler_profile") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table('cache_traveler_profile')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class TravelerProfile { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - private $name; + #[OneToOne(targetEntity: 'TravelerProfileInfo', mappedBy: 'profile')] + #[Cache] + private TravelerProfileInfo|null $info = null; - /** - * @var TravelerProfileInfo - * @OneToOne(targetEntity="TravelerProfileInfo", mappedBy="profile") - * @Cache() - */ - private $info; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(unique: true)] + private string $name, + ) { } public function getId(): int diff --git a/tests/Tests/Models/Cache/TravelerProfileInfo.php b/tests/Tests/Models/Cache/TravelerProfileInfo.php index 6cec9795930..0d608f18fa0 100644 --- a/tests/Tests/Models/Cache/TravelerProfileInfo.php +++ b/tests/Tests/Models/Cache/TravelerProfileInfo.php @@ -13,39 +13,25 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table("cache_traveler_profile_info") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table('cache_traveler_profile_info')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class TravelerProfileInfo { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(unique=true) - */ - private $description; - - /** - * @var TravelerProfile - * @Cache() - * @JoinColumn(name="profile_id", referencedColumnName="id") - * @OneToOne(targetEntity="TravelerProfile", inversedBy="info") - */ - private $profile; - - public function __construct(TravelerProfile $profile, string $description) - { - $this->profile = $profile; - $this->description = $description; + public function __construct( + #[Cache] + #[JoinColumn(name: 'profile_id', referencedColumnName: 'id')] + #[OneToOne(targetEntity: 'TravelerProfile', inversedBy: 'info')] + private TravelerProfile $profile, + #[Column(unique: true)] + private string $description, + ) { } public function getId(): int diff --git a/tests/Tests/Models/Company/CompanyAuction.php b/tests/Tests/Models/Company/CompanyAuction.php index 10766f79710..5bafabf8e89 100644 --- a/tests/Tests/Models/Company/CompanyAuction.php +++ b/tests/Tests/Models/Company/CompanyAuction.php @@ -8,17 +8,12 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_auctions") - */ +#[Table(name: 'company_auctions')] +#[Entity] class CompanyAuction extends CompanyEvent { - /** - * @var string - * @Column(type="string", length=255) - */ - private $data; + #[Column(type: 'string', length: 255)] + private string|null $data = null; public function setData(string $data): void { diff --git a/tests/Tests/Models/Company/CompanyCar.php b/tests/Tests/Models/Company/CompanyCar.php index 2fb0f1e7b57..67ead59dbf2 100644 --- a/tests/Tests/Models/Company/CompanyCar.php +++ b/tests/Tests/Models/Company/CompanyCar.php @@ -10,29 +10,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_cars") - */ +#[Table(name: 'company_cars')] +#[Entity] class CompanyCar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string|null - * @Column(type="string", length=50) - */ - private $brand; - - public function __construct(?string $brand = null) - { - $this->brand = $brand; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + public function __construct( + #[Column(type: 'string', length: 50)] + private string|null $brand = null, + ) { } public function getId(): int @@ -40,7 +30,7 @@ public function getId(): int return $this->id; } - public function getBrand(): ?string + public function getBrand(): string|null { return $this->brand; } diff --git a/tests/Tests/Models/Company/CompanyContract.php b/tests/Tests/Models/Company/CompanyContract.php index ea2ad7168a5..9f6cc63b32b 100644 --- a/tests/Tests/Models/Company/CompanyContract.php +++ b/tests/Tests/Models/Company/CompanyContract.php @@ -10,77 +10,12 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\DiscriminatorColumn; -use Doctrine\ORM\Mapping\DiscriminatorMap; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityListeners; -use Doctrine\ORM\Mapping\EntityResult; -use Doctrine\ORM\Mapping\FieldResult; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; -use Doctrine\ORM\Mapping\NamedNativeQueries; -use Doctrine\ORM\Mapping\NamedNativeQuery; -use Doctrine\ORM\Mapping\SqlResultSetMapping; -use Doctrine\ORM\Mapping\SqlResultSetMappings; -use Doctrine\ORM\Mapping\Table; - -/** - * @Entity - * @Table(name="company_contracts") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @EntityListeners({"CompanyContractListener"}) - * @DiscriminatorMap({ - * "fix" = "CompanyFixContract", - * "flexible" = "CompanyFlexContract", - * "flexultra" = "CompanyFlexUltraContract" - * }) - * @NamedNativeQueries({ - * @NamedNativeQuery( - * name = "all-contracts", - * resultClass = "__CLASS__", - * query = "SELECT id, completed, discr FROM company_contracts" - * ), - * @NamedNativeQuery( - * name = "all", - * resultClass = "__CLASS__", - * query = "SELECT id, completed, discr FROM company_contracts" - * ), - * }) - * @SqlResultSetMappings({ - * @SqlResultSetMapping( - * name = "mapping-all-contracts", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * discriminatorColumn = "discr", - * fields = { - * @FieldResult("id"), - * @FieldResult("completed"), - * } - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mapping-all", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * discriminatorColumn = "discr", - * fields = { - * @FieldResult("id"), - * @FieldResult("completed"), - * } - * ) - * } - * ), - * }) - */ + #[ORM\Entity] #[ORM\Table(name: 'company_contracts')] #[ORM\InheritanceType('SINGLE_TABLE')] @@ -89,37 +24,22 @@ #[ORM\EntityListeners(['CompanyContractListener'])] abstract class CompanyContract { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue] - private $id; - - /** - * @var CompanyEmployee - * @ManyToOne(targetEntity="CompanyEmployee", inversedBy="soldContracts") - */ - private $salesPerson; - - /** - * @Column(type="boolean") - * @var bool - */ - private $completed = false; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CompanyEmployee", inversedBy="contracts") - * @JoinTable(name="company_contract_employees", - * joinColumns={@JoinColumn(name="contract_id", referencedColumnName="id", onDelete="CASCADE")}, - * inverseJoinColumns={@JoinColumn(name="employee_id", referencedColumnName="id")} - * ) - */ + private int $id; + + #[ManyToOne(targetEntity: 'CompanyEmployee', inversedBy: 'soldContracts')] + private CompanyEmployee|null $salesPerson = null; + + #[Column(type: 'boolean')] + private bool $completed = false; + + /** @phpstan-var Collection */ + #[JoinTable(name: 'company_contract_employees')] + #[JoinColumn(name: 'contract_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[InverseJoinColumn(name: 'employee_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'CompanyEmployee', inversedBy: 'contracts')] private $engineers; public function __construct() @@ -178,7 +98,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'name' => 'discr', 'type' => 'string', - ] + ], ); $metadata->mapField( @@ -186,7 +106,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'name' => 'id', 'fieldName' => 'id', - ] + ], ); $metadata->mapField( @@ -194,7 +114,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'boolean', 'name' => 'completed', 'fieldName' => 'completed', - ] + ], ); $metadata->setDiscriminatorMap( @@ -202,7 +122,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fix' => 'CompanyFixContract', 'flexible' => 'CompanyFlexContract', 'flexultra' => 'CompanyFlexUltraContract', - ] + ], ); $metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler'); diff --git a/tests/Tests/Models/Company/CompanyContractListener.php b/tests/Tests/Models/Company/CompanyContractListener.php index e32cfd7ed7d..ea6f37919ac 100644 --- a/tests/Tests/Models/Company/CompanyContractListener.php +++ b/tests/Tests/Models/Company/CompanyContractListener.php @@ -5,14 +5,6 @@ namespace Doctrine\Tests\Models\Company; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\PostLoad; -use Doctrine\ORM\Mapping\PostPersist; -use Doctrine\ORM\Mapping\PostRemove; -use Doctrine\ORM\Mapping\PostUpdate; -use Doctrine\ORM\Mapping\PreFlush; -use Doctrine\ORM\Mapping\PrePersist; -use Doctrine\ORM\Mapping\PreRemove; -use Doctrine\ORM\Mapping\PreUpdate; use function func_get_args; @@ -42,56 +34,48 @@ class CompanyContractListener /** @phpstan-var list> */ public $postLoadCalls; - /** @PostPersist */ #[ORM\PostPersist] public function postPersistHandler(CompanyContract $contract): void { $this->postPersistCalls[] = func_get_args(); } - /** @PrePersist */ #[ORM\PrePersist] public function prePersistHandler(CompanyContract $contract): void { $this->prePersistCalls[] = func_get_args(); } - /** @PostUpdate */ #[ORM\PostUpdate] public function postUpdateHandler(CompanyContract $contract): void { $this->postUpdateCalls[] = func_get_args(); } - /** @PreUpdate */ #[ORM\PreUpdate] public function preUpdateHandler(CompanyContract $contract): void { $this->preUpdateCalls[] = func_get_args(); } - /** @PostRemove */ #[ORM\PostRemove] public function postRemoveHandler(CompanyContract $contract): void { $this->postRemoveCalls[] = func_get_args(); } - /** @PreRemove */ #[ORM\PreRemove] public function preRemoveHandler(CompanyContract $contract): void { $this->preRemoveCalls[] = func_get_args(); } - /** @PreFlush */ #[ORM\PreFlush] public function preFlushHandler(CompanyContract $contract): void { $this->preFlushCalls[] = func_get_args(); } - /** @PostLoad */ #[ORM\PostLoad] public function postLoadHandler(CompanyContract $contract): void { diff --git a/tests/Tests/Models/Company/CompanyEmployee.php b/tests/Tests/Models/Company/CompanyEmployee.php index 448fda34a2a..6d17eae9d42 100644 --- a/tests/Tests/Models/Company/CompanyEmployee.php +++ b/tests/Tests/Models/Company/CompanyEmployee.php @@ -12,40 +12,25 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_employees") - */ +#[Table(name: 'company_employees')] +#[Entity] class CompanyEmployee extends CompanyPerson { - /** - * @var int - * @Column(type="integer") - */ - private $salary; + #[Column(type: 'integer')] + private int|null $salary = null; - /** - * @var string - * @Column(type="string", length=255) - */ - private $department; + #[Column(type: 'string', length: 255)] + private string|null $department = null; - /** - * @var DateTime|null - * @Column(type="datetime", nullable=true) - */ - private $startDate; + #[Column(type: 'datetime', nullable: true)] + private DateTime|null $startDate = null; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CompanyContract", mappedBy="engineers", fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'CompanyContract', mappedBy: 'engineers', fetch: 'EXTRA_LAZY')] public $contracts; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="CompanyFlexUltraContract", mappedBy="salesPerson", fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'CompanyFlexUltraContract', mappedBy: 'salesPerson', fetch: 'EXTRA_LAZY')] public $soldContracts; public function getSalary(): int @@ -68,7 +53,7 @@ public function setDepartment(string $dep): void $this->department = $dep; } - public function getStartDate(): ?DateTime + public function getStartDate(): DateTime|null { return $this->startDate; } diff --git a/tests/Tests/Models/Company/CompanyEvent.php b/tests/Tests/Models/Company/CompanyEvent.php index e107a0eccbe..22b6fb42f06 100644 --- a/tests/Tests/Models/Company/CompanyEvent.php +++ b/tests/Tests/Models/Company/CompanyEvent.php @@ -15,29 +15,21 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_events") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="event_type", type="string", length=255) - * @DiscriminatorMap({"auction"="CompanyAuction", "raffle"="CompanyRaffle"}) - */ +#[Table(name: 'company_events')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'event_type', type: 'string', length: 255)] +#[DiscriminatorMap(['auction' => 'CompanyAuction', 'raffle' => 'CompanyRaffle'])] abstract class CompanyEvent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var CompanyOrganization - * @ManyToOne(targetEntity="CompanyOrganization", inversedBy="events", cascade={"persist"}) - * @JoinColumn(name="org_id", referencedColumnName="id") - */ - private $organization; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + #[ManyToOne(targetEntity: 'CompanyOrganization', inversedBy: 'events', cascade: ['persist'])] + #[JoinColumn(name: 'org_id', referencedColumnName: 'id')] + private CompanyOrganization|null $organization = null; public function getId(): int { diff --git a/tests/Tests/Models/Company/CompanyFixContract.php b/tests/Tests/Models/Company/CompanyFixContract.php index 93632016532..0e2793fe55c 100644 --- a/tests/Tests/Models/Company/CompanyFixContract.php +++ b/tests/Tests/Models/Company/CompanyFixContract.php @@ -7,17 +7,12 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -/** @Entity */ #[ORM\Entity] class CompanyFixContract extends CompanyContract { - /** - * @Column(type="integer") - * @var int - */ - private $fixPrice = 0; + #[Column(type: 'integer')] + private int $fixPrice = 0; public function calculatePrice(): int { @@ -41,7 +36,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'name' => 'fixPrice', 'fieldName' => 'fixPrice', - ] + ], ); } } diff --git a/tests/Tests/Models/Company/CompanyFlexContract.php b/tests/Tests/Models/Company/CompanyFlexContract.php index 29391853d7c..e8c71e1ee18 100644 --- a/tests/Tests/Models/Company/CompanyFlexContract.php +++ b/tests/Tests/Models/Company/CompanyFlexContract.php @@ -8,83 +8,25 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityResult; -use Doctrine\ORM\Mapping\FieldResult; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; -use Doctrine\ORM\Mapping\NamedNativeQueries; -use Doctrine\ORM\Mapping\NamedNativeQuery; -use Doctrine\ORM\Mapping\SqlResultSetMapping; -use Doctrine\ORM\Mapping\SqlResultSetMappings; - -/** - * @Entity - * @NamedNativeQueries({ - * @NamedNativeQuery( - * name = "all", - * resultClass = "__CLASS__", - * query = "SELECT id, hoursWorked, discr FROM company_contracts" - * ), - * @NamedNativeQuery( - * name = "all-flex", - * resultClass = "CompanyFlexContract", - * query = "SELECT id, hoursWorked, discr FROM company_contracts" - * ), - * }) - * @SqlResultSetMappings({ - * @SqlResultSetMapping( - * name = "mapping-all-flex", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * discriminatorColumn = "discr", - * fields = { - * @FieldResult("id"), - * @FieldResult("hoursWorked"), - * } - * ) - * } - * ), - * @SqlResultSetMapping( - * name = "mapping-all", - * entities= { - * @EntityResult( - * entityClass = "CompanyFlexContract", - * discriminatorColumn = "discr", - * fields = { - * @FieldResult("id"), - * @FieldResult("hoursWorked"), - * } - * ) - * } - * ), - * }) - */ + #[ORM\Entity] class CompanyFlexContract extends CompanyContract { - /** - * @Column(type="integer") - * @var int - */ - private $hoursWorked = 0; - - /** - * @var int - * @Column(type="integer") - */ - private $pricePerHour = 0; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CompanyManager", inversedBy="managedContracts", fetch="EXTRA_LAZY") - * @JoinTable(name="company_contract_managers", - * joinColumns={@JoinColumn(name="contract_id", referencedColumnName="id", onDelete="CASCADE")}, - * inverseJoinColumns={@JoinColumn(name="employee_id", referencedColumnName="id")} - * ) - */ + #[Column(type: 'integer')] + private int $hoursWorked = 0; + + #[Column(type: 'integer')] + private int $pricePerHour = 0; + + /** @phpstan-var Collection */ + #[JoinTable(name: 'company_contract_managers')] + #[JoinColumn(name: 'contract_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[InverseJoinColumn(name: 'employee_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'CompanyManager', inversedBy: 'managedContracts', fetch: 'EXTRA_LAZY')] public $managers; public function calculatePrice(): int @@ -135,7 +77,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'name' => 'hoursWorked', 'fieldName' => 'hoursWorked', - ] + ], ); $metadata->mapField( @@ -143,7 +85,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'name' => 'pricePerHour', 'fieldName' => 'pricePerHour', - ] + ], ); } } diff --git a/tests/Tests/Models/Company/CompanyFlexUltraContract.php b/tests/Tests/Models/Company/CompanyFlexUltraContract.php index 53e8595c179..c6893929e15 100644 --- a/tests/Tests/Models/Company/CompanyFlexUltraContract.php +++ b/tests/Tests/Models/Company/CompanyFlexUltraContract.php @@ -8,24 +8,15 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityListeners; use function max; -/** - * @Entity - * @EntityListeners({"CompanyContractListener","CompanyFlexUltraContractListener"}) - */ #[ORM\Entity] #[ORM\EntityListeners(['CompanyContractListener', 'CompanyFlexUltraContractListener'])] class CompanyFlexUltraContract extends CompanyFlexContract { - /** - * @Column(type="integer") - * @var int - */ - private $maxPrice = 0; + #[Column(type: 'integer')] + private int $maxPrice = 0; public function calculatePrice(): int { @@ -49,7 +40,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'name' => 'maxPrice', 'fieldName' => 'maxPrice', - ] + ], ); $metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler'); $metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler'); diff --git a/tests/Tests/Models/Company/CompanyFlexUltraContractListener.php b/tests/Tests/Models/Company/CompanyFlexUltraContractListener.php index f5f3953591e..70bc525c523 100644 --- a/tests/Tests/Models/Company/CompanyFlexUltraContractListener.php +++ b/tests/Tests/Models/Company/CompanyFlexUltraContractListener.php @@ -6,7 +6,6 @@ use Doctrine\ORM\Event\PrePersistEventArgs; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\PrePersist; use function func_get_args; @@ -15,14 +14,12 @@ class CompanyFlexUltraContractListener /** @phpstan-var list */ public $prePersistCalls; - /** @PrePersist */ #[ORM\PrePersist] public function prePersistHandler1(CompanyContract $contract, PrePersistEventArgs $args): void { $this->prePersistCalls[] = func_get_args(); } - /** @PrePersist */ #[ORM\PrePersist] public function prePersistHandler2(CompanyContract $contract, PrePersistEventArgs $args): void { diff --git a/tests/Tests/Models/Company/CompanyManager.php b/tests/Tests/Models/Company/CompanyManager.php index cee3f0032bf..d753bbcdfc9 100644 --- a/tests/Tests/Models/Company/CompanyManager.php +++ b/tests/Tests/Models/Company/CompanyManager.php @@ -12,29 +12,19 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_managers") - */ +#[Table(name: 'company_managers')] +#[Entity] class CompanyManager extends CompanyEmployee { - /** - * @var string - * @Column(type="string", length=250) - */ - private $title; - - /** - * @var CompanyCar - * @OneToOne(targetEntity="CompanyCar", cascade={"persist"}) - * @JoinColumn(name="car_id", referencedColumnName="id") - */ - private $car; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CompanyFlexContract", mappedBy="managers", fetch="EXTRA_LAZY") - */ + #[Column(type: 'string', length: 250)] + private string|null $title = null; + + #[OneToOne(targetEntity: 'CompanyCar', cascade: ['persist'])] + #[JoinColumn(name: 'car_id', referencedColumnName: 'id')] + private CompanyCar|null $car = null; + + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'CompanyFlexContract', mappedBy: 'managers', fetch: 'EXTRA_LAZY')] public $managedContracts; public function getTitle(): string diff --git a/tests/Tests/Models/Company/CompanyOrganization.php b/tests/Tests/Models/Company/CompanyOrganization.php index 553cc2d5dd6..ecc3e818393 100644 --- a/tests/Tests/Models/Company/CompanyOrganization.php +++ b/tests/Tests/Models/Company/CompanyOrganization.php @@ -14,24 +14,17 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_organizations") - */ +#[Table(name: 'company_organizations')] +#[Entity] class CompanyOrganization { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="CompanyEvent", mappedBy="organization", cascade={"persist"}, fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'CompanyEvent', mappedBy: 'organization', cascade: ['persist'], fetch: 'EXTRA_LAZY')] public $events; public function getId(): int @@ -51,14 +44,11 @@ public function addEvent(CompanyEvent $event): void $event->setOrganization($this); } - /** - * @var CompanyEvent|null - * @OneToOne(targetEntity="CompanyEvent", cascade={"persist"}) - * @JoinColumn(name="main_event_id", referencedColumnName="id", nullable=true) - */ - private $mainevent; + #[OneToOne(targetEntity: 'CompanyEvent', cascade: ['persist'])] + #[JoinColumn(name: 'main_event_id', referencedColumnName: 'id', nullable: true)] + private CompanyEvent|null $mainevent = null; - public function getMainEvent(): ?CompanyEvent + public function getMainEvent(): CompanyEvent|null { return $this->mainevent; } diff --git a/tests/Tests/Models/Company/CompanyPerson.php b/tests/Tests/Models/Company/CompanyPerson.php index 2b941fa5259..f6cd16327e1 100644 --- a/tests/Tests/Models/Company/CompanyPerson.php +++ b/tests/Tests/Models/Company/CompanyPerson.php @@ -11,97 +11,43 @@ use Doctrine\ORM\Mapping\DiscriminatorColumn; use Doctrine\ORM\Mapping\DiscriminatorMap; use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\EntityResult; -use Doctrine\ORM\Mapping\FieldResult; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; -use Doctrine\ORM\Mapping\NamedNativeQueries; -use Doctrine\ORM\Mapping\NamedNativeQuery; use Doctrine\ORM\Mapping\OneToOne; -use Doctrine\ORM\Mapping\SqlResultSetMapping; -use Doctrine\ORM\Mapping\SqlResultSetMappings; use Doctrine\ORM\Mapping\Table; /** * Description of CompanyPerson - * - * @Entity - * @Table(name="company_persons") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({ - * "person" = "CompanyPerson", - * "manager" = "CompanyManager", - * "employee" = "CompanyEmployee" - * }) - * @NamedNativeQueries({ - * @NamedNativeQuery( - * name = "fetchAllWithResultClass", - * resultClass = "__CLASS__", - * query = "SELECT id, name, discr FROM company_persons ORDER BY name" - * ), - * @NamedNativeQuery( - * name = "fetchAllWithSqlResultSetMapping", - * resultSetMapping= "mappingFetchAll", - * query = "SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name" - * ) - * }) - * @SqlResultSetMappings({ - * @SqlResultSetMapping( - * name = "mappingFetchAll", - * entities= { - * @EntityResult( - * entityClass = "__CLASS__", - * discriminatorColumn = "discriminator", - * fields = { - * @FieldResult("id"), - * @FieldResult("name"), - * } - * ) - * } - * ) - * }) */ +#[Table(name: 'company_persons')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['person' => 'CompanyPerson', 'manager' => 'CompanyManager', 'employee' => 'CompanyEmployee'])] class CompanyPerson { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @var CompanyPerson|null - * @OneToOne(targetEntity="CompanyPerson") - * @JoinColumn(name="spouse_id", referencedColumnName="id", onDelete="CASCADE") - */ - private $spouse; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CompanyPerson") - * @JoinTable( - * name="company_persons_friends", - * joinColumns={ - * @JoinColumn(name="person_id", referencedColumnName="id", onDelete="CASCADE") - * }, - * inverseJoinColumns={ - * @JoinColumn(name="friend_id", referencedColumnName="id", onDelete="CASCADE") - * } - * ) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + #[Column] + private string|null $name = null; + + #[OneToOne(targetEntity: 'CompanyPerson')] + #[JoinColumn(name: 'spouse_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + private CompanyPerson|null $spouse = null; + + /** @phpstan-var Collection */ + #[JoinTable(name: 'company_persons_friends')] + #[JoinColumn(name: 'person_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[InverseJoinColumn(name: 'friend_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[ManyToMany(targetEntity: 'CompanyPerson')] private $friends; public function __construct() @@ -124,7 +70,7 @@ public function setName(string $name): void $this->name = $name; } - public function getSpouse(): ?CompanyPerson + public function getSpouse(): CompanyPerson|null { return $this->spouse; } @@ -154,46 +100,7 @@ public function setSpouse(CompanyPerson $spouse): void public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setPrimaryTable( - ['name' => 'company_person'] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllWithResultClass', - 'query' => 'SELECT id, name, discr FROM company_persons ORDER BY name', - 'resultClass' => self::class, - ] - ); - - $metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllWithSqlResultSetMapping', - 'query' => 'SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name', - 'resultSetMapping' => 'mappingFetchAll', - ] - ); - - $metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingFetchAll', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - ], - 'entityClass' => self::class, - 'discriminatorColumn' => 'discriminator', - ], - ], - ] + ['name' => 'company_person'], ); } } diff --git a/tests/Tests/Models/Company/CompanyRaffle.php b/tests/Tests/Models/Company/CompanyRaffle.php index 105f841cc12..3826f4f28df 100644 --- a/tests/Tests/Models/Company/CompanyRaffle.php +++ b/tests/Tests/Models/Company/CompanyRaffle.php @@ -8,17 +8,12 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="company_raffles") - */ +#[Table(name: 'company_raffles')] +#[Entity] class CompanyRaffle extends CompanyEvent { - /** - * @var string - * @Column - */ - private $data; + #[Column] + private string|null $data = null; public function setData(string $data): void { diff --git a/tests/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php b/tests/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php index 0829de72ed5..b1a8fb9db79 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php @@ -8,19 +8,14 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class JoinedChildClass extends JoinedRootClass { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $extension = 'ext'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ - private $additionalId = 'additional'; + #[Column(type: 'string', length: 255)] + #[Id] + private string $additionalId = 'additional'; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedChildClass.php b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedChildClass.php index 2696191bcec..3089c2fd606 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedChildClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedChildClass.php @@ -9,22 +9,15 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name = "joined_derived_child") - */ +#[Table(name: 'joined_derived_child')] +#[Entity] class JoinedDerivedChildClass extends JoinedDerivedRootClass { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $extension = 'ext'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ - private $additionalId = 'additional'; + #[Column(type: 'string', length: 255)] + #[Id] + private string $additionalId = 'additional'; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedIdentityClass.php b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedIdentityClass.php index b249224f6c0..a364b8914cf 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedIdentityClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedIdentityClass.php @@ -10,25 +10,16 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name = "joined_derived_identity") - */ +#[Table(name: 'joined_derived_identity')] +#[Entity] class JoinedDerivedIdentityClass { - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $id = 'part-0'; - /** - * @var JoinedDerivedRootClass[] - * @OneToMany( - * targetEntity="JoinedDerivedRootClass", - * mappedBy="keyPart1" - * ) - */ + /** @var JoinedDerivedRootClass[] */ + #[OneToMany(targetEntity: 'JoinedDerivedRootClass', mappedBy: 'keyPart1')] protected $children; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedRootClass.php b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedRootClass.php index 9cb9af1e576..bbdd6d371e5 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedRootClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/JoinedDerivedRootClass.php @@ -13,29 +13,20 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name = "joined_derived_root") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({"child" = "JoinedDerivedChildClass", "root" = "JoinedDerivedRootClass"}) - */ +#[Table(name: 'joined_derived_root')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['child' => 'JoinedDerivedChildClass', 'root' => 'JoinedDerivedRootClass'])] class JoinedDerivedRootClass { - /** - * @var JoinedDerivedIdentityClass - * @ManyToOne( - * targetEntity="JoinedDerivedIdentityClass", - * inversedBy="children" - * ) - * @Id - */ + /** @var JoinedDerivedIdentityClass */ + #[ManyToOne(targetEntity: 'JoinedDerivedIdentityClass', inversedBy: 'children')] + #[Id] protected $keyPart1 = 'part-1'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $keyPart2 = 'part-2'; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php b/tests/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php index d34be1ac572..3d89d1bd7a9 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php @@ -11,25 +11,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"child" = "JoinedChildClass", "root" = "JoinedRootClass"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['child' => 'JoinedChildClass', 'root' => 'JoinedRootClass'])] class JoinedRootClass { - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $keyPart1 = 'part-1'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $keyPart2 = 'part-2'; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/SingleChildClass.php b/tests/Tests/Models/CompositeKeyInheritance/SingleChildClass.php index da59a20a4f5..7e6e21192fa 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/SingleChildClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/SingleChildClass.php @@ -8,19 +8,14 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class SingleChildClass extends SingleRootClass { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $extension = 'ext'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ - private $additionalId = 'additional'; + #[Column(type: 'string', length: 255)] + #[Id] + private string $additionalId = 'additional'; } diff --git a/tests/Tests/Models/CompositeKeyInheritance/SingleRootClass.php b/tests/Tests/Models/CompositeKeyInheritance/SingleRootClass.php index 48cb9e773db..82842a6e39a 100644 --- a/tests/Tests/Models/CompositeKeyInheritance/SingleRootClass.php +++ b/tests/Tests/Models/CompositeKeyInheritance/SingleRootClass.php @@ -11,25 +11,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({"child" = "SingleChildClass", "root" = "SingleRootClass"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['child' => 'SingleChildClass', 'root' => 'SingleRootClass'])] class SingleRootClass { - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $keyPart1 = 'part-1'; - /** - * @var string - * @Column(type="string", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'string', length: 255)] + #[Id] protected $keyPart2 = 'part-2'; } diff --git a/tests/Tests/Models/CompositeKeyRelations/CustomerClass.php b/tests/Tests/Models/CompositeKeyRelations/CustomerClass.php index aca13e4c8ee..fb557f990da 100644 --- a/tests/Tests/Models/CompositeKeyRelations/CustomerClass.php +++ b/tests/Tests/Models/CompositeKeyRelations/CustomerClass.php @@ -8,26 +8,17 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class CustomerClass { - /** - * @var string - * @Id - * @Column(type="string") - */ - public $companyCode; + #[Id] + #[Column(type: 'string')] + public string $companyCode; - /** - * @var string - * @Id - * @Column(type="string") - */ - public $code; + #[Id] + #[Column(type: 'string')] + public string $code; - /** - * @var string - * @Column(type="string") - */ - public $name; + #[Column(type: 'string')] + public string $name; } diff --git a/tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php b/tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php index 049e6511d51..25295ade3b1 100644 --- a/tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php +++ b/tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php @@ -8,39 +8,24 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class InvoiceClass { - /** - * @var string - * @Id - * @Column(type="string") - */ - public $companyCode; + #[Id] + #[Column(type: 'string')] + public string $companyCode; - /** - * @var string - * @Id - * @Column(type="string") - */ - public $invoiceNumber; + #[Id] + #[Column(type: 'string')] + public string $invoiceNumber; - /** - * @var CustomerClass|null - * @ManyToOne(targetEntity="CustomerClass") - * @JoinColumns({ - * @JoinColumn(name="companyCode", referencedColumnName="companyCode"), - * @JoinColumn(name="customerCode", referencedColumnName="code") - * }) - */ - public $customer; + #[ManyToOne(targetEntity: CustomerClass::class)] + #[JoinColumn(name: 'companyCode', referencedColumnName: 'companyCode')] + #[JoinColumn(name: 'customerCode', referencedColumnName: 'code')] + public CustomerClass|null $customer; - /** - * @var string|null - * @Column(type="string", nullable=true) - */ - public $customerCode; + #[Column(type: 'string', nullable: true)] + public string|null $customerCode = null; } diff --git a/tests/Tests/Models/CustomType/CustomIdObjectTypeChild.php b/tests/Tests/Models/CustomType/CustomIdObjectTypeChild.php index 3c3f89ce4f4..38cc701f67e 100644 --- a/tests/Tests/Models/CustomType/CustomIdObjectTypeChild.php +++ b/tests/Tests/Models/CustomType/CustomIdObjectTypeChild.php @@ -11,28 +11,16 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\CustomIdObject; -/** - * @Entity - * @Table(name="custom_id_type_child") - */ +#[Table(name: 'custom_id_type_child')] +#[Entity] class CustomIdObjectTypeChild { - /** - * @Id - * @Column(type="CustomIdObject", length=255) - * @var CustomIdObject - */ - public $id; - - /** - * @var CustomIdObjectTypeParent - * @ManyToOne(targetEntity="Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent", inversedBy="children") - */ - public $parent; - - public function __construct(CustomIdObject $id, CustomIdObjectTypeParent $parent) - { - $this->id = $id; - $this->parent = $parent; + public function __construct( + #[Id] + #[Column(type: 'CustomIdObject', length: 255)] + public CustomIdObject $id, + #[ManyToOne(targetEntity: 'Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent', inversedBy: 'children')] + public CustomIdObjectTypeParent $parent, + ) { } } diff --git a/tests/Tests/Models/CustomType/CustomIdObjectTypeParent.php b/tests/Tests/Models/CustomType/CustomIdObjectTypeParent.php index 3b811e5be4f..92e6f029cea 100644 --- a/tests/Tests/Models/CustomType/CustomIdObjectTypeParent.php +++ b/tests/Tests/Models/CustomType/CustomIdObjectTypeParent.php @@ -13,28 +13,19 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\CustomIdObject; -/** - * @Entity - * @Table(name="custom_id_type_parent") - */ +#[Table(name: 'custom_id_type_parent')] +#[Entity] class CustomIdObjectTypeParent { - /** - * @Id - * @Column(type="CustomIdObject", length=255) - * @var CustomIdObject - */ - public $id; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild", cascade={"persist", "remove"}, mappedBy="parent") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild', cascade: ['persist', 'remove'], mappedBy: 'parent')] public $children; - public function __construct(CustomIdObject $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'CustomIdObject', length: 255)] + public CustomIdObject $id, + ) { $this->children = new ArrayCollection(); } } diff --git a/tests/Tests/Models/CustomType/CustomTypeChild.php b/tests/Tests/Models/CustomType/CustomTypeChild.php index e727da2c810..fdcf7b30a0d 100644 --- a/tests/Tests/Models/CustomType/CustomTypeChild.php +++ b/tests/Tests/Models/CustomType/CustomTypeChild.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="customtype_children") - */ +#[Table(name: 'customtype_children')] +#[Entity] class CustomTypeChild { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="upper_case_string", length=255) - */ + /** @var string */ + #[Column(type: 'upper_case_string', length: 255)] public $lowerCaseString = 'foo'; } diff --git a/tests/Tests/Models/CustomType/CustomTypeParent.php b/tests/Tests/Models/CustomType/CustomTypeParent.php index 377bdde353d..3771812ed19 100644 --- a/tests/Tests/Models/CustomType/CustomTypeParent.php +++ b/tests/Tests/Models/CustomType/CustomTypeParent.php @@ -10,53 +10,40 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="customtype_parents") - */ +#[Table(name: 'customtype_parents')] +#[Entity] class CustomTypeParent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var int - * @Column(type="negative_to_positive", nullable=true) - */ + /** @var int */ + #[Column(type: 'negative_to_positive', nullable: true)] public $customInteger; - /** - * @var CustomTypeChild - * @OneToOne(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeChild", cascade={"persist", "remove"}) - */ + /** @var CustomTypeChild */ + #[OneToOne(targetEntity: 'Doctrine\Tests\Models\CustomType\CustomTypeChild', cascade: ['persist', 'remove'])] public $child; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeParent", mappedBy="myFriends") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'Doctrine\Tests\Models\CustomType\CustomTypeParent', mappedBy: 'myFriends')] private $friendsWithMe; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeParent", inversedBy="friendsWithMe") - * @JoinTable( - * name="customtype_parent_friends", - * joinColumns={@JoinColumn(name="customtypeparent_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="friend_customtypeparent_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'customtype_parent_friends')] + #[JoinColumn(name: 'customtypeparent_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'friend_customtypeparent_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'Doctrine\Tests\Models\CustomType\CustomTypeParent', inversedBy: 'friendsWithMe')] private $myFriends; public function __construct() diff --git a/tests/Tests/Models/CustomType/CustomTypeUpperCase.php b/tests/Tests/Models/CustomType/CustomTypeUpperCase.php index df6c288ff55..8c2daf2cac0 100644 --- a/tests/Tests/Models/CustomType/CustomTypeUpperCase.php +++ b/tests/Tests/Models/CustomType/CustomTypeUpperCase.php @@ -10,29 +10,21 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="customtype_uppercases") - */ +#[Table(name: 'customtype_uppercases')] +#[Entity] class CustomTypeUpperCase { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="upper_case_string", length=255) - */ + /** @var string */ + #[Column(type: 'upper_case_string', length: 255)] public $lowerCaseString; - /** - * @var string - * @Column(type="upper_case_string", length=255, name="named_lower_case_string", nullable = true) - */ + /** @var string */ + #[Column(type: 'upper_case_string', length: 255, name: 'named_lower_case_string', nullable: true)] public $namedLowerCaseString; } diff --git a/tests/Tests/Models/DDC117/DDC117ApproveChanges.php b/tests/Tests/Models/DDC117/DDC117ApproveChanges.php index 7b92dd0f918..4f20de3d8b4 100644 --- a/tests/Tests/Models/DDC117/DDC117ApproveChanges.php +++ b/tests/Tests/Models/DDC117/DDC117ApproveChanges.php @@ -9,55 +9,29 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class DDC117ApproveChanges { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var DDC117ArticleDetails - * @ManyToOne(targetEntity="DDC117ArticleDetails") - * @JoinColumn(name="details_id", referencedColumnName="article_id") - */ - private $articleDetails; - - /** - * @var DDC117Reference - * @ManyToOne(targetEntity="DDC117Reference") - * @JoinColumns({ - * @JoinColumn(name="source_id", referencedColumnName="source_id"), - * @JoinColumn(name="target_id", referencedColumnName="target_id") - * }) - */ - private $reference; - - /** - * @var DDC117Translation - * @ManyToOne(targetEntity="DDC117Translation") - * @JoinColumns({ - * @JoinColumn(name="trans_article_id", referencedColumnName="article_id"), - * @JoinColumn(name="trans_language", referencedColumnName="language") - * }) - */ - private $translation; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; public function __construct( - DDC117ArticleDetails $details, - DDC117Reference $reference, - DDC117Translation $translation + #[ManyToOne(targetEntity: 'DDC117ArticleDetails')] + #[JoinColumn(name: 'details_id', referencedColumnName: 'article_id')] + private DDC117ArticleDetails $articleDetails, + #[JoinColumn(name: 'source_id', referencedColumnName: 'source_id')] + #[JoinColumn(name: 'target_id', referencedColumnName: 'target_id')] + #[ManyToOne(targetEntity: 'DDC117Reference')] + private DDC117Reference $reference, + #[JoinColumn(name: 'trans_article_id', referencedColumnName: 'article_id')] + #[JoinColumn(name: 'trans_language', referencedColumnName: 'language')] + #[ManyToOne(targetEntity: 'DDC117Translation')] + private DDC117Translation $translation, ) { - $this->articleDetails = $details; - $this->reference = $reference; - $this->translation = $translation; } public function getId(): int diff --git a/tests/Tests/Models/DDC117/DDC117Article.php b/tests/Tests/Models/DDC117/DDC117Article.php index 2d2d4e641cc..b6c2778b0d3 100644 --- a/tests/Tests/Models/DDC117/DDC117Article.php +++ b/tests/Tests/Models/DDC117/DDC117Article.php @@ -13,50 +13,33 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; -/** @Entity */ +#[Entity] class DDC117Article { - /** - * @var int - * @Id - * @Column(type="integer", name="article_id") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $title; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC117Reference", mappedBy="source", cascade={"remove"}) - */ + #[Id] + #[Column(type: 'integer', name: 'article_id')] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC117Reference', mappedBy: 'source', cascade: ['remove'])] private $references; - /** - * @var DDC117ArticleDetails - * @OneToOne(targetEntity="DDC117ArticleDetails", mappedBy="article", cascade={"persist", "remove"}) - */ - private $details; + #[OneToOne(targetEntity: 'DDC117ArticleDetails', mappedBy: 'article', cascade: ['persist', 'remove'])] + private DDC117ArticleDetails|null $details = null; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC117Translation", mappedBy="article", cascade={"persist", "remove"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC117Translation', mappedBy: 'article', cascade: ['persist', 'remove'])] private $translations; - /** - * @var Collection - * @OneToMany(targetEntity="DDC117Link", mappedBy="source", indexBy="target_id", cascade={"persist", "remove"}) - */ - private $links; + /** @var Collection */ + #[OneToMany(targetEntity: 'DDC117Link', mappedBy: 'source', indexBy: 'target_id', cascade: ['persist', 'remove'])] + private Collection $links; - public function __construct(string $title) - { - $this->title = $title; + public function __construct( + #[Column] + private string $title, + ) { $this->references = new ArrayCollection(); $this->translations = new ArrayCollection(); } diff --git a/tests/Tests/Models/DDC117/DDC117ArticleDetails.php b/tests/Tests/Models/DDC117/DDC117ArticleDetails.php index 82fe18ef51b..2b0646ef88d 100644 --- a/tests/Tests/Models/DDC117/DDC117ArticleDetails.php +++ b/tests/Tests/Models/DDC117/DDC117ArticleDetails.php @@ -10,26 +10,20 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; -/** @Entity */ +#[Entity] class DDC117ArticleDetails { - /** - * @var DDC117Article - * @Id - * @OneToOne(targetEntity="DDC117Article", inversedBy="details") - * @JoinColumn(name="article_id", referencedColumnName="article_id") - */ - private $article; - - /** - * @var string - * @Column(type="text") - */ - private $text; - - public function __construct(DDC117Article $article, string $text) - { - $this->article = $article; + #[Column(type: 'text')] + private string $text; + + public function __construct( + /** @var DDC117Article */ + #[Id] + #[OneToOne(targetEntity: 'DDC117Article', inversedBy: 'details')] + #[JoinColumn(name: 'article_id', referencedColumnName: 'article_id')] + private $article, + string $text, + ) { $article->setDetails($this); $this->update($text); diff --git a/tests/Tests/Models/DDC117/DDC117Editor.php b/tests/Tests/Models/DDC117/DDC117Editor.php index 82a03bce622..99f8eda98b1 100644 --- a/tests/Tests/Models/DDC117/DDC117Editor.php +++ b/tests/Tests/Models/DDC117/DDC117Editor.php @@ -10,57 +10,39 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class DDC117Editor { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string|null - * @Column(type="string", length=255) - */ - public $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC117Translation", inversedBy="reviewedByEditors") - * @JoinTable( - * inverseJoinColumns={ - * @JoinColumn(name="article_id", referencedColumnName="article_id"), - * @JoinColumn(name="language", referencedColumnName="language") - * }, - * joinColumns={ - * @JoinColumn(name="editor_id", referencedColumnName="id") - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable] + #[JoinColumn(name: 'editor_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'article_id', referencedColumnName: 'article_id')] + #[InverseJoinColumn(name: 'language', referencedColumnName: 'language')] + #[ManyToMany(targetEntity: 'DDC117Translation', inversedBy: 'reviewedByEditors')] public $reviewingTranslations; - /** - * @var DDC117Translation - * @ManyToOne(targetEntity="DDC117Translation", inversedBy="lastTranslatedBy") - * @JoinColumns({ - * @JoinColumn(name="lt_article_id", referencedColumnName="article_id"), - * @JoinColumn(name="lt_language", referencedColumnName="language") - * }) - */ + /** @var DDC117Translation */ + #[JoinColumn(name: 'lt_article_id', referencedColumnName: 'article_id')] + #[JoinColumn(name: 'lt_language', referencedColumnName: 'language')] + #[ManyToOne(targetEntity: 'DDC117Translation', inversedBy: 'lastTranslatedBy')] public $lastTranslation; - public function __construct(?string $name = '') - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string|null $name = '', + ) { $this->reviewingTranslations = new ArrayCollection(); } diff --git a/tests/Tests/Models/DDC117/DDC117Link.php b/tests/Tests/Models/DDC117/DDC117Link.php index 7e7fb65f4c6..8bff0ce81ef 100644 --- a/tests/Tests/Models/DDC117/DDC117Link.php +++ b/tests/Tests/Models/DDC117/DDC117Link.php @@ -11,30 +11,20 @@ /** * Foreign Key Entity without additional fields! - * - * @Entity */ +#[Entity] class DDC117Link { - /** - * @var DDC117Article - * @Id - * @ManyToOne(targetEntity="DDC117Article", inversedBy="links") - * @JoinColumn(name="source_id", referencedColumnName="article_id") - */ - public $source; - - /** - * @var DDC117Article - * @Id - * @ManyToOne(targetEntity="DDC117Article") - * @JoinColumn(name="target_id", referencedColumnName="article_id") - */ - public $target; - - public function __construct($source, $target, $description) - { - $this->source = $source; - $this->target = $target; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'links')] + #[JoinColumn(name: 'source_id', referencedColumnName: 'article_id')] + public DDC117Article $source, + #[Id] + #[ManyToOne(targetEntity: 'DDC117Article')] + #[JoinColumn(name: 'target_id', referencedColumnName: 'article_id')] + public DDC117Article $target, + $description, + ) { } } diff --git a/tests/Tests/Models/DDC117/DDC117Reference.php b/tests/Tests/Models/DDC117/DDC117Reference.php index 2a8529e1608..3f8460fa3f2 100644 --- a/tests/Tests/Models/DDC117/DDC117Reference.php +++ b/tests/Tests/Models/DDC117/DDC117Reference.php @@ -11,46 +11,36 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class DDC117Reference { - /** - * @var DDC117Article - * @Id - * @ManyToOne(targetEntity="DDC117Article", inversedBy="references") - * @JoinColumn(name="source_id", referencedColumnName="article_id") - */ + /** @var DDC117Article */ + #[Id] + #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'references')] + #[JoinColumn(name: 'source_id', referencedColumnName: 'article_id')] private $source; - /** - * @var DDC117Article - * @Id - * @ManyToOne(targetEntity="DDC117Article") - * @JoinColumn(name="target_id", referencedColumnName="article_id") - */ + /** @var DDC117Article */ + #[Id] + #[ManyToOne(targetEntity: 'DDC117Article')] + #[JoinColumn(name: 'target_id', referencedColumnName: 'article_id')] private $target; - /** - * @var string - * @Column(type="string", length=255) - */ - private $description; + #[Column(type: 'datetime')] + private DateTime $created; - /** - * @var DateTime - * @Column(type="datetime") - */ - private $created; - - public function __construct(DDC117Article $source, DDC117Article $target, string $description) - { + public function __construct( + DDC117Article $source, + DDC117Article $target, + #[Column(type: 'string', length: 255)] + private string $description, + ) { $source->addReference($this); $target->addReference($this); - $this->source = $source; - $this->target = $target; - $this->description = $description; - $this->created = new DateTime('now'); + $this->source = $source; + $this->target = $target; + $this->created = new DateTime('now'); } public function source(): DDC117Article diff --git a/tests/Tests/Models/DDC117/DDC117Translation.php b/tests/Tests/Models/DDC117/DDC117Translation.php index b439daad5f7..cccc8d8d80a 100644 --- a/tests/Tests/Models/DDC117/DDC117Translation.php +++ b/tests/Tests/Models/DDC117/DDC117Translation.php @@ -14,47 +14,29 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; -/** @Entity */ +#[Entity] class DDC117Translation { - /** - * @var DDC117Article - * @Id - * @ManyToOne(targetEntity="DDC117Article", inversedBy="translations") - * @JoinColumn(name="article_id", referencedColumnName="article_id") - */ - private $article; - - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - private $language; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $title; - - /** - * @var Collection - * @ManyToMany(targetEntity="DDC117Editor", mappedBy="reviewingTranslations") - */ + /** @var Collection */ + #[ManyToMany(targetEntity: 'DDC117Editor', mappedBy: 'reviewingTranslations')] public $reviewedByEditors; - /** - * @var Collection - * @OneToMany(targetEntity="DDC117Editor", mappedBy="lastTranslation") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'DDC117Editor', mappedBy: 'lastTranslation')] public $lastTranslatedBy; - public function __construct(DDC117Article $article, string $language, string $title) - { - $this->article = $article; - $this->language = $language; - $this->title = $title; + public function __construct( + /** @var DDC117Article */ + #[Id] + #[ManyToOne(targetEntity: 'DDC117Article', inversedBy: 'translations')] + #[JoinColumn(name: 'article_id', referencedColumnName: 'article_id')] + private $article, + #[Id] + #[Column(type: 'string', length: 255)] + private string $language, + #[Column(type: 'string', length: 255)] + private string $title, + ) { $this->reviewedByEditors = new ArrayCollection(); $this->lastTranslatedBy = new ArrayCollection(); } diff --git a/tests/Tests/Models/DDC1476/DDC1476EntityWithDefaultFieldType.php b/tests/Tests/Models/DDC1476/DDC1476EntityWithDefaultFieldType.php index 45b5d5c7b25..eff6c03bcd5 100644 --- a/tests/Tests/Models/DDC1476/DDC1476EntityWithDefaultFieldType.php +++ b/tests/Tests/Models/DDC1476/DDC1476EntityWithDefaultFieldType.php @@ -6,30 +6,17 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -/** @Entity() */ #[ORM\Entity] class DDC1476EntityWithDefaultFieldType { - /** - * @var int - * @Id - * @Column() - * @GeneratedValue("NONE") - */ + /** @var int */ #[ORM\Id] #[ORM\Column] #[ORM\GeneratedValue(strategy: 'NONE')] protected $id; - /** - * @var string - * @Column() - */ + /** @var string */ #[ORM\Column] protected $name; @@ -54,10 +41,10 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->mapField( - ['fieldName' => 'name'] + ['fieldName' => 'name'], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); diff --git a/tests/Tests/Models/DDC1590/DDC1590Entity.php b/tests/Tests/Models/DDC1590/DDC1590Entity.php index 1078ffeb33c..3c92a73b31b 100644 --- a/tests/Tests/Models/DDC1590/DDC1590Entity.php +++ b/tests/Tests/Models/DDC1590/DDC1590Entity.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\MappedSuperclass; -/** - * @MappedSuperclass - */ +#[MappedSuperclass] abstract class DDC1590Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var DateTime - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Column(type: 'datetime')] protected $createdAt; public function getId(): int diff --git a/tests/Tests/Models/DDC1590/DDC1590User.php b/tests/Tests/Models/DDC1590/DDC1590User.php index 44a11ce9f17..789dc9eadad 100644 --- a/tests/Tests/Models/DDC1590/DDC1590User.php +++ b/tests/Tests/Models/DDC1590/DDC1590User.php @@ -9,15 +9,11 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\Models\DDC1590\DDC1590Entity; -/** - * @Entity - * @Table(name="users") - */ +#[Table(name: 'users')] +#[Entity] class DDC1590User extends DDC1590Entity { - /** - * @var string - * @Column(type="string", length=255, length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC1872/DDC1872Bar.php b/tests/Tests/Models/DDC1872/DDC1872Bar.php index dd75d201f3e..cdd34457df0 100644 --- a/tests/Tests/Models/DDC1872/DDC1872Bar.php +++ b/tests/Tests/Models/DDC1872/DDC1872Bar.php @@ -8,13 +8,10 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class DDC1872Bar { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - private $id; + #[Id] + #[Column(type: 'string', length: 255)] + private string $id; } diff --git a/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithOverride.php b/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithOverride.php index 94e04997403..e37332c0ba2 100644 --- a/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithOverride.php +++ b/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithOverride.php @@ -12,27 +12,9 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\JoinColumn; -/** - * @Entity - * @AttributeOverrides({ - * @AttributeOverride(name="foo", - * column=@Column( - * name = "foo_overridden", - * type = "integer", - * length = 140, - * nullable = false, - * unique = false - * ) - * ) - * }) - * @AssociationOverrides({ - * @AssociationOverride(name="bar", - * joinColumns=@JoinColumn( - * name="example_entity_overridden_bar_id", referencedColumnName="id" - * ) - * ) - * }) - */ +#[Entity] +#[AttributeOverrides([new AttributeOverride(name: 'foo', column: new Column(name: 'foo_overridden', type: 'integer', length: 140, nullable: false, unique: false))])] +#[AssociationOverrides([new AssociationOverride(name: 'bar', joinColumns: new JoinColumn(name: 'example_entity_overridden_bar_id', referencedColumnName: 'id'))])] class DDC1872ExampleEntityWithOverride { use DDC1872ExampleTrait; diff --git a/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithoutOverride.php b/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithoutOverride.php index 6f3a46e18e5..205d36ee184 100644 --- a/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithoutOverride.php +++ b/tests/Tests/Models/DDC1872/DDC1872ExampleEntityWithoutOverride.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class DDC1872ExampleEntityWithoutOverride { use DDC1872ExampleTrait; diff --git a/tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php b/tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php index bf34c2acdfa..fdad6f4182b 100644 --- a/tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php +++ b/tests/Tests/Models/DDC1872/DDC1872ExampleTrait.php @@ -11,23 +11,17 @@ trait DDC1872ExampleTrait { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] private $id; - /** - * @var int - * @Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true) - */ + /** @var int */ + #[Column(name: 'trait_foo', type: 'integer', length: 100, nullable: true, unique: true)] protected $foo; - /** - * @var DDC1872Bar - * @OneToOne(targetEntity="DDC1872Bar", cascade={"persist", "merge"}) - * @JoinColumn(name="example_trait_bar_id", referencedColumnName="id") - */ + /** @var DDC1872Bar */ + #[OneToOne(targetEntity: 'DDC1872Bar', cascade: ['persist'])] + #[JoinColumn(name: 'example_trait_bar_id', referencedColumnName: 'id')] protected $bar; } diff --git a/tests/Tests/Models/DDC2372/DDC2372Address.php b/tests/Tests/Models/DDC2372/DDC2372Address.php index 2b7515bcbdd..475396fcb90 100644 --- a/tests/Tests/Models/DDC2372/DDC2372Address.php +++ b/tests/Tests/Models/DDC2372/DDC2372Address.php @@ -11,30 +11,20 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="addresses") - */ +#[Table(name: 'addresses')] +#[Entity] class DDC2372Address { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $street; + #[Column(type: 'string', length: 255)] + private string|null $street = null; - /** - * @var User - * @OneToOne(targetEntity="User", mappedBy="address") - */ + /** @var User */ + #[OneToOne(targetEntity: 'User', mappedBy: 'address')] private $user; public function getId(): int diff --git a/tests/Tests/Models/DDC2372/DDC2372Admin.php b/tests/Tests/Models/DDC2372/DDC2372Admin.php index 404c373d239..c3cdce6d382 100644 --- a/tests/Tests/Models/DDC2372/DDC2372Admin.php +++ b/tests/Tests/Models/DDC2372/DDC2372Admin.php @@ -7,10 +7,8 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="admins") - */ +#[Table(name: 'admins')] +#[Entity] class DDC2372Admin extends DDC2372User { } diff --git a/tests/Tests/Models/DDC2372/DDC2372User.php b/tests/Tests/Models/DDC2372/DDC2372User.php index f11b248e2d3..9a812d0dc8e 100644 --- a/tests/Tests/Models/DDC2372/DDC2372User.php +++ b/tests/Tests/Models/DDC2372/DDC2372User.php @@ -14,30 +14,22 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\Models\DDC2372\Traits\DDC2372AddressAndAccessors; -/** - * @Entity - * @Table(name="users") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn("type") - * @DiscriminatorMap({"user"="DDC2372User", "admin"="DDC2372Admin"}) - */ +#[Table(name: 'users')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn('type')] +#[DiscriminatorMap(['user' => 'DDC2372User', 'admin' => 'DDC2372Admin'])] class DDC2372User { use DDC2372AddressAndAccessors; - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", length=50) - */ - private $name; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', length: 50)] + private string|null $name = null; public function getId(): int { diff --git a/tests/Tests/Models/DDC2372/Traits/DDC2372AddressAndAccessors.php b/tests/Tests/Models/DDC2372/Traits/DDC2372AddressAndAccessors.php index 75943fe484d..0114e22f6ad 100644 --- a/tests/Tests/Models/DDC2372/Traits/DDC2372AddressAndAccessors.php +++ b/tests/Tests/Models/DDC2372/Traits/DDC2372AddressAndAccessors.php @@ -10,11 +10,9 @@ trait DDC2372AddressAndAccessors { - /** - * @var DDC2372Address - * @OneToOne(targetEntity="Doctrine\Tests\Models\DDC2372\DDC2372Address", inversedBy="user") - * @JoinColumn(name="address_id", referencedColumnName="id") - */ + /** @var DDC2372Address */ + #[OneToOne(targetEntity: 'Doctrine\Tests\Models\DDC2372\DDC2372Address', inversedBy: 'user')] + #[JoinColumn(name: 'address_id', referencedColumnName: 'id')] private $address; public function getAddress(): DDC2372Address diff --git a/tests/Tests/Models/DDC2504/DDC2504ChildClass.php b/tests/Tests/Models/DDC2504/DDC2504ChildClass.php index 1fbc5c9ff73..e3ebaab39a8 100644 --- a/tests/Tests/Models/DDC2504/DDC2504ChildClass.php +++ b/tests/Tests/Models/DDC2504/DDC2504ChildClass.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class DDC2504ChildClass extends DDC2504RootClass { } diff --git a/tests/Tests/Models/DDC2504/DDC2504OtherClass.php b/tests/Tests/Models/DDC2504/DDC2504OtherClass.php index efb6cebee2c..360343da913 100644 --- a/tests/Tests/Models/DDC2504/DDC2504OtherClass.php +++ b/tests/Tests/Models/DDC2504/DDC2504OtherClass.php @@ -12,22 +12,20 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\PersistentCollection; -/** @Entity */ +#[Entity] class DDC2504OtherClass { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; /** * @var DDC2504ChildClass - * @OneToMany(targetEntity="DDC2504ChildClass", mappedBy="other", fetch="EXTRA_LAZY") * @var ArrayCollection|PersistentCollection */ + #[OneToMany(targetEntity: 'DDC2504ChildClass', mappedBy: 'other', fetch: 'EXTRA_LAZY')] public $childClasses; public function __construct() diff --git a/tests/Tests/Models/DDC2504/DDC2504RootClass.php b/tests/Tests/Models/DDC2504/DDC2504RootClass.php index 9210d129b3a..16e79bd284d 100644 --- a/tests/Tests/Models/DDC2504/DDC2504RootClass.php +++ b/tests/Tests/Models/DDC2504/DDC2504RootClass.php @@ -13,28 +13,19 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\ManyToOne; -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({ - * "root" = "DDC2504RootClass", - * "child" = "DDC2504ChildClass" - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['root' => 'DDC2504RootClass', 'child' => 'DDC2504ChildClass'])] class DDC2504RootClass { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var DDC2504OtherClass - * @ManyToOne(targetEntity="DDC2504OtherClass", inversedBy="childClasses") - */ + /** @var DDC2504OtherClass */ + #[ManyToOne(targetEntity: 'DDC2504OtherClass', inversedBy: 'childClasses')] public $other; } diff --git a/tests/Tests/Models/DDC2825/ExplicitSchemaAndTable.php b/tests/Tests/Models/DDC2825/ExplicitSchemaAndTable.php index 62d70309400..8f0306ce2d5 100644 --- a/tests/Tests/Models/DDC2825/ExplicitSchemaAndTable.php +++ b/tests/Tests/Models/DDC2825/ExplicitSchemaAndTable.php @@ -5,26 +5,12 @@ namespace Doctrine\Tests\Models\DDC2825; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="explicit_table", schema="explicit_schema") - */ #[ORM\Entity] #[ORM\Table(name: 'explicit_table', schema: 'explicit_schema')] class ExplicitSchemaAndTable { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'AUTO')] diff --git a/tests/Tests/Models/DDC2825/SchemaAndTableInTableName.php b/tests/Tests/Models/DDC2825/SchemaAndTableInTableName.php index d95f61f2032..ea27862ffc2 100644 --- a/tests/Tests/Models/DDC2825/SchemaAndTableInTableName.php +++ b/tests/Tests/Models/DDC2825/SchemaAndTableInTableName.php @@ -5,29 +5,16 @@ namespace Doctrine\Tests\Models\DDC2825; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\Table; /** * Quoted column name to check that sequence names are * correctly handled - * - * @Entity - * @Table(name="implicit_schema.implicit_table") */ #[ORM\Entity] #[ORM\Table(name: 'implicit_schema.implicit_table')] class SchemaAndTableInTableName { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'AUTO')] diff --git a/tests/Tests/Models/DDC3231/DDC3231User1.php b/tests/Tests/Models/DDC3231/DDC3231User1.php index 6eb920ad09c..0e50ff1a97d 100644 --- a/tests/Tests/Models/DDC3231/DDC3231User1.php +++ b/tests/Tests/Models/DDC3231/DDC3231User1.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity(repositoryClass="DDC3231User1Repository") - * @Table(name="users") - */ +#[Table(name: 'users')] +#[Entity(repositoryClass: 'DDC3231User1Repository')] class DDC3231User1 { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php b/tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php index 9b7406095fc..a5e2ea2b4b1 100644 --- a/tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php +++ b/tests/Tests/Models/DDC3231/DDC3231User1NoNamespace.php @@ -8,23 +8,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity(repositoryClass="DDC3231User1NoNamespaceRepository") - * @Table(name="no_namespace_users") - */ +#[Table(name: 'no_namespace_users')] +#[Entity(repositoryClass: 'DDC3231User1NoNamespaceRepository')] class DDC3231User1NoNamespace { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC3231/DDC3231User2.php b/tests/Tests/Models/DDC3231/DDC3231User2.php index 6e910e7d13e..4e82a5750c7 100644 --- a/tests/Tests/Models/DDC3231/DDC3231User2.php +++ b/tests/Tests/Models/DDC3231/DDC3231User2.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity(repositoryClass="DDC3231User2Repository") - * @Table(name="users2") - */ +#[Table(name: 'users2')] +#[Entity(repositoryClass: 'DDC3231User2Repository')] class DDC3231User2 { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php b/tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php index 7312603fc80..d8c1e6a2afc 100644 --- a/tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php +++ b/tests/Tests/Models/DDC3231/DDC3231User2NoNamespace.php @@ -8,23 +8,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity(repositoryClass="DDC3231User2NoNamespaceRepository") - * @Table(name="no_namespace_users2") - */ +#[Table(name: 'no_namespace_users2')] +#[Entity(repositoryClass: 'DDC3231User2NoNamespaceRepository')] class DDC3231User2NoNamespace { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC3346/DDC3346Article.php b/tests/Tests/Models/DDC3346/DDC3346Article.php index f1539db1f2e..fddb0231667 100644 --- a/tests/Tests/Models/DDC3346/DDC3346Article.php +++ b/tests/Tests/Models/DDC3346/DDC3346Article.php @@ -12,24 +12,18 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="ddc3346_articles") - */ +#[Table(name: 'ddc3346_articles')] +#[Entity] class DDC3346Article { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC3346Author - * @ManyToOne(targetEntity="DDC3346Author", inversedBy="articles") - * @JoinColumn(name="user_id", referencedColumnName="id") - */ + /** @var DDC3346Author */ + #[ManyToOne(targetEntity: 'DDC3346Author', inversedBy: 'articles')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] public $user; } diff --git a/tests/Tests/Models/DDC3346/DDC3346Author.php b/tests/Tests/Models/DDC3346/DDC3346Author.php index 6b85bff3b9a..8c76fb3a85f 100644 --- a/tests/Tests/Models/DDC3346/DDC3346Author.php +++ b/tests/Tests/Models/DDC3346/DDC3346Author.php @@ -12,29 +12,21 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="ddc3346_users") - */ +#[Table(name: 'ddc3346_users')] +#[Entity] class DDC3346Author { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255, unique=true) - */ + /** @var string */ + #[Column(type: 'string', length: 255, unique: true)] public $username; - /** - * @var Collection - * @OneToMany(targetEntity="DDC3346Article", mappedBy="user", fetch="EAGER", cascade={"detach"}) - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'DDC3346Article', mappedBy: 'user', fetch: 'EAGER', cascade: ['detach'])] public $articles = []; } diff --git a/tests/Tests/Models/DDC3579/DDC3579Admin.php b/tests/Tests/Models/DDC3579/DDC3579Admin.php index cc0d1c75d6b..e8456aa4618 100644 --- a/tests/Tests/Models/DDC3579/DDC3579Admin.php +++ b/tests/Tests/Models/DDC3579/DDC3579Admin.php @@ -8,15 +8,6 @@ use Doctrine\ORM\Mapping\AssociationOverrides; use Doctrine\ORM\Mapping\Entity; -/** - * @Entity - * @AssociationOverrides({ - * @AssociationOverride( - * name="groups", - * inversedBy="admins" - * ) - * }) - */ #[Entity] #[AssociationOverrides([new AssociationOverride(name: 'groups', inversedBy: 'admins')])] class DDC3579Admin extends DDC3579User diff --git a/tests/Tests/Models/DDC3579/DDC3579Group.php b/tests/Tests/Models/DDC3579/DDC3579Group.php index 55b186ddc5e..174483b0c86 100644 --- a/tests/Tests/Models/DDC3579/DDC3579Group.php +++ b/tests/Tests/Models/DDC3579/DDC3579Group.php @@ -12,38 +12,22 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; -/** @Entity */ #[Entity] class DDC3579Group { - /** - * @var int - * @GeneratedValue - * @Id - * @Column(type="integer") - */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] - private $id; + private int $id; - /** - * @var string|null - * @Column - */ - #[Column] - private $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC3579Admin", mappedBy="groups") - */ + /** @phpstan-var Collection */ #[ManyToMany(targetEntity: DDC3579Admin::class, mappedBy: 'groups')] private $admins; - public function __construct(?string $name = null) - { - $this->name = $name; + public function __construct( + #[Column] + private string|null $name = null, + ) { $this->admins = new ArrayCollection(); } @@ -52,7 +36,7 @@ public function setName(string $name): void $this->name = $name; } - public function getName(): ?string + public function getName(): string|null { return $this->name; } diff --git a/tests/Tests/Models/DDC3579/DDC3579User.php b/tests/Tests/Models/DDC3579/DDC3579User.php index 5edf171115c..a046da9bea7 100644 --- a/tests/Tests/Models/DDC3579/DDC3579User.php +++ b/tests/Tests/Models/DDC3579/DDC3579User.php @@ -12,38 +12,23 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ #[MappedSuperclass] class DDC3579User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="user_id", length=150) - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer', name: 'user_id', length: 150)] protected $id; - /** - * @var string - * @Column(name="user_name", nullable=true, unique=false, length=250) - */ - #[Column(name: 'user_name', nullable: true, unique: false, length: 250)] - protected $name; - - /** - * @var ArrayCollection - * @ManyToMany(targetEntity="DDC3579Group") - */ + /** @var ArrayCollection */ #[ManyToMany(targetEntity: DDC3579Group::class)] protected $groups; - public function __construct(?string $name = null) - { - $this->name = $name; + public function __construct( + #[Column(name: 'user_name', nullable: true, unique: false, length: 250)] + protected string|null $name = null, + ) { $this->groups = new ArrayCollection(); } @@ -84,7 +69,7 @@ public static function loadMetadata($metadata): void 'type' => 'integer', 'columnName' => 'user_id', 'length' => 150, - ] + ], ); $metadata->mapField( @@ -95,14 +80,14 @@ public static function loadMetadata($metadata): void 'nullable' => true, 'unique' => false, 'length' => 250, - ] + ], ); $metadata->mapManyToMany( [ 'fieldName' => 'groups', 'targetEntity' => 'DDC3579Group', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/Models/DDC3597/DDC3597Image.php b/tests/Tests/Models/DDC3597/DDC3597Image.php index 612b1983a90..edfcefdf3eb 100644 --- a/tests/Tests/Models/DDC3597/DDC3597Image.php +++ b/tests/Tests/Models/DDC3597/DDC3597Image.php @@ -10,16 +10,12 @@ /** * Description of Image - * - * @Entity */ +#[Entity] class DDC3597Image extends DDC3597Media { - /** - * @var DDC3597Dimension - * @Embedded(class = "Doctrine\Tests\Models\DDC3597\Embeddable\DDC3597Dimension", columnPrefix = false) - */ - private $dimension; + #[Embedded(class: 'Doctrine\Tests\Models\DDC3597\Embeddable\DDC3597Dimension', columnPrefix: false)] + private DDC3597Dimension $dimension; public function __construct(string $distributionHash) { diff --git a/tests/Tests/Models/DDC3597/DDC3597Media.php b/tests/Tests/Models/DDC3597/DDC3597Media.php index 437cf869f3d..e2517de9430 100644 --- a/tests/Tests/Models/DDC3597/DDC3597Media.php +++ b/tests/Tests/Models/DDC3597/DDC3597Media.php @@ -9,32 +9,20 @@ /** * Description of Media - * - * @Entity */ +#[Entity] abstract class DDC3597Media extends DDC3597Root { - /** - * @var string - * @Column - */ - private $distributionHash; + #[Column] + private int $size = 0; - /** - * @var int - * @Column - */ - private $size = 0; + #[Column] + private string|null $format = null; - /** - * @var string - * @Column - */ - private $format; - - public function __construct($distributionHash) - { - $this->distributionHash = $distributionHash; + public function __construct( + #[Column] + private string $distributionHash, + ) { } public function getDistributionHash(): string diff --git a/tests/Tests/Models/DDC3597/DDC3597Root.php b/tests/Tests/Models/DDC3597/DDC3597Root.php index dddcbde6d35..6f0ae3b4fae 100644 --- a/tests/Tests/Models/DDC3597/DDC3597Root.php +++ b/tests/Tests/Models/DDC3597/DDC3597Root.php @@ -18,40 +18,32 @@ /** * Description of Root - * - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discriminator", type="string", length=255) - * @DiscriminatorMap({ "image" = "DDC3597Image"}) - * @HasLifecycleCallbacks */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discriminator', type: 'string', length: 255)] +#[DiscriminatorMap(['image' => 'DDC3597Image'])] +#[HasLifecycleCallbacks] abstract class DDC3597Root { - /** - * @var int - * @Column(name="id", type="integer", nullable=false) - * @Id - * @GeneratedValue(strategy="IDENTITY") - */ + /** @var int */ + #[Column(name: 'id', type: 'integer', nullable: false)] + #[Id] + #[GeneratedValue(strategy: 'IDENTITY')] protected $id; - /** - * @var DateTime - * @Column(name="created_at", type="datetime", nullable=false) - */ + /** @var DateTime */ + #[Column(name: 'created_at', type: 'datetime', nullable: false)] protected $createdAt = null; - /** - * @var DateTime - * @Column(name="updated_at", type="datetime", nullable=false) - */ + /** @var DateTime */ + #[Column(name: 'updated_at', type: 'datetime', nullable: false)] protected $updatedAt = null; /** * Set createdAt - * - * @PrePersist */ + #[PrePersist] public function prePersist(): void { $this->updatedAt = $this->createdAt = new DateTime(); @@ -59,9 +51,8 @@ public function prePersist(): void /** * Set updatedAt - * - * @PreUpdate */ + #[PreUpdate] public function preUpdate(): void { $this->updatedAt = new DateTime(); diff --git a/tests/Tests/Models/DDC3597/Embeddable/DDC3597Dimension.php b/tests/Tests/Models/DDC3597/Embeddable/DDC3597Dimension.php index d5f36c9e268..d5a92ef8558 100644 --- a/tests/Tests/Models/DDC3597/Embeddable/DDC3597Dimension.php +++ b/tests/Tests/Models/DDC3597/Embeddable/DDC3597Dimension.php @@ -9,22 +9,15 @@ /** * Description of DDC3597Dimension - * - * @Embeddable */ +#[Embeddable] class DDC3597Dimension { - /** - * @var int - * @Column(type="integer", name="width") - */ - private $width; - - /** - * @var int - * @Column(type="integer", name="height") - */ - private $height; + #[Column(type: 'integer', name: 'width')] + private int $width; + + #[Column(type: 'integer', name: 'height')] + private int $height; public function __construct($width = 0, $height = 0) { diff --git a/tests/Tests/Models/DDC3699/DDC3699Child.php b/tests/Tests/Models/DDC3699/DDC3699Child.php index 9ef7fb1610b..f2b85bb0040 100644 --- a/tests/Tests/Models/DDC3699/DDC3699Child.php +++ b/tests/Tests/Models/DDC3699/DDC3699Child.php @@ -12,34 +12,24 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="ddc3699_child") - */ +#[Table(name: 'ddc3699_child')] +#[Entity] class DDC3699Child extends DDC3699Parent { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $childField; - /** - * @var DDC3699RelationOne - * @OneToOne(targetEntity="DDC3699RelationOne", inversedBy="child") - */ + /** @var DDC3699RelationOne */ + #[OneToOne(targetEntity: 'DDC3699RelationOne', inversedBy: 'child')] public $oneRelation; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3699RelationMany", mappedBy="child") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3699RelationMany', mappedBy: 'child')] public $relations; } diff --git a/tests/Tests/Models/DDC3699/DDC3699Parent.php b/tests/Tests/Models/DDC3699/DDC3699Parent.php index 82f22833e89..3644975bc7d 100644 --- a/tests/Tests/Models/DDC3699/DDC3699Parent.php +++ b/tests/Tests/Models/DDC3699/DDC3699Parent.php @@ -7,12 +7,10 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class DDC3699Parent { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $parentField; } diff --git a/tests/Tests/Models/DDC3699/DDC3699RelationMany.php b/tests/Tests/Models/DDC3699/DDC3699RelationMany.php index 5d6efc01aa6..a990663438c 100644 --- a/tests/Tests/Models/DDC3699/DDC3699RelationMany.php +++ b/tests/Tests/Models/DDC3699/DDC3699RelationMany.php @@ -10,22 +10,16 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="ddc3699_relation_many") - */ +#[Table(name: 'ddc3699_relation_many')] +#[Entity] class DDC3699RelationMany { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var DDC3699Child - * @ManyToOne(targetEntity="DDC3699Child", inversedBy="relations") - */ + /** @var DDC3699Child */ + #[ManyToOne(targetEntity: 'DDC3699Child', inversedBy: 'relations')] public $child; } diff --git a/tests/Tests/Models/DDC3699/DDC3699RelationOne.php b/tests/Tests/Models/DDC3699/DDC3699RelationOne.php index e99e1f97050..c75246899d1 100644 --- a/tests/Tests/Models/DDC3699/DDC3699RelationOne.php +++ b/tests/Tests/Models/DDC3699/DDC3699RelationOne.php @@ -10,22 +10,16 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="ddc3699_relation_one") - */ +#[Table(name: 'ddc3699_relation_one')] +#[Entity] class DDC3699RelationOne { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var DDC3699Child - * @OneToOne(targetEntity="DDC3699Child", mappedBy="oneRelation") - */ + /** @var DDC3699Child */ + #[OneToOne(targetEntity: 'DDC3699Child', mappedBy: 'oneRelation')] public $child; } diff --git a/tests/Tests/Models/DDC3711/DDC3711EntityA.php b/tests/Tests/Models/DDC3711/DDC3711EntityA.php index 435f35e3280..4ec4d05ece9 100644 --- a/tests/Tests/Models/DDC3711/DDC3711EntityA.php +++ b/tests/Tests/Models/DDC3711/DDC3711EntityA.php @@ -17,26 +17,22 @@ class DDC3711EntityA /** @var ArrayCollection */ private $entityB; - /** @return mixed */ - public function getId1() + public function getId1(): mixed { return $this->id1; } - /** @param mixed $id1 */ - public function setId1($id1): void + public function setId1(mixed $id1): void { $this->id1 = $id1; } - /** @return mixed */ - public function getId2() + public function getId2(): mixed { return $this->id2; } - /** @param mixed $id2 */ - public function setId2($id2): void + public function setId2(mixed $id2): void { $this->id2 = $id2; } diff --git a/tests/Tests/Models/DDC3711/DDC3711EntityB.php b/tests/Tests/Models/DDC3711/DDC3711EntityB.php index cdca9cd2fe4..9e0e01667a0 100644 --- a/tests/Tests/Models/DDC3711/DDC3711EntityB.php +++ b/tests/Tests/Models/DDC3711/DDC3711EntityB.php @@ -8,11 +8,9 @@ class DDC3711EntityB { - /** @var int */ - private $id1; + private int|null $id1 = null; - /** @var int */ - private $id2; + private int|null $id2 = null; /** @var ArrayCollection */ private $entityA; diff --git a/tests/Tests/Models/DDC3899/DDC3899Contract.php b/tests/Tests/Models/DDC3899/DDC3899Contract.php index 54da4d163e9..43e585eaca2 100644 --- a/tests/Tests/Models/DDC3899/DDC3899Contract.php +++ b/tests/Tests/Models/DDC3899/DDC3899Contract.php @@ -13,34 +13,23 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="dc3899_contracts") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({ - * "fix" = "DDC3899FixContract", - * "flexible" = "DDC3899FlexContract" - * }) - */ +#[Table(name: 'dc3899_contracts')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['fix' => 'DDC3899FixContract', 'flexible' => 'DDC3899FlexContract'])] abstract class DDC3899Contract { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var bool - * @Column(type="boolean") - */ + /** @var bool */ + #[Column(type: 'boolean')] public $completed = false; - /** - * @var DDC3899User - * @ManyToOne(targetEntity="DDC3899User", inversedBy="contract") - */ + /** @var DDC3899User */ + #[ManyToOne(targetEntity: 'DDC3899User', inversedBy: 'contract')] public $user; } diff --git a/tests/Tests/Models/DDC3899/DDC3899FixContract.php b/tests/Tests/Models/DDC3899/DDC3899FixContract.php index 0406e1eee5f..10f83f1c6f5 100644 --- a/tests/Tests/Models/DDC3899/DDC3899FixContract.php +++ b/tests/Tests/Models/DDC3899/DDC3899FixContract.php @@ -7,12 +7,10 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class DDC3899FixContract extends DDC3899Contract { - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $fixPrice = 0; } diff --git a/tests/Tests/Models/DDC3899/DDC3899FlexContract.php b/tests/Tests/Models/DDC3899/DDC3899FlexContract.php index 80cf146316b..aa87d1d7cea 100644 --- a/tests/Tests/Models/DDC3899/DDC3899FlexContract.php +++ b/tests/Tests/Models/DDC3899/DDC3899FlexContract.php @@ -7,18 +7,14 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class DDC3899FlexContract extends DDC3899Contract { - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $hoursWorked = 0; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $pricePerHour = 0; } diff --git a/tests/Tests/Models/DDC3899/DDC3899User.php b/tests/Tests/Models/DDC3899/DDC3899User.php index 06e5d90cc1a..32b3de82589 100644 --- a/tests/Tests/Models/DDC3899/DDC3899User.php +++ b/tests/Tests/Models/DDC3899/DDC3899User.php @@ -11,22 +11,16 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="dc3899_users") - */ +#[Table(name: 'dc3899_users')] +#[Entity] class DDC3899User { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3899Contract", mappedBy="user") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3899Contract', mappedBy: 'user')] public $contracts; } diff --git a/tests/Tests/Models/DDC4006/DDC4006User.php b/tests/Tests/Models/DDC4006/DDC4006User.php index 88c8da56401..b728a1441c7 100644 --- a/tests/Tests/Models/DDC4006/DDC4006User.php +++ b/tests/Tests/Models/DDC4006/DDC4006User.php @@ -7,12 +7,9 @@ use Doctrine\ORM\Mapping\Embedded; use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class DDC4006User { - /** - * @var DDC4006UserId - * @Embedded(class="DDC4006UserId") - */ - private $id; + #[Embedded(class: 'DDC4006UserId')] + private DDC4006UserId $id; } diff --git a/tests/Tests/Models/DDC4006/DDC4006UserId.php b/tests/Tests/Models/DDC4006/DDC4006UserId.php index 944935ed661..c5650fcb82f 100644 --- a/tests/Tests/Models/DDC4006/DDC4006UserId.php +++ b/tests/Tests/Models/DDC4006/DDC4006UserId.php @@ -9,14 +9,11 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Embeddable */ +#[Embeddable] class DDC4006UserId { - /** - * @var int - * @Id - * @GeneratedValue("IDENTITY") - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue('IDENTITY')] + #[Column(type: 'integer')] + private int $id; } diff --git a/tests/Tests/Models/DDC5934/DDC5934BaseContract.php b/tests/Tests/Models/DDC5934/DDC5934BaseContract.php index 58beefa2b80..61d07001c67 100644 --- a/tests/Tests/Models/DDC5934/DDC5934BaseContract.php +++ b/tests/Tests/Models/DDC5934/DDC5934BaseContract.php @@ -13,25 +13,16 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ #[MappedSuperclass] class DDC5934BaseContract { - /** - * @var int - * @Id() - * @Column(name="id", type="integer") - * @GeneratedValue() - */ + /** @var int */ #[Id] #[Column] #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC5934Member", fetch="LAZY", inversedBy="contracts") - */ + /** @phpstan-var Collection */ #[ManyToMany(targetEntity: DDC5934Member::class, fetch: 'LAZY', inversedBy: 'contracts')] public $members; diff --git a/tests/Tests/Models/DDC5934/DDC5934Contract.php b/tests/Tests/Models/DDC5934/DDC5934Contract.php index ab064c23127..e924e830aa2 100644 --- a/tests/Tests/Models/DDC5934/DDC5934Contract.php +++ b/tests/Tests/Models/DDC5934/DDC5934Contract.php @@ -9,12 +9,6 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Entity; -/** - * @Entity - * @AssociationOverrides( - * @AssociationOverride(name="members", fetch="EXTRA_LAZY") - * ) - */ #[Entity] #[AssociationOverrides([new AssociationOverride(name: 'members', fetch: 'EXTRA_LAZY')])] class DDC5934Contract extends DDC5934BaseContract diff --git a/tests/Tests/Models/DDC5934/DDC5934Member.php b/tests/Tests/Models/DDC5934/DDC5934Member.php index 3e8af0d865c..c43ddfccd5a 100644 --- a/tests/Tests/Models/DDC5934/DDC5934Member.php +++ b/tests/Tests/Models/DDC5934/DDC5934Member.php @@ -7,15 +7,10 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; -/** @ORM\Entity() */ #[ORM\Entity] class DDC5934Member { - /** - * @ORM\ManyToMany(targetEntity="DDC5934BaseContract", mappedBy="members") - * - * @var ArrayCollection - */ + /** @var ArrayCollection */ #[ORM\ManyToMany(targetEntity: DDC5934BaseContract::class, mappedBy: 'members')] public $contracts; diff --git a/tests/Tests/Models/DDC6412/DDC6412File.php b/tests/Tests/Models/DDC6412/DDC6412File.php index b77c1de6872..0596adc2624 100644 --- a/tests/Tests/Models/DDC6412/DDC6412File.php +++ b/tests/Tests/Models/DDC6412/DDC6412File.php @@ -8,19 +8,15 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; -/** @Entity */ +#[Entity] class DDC6412File { - /** - * @var int - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(length=50, name="file_name") - */ + /** @var string */ + #[Column(length: 50, name: 'file_name')] public $name; } diff --git a/tests/Tests/Models/DDC6573/DDC6573Currency.php b/tests/Tests/Models/DDC6573/DDC6573Currency.php new file mode 100644 index 00000000000..9aa5b0eb9e1 --- /dev/null +++ b/tests/Tests/Models/DDC6573/DDC6573Currency.php @@ -0,0 +1,17 @@ +code; + } +} diff --git a/tests/Tests/Models/DDC6573/DDC6573Item.php b/tests/Tests/Models/DDC6573/DDC6573Item.php new file mode 100644 index 00000000000..29b99a2d6fb --- /dev/null +++ b/tests/Tests/Models/DDC6573/DDC6573Item.php @@ -0,0 +1,44 @@ +name = $name; + $this->priceAmount = $price->getAmount(); + $this->priceCurrency = $price->getCurrency()->getCode(); + } + + public function getPrice(): DDC6573Money + { + return new DDC6573Money($this->priceAmount, new DDC6573Currency($this->priceCurrency)); + } +} diff --git a/tests/Tests/Models/DDC6573/DDC6573Money.php b/tests/Tests/Models/DDC6573/DDC6573Money.php new file mode 100644 index 00000000000..f0d0d59ea48 --- /dev/null +++ b/tests/Tests/Models/DDC6573/DDC6573Money.php @@ -0,0 +1,24 @@ +amount; + } + + public function getCurrency(): DDC6573Currency + { + return $this->currency; + } +} diff --git a/tests/Tests/Models/DDC753/DDC753EntityWithCustomRepository.php b/tests/Tests/Models/DDC753/DDC753EntityWithCustomRepository.php index 2d773b2e641..ff75104078d 100644 --- a/tests/Tests/Models/DDC753/DDC753EntityWithCustomRepository.php +++ b/tests/Tests/Models/DDC753/DDC753EntityWithCustomRepository.php @@ -9,20 +9,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity(repositoryClass = "Doctrine\Tests\Models\DDC753\DDC753CustomRepository") */ +#[Entity(repositoryClass: 'Doctrine\Tests\Models\DDC753\DDC753CustomRepository')] class DDC753EntityWithCustomRepository { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC753/DDC753EntityWithDefaultCustomRepository.php b/tests/Tests/Models/DDC753/DDC753EntityWithDefaultCustomRepository.php index 8a1b5ba0857..81fd2f77c04 100644 --- a/tests/Tests/Models/DDC753/DDC753EntityWithDefaultCustomRepository.php +++ b/tests/Tests/Models/DDC753/DDC753EntityWithDefaultCustomRepository.php @@ -9,20 +9,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity() */ +#[Entity] class DDC753EntityWithDefaultCustomRepository { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC753/DDC753EntityWithInvalidRepository.php b/tests/Tests/Models/DDC753/DDC753EntityWithInvalidRepository.php index a6ba10707e3..e99dbe2b317 100644 --- a/tests/Tests/Models/DDC753/DDC753EntityWithInvalidRepository.php +++ b/tests/Tests/Models/DDC753/DDC753EntityWithInvalidRepository.php @@ -9,20 +9,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity(repositoryClass = "\stdClass") */ +#[Entity(repositoryClass: '\stdClass')] class DDC753EntityWithInvalidRepository { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; } diff --git a/tests/Tests/Models/DDC869/DDC869ChequePayment.php b/tests/Tests/Models/DDC869/DDC869ChequePayment.php index e897023ffc7..5497299ec9b 100644 --- a/tests/Tests/Models/DDC869/DDC869ChequePayment.php +++ b/tests/Tests/Models/DDC869/DDC869ChequePayment.php @@ -6,17 +6,11 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -/** @Entity */ #[ORM\Entity] class DDC869ChequePayment extends DDC869Payment { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ #[ORM\Column(type: 'string')] protected $serialNumber; @@ -26,7 +20,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'fieldName' => 'serialNumber', 'type' => 'string', - ] + ], ); } } diff --git a/tests/Tests/Models/DDC869/DDC869CreditCardPayment.php b/tests/Tests/Models/DDC869/DDC869CreditCardPayment.php index efe7f17b927..c6a3b21dabb 100644 --- a/tests/Tests/Models/DDC869/DDC869CreditCardPayment.php +++ b/tests/Tests/Models/DDC869/DDC869CreditCardPayment.php @@ -6,17 +6,11 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -/** @Entity */ #[ORM\Entity] class DDC869CreditCardPayment extends DDC869Payment { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ #[ORM\Column(type: 'string')] protected $creditCardNumber; @@ -26,7 +20,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'fieldName' => 'creditCardNumber', 'type' => 'string', - ] + ], ); } } diff --git a/tests/Tests/Models/DDC869/DDC869Payment.php b/tests/Tests/Models/DDC869/DDC869Payment.php index 0e89dc603b3..cd6c105b2b1 100644 --- a/tests/Tests/Models/DDC869/DDC869Payment.php +++ b/tests/Tests/Models/DDC869/DDC869Payment.php @@ -6,30 +6,17 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass(repositoryClass = "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository") */ #[ORM\MappedSuperclass(repositoryClass: DDC869PaymentRepository::class)] class DDC869Payment { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue] protected $id; - /** - * @var float - * @Column(type="float") - */ + /** @var float */ #[ORM\Column(type: 'float')] protected $value; @@ -41,13 +28,13 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'id', 'type' => 'integer', 'columnName' => 'id', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'value', 'type' => 'float', - ] + ], ); $metadata->isMappedSuperclass = true; $metadata->setCustomRepositoryClass(DDC869PaymentRepository::class); diff --git a/tests/Tests/Models/DDC889/DDC889Class.php b/tests/Tests/Models/DDC889/DDC889Class.php index c9533aa18c5..565f50e1db1 100644 --- a/tests/Tests/Models/DDC889/DDC889Class.php +++ b/tests/Tests/Models/DDC889/DDC889Class.php @@ -11,12 +11,10 @@ class DDC889Class extends DDC889SuperClass { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; public static function loadMetadata(ClassMetadata $metadata): void @@ -27,7 +25,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'id', 'type' => 'integer', 'columnName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/Models/DDC889/DDC889Entity.php b/tests/Tests/Models/DDC889/DDC889Entity.php index 61e53296fdf..d24304489ea 100644 --- a/tests/Tests/Models/DDC889/DDC889Entity.php +++ b/tests/Tests/Models/DDC889/DDC889Entity.php @@ -6,9 +6,7 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Entity; -/** @Entity */ #[ORM\Entity] class DDC889Entity extends DDC889SuperClass { diff --git a/tests/Tests/Models/DDC889/DDC889SuperClass.php b/tests/Tests/Models/DDC889/DDC889SuperClass.php index f742cbaefbf..74e36ab275a 100644 --- a/tests/Tests/Models/DDC889/DDC889SuperClass.php +++ b/tests/Tests/Models/DDC889/DDC889SuperClass.php @@ -6,24 +6,18 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ #[ORM\MappedSuperclass] class DDC889SuperClass { - /** - * @var string - * @Column() - */ + /** @var string */ #[ORM\Column] protected $name; public static function loadMetadata(ClassMetadata $metadata): void { $metadata->mapField( - ['fieldName' => 'name'] + ['fieldName' => 'name'], ); $metadata->isMappedSuperclass = true; diff --git a/tests/Tests/Models/DDC964/DDC964Address.php b/tests/Tests/Models/DDC964/DDC964Address.php index 56e3153e7f8..c4ea9f10a68 100644 --- a/tests/Tests/Models/DDC964/DDC964Address.php +++ b/tests/Tests/Models/DDC964/DDC964Address.php @@ -9,51 +9,24 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class DDC964Address { - /** - * @var int - * @GeneratedValue - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var string|null - * @Column - */ - private $country; - - /** - * @var string|null - * @Column - */ - private $zip; - - /** - * @var string|null - * @Column - */ - private $city; - - /** - * @var string|null - * @Column - */ - private $street; + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] + private int $id; public function __construct( - ?string $zip = null, - ?string $country = null, - ?string $city = null, - ?string $street = null + #[Column] + private string|null $zip = null, + #[Column] + private string|null $country = null, + #[Column] + private string|null $city = null, + #[Column] + private string|null $street = null, ) { - $this->zip = $zip; - $this->country = $country; - $this->city = $city; - $this->street = $street; } public function getId(): int @@ -61,7 +34,7 @@ public function getId(): int return $this->id; } - public function getCountry(): ?string + public function getCountry(): string|null { return $this->country; } @@ -71,7 +44,7 @@ public function setCountry(string $country): void $this->country = $country; } - public function getZip(): ?string + public function getZip(): string|null { return $this->zip; } @@ -81,7 +54,7 @@ public function setZip(string $zip): void $this->zip = $zip; } - public function getCity(): ?string + public function getCity(): string|null { return $this->city; } @@ -91,7 +64,7 @@ public function setCity(string $city): void $this->city = $city; } - public function getStreet(): ?string + public function getStreet(): string|null { return $this->street; } diff --git a/tests/Tests/Models/DDC964/DDC964Admin.php b/tests/Tests/Models/DDC964/DDC964Admin.php index bcde2787862..3668828f052 100644 --- a/tests/Tests/Models/DDC964/DDC964Admin.php +++ b/tests/Tests/Models/DDC964/DDC964Admin.php @@ -11,23 +11,6 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; -/** - * @Entity - * @AssociationOverrides({ - * @AssociationOverride(name="groups", - * joinTable=@JoinTable( - * name="ddc964_users_admingroups", - * joinColumns=@JoinColumn(name="adminuser_id"), - * inverseJoinColumns=@JoinColumn(name="admingroup_id") - * ) - * ), - * @AssociationOverride(name="address", - * joinColumns=@JoinColumn( - * name="adminaddress_id", referencedColumnName="id" - * ) - * ) - * }) - */ #[Entity] #[AssociationOverrides([new AssociationOverride(name: 'groups', joinTable: new JoinTable(name: 'ddc964_users_admingroups'), joinColumns: [new JoinColumn(name: 'adminuser_id')], inverseJoinColumns: [new JoinColumn(name: 'admingroup_id')]), new AssociationOverride(name: 'address', joinColumns: [new JoinColumn(name: 'adminaddress_id', referencedColumnName: 'id')])])] class DDC964Admin extends DDC964User @@ -43,7 +26,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'referencedColumnName' => 'id', ], ], - ] + ], ); $metadata->setAssociationOverride( @@ -52,13 +35,13 @@ public static function loadMetadata(ClassMetadata $metadata): void 'joinTable' => [ 'name' => 'ddc964_users_admingroups', 'joinColumns' => [ - ['name' => 'adminuser_id'], + ['name' => 'adminuser_id', 'referencedColumnName' => 'id'], ], 'inverseJoinColumns' => [ - ['name' => 'admingroup_id'], + ['name' => 'admingroup_id', 'referencedColumnName' => 'id'], ], ], - ] + ], ); } } diff --git a/tests/Tests/Models/DDC964/DDC964Group.php b/tests/Tests/Models/DDC964/DDC964Group.php index 8c6704e48bc..9210e097fed 100644 --- a/tests/Tests/Models/DDC964/DDC964Group.php +++ b/tests/Tests/Models/DDC964/DDC964Group.php @@ -11,32 +11,22 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; -/** @Entity */ +#[Entity] class DDC964Group { - /** - * @var int - * @GeneratedValue - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var string|null - * @Column - */ - private $name; - - /** - * @phpstan-var ArrayCollection - * @ManyToMany(targetEntity="DDC964User", mappedBy="groups") - */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] + private int $id; + + /** @phpstan-var ArrayCollection */ + #[ManyToMany(targetEntity: 'DDC964User', mappedBy: 'groups')] private $users; - public function __construct($name = null) - { - $this->name = $name; + public function __construct( + #[Column] + private string|null $name = null, + ) { $this->users = new ArrayCollection(); } @@ -45,7 +35,7 @@ public function setName(string $name): void $this->name = $name; } - public function getName(): ?string + public function getName(): string|null { return $this->name; } diff --git a/tests/Tests/Models/DDC964/DDC964Guest.php b/tests/Tests/Models/DDC964/DDC964Guest.php index d5967dbec92..501c67d9680 100644 --- a/tests/Tests/Models/DDC964/DDC964Guest.php +++ b/tests/Tests/Models/DDC964/DDC964Guest.php @@ -10,26 +10,6 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** - * @Entity - * @AttributeOverrides({ - * @AttributeOverride(name="id", - * column=@Column( - * name = "guest_id", - * type = "integer", - length = 140 - * ) - * ), - * @AttributeOverride(name="name", - * column=@Column( - * name = "guest_name", - * nullable = false, - * unique = true, - * length = 240 - * ) - * ) - * }) - */ #[Entity] #[AttributeOverrides([new AttributeOverride(name: 'id', column: new Column(name: 'guest_id', type: 'integer', length: 140)), new AttributeOverride(name: 'name', column: new Column(name: 'guest_name', nullable: false, unique: true, length: 240))])] class DDC964Guest extends DDC964User @@ -49,7 +29,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'nullable' => false, 'unique' => true, 'length' => 240, - ] + ], ); } } diff --git a/tests/Tests/Models/DDC964/DDC964User.php b/tests/Tests/Models/DDC964/DDC964User.php index 6b99bcbbc8f..bae9c360d0c 100644 --- a/tests/Tests/Models/DDC964/DDC964User.php +++ b/tests/Tests/Models/DDC964/DDC964User.php @@ -17,54 +17,31 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ #[MappedSuperclass] class DDC964User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="user_id", length=150) - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer', name: 'user_id', length: 150)] protected $id; - /** - * @var string|null - * @Column(name="user_name", nullable=true, unique=false, length=250) - */ - #[Column(name: 'user_name', nullable: true, unique: false, length: 250)] - protected $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC964Group", inversedBy="users", cascade={"persist", "merge", "detach"}) - * @JoinTable(name="ddc964_users_groups", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} - * ) - */ - #[ManyToMany(targetEntity: DDC964Group::class, inversedBy: 'users', cascade: ['persist', 'merge', 'detach'])] + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: DDC964Group::class, inversedBy: 'users', cascade: ['persist', 'detach'])] #[JoinTable(name: 'ddc964_users_groups')] #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] protected $groups; - /** - * @var DDC964Address - * @ManyToOne(targetEntity="DDC964Address", cascade={"persist", "merge"}) - * @JoinColumn(name="address_id", referencedColumnName="id") - */ - #[ManyToOne(targetEntity: DDC964Address::class, cascade: ['persist', 'merge'])] + /** @var DDC964Address */ + #[ManyToOne(targetEntity: DDC964Address::class, cascade: ['persist'])] #[JoinColumn(name: 'address_id', referencedColumnName: 'id')] protected $address; - public function __construct(?string $name = null) - { - $this->name = $name; + public function __construct( + #[Column(name: 'user_name', nullable: true, unique: false, length: 250)] + protected string|null $name = null, + ) { $this->groups = new ArrayCollection(); } @@ -73,7 +50,7 @@ public function getId(): int return $this->id; } - public function getName(): ?string + public function getName(): string|null { return $this->name; } @@ -116,7 +93,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'columnName' => 'user_id', 'length' => 150, - ] + ], ); $metadata->mapField( [ @@ -126,16 +103,16 @@ public static function loadMetadata(ClassMetadata $metadata): void 'nullable' => true, 'unique' => false, 'length' => 250, - ] + ], ); $metadata->mapManyToOne( [ 'fieldName' => 'address', 'targetEntity' => 'DDC964Address', - 'cascade' => ['persist','merge'], - 'joinColumns' => [['name' => 'address_id', 'referencedColumnMame' => 'id']], - ] + 'cascade' => ['persist'], + 'joinColumns' => [['name' => 'address_id', 'referencedColumnName' => 'id']], + ], ); $metadata->mapManyToMany( @@ -143,7 +120,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'groups', 'targetEntity' => 'DDC964Group', 'inversedBy' => 'users', - 'cascade' => ['persist','merge','detach'], + 'cascade' => ['persist','detach'], 'joinTable' => [ 'name' => 'ddc964_users_groups', 'joinColumns' => [ @@ -159,7 +136,7 @@ public static function loadMetadata(ClassMetadata $metadata): void ], ], ], - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/Models/DataTransferObjects/DtoWithEnum.php b/tests/Tests/Models/DataTransferObjects/DtoWithEnum.php index dfef95e5baf..eedb3718e84 100644 --- a/tests/Tests/Models/DataTransferObjects/DtoWithEnum.php +++ b/tests/Tests/Models/DataTransferObjects/DtoWithEnum.php @@ -11,7 +11,7 @@ final class DtoWithEnum /** @var Suit|null */ public $suit; - public function __construct(?Suit $suit) + public function __construct(Suit|null $suit) { $this->suit = $suit; } diff --git a/tests/Tests/Models/DirectoryTree/AbstractContentItem.php b/tests/Tests/Models/DirectoryTree/AbstractContentItem.php index c12b4ecdc4e..b3330101ba4 100644 --- a/tests/Tests/Models/DirectoryTree/AbstractContentItem.php +++ b/tests/Tests/Models/DirectoryTree/AbstractContentItem.php @@ -10,35 +10,22 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class AbstractContentItem { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var Directory - * @ManyToOne(targetEntity="Directory") - */ - protected $parentDirectory; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; /** * This field is transient and private on purpose - * - * @var bool */ - private $nodeIsLoaded = false; + private bool $nodeIsLoaded = false; /** * This field is transient on purpose @@ -47,9 +34,10 @@ abstract class AbstractContentItem */ public static $fileSystem; - public function __construct(?Directory $parentDir = null) - { - $this->parentDirectory = $parentDir; + public function __construct( + #[ManyToOne(targetEntity: 'Directory')] + protected Directory|null $parentDirectory = null, + ) { } public function getId(): int diff --git a/tests/Tests/Models/DirectoryTree/Directory.php b/tests/Tests/Models/DirectoryTree/Directory.php index 62c62e843d2..47bdb67441f 100644 --- a/tests/Tests/Models/DirectoryTree/Directory.php +++ b/tests/Tests/Models/DirectoryTree/Directory.php @@ -7,13 +7,11 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class Directory extends AbstractContentItem { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $path; public function setPath(string $path): void diff --git a/tests/Tests/Models/DirectoryTree/File.php b/tests/Tests/Models/DirectoryTree/File.php index fecd7098d80..6b9f9aa1132 100644 --- a/tests/Tests/Models/DirectoryTree/File.php +++ b/tests/Tests/Models/DirectoryTree/File.php @@ -8,19 +8,15 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`file`") - */ +#[Table(name: '`file`')] +#[Entity] class File extends AbstractContentItem { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $extension = 'html'; - public function __construct(?Directory $parent = null) + public function __construct(Directory|null $parent = null) { parent::__construct($parent); } diff --git a/tests/Tests/Models/ECommerce/ECommerceCart.php b/tests/Tests/Models/ECommerce/ECommerceCart.php index fbae823a530..9e494daeea4 100644 --- a/tests/Tests/Models/ECommerce/ECommerceCart.php +++ b/tests/Tests/Models/ECommerce/ECommerceCart.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -19,40 +20,28 @@ /** * ECommerceCart * Represents a typical cart of a shopping application. - * - * @Entity - * @Table(name="ecommerce_carts") */ +#[Table(name: 'ecommerce_carts')] +#[Entity] class ECommerceCart { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column(length=50, nullable=true) - */ - private $payment; - - /** - * @var ECommerceCustomer|null - * @OneToOne(targetEntity="ECommerceCustomer", inversedBy="cart") - * @JoinColumn(name="customer_id", referencedColumnName="id") - */ - private $customer; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="ECommerceProduct", cascade={"persist"}) - * @JoinTable(name="ecommerce_carts_products", - * joinColumns={@JoinColumn(name="cart_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}) - */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] + private int $id; + + #[Column(length: 50, nullable: true)] + private string|null $payment = null; + + #[OneToOne(targetEntity: 'ECommerceCustomer', inversedBy: 'cart')] + #[JoinColumn(name: 'customer_id', referencedColumnName: 'id')] + private ECommerceCustomer|null $customer = null; + + /** @phpstan-var Collection */ + #[JoinTable(name: 'ecommerce_carts_products')] + #[JoinColumn(name: 'cart_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'ECommerceProduct', cascade: ['persist'])] private $products; public function __construct() @@ -92,7 +81,7 @@ public function removeCustomer(): void } } - public function getCustomer(): ?ECommerceCustomer + public function getCustomer(): ECommerceCustomer|null { return $this->customer; } diff --git a/tests/Tests/Models/ECommerce/ECommerceCategory.php b/tests/Tests/Models/ECommerce/ECommerceCategory.php index cb9db0e18ca..959d771af22 100644 --- a/tests/Tests/Models/ECommerce/ECommerceCategory.php +++ b/tests/Tests/Models/ECommerce/ECommerceCategory.php @@ -19,44 +19,30 @@ /** * ECommerceCategory * Represents a tag applied on particular products. - * - * @Entity - * @Table(name="ecommerce_categories") */ +#[Table(name: 'ecommerce_categories')] +#[Entity] class ECommerceCategory { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", length=50) - */ - private $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="ECommerceProduct", mappedBy="categories") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', length: 50)] + private string|null $name = null; + + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'ECommerceProduct', mappedBy: 'categories')] private $products; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="ECommerceCategory", mappedBy="parent", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'ECommerceCategory', mappedBy: 'parent', cascade: ['persist'])] private $children; - /** - * @var ECommerceCategory - * @ManyToOne(targetEntity="ECommerceCategory", inversedBy="children") - * @JoinColumn(name="parent_id", referencedColumnName="id") - */ - private $parent; + #[ManyToOne(targetEntity: 'ECommerceCategory', inversedBy: 'children')] + #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')] + private ECommerceCategory|null $parent = null; public function __construct() { @@ -112,7 +98,7 @@ public function getChildren(): Collection return $this->children; } - public function getParent(): ?ECommerceCategory + public function getParent(): ECommerceCategory|null { return $this->parent; } diff --git a/tests/Tests/Models/ECommerce/ECommerceCustomer.php b/tests/Tests/Models/ECommerce/ECommerceCustomer.php index b40284083af..6a03e5d76b3 100644 --- a/tests/Tests/Models/ECommerce/ECommerceCustomer.php +++ b/tests/Tests/Models/ECommerce/ECommerceCustomer.php @@ -15,42 +15,30 @@ /** * ECommerceCustomer * Represents a registered user of a shopping application. - * - * @Entity - * @Table(name="ecommerce_customers") */ +#[Table(name: 'ecommerce_customers')] +#[Entity] class ECommerceCustomer { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(type="string", length=50) - */ - private $name; + #[Column(type: 'string', length: 50)] + private string|null $name = null; - /** - * @var ECommerceCart|null - * @OneToOne(targetEntity="ECommerceCart", mappedBy="customer", cascade={"persist"}) - */ - private $cart; + #[OneToOne(targetEntity: 'ECommerceCart', mappedBy: 'customer', cascade: ['persist'])] + private ECommerceCart|null $cart = null; /** * Example of a one-one self referential association. A mentor can follow * only one customer at the time, while a customer can choose only one * mentor. Not properly appropriate but it works. - * - * @var ECommerceCustomer|null - * @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"}, fetch="EAGER") - * @JoinColumn(name="mentor_id", referencedColumnName="id") */ - private $mentor; + #[OneToOne(targetEntity: 'ECommerceCustomer', cascade: ['persist'], fetch: 'EAGER')] + #[JoinColumn(name: 'mentor_id', referencedColumnName: 'id')] + private ECommerceCustomer|null $mentor = null; public function getId(): int { @@ -81,7 +69,7 @@ public function brokenSetCart(ECommerceCart $cart): void $this->cart = $cart; } - public function getCart(): ?ECommerceCart + public function getCart(): ECommerceCart|null { return $this->cart; } @@ -105,7 +93,7 @@ public function removeMentor(): void $this->mentor = null; } - public function getMentor(): ?ECommerceCustomer + public function getMentor(): ECommerceCustomer|null { return $this->mentor; } diff --git a/tests/Tests/Models/ECommerce/ECommerceFeature.php b/tests/Tests/Models/ECommerce/ECommerceFeature.php index 7c6624b4f72..7e33ba09044 100644 --- a/tests/Tests/Models/ECommerce/ECommerceFeature.php +++ b/tests/Tests/Models/ECommerce/ECommerceFeature.php @@ -14,39 +14,29 @@ /** * Describes a product feature. - * - * @Entity - * @Table(name="ecommerce_features") */ +#[Table(name: 'ecommerce_features')] +#[Entity] class ECommerceFeature { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] + private int $id; - /** - * @var string|null - * @Column(length=50) - */ - private $description; + #[Column(length: 50)] + private string|null $description = null; - /** - * @var ECommerceProduct|null - * @ManyToOne(targetEntity="ECommerceProduct", inversedBy="features") - * @JoinColumn(name="product_id", referencedColumnName="id") - */ - private $product; + #[ManyToOne(targetEntity: 'ECommerceProduct', inversedBy: 'features')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + private ECommerceProduct|null $product = null; public function getId(): int { return $this->id; } - public function getDescription(): ?string + public function getDescription(): string|null { return $this->description; } @@ -70,7 +60,7 @@ public function removeProduct(): void } } - public function getProduct(): ?ECommerceProduct + public function getProduct(): ECommerceProduct|null { return $this->product; } diff --git a/tests/Tests/Models/ECommerce/ECommerceProduct.php b/tests/Tests/Models/ECommerce/ECommerceProduct.php index 26318df1eb2..0a8329e62d0 100644 --- a/tests/Tests/Models/ECommerce/ECommerceProduct.php +++ b/tests/Tests/Models/ECommerce/ECommerceProduct.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Index; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -21,46 +22,33 @@ /** * ECommerceProduct * Represents a type of product of a shopping application. - * - * @Entity - * @Table(name="ecommerce_products",indexes={@Index(name="name_idx", columns={"name"})}) */ +#[Table(name: 'ecommerce_products')] +#[Index(name: 'name_idx', columns: ['name'])] +#[Entity] class ECommerceProduct { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column(type="string", length=50, nullable=true) - */ - private $name; + #[Column(type: 'string', length: 50, nullable: true)] + private string|null $name = null; - /** - * @var ECommerceShipping|null - * @OneToOne(targetEntity="ECommerceShipping", cascade={"persist"}) - * @JoinColumn(name="shipping_id", referencedColumnName="id") - */ - private $shipping; + #[OneToOne(targetEntity: 'ECommerceShipping', cascade: ['persist'])] + #[JoinColumn(name: 'shipping_id', referencedColumnName: 'id')] + private ECommerceShipping|null $shipping = null; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="ECommerceFeature", mappedBy="product", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'ECommerceFeature', mappedBy: 'product', cascade: ['persist'])] private $features; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="ECommerceCategory", cascade={"persist"}, inversedBy="products") - * @JoinTable(name="ecommerce_products_categories", - * joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="category_id", referencedColumnName="id")}) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'ecommerce_products_categories')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'category_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'ECommerceCategory', cascade: ['persist'], inversedBy: 'products')] private $categories; /** @@ -68,11 +56,11 @@ class ECommerceProduct * simplicity. * * @phpstan-var Collection - * @ManyToMany(targetEntity="ECommerceProduct", cascade={"persist"}) - * @JoinTable(name="ecommerce_products_related", - * joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="related_id", referencedColumnName="id")}) */ + #[JoinTable(name: 'ecommerce_products_related')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'related_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'ECommerceProduct', cascade: ['persist'])] private $related; /** @var bool */ @@ -103,7 +91,7 @@ public function setName(string $name): void $this->name = $name; } - public function getShipping(): ?ECommerceShipping + public function getShipping(): ECommerceShipping|null { return $this->shipping; } diff --git a/tests/Tests/Models/ECommerce/ECommerceProduct2.php b/tests/Tests/Models/ECommerce/ECommerceProduct2.php index 89f37417d2b..1cbe939ef5b 100644 --- a/tests/Tests/Models/ECommerce/ECommerceProduct2.php +++ b/tests/Tests/Models/ECommerce/ECommerceProduct2.php @@ -14,32 +14,26 @@ /** * ECommerceProduct2 * Resets the id when being cloned. - * - * @Entity - * @Table(name="ecommerce_products",indexes={@Index(name="name_idx", columns={"name"})}) */ +#[Entity] +#[Table(name: 'ecommerce_products')] +#[Index(name: 'name_idx', columns: ['name'])] class ECommerceProduct2 { - /** - * @var int|null - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column(type="string", length=50, nullable=true) - */ - private $name; - - public function getId(): ?int + #[Column] + #[Id] + #[GeneratedValue] + private int|null $id = null; + + #[Column(length: 50, nullable: true)] + private string|null $name = null; + + public function getId(): int|null { return $this->id; } - public function getName(): string + public function getName(): string|null { return $this->name; } diff --git a/tests/Tests/Models/ECommerce/ECommerceShipping.php b/tests/Tests/Models/ECommerce/ECommerceShipping.php index 92e7b4be077..4564d2c4c46 100644 --- a/tests/Tests/Models/ECommerce/ECommerceShipping.php +++ b/tests/Tests/Models/ECommerce/ECommerceShipping.php @@ -13,39 +13,30 @@ /** * ECommerceShipping * Represents a shipping method. - * - * @Entity - * @Table(name="ecommerce_shippings") */ +#[Table(name: 'ecommerce_shippings')] +#[Entity] class ECommerceShipping { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var int|string - * @Column(type="integer") - */ - private $days; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + #[Column(type: 'integer')] + private int|string|null $days = null; public function getId(): int { return $this->id; } - /** @return int|string */ - public function getDays() + public function getDays(): int|string { return $this->days; } - /** @param int|string $days */ - public function setDays($days): void + public function setDays(int|string $days): void { $this->days = $days; } diff --git a/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php b/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php index af16c686970..d369be822d9 100644 --- a/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php +++ b/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php @@ -8,34 +8,21 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="eager_composite_join_root") - */ +#[ORM\Entity] +#[ORM\Table(name: 'eager_composite_join_root')] class RootEntity { - /** - * @ORM\Id - * @ORM\Column(type="integer", nullable=false) - * - * @var int|null - */ - private $id = null; - - /** - * @ORM\Id - * @ORM\Column(type="string", nullable=false, name="other_key") - * - * @var string - */ - private $otherKey; - - /** - * @ORM\OneToMany(mappedBy="root", targetEntity=SecondLevel::class, fetch="EAGER") - * - * @var Collection - */ - private $secondLevel; + #[ORM\Id] + #[ORM\Column(type: 'integer', nullable: false)] + private int|null $id = null; + + #[ORM\Id] + #[ORM\Column(type: 'string', nullable: false, name: 'other_key', length: 42)] + private string $otherKey; + + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'root', targetEntity: SecondLevel::class, fetch: 'EAGER')] + private Collection $secondLevel; public function __construct(int $id, string $other) { @@ -44,7 +31,7 @@ public function __construct(int $id, string $other) $this->id = $id; } - public function getId(): ?int + public function getId(): int|null { return $this->id; } diff --git a/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevel.php b/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevel.php index 33f878c8f41..0608dcffba4 100644 --- a/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevel.php +++ b/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevel.php @@ -6,40 +6,23 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="eager_composite_join_second_level", indexes={ - * @ORM\Index(name="root_other_key_idx", columns={"root_other_key", "root_id"}) - * }) - */ +#[ORM\Entity] +#[ORM\Table(name: 'eager_composite_join_second_level')] +#[ORM\Index(name: 'root_other_key_idx', columns: ['root_other_key', 'root_id'])] class SecondLevel { - /** - * @ORM\Id - * @ORM\Column(type="integer", nullable=false) - * - * @var int|null - */ - private $upperId; - - /** - * @ORM\Id - * @ORM\Column(type="string", nullable=false, name="other_key") - * - * @var string - */ - private $otherKey; - - /** - * @ORM\ManyToOne(targetEntity=RootEntity::class, inversedBy="secondLevel") - * @ORM\JoinColumns({ - * @ORM\JoinColumn(name="root_id", referencedColumnName="id"), - * @ORM\JoinColumn(name="root_other_key", referencedColumnName="other_key") - * }) - * - * @var RootEntity - */ - private $root; + #[ORM\Id] + #[ORM\Column(type: 'integer', nullable: false)] + private int|null $upperId; + + #[ORM\Id] + #[ORM\Column(type: 'string', nullable: false, name: 'other_key')] + private string $otherKey; + + #[ORM\ManyToOne(targetEntity: RootEntity::class, inversedBy: 'secondLevel')] + #[ORM\JoinColumn(name: 'root_id', referencedColumnName: 'id')] + #[ORM\JoinColumn(name: 'root_other_key', referencedColumnName: 'other_key')] + private RootEntity $root; public function __construct(RootEntity $upper) { @@ -48,7 +31,7 @@ public function __construct(RootEntity $upper) $this->root = $upper; } - public function getId(): ?int + public function getId(): int|null { return $this->id; } diff --git a/tests/Tests/Models/Enums/Card.php b/tests/Tests/Models/Enums/Card.php index 45e4bcb69fe..137b4b3c76a 100644 --- a/tests/Tests/Models/Enums/Card.php +++ b/tests/Tests/Models/Enums/Card.php @@ -10,23 +10,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ #[Entity] class Card { - /** - * @Id @GeneratedValue @Column(type="integer") - * @var int - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @Column(type="string", length=255, enumType=Suit::class) - * @var Suit - */ + /** @var Suit */ #[Column(type: 'string', enumType: Suit::class)] public $suit; @@ -37,14 +30,14 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'suit', 'type' => 'string', 'enumType' => Suit::class, - ] + ], ); } } diff --git a/tests/Tests/Models/Enums/CardNativeEnum.php b/tests/Tests/Models/Enums/CardNativeEnum.php new file mode 100644 index 00000000000..83d4d59a778 --- /dev/null +++ b/tests/Tests/Models/Enums/CardNativeEnum.php @@ -0,0 +1,25 @@ + ['H', 'D', 'C', 'S', 'Z']])] + public $suit; +} diff --git a/tests/Tests/Models/Enums/CardWithNullable.php b/tests/Tests/Models/Enums/CardWithNullable.php index 6d005a6a392..f0a8ec68062 100644 --- a/tests/Tests/Models/Enums/CardWithNullable.php +++ b/tests/Tests/Models/Enums/CardWithNullable.php @@ -10,23 +10,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ #[Entity] class CardWithNullable { - /** - * @Id @GeneratedValue @Column(type="integer") - * @var int - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @Column(type="string", length=255, enumType=Suit::class, nullable=true) - * @var ?Suit - */ + /** @var ?Suit */ #[Column(type: 'string', nullable: true, enumType: Suit::class)] public $suit; @@ -37,7 +30,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ @@ -45,7 +38,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'string', 'enumType' => Suit::class, 'nullable' => true, - ] + ], ); } } diff --git a/tests/Tests/Models/Enums/FaultySwitch.php b/tests/Tests/Models/Enums/FaultySwitch.php index dcb01d446cb..97320be73a9 100644 --- a/tests/Tests/Models/Enums/FaultySwitch.php +++ b/tests/Tests/Models/Enums/FaultySwitch.php @@ -11,10 +11,6 @@ class FaultySwitch #[Column(type: 'string')] public string $value; - /** - * The following line is ignored on psalm and phpstan so that we can test - * that the mapping is throwing an exception when a non-backed enum is used. - */ #[Column(enumType: SwitchStatus::class)] public SwitchStatus $status; } diff --git a/tests/Tests/Models/Enums/Scale.php b/tests/Tests/Models/Enums/Scale.php index 5b42a2476b0..3ea79d4625f 100644 --- a/tests/Tests/Models/Enums/Scale.php +++ b/tests/Tests/Models/Enums/Scale.php @@ -10,23 +10,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ #[Entity] class Scale { - /** - * @Id @GeneratedValue @Column(type="integer") - * @var int - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @Column(type="simple_array", enumType=Unit::class) - * @var Unit[] - */ + /** @var Unit[] */ #[Column(type: 'simple_array', enumType: Unit::class)] public $supportedUnits; @@ -37,14 +30,14 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'supportedUnits', 'type' => 'simple_array', 'enumType' => Unit::class, - ] + ], ); } } diff --git a/tests/Tests/Models/Enums/TypedCardEnumCompositeId.php b/tests/Tests/Models/Enums/TypedCardEnumCompositeId.php index 2e7f3369bfb..7e23a2b2f9b 100644 --- a/tests/Tests/Models/Enums/TypedCardEnumCompositeId.php +++ b/tests/Tests/Models/Enums/TypedCardEnumCompositeId.php @@ -4,27 +4,17 @@ namespace Doctrine\Tests\Models\Enums; -use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ #[Entity] class TypedCardEnumCompositeId { - /** - * @ORM\Id() - * @ORM\Column(type="string", enumType=Suit::class) - */ #[Id] #[Column(type: 'string', enumType: Suit::class)] public Suit $suit; - /** - * @ORM\Id() - * @ORM\Column(type="string", enumType=Unit::class) - */ #[Id] #[Column(type: 'string', enumType: Unit::class)] public Unit $unit; diff --git a/tests/Tests/Models/Enums/TypedCardEnumId.php b/tests/Tests/Models/Enums/TypedCardEnumId.php index 1cd038f82de..dc14689b4a8 100644 --- a/tests/Tests/Models/Enums/TypedCardEnumId.php +++ b/tests/Tests/Models/Enums/TypedCardEnumId.php @@ -4,19 +4,13 @@ namespace Doctrine\Tests\Models\Enums; -use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** @Entity */ #[Entity] class TypedCardEnumId { - /** - * @ORM\Id() - * @ORM\Column(type="string", enumType=Suit::class) - */ #[Id] #[Column(type: 'string', enumType: Suit::class)] public Suit $suit; diff --git a/tests/Tests/Models/Enums/TypedCardNativeEnum.php b/tests/Tests/Models/Enums/TypedCardNativeEnum.php new file mode 100644 index 00000000000..59e4eb00e55 --- /dev/null +++ b/tests/Tests/Models/Enums/TypedCardNativeEnum.php @@ -0,0 +1,23 @@ + - * @OneToMany(targetEntity="ForumBoard", mappedBy="category") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'ForumBoard', mappedBy: 'category')] public $boards; public function getId(): int diff --git a/tests/Tests/Models/Forum/ForumEntry.php b/tests/Tests/Models/Forum/ForumEntry.php index d12835b5172..1ab7b53dbc7 100644 --- a/tests/Tests/Models/Forum/ForumEntry.php +++ b/tests/Tests/Models/Forum/ForumEntry.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="forum_entries") - */ +#[Table(name: 'forum_entries')] +#[Entity] class ForumEntry { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=50) - */ + /** @var string */ + #[Column(type: 'string', length: 50)] public $topic; public function &getTopicByReference(): string diff --git a/tests/Tests/Models/Forum/ForumUser.php b/tests/Tests/Models/Forum/ForumUser.php index dfb5756c569..08a4b937715 100644 --- a/tests/Tests/Models/Forum/ForumUser.php +++ b/tests/Tests/Models/Forum/ForumUser.php @@ -12,31 +12,23 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="forum_users") - */ +#[Table(name: 'forum_users')] +#[Entity] class ForumUser { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=50) - */ + /** @var string */ + #[Column(type: 'string', length: 50)] public $username; - /** - * @var ForumAvatar - * @OneToOne(targetEntity="ForumAvatar", cascade={"persist"}) - * @JoinColumn(name="avatar_id", referencedColumnName="id") - */ + /** @var ForumAvatar */ + #[OneToOne(targetEntity: 'ForumAvatar', cascade: ['persist'])] + #[JoinColumn(name: 'avatar_id', referencedColumnName: 'id')] public $avatar; public function getId(): int diff --git a/tests/Tests/Models/GH10132/Complex.php b/tests/Tests/Models/GH10132/Complex.php index 816a97dae3e..2c9bcb91bc8 100644 --- a/tests/Tests/Models/GH10132/Complex.php +++ b/tests/Tests/Models/GH10132/Complex.php @@ -12,16 +12,14 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\Models\Enums\Suit; -/** @Entity */ +#[Entity] class Complex { - /** - * @Id - * @Column(type = "string", enumType = Suit::class) - */ + #[Id] + #[Column(type: 'string', enumType: Suit::class)] protected Suit $type; - /** @OneToMany(targetEntity = ComplexChild::class, mappedBy = "complex", cascade = {"persist"}) */ + #[OneToMany(targetEntity: ComplexChild::class, mappedBy: 'complex', cascade: ['persist'])] protected Collection $complexChildren; public function __construct() diff --git a/tests/Tests/Models/GH10132/ComplexChild.php b/tests/Tests/Models/GH10132/ComplexChild.php index 393ff8b13ad..cce0ec0733e 100644 --- a/tests/Tests/Models/GH10132/ComplexChild.php +++ b/tests/Tests/Models/GH10132/ComplexChild.php @@ -11,19 +11,15 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\Models\Enums\Suit; -/** @Entity */ +#[Entity] class ComplexChild { - /** - * @ManyToOne(targetEntity = Complex::class, inversedBy = "complexChildren") - * @JoinColumn(name = "complexType", referencedColumnName = "type", nullable = false) - */ + #[ManyToOne(inversedBy: 'complexChildren')] + #[JoinColumn(name: 'complexType', referencedColumnName: 'type', nullable: false)] protected Complex $complex; - /** - * @Id - * @Column(type = "string", enumType = Suit::class) - */ + #[Id] + #[Column(type: 'string', enumType: Suit::class)] protected Suit $complexType; public function setComplex(Complex $complex): void diff --git a/tests/Tests/Models/GH10334/GH10334Foo.php b/tests/Tests/Models/GH10334/GH10334Foo.php index 1918d32f7b3..f527bb1a9d6 100644 --- a/tests/Tests/Models/GH10334/GH10334Foo.php +++ b/tests/Tests/Models/GH10334/GH10334Foo.php @@ -11,26 +11,18 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** - * @Entity - */ +#[Entity] class GH10334Foo { - /** - * @var GH10334FooCollection - * @Id - * @ManyToOne(targetEntity="GH10334FooCollection", inversedBy="foos") - * @JoinColumn(name="foo_collection_id", referencedColumnName="id", nullable = false) - * @GeneratedValue - */ - protected $collection; + #[Id] + #[ManyToOne(targetEntity: GH10334FooCollection::class, inversedBy: 'foos')] + #[JoinColumn(name: 'foo_collection_id', referencedColumnName: 'id', nullable: false)] + #[GeneratedValue] + protected GH10334FooCollection $collection; - /** - * @var GH10334ProductTypeId - * @Id - * @Column(type="string", enumType="Doctrine\Tests\Models\GH10334\GH10334ProductTypeId") - */ - protected $productTypeId; + #[Id] + #[Column(type: 'string', enumType: 'Doctrine\Tests\Models\GH10334\GH10334ProductTypeId')] + protected GH10334ProductTypeId $productTypeId; public function __construct(GH10334FooCollection $collection, GH10334ProductTypeId $productTypeId) { diff --git a/tests/Tests/Models/GH10334/GH10334FooCollection.php b/tests/Tests/Models/GH10334/GH10334FooCollection.php index 749fd899c20..d58ae3db9d6 100644 --- a/tests/Tests/Models/GH10334/GH10334FooCollection.php +++ b/tests/Tests/Models/GH10334/GH10334FooCollection.php @@ -12,33 +12,24 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToMany; -/** - * @Entity - */ +#[Entity] class GH10334FooCollection { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - protected $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + protected int $id; - /** - * @OneToMany(targetEntity="GH10334Foo", mappedBy="collection", cascade={"persist", "remove"}) - * @var Collection $foos - */ - private $foos; + /** @var Collection $foos */ + #[OneToMany(targetEntity: 'GH10334Foo', mappedBy: 'collection', cascade: ['persist', 'remove'])] + private Collection $foos; public function __construct() { $this->foos = new ArrayCollection(); } - /** - * @return Collection - */ + /** @return Collection */ public function getFoos(): Collection { return $this->foos; diff --git a/tests/Tests/Models/GH10334/GH10334Product.php b/tests/Tests/Models/GH10334/GH10334Product.php index 28c5c39c5a6..7bc13cde9e8 100644 --- a/tests/Tests/Models/GH10334/GH10334Product.php +++ b/tests/Tests/Models/GH10334/GH10334Product.php @@ -11,31 +11,20 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** - * @Entity - */ +#[Entity] class GH10334Product { - /** - * @var int - * @Id - * @Column(name="product_id", type="integer") - * @GeneratedValue() - */ - protected $id; - - /** - * @var string - * @Column(name="name", type="string") - */ - private $name; - - /** - * @var GH10334ProductType $productType - * @ManyToOne(targetEntity="GH10334ProductType", inversedBy="products") - * @JoinColumn(name="product_type_id", referencedColumnName="id", nullable = false) - */ - private $productType; + #[Id] + #[Column(name: 'product_id', type: 'integer')] + #[GeneratedValue] + protected int $id; + + #[Column(name: 'name', type: 'string')] + private string $name; + + #[ManyToOne(targetEntity: 'GH10334ProductType', inversedBy: 'products')] + #[JoinColumn(name: 'product_type_id', referencedColumnName: 'id', nullable: false)] + private GH10334ProductType $productType; public function __construct(string $name, GH10334ProductType $productType) { diff --git a/tests/Tests/Models/GH10334/GH10334ProductType.php b/tests/Tests/Models/GH10334/GH10334ProductType.php index 0e26e956563..046da33a330 100644 --- a/tests/Tests/Models/GH10334/GH10334ProductType.php +++ b/tests/Tests/Models/GH10334/GH10334ProductType.php @@ -11,29 +11,18 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToMany; -/** - * @Entity - */ +#[Entity] class GH10334ProductType { - /** - * @var GH10334ProductTypeId - * @Id - * @Column(type="string", enumType="Doctrine\Tests\Models\GH10334\GH10334ProductTypeId") - */ - protected $id; - - /** - * @var float - * @Column(type="float") - */ - private $value; - - /** - * @OneToMany(targetEntity="GH10334Product", mappedBy="productType", cascade={"persist", "remove"}) - * @var Collection $products - */ - private $products; + #[Id] + #[Column(type: 'string', enumType: 'Doctrine\Tests\Models\GH10334\GH10334ProductTypeId', length: 255)] + protected GH10334ProductTypeId $id; + + #[Column(type: 'float')] + private float $value; + + #[OneToMany(targetEntity: 'GH10334Product', mappedBy: 'productType', cascade: ['persist', 'remove'])] + private Collection $products; public function __construct(GH10334ProductTypeId $id, float $value) { diff --git a/tests/Tests/Models/GH10336/GH10336Entity.php b/tests/Tests/Models/GH10336/GH10336Entity.php index 9c4fe9da08b..2935fd379e5 100644 --- a/tests/Tests/Models/GH10336/GH10336Entity.php +++ b/tests/Tests/Models/GH10336/GH10336Entity.php @@ -6,22 +6,16 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="gh10336_entities") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10336_entities')] class GH10336Entity { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - */ - public ?int $id = null; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int|null $id = null; - /** - * @ORM\ManyToOne(targetEntity="GH10336Relation") - * @ORM\JoinColumn(name="relation_id", referencedColumnName="id", nullable=true) - */ - public ?GH10336Relation $relation = null; + #[ORM\ManyToOne(targetEntity: GH10336Relation::class)] + #[ORM\JoinColumn(name: 'relation_id', referencedColumnName: 'id', nullable: true)] + public GH10336Relation|null $relation = null; } diff --git a/tests/Tests/Models/GH10336/GH10336Relation.php b/tests/Tests/Models/GH10336/GH10336Relation.php index 664cf4cafeb..1d096686eee 100644 --- a/tests/Tests/Models/GH10336/GH10336Relation.php +++ b/tests/Tests/Models/GH10336/GH10336Relation.php @@ -6,21 +6,15 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="gh10336_relations") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10336_relations')] class GH10336Relation { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - */ - public ?int $id = null; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int|null $id = null; - /** - * @ORM\Column(type="string") - */ + #[ORM\Column(type: 'string')] public string $value; } diff --git a/tests/Tests/Models/GH7717/GH7717Child.php b/tests/Tests/Models/GH7717/GH7717Child.php index 0ea30c8437f..0ed114a84af 100644 --- a/tests/Tests/Models/GH7717/GH7717Child.php +++ b/tests/Tests/Models/GH7717/GH7717Child.php @@ -6,21 +6,15 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="gh7717_children") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh7717_children')] class GH7717Child { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - */ - public ?int $id = null; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int|null $id = null; - /** - * @ORM\Column(type="string", nullable=true) - */ - public ?string $nullableProperty = null; + #[ORM\Column(type: 'string', nullable: true)] + public string|null $nullableProperty = null; } diff --git a/tests/Tests/Models/GH7717/GH7717Parent.php b/tests/Tests/Models/GH7717/GH7717Parent.php index 74bbd954de6..8bcace53d6c 100644 --- a/tests/Tests/Models/GH7717/GH7717Parent.php +++ b/tests/Tests/Models/GH7717/GH7717Parent.php @@ -7,23 +7,16 @@ use Doctrine\Common\Collections\Selectable; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="gh7717_parents") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh7717_parents')] class GH7717Parent { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - */ - public ?int $id = null; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int|null $id = null; - /** - * @ORM\ManyToMany(targetEntity="GH7717Child", cascade={"persist"}) - * - * @var Selectable - */ + /** @var Selectable */ + #[ORM\ManyToMany(targetEntity: GH7717Child::class, cascade: ['persist'])] public Selectable $children; } diff --git a/tests/Tests/Models/GH8565/GH8565Employee.php b/tests/Tests/Models/GH8565/GH8565Employee.php index 1c539f24c4c..c9c81223806 100644 --- a/tests/Tests/Models/GH8565/GH8565Employee.php +++ b/tests/Tests/Models/GH8565/GH8565Employee.php @@ -9,15 +9,11 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\GH8565EmployeePayloadType; -/** - * @Entity - * @Table(name="gh8565_employees") - */ +#[Table(name: 'gh8565_employees')] +#[Entity] class GH8565Employee extends GH8565Person { - /** - * @Column(type="GH8565EmployeePayloadType", nullable=false) - * @var GH8565EmployeePayloadType - */ + /** @var GH8565EmployeePayloadType */ + #[Column(type: 'GH8565EmployeePayloadType', nullable: false)] public $type; } diff --git a/tests/Tests/Models/GH8565/GH8565Manager.php b/tests/Tests/Models/GH8565/GH8565Manager.php index a3bd9546d38..f70cf34b4f2 100644 --- a/tests/Tests/Models/GH8565/GH8565Manager.php +++ b/tests/Tests/Models/GH8565/GH8565Manager.php @@ -9,15 +9,11 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\GH8565ManagerPayloadType; -/** - * @Entity - * @Table(name="gh8565_managers") - */ +#[Table(name: 'gh8565_managers')] +#[Entity] class GH8565Manager extends GH8565Person { - /** - * @Column(type="GH8565ManagerPayloadType", nullable=false) - * @var GH8565ManagerPayloadType - */ + /** @var GH8565ManagerPayloadType */ + #[Column(type: 'GH8565ManagerPayloadType', nullable: false)] public $type; } diff --git a/tests/Tests/Models/GH8565/GH8565Person.php b/tests/Tests/Models/GH8565/GH8565Person.php index 1bd144c4cbb..fe0682a11fd 100644 --- a/tests/Tests/Models/GH8565/GH8565Person.php +++ b/tests/Tests/Models/GH8565/GH8565Person.php @@ -13,24 +13,16 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="gh8565_persons") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({ - * "person" = "GH8565Person", - * "manager" = "GH8565Manager", - * "employee" = "GH8565Employee" - * }) - */ +#[Table(name: 'gh8565_persons')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['person' => 'GH8565Person', 'manager' => 'GH8565Manager', 'employee' => 'GH8565Employee'])] class GH8565Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/Models/Generic/BooleanModel.php b/tests/Tests/Models/Generic/BooleanModel.php index f859de27299..2acf73d550f 100644 --- a/tests/Tests/Models/Generic/BooleanModel.php +++ b/tests/Tests/Models/Generic/BooleanModel.php @@ -10,23 +10,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="boolean_model") - */ +#[Table(name: 'boolean_model')] +#[Entity] class BooleanModel { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var bool - * @Column(type="boolean") - */ + /** @var bool */ + #[Column(type: 'boolean')] public $booleanField; } diff --git a/tests/Tests/Models/Generic/DateTimeModel.php b/tests/Tests/Models/Generic/DateTimeModel.php index 9a27b48f730..95b7db1a5b4 100644 --- a/tests/Tests/Models/Generic/DateTimeModel.php +++ b/tests/Tests/Models/Generic/DateTimeModel.php @@ -10,35 +10,25 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="date_time_model") - */ +#[Table(name: 'date_time_model')] +#[Entity] class DateTimeModel { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DateTime|null - * @Column(name="col_datetime", type="datetime", nullable=true) - */ + /** @var DateTime|null */ + #[Column(name: 'col_datetime', type: 'datetime', nullable: true)] public $datetime; - /** - * @var DateTime|null - * @Column(name="col_date", type="date", nullable=true) - */ + /** @var DateTime|null */ + #[Column(name: 'col_date', type: 'date', nullable: true)] public $date; - /** - * @var DateTime|null - * @Column(name="col_time", type="time", nullable=true) - */ + /** @var DateTime|null */ + #[Column(name: 'col_time', type: 'time', nullable: true)] public $time; } diff --git a/tests/Tests/Models/Generic/DecimalModel.php b/tests/Tests/Models/Generic/DecimalModel.php index 94dae1fad3e..faeffc577f3 100644 --- a/tests/Tests/Models/Generic/DecimalModel.php +++ b/tests/Tests/Models/Generic/DecimalModel.php @@ -10,29 +10,21 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="decimal_model") - */ +#[Table(name: 'decimal_model')] +#[Entity] class DecimalModel { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var float - * @Column(name="`decimal`", type="decimal", scale=2, precision=5) - */ + /** @var float */ + #[Column(name: '`decimal`', type: 'decimal', scale: 2, precision: 5)] public $decimal; - /** - * @var float - * @Column(name="`high_scale`", type="decimal", scale=4, precision=14) - */ + /** @var float */ + #[Column(name: '`high_scale`', type: 'decimal', scale: 4, precision: 14)] public $highScale; } diff --git a/tests/Tests/Models/Generic/NonAlphaColumnsEntity.php b/tests/Tests/Models/Generic/NonAlphaColumnsEntity.php index 906a7a64355..8ec55238cc5 100644 --- a/tests/Tests/Models/Generic/NonAlphaColumnsEntity.php +++ b/tests/Tests/Models/Generic/NonAlphaColumnsEntity.php @@ -10,28 +10,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`not-a-simple-entity`") - */ +#[Table(name: '`not-a-simple-entity`')] +#[Entity] class NonAlphaColumnsEntity { - /** - * @var int - * @Id - * @Column(type="integer", name="`simple-entity-id`") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer', name: '`simple-entity-id`')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255, name="`simple-entity-value`") - */ - public $value; - - public function __construct(string $value) - { - $this->value = $value; + public function __construct( + #[Column(type: 'string', length: 255, name: '`simple-entity-value`')] + public string $value, + ) { } } diff --git a/tests/Tests/Models/Generic/SerializationModel.php b/tests/Tests/Models/Generic/SerializationModel.php index ab61dcff606..0508ccae3a1 100644 --- a/tests/Tests/Models/Generic/SerializationModel.php +++ b/tests/Tests/Models/Generic/SerializationModel.php @@ -10,29 +10,21 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="serialize_model") - */ +#[Table(name: 'serialize_model')] +#[Entity] class SerializationModel { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var mixed[] - * @Column(name="the_array", type="array", nullable=true) - */ + /** @var mixed[] */ + #[Column(name: 'the_array', type: 'array', nullable: true)] public $array; - /** - * @var object - * @Column(name="the_obj", type="object", nullable=true) - */ + /** @var object */ + #[Column(name: 'the_obj', type: 'object', nullable: true)] public $object; } diff --git a/tests/Tests/Models/GeoNames/Admin1.php b/tests/Tests/Models/GeoNames/Admin1.php index a155b9e46f6..d21290f6928 100644 --- a/tests/Tests/Models/GeoNames/Admin1.php +++ b/tests/Tests/Models/GeoNames/Admin1.php @@ -14,47 +14,28 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="geonames_admin1") - * @Cache - */ +#[Table(name: 'geonames_admin1')] +#[Entity] +#[Cache] class Admin1 { - /** - * @var int - * @Id - * @Column(type="integer", length=25) - * @GeneratedValue(strategy="NONE") - */ - public $id; - - /** - * @var Country - * @Id - * @ManyToOne(targetEntity="Country") - * @JoinColumn(name="country", referencedColumnName="id") - * @Cache - */ - public $country; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Admin1AlternateName", mappedBy="admin1") - * @Cache - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Admin1AlternateName', mappedBy: 'admin1')] + #[Cache] public $names = []; - /** - * @var string - * @Column(type="string", length=255); - */ - public $name; - - public function __construct(int $id, string $name, Country $country) - { - $this->id = $id; - $this->name = $name; - $this->country = $country; + public function __construct( + #[Id] + #[Column(type: 'integer', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + public int $id, + #[Column(type: 'string', length: 255)] + public string $name, + #[Id] + #[ManyToOne(targetEntity: 'Country')] + #[JoinColumn(name: 'country', referencedColumnName: 'id')] + #[Cache] + public Country $country, + ) { } } diff --git a/tests/Tests/Models/GeoNames/Admin1AlternateName.php b/tests/Tests/Models/GeoNames/Admin1AlternateName.php index 5f93aa677a5..554e83ec18c 100644 --- a/tests/Tests/Models/GeoNames/Admin1AlternateName.php +++ b/tests/Tests/Models/GeoNames/Admin1AlternateName.php @@ -10,46 +10,29 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="geonames_admin1_alternate_name") - * @Cache - */ +#[Table(name: 'geonames_admin1_alternate_name')] +#[Entity] +#[Cache] class Admin1AlternateName { - /** - * @var int - * @Id - * @Column(type="string", length=25) - * @GeneratedValue(strategy="NONE") - */ - public $id; + #[Id] + #[Column(type: 'string', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + public string $id; - /** - * @var Admin1 - * @ManyToOne(targetEntity="Admin1", inversedBy="names") - * @JoinColumns({ - * @JoinColumn(name="admin1", referencedColumnName="id"), - * @JoinColumn(name="country", referencedColumnName="country") - * }) - * @Cache - */ - public $admin1; - - /** - * @var string - * @Column(type="string", length=255); - */ - public $name; - - public function __construct(int $id, string $name, Admin1 $admin1) - { - $this->id = $id; - $this->name = $name; - $this->admin1 = $admin1; + public function __construct( + int $id, + #[Column(type: 'string', length: 255)] + public string $name, + #[JoinColumn(name: 'admin1', referencedColumnName: 'id')] + #[JoinColumn(name: 'country', referencedColumnName: 'country')] + #[ManyToOne(targetEntity: 'Admin1', inversedBy: 'names')] + #[Cache] + public Admin1 $admin1, + ) { + $this->id = (string) $id; } } diff --git a/tests/Tests/Models/GeoNames/City.php b/tests/Tests/Models/GeoNames/City.php index 2fbc34edbc7..5ffe66db364 100644 --- a/tests/Tests/Models/GeoNames/City.php +++ b/tests/Tests/Models/GeoNames/City.php @@ -10,53 +10,37 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="geonames_city") - * @Cache - */ +#[Table(name: 'geonames_city')] +#[Entity] +#[Cache] class City { - /** - * @var string - * @Id - * @Column(type="string", length=25) - * @GeneratedValue(strategy="NONE") - */ - public $id; - - /** - * @var Country - * @ManyToOne(targetEntity="Country") - * @JoinColumn(name="country", referencedColumnName="id") - * @Cache - */ + #[Id] + #[Column(type: 'string', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + public string $id; + + /** @var Country */ + #[ManyToOne(targetEntity: 'Country')] + #[JoinColumn(name: 'country', referencedColumnName: 'id')] + #[Cache] public $country; - /** - * @var Admin1 - * @ManyToOne(targetEntity="Admin1") - * @JoinColumns({ - * @JoinColumn(name="admin1", referencedColumnName="id"), - * @JoinColumn(name="country", referencedColumnName="country") - * }) - * @Cache - */ + /** @var Admin1 */ + #[JoinColumn(name: 'admin1', referencedColumnName: 'id')] + #[JoinColumn(name: 'country', referencedColumnName: 'country')] + #[ManyToOne(targetEntity: 'Admin1')] + #[Cache] public $admin1; - /** - * @var string - * @Column(type="string", length=255); - */ - public $name; - - public function __construct(int $id, string $name) - { - $this->id = $id; - $this->name = $name; + public function __construct( + int $id, + #[Column(type: 'string', length: 255)] + public string $name, + ) { + $this->id = (string) $id; } } diff --git a/tests/Tests/Models/GeoNames/Country.php b/tests/Tests/Models/GeoNames/Country.php index c441ab12ca9..ab4cb345f1f 100644 --- a/tests/Tests/Models/GeoNames/Country.php +++ b/tests/Tests/Models/GeoNames/Country.php @@ -11,30 +11,18 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="geonames_country") - * @Cache - */ +#[Table(name: 'geonames_country')] +#[Entity] +#[Cache] class Country { - /** - * @var string - * @Id - * @Column(type="string", length=2) - * @GeneratedValue(strategy="NONE") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255); - */ - public $name; - - public function __construct($id, $name) - { - $this->id = $id; - $this->name = $name; + public function __construct( + #[Id] + #[Column(type: 'string', length: 2)] + #[GeneratedValue(strategy: 'NONE')] + public string $id, + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } diff --git a/tests/Tests/Models/Global/GlobalNamespaceModel.php b/tests/Tests/Models/Global/GlobalNamespaceModel.php index 5ee5cf516d0..5c2e6ebcb62 100644 --- a/tests/Tests/Models/Global/GlobalNamespaceModel.php +++ b/tests/Tests/Models/Global/GlobalNamespaceModel.php @@ -6,79 +6,55 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="articles") - */ +#[Table(name: 'articles')] +#[Entity] class DoctrineGlobalArticle { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] protected $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $headline; - /** - * @var string - * @Column(type="text") - */ + /** @var string */ + #[Column(type: 'text')] protected $text; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DoctrineGlobalUser") - * @JoinTable(name="author_articles", - * joinColumns={@JoinColumn(name="article_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="author_id", referencedColumnName="id", unique=true)} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'author_articles')] + #[JoinColumn(name: 'article_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'author_id', referencedColumnName: 'id', unique: true)] + #[ManyToMany(targetEntity: 'DoctrineGlobalUser')] protected $author; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DoctrineGlobalUser") - * @JoinTable(name="editor_articles", - * joinColumns={@JoinColumn(name="article_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="editor_id", referencedColumnName="id", unique=true)} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'editor_articles')] + #[JoinColumn(name: 'article_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'editor_id', referencedColumnName: 'id', unique: true)] + #[ManyToMany(targetEntity: 'DoctrineGlobalUser')] protected $editor; } -/** - * @Entity - * @Table(name="users") - */ +#[Table(name: 'users')] +#[Entity] class DoctrineGlobalUser { - /** - * @Id - * @Column(type="integer") - * @var int - */ - private $id; + #[Id] + #[Column(type: 'integer')] + private int $id; - /** - * @Column(type="string", length=64) - * @var string - */ - private $username; + #[Column(type: 'string', length: 64)] + private string $username; - /** - * @Column(type="string", length=128) - * @var string - */ - private $email; + #[Column(type: 'string', length: 128)] + private string $email; } diff --git a/tests/Tests/Models/Hydration/EntityWithArrayDefaultArrayValueM2M.php b/tests/Tests/Models/Hydration/EntityWithArrayDefaultArrayValueM2M.php index 39b9e626177..0216cdece8d 100644 --- a/tests/Tests/Models/Hydration/EntityWithArrayDefaultArrayValueM2M.php +++ b/tests/Tests/Models/Hydration/EntityWithArrayDefaultArrayValueM2M.php @@ -11,20 +11,16 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; -/** @Entity */ +#[Entity] class EntityWithArrayDefaultArrayValueM2M { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity=SimpleEntity::class) - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: SimpleEntity::class)] public $collection = []; } diff --git a/tests/Tests/Models/Hydration/SimpleEntity.php b/tests/Tests/Models/Hydration/SimpleEntity.php index 770c03c0b57..ec77a24ab9a 100644 --- a/tests/Tests/Models/Hydration/SimpleEntity.php +++ b/tests/Tests/Models/Hydration/SimpleEntity.php @@ -9,14 +9,12 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class SimpleEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/Models/InvalidXml.php b/tests/Tests/Models/InvalidXml.php new file mode 100644 index 00000000000..8c3f7fe7c39 --- /dev/null +++ b/tests/Tests/Models/InvalidXml.php @@ -0,0 +1,9 @@ + 'Issue5989Person', 'manager' => 'Issue5989Manager', 'employee' => 'Issue5989Employee'])] class Issue5989Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/Models/Issue9300/Issue9300Child.php b/tests/Tests/Models/Issue9300/Issue9300Child.php index 0919d6d33b4..c8544417b08 100644 --- a/tests/Tests/Models/Issue9300/Issue9300Child.php +++ b/tests/Tests/Models/Issue9300/Issue9300Child.php @@ -12,29 +12,21 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; -/** - * @Entity - */ +#[Entity] class Issue9300Child { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Collection - * @ManyToMany(targetEntity="Issue9300Parent") - */ + /** @var Collection */ + #[ManyToMany(targetEntity: Issue9300Parent::class)] public $parents; - /** - * @var string - * @Column(type="string") - */ + /** @var string */ + #[Column(type: 'string')] public $name; public function __construct() diff --git a/tests/Tests/Models/Issue9300/Issue9300Parent.php b/tests/Tests/Models/Issue9300/Issue9300Parent.php index f9b50b36c95..0bb97c18e86 100644 --- a/tests/Tests/Models/Issue9300/Issue9300Parent.php +++ b/tests/Tests/Models/Issue9300/Issue9300Parent.php @@ -9,22 +9,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** - * @Entity - */ +#[Entity] class Issue9300Parent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string") - */ + /** @var string */ + #[Column(type: 'string')] public $name; } diff --git a/tests/Tests/Models/JoinedInheritanceType/AnotherChildClass.php b/tests/Tests/Models/JoinedInheritanceType/AnotherChildClass.php index 184b1762853..60ea8ab7932 100644 --- a/tests/Tests/Models/JoinedInheritanceType/AnotherChildClass.php +++ b/tests/Tests/Models/JoinedInheritanceType/AnotherChildClass.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class AnotherChildClass extends ChildClass { } diff --git a/tests/Tests/Models/JoinedInheritanceType/ChildClass.php b/tests/Tests/Models/JoinedInheritanceType/ChildClass.php index 4ba72c516f3..d017df22df4 100644 --- a/tests/Tests/Models/JoinedInheritanceType/ChildClass.php +++ b/tests/Tests/Models/JoinedInheritanceType/ChildClass.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\MappedSuperclass; -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class ChildClass extends RootClass { } diff --git a/tests/Tests/Models/JoinedInheritanceType/RootClass.php b/tests/Tests/Models/JoinedInheritanceType/RootClass.php index 8615d440fbd..0c12e9715f4 100644 --- a/tests/Tests/Models/JoinedInheritanceType/RootClass.php +++ b/tests/Tests/Models/JoinedInheritanceType/RootClass.php @@ -10,17 +10,13 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; -/** - * @Entity - * @InheritanceType("JOINED") - */ +#[Entity] +#[InheritanceType('JOINED')] class RootClass { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/Models/Legacy/LegacyArticle.php b/tests/Tests/Models/Legacy/LegacyArticle.php index c41dea37679..a910ea22eeb 100644 --- a/tests/Tests/Models/Legacy/LegacyArticle.php +++ b/tests/Tests/Models/Legacy/LegacyArticle.php @@ -12,37 +12,27 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="legacy_articles") - */ +#[Table(name: 'legacy_articles')] +#[Entity] class LegacyArticle { - /** - * @var int - * @Id - * @Column(name="iArticleId", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'iArticleId', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="sTopic", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'sTopic', type: 'string', length: 255)] public $topic; - /** - * @var string - * @Column(name="sText", type="text") - */ + /** @var string */ + #[Column(name: 'sText', type: 'text')] public $text; - /** - * @var LegacyUser - * @ManyToOne(targetEntity="LegacyUser", inversedBy="articles") - * @JoinColumn(name="iUserId", referencedColumnName="iUserId") - */ + /** @var LegacyUser */ + #[ManyToOne(targetEntity: 'LegacyUser', inversedBy: 'articles')] + #[JoinColumn(name: 'iUserId', referencedColumnName: 'iUserId')] public $user; public function setAuthor(LegacyUser $author): void diff --git a/tests/Tests/Models/Legacy/LegacyCar.php b/tests/Tests/Models/Legacy/LegacyCar.php index e22c45c1e28..42ea5b609fb 100644 --- a/tests/Tests/Models/Legacy/LegacyCar.php +++ b/tests/Tests/Models/Legacy/LegacyCar.php @@ -12,30 +12,22 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="legacy_cars") - */ +#[Table(name: 'legacy_cars')] +#[Entity] class LegacyCar { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="iCarId", type="integer", nullable=false) - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(name: 'iCarId', type: 'integer', nullable: false)] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="LegacyUser", mappedBy="cars") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'LegacyUser', mappedBy: 'cars')] public $users; - /** - * @var string - * @Column(name="sDescription", type="string", length=255, unique=true) - */ + /** @var string */ + #[Column(name: 'sDescription', type: 'string', length: 255, unique: true)] public $description; public function getDescription(): string diff --git a/tests/Tests/Models/Legacy/LegacyUser.php b/tests/Tests/Models/Legacy/LegacyUser.php index 009a954949c..a57ca8f3ab2 100644 --- a/tests/Tests/Models/Legacy/LegacyUser.php +++ b/tests/Tests/Models/Legacy/LegacyUser.php @@ -10,58 +10,44 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="legacy_users") - */ +#[Table(name: 'legacy_users')] +#[Entity] class LegacyUser { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="iUserId", type="integer", nullable=false) - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(name: 'iUserId', type: 'integer', nullable: false)] public $id; - /** - * @var string - * @Column(name="sUsername", type="string", length=255, unique=true) - */ + /** @var string */ + #[Column(name: 'sUsername', type: 'string', length: 255, unique: true)] public $username; - /** - * @var string - * @Column(type="string", length=255, name="name") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'name')] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="LegacyArticle", mappedBy="user") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'LegacyArticle', mappedBy: 'user')] public $articles; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="LegacyUserReference", mappedBy="_source", cascade={"remove"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'LegacyUserReference', mappedBy: '_source', cascade: ['remove'])] public $references; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="LegacyCar", inversedBy="users", cascade={"persist", "merge"}) - * @JoinTable(name="legacy_users_cars", - * joinColumns={@JoinColumn(name="iUserId", referencedColumnName="iUserId")}, - * inverseJoinColumns={@JoinColumn(name="iCarId", referencedColumnName="iCarId")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'legacy_users_cars')] + #[JoinColumn(name: 'iUserId', referencedColumnName: 'iUserId')] + #[InverseJoinColumn(name: 'iCarId', referencedColumnName: 'iCarId')] + #[ManyToMany(targetEntity: 'LegacyCar', inversedBy: 'users', cascade: ['persist'])] public $cars; public function __construct() diff --git a/tests/Tests/Models/Legacy/LegacyUserReference.php b/tests/Tests/Models/Legacy/LegacyUserReference.php index 506661af37a..f0586fda85a 100644 --- a/tests/Tests/Models/Legacy/LegacyUserReference.php +++ b/tests/Tests/Models/Legacy/LegacyUserReference.php @@ -12,49 +12,35 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="legacy_users_reference") - */ +#[Table(name: 'legacy_users_reference')] +#[Entity] class LegacyUserReference { - /** - * @var LegacyUser - * @Id - * @ManyToOne(targetEntity="LegacyUser", inversedBy="references") - * @JoinColumn(name="iUserIdSource", referencedColumnName="iUserId") - */ - private $_source; + #[Id] + #[ManyToOne(targetEntity: 'LegacyUser', inversedBy: 'references')] + #[JoinColumn(name: 'iUserIdSource', referencedColumnName: 'iUserId')] + private LegacyUser $_source; - /** - * @var LegacyUser - * @Id - * @ManyToOne(targetEntity="LegacyUser") - * @JoinColumn(name="iUserIdTarget", referencedColumnName="iUserId") - */ - private $_target; + #[Id] + #[ManyToOne(targetEntity: 'LegacyUser')] + #[JoinColumn(name: 'iUserIdTarget', referencedColumnName: 'iUserId')] + private LegacyUser $_target; - /** - * @var string - * @Column(type="string", length=255, name="description") - */ - private $_description; + #[Column(type: 'datetime', name: 'created')] + private DateTime $created; - /** - * @var DateTime - * @Column(type="datetime", name="created") - */ - private $created; - - public function __construct(LegacyUser $source, LegacyUser $target, string $description) - { + public function __construct( + LegacyUser $source, + LegacyUser $target, + #[Column(type: 'string', length: 255, name: 'description')] + private string $_description, + ) { $source->addReference($this); $target->addReference($this); - $this->_source = $source; - $this->_target = $target; - $this->_description = $description; - $this->created = new DateTime('now'); + $this->_source = $source; + $this->_target = $target; + $this->created = new DateTime('now'); } public function source(): LegacyUser diff --git a/tests/Tests/Models/ManyToManyPersister/ChildClass.php b/tests/Tests/Models/ManyToManyPersister/ChildClass.php index 7bab5fe81ad..30fb904c02b 100644 --- a/tests/Tests/Models/ManyToManyPersister/ChildClass.php +++ b/tests/Tests/Models/ManyToManyPersister/ChildClass.php @@ -9,52 +9,37 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="manytomanypersister_child") - */ +#[Table(name: 'manytomanypersister_child')] +#[Entity] class ChildClass { /** - * @Id - * @Column(name="id1", type="integer") - * @var int - */ - public $id1; - - /** - * @Id - * @ManyToOne(targetEntity=OtherParentClass::class, cascade={"persist"}) - * @JoinColumn(name="other_parent_id", referencedColumnName="id") - * @var OtherParentClass - */ - public $otherParent; - - /** - * @ManyToMany(targetEntity=ParentClass::class, inversedBy="children") - * @JoinTable( - * name="parent_child", - * joinColumns={ - * @JoinColumn(name="child_id1", referencedColumnName="id1"), - * @JoinColumn(name="child_id2", referencedColumnName="other_parent_id") - * }, - * inverseJoinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")} - * ) * @var Collection|ParentClass[] * @phpstan-var Collection */ + #[JoinTable(name: 'parent_child')] + #[JoinColumn(name: 'child_id1', referencedColumnName: 'id1')] + #[JoinColumn(name: 'child_id2', referencedColumnName: 'other_parent_id')] + #[InverseJoinColumn(name: 'parent_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: ParentClass::class, inversedBy: 'children')] public $parents; - public function __construct(int $id1, OtherParentClass $otherParent) - { - $this->id1 = $id1; - $this->otherParent = $otherParent; - $this->parents = new ArrayCollection(); + public function __construct( + #[Id] + #[Column(name: 'id1', type: 'integer')] + public int $id1, + #[Id] + #[ManyToOne(targetEntity: OtherParentClass::class, cascade: ['persist'])] + #[JoinColumn(name: 'other_parent_id', referencedColumnName: 'id')] + public OtherParentClass $otherParent, + ) { + $this->parents = new ArrayCollection(); } } diff --git a/tests/Tests/Models/ManyToManyPersister/OtherParentClass.php b/tests/Tests/Models/ManyToManyPersister/OtherParentClass.php index 68e311e5423..30c7392a844 100644 --- a/tests/Tests/Models/ManyToManyPersister/OtherParentClass.php +++ b/tests/Tests/Models/ManyToManyPersister/OtherParentClass.php @@ -9,21 +9,14 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="manytomanypersister_other_parent") - */ +#[Table(name: 'manytomanypersister_other_parent')] +#[Entity] class OtherParentClass { - /** - * @Id - * @Column(name="id", type="integer") - * @var int - */ - public $id; - - public function __construct(int $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(name: 'id', type: 'integer')] + public int $id, + ) { } } diff --git a/tests/Tests/Models/ManyToManyPersister/ParentClass.php b/tests/Tests/Models/ManyToManyPersister/ParentClass.php index e69a8cfe0a0..bcba7da0fdb 100644 --- a/tests/Tests/Models/ManyToManyPersister/ParentClass.php +++ b/tests/Tests/Models/ManyToManyPersister/ParentClass.php @@ -12,29 +12,22 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="manytomanypersister_parent") - */ +#[Table(name: 'manytomanypersister_parent')] +#[Entity] class ParentClass { /** - * @Id - * @Column(name="id", type="integer") - * @var int - */ - public $id; - - /** - * @ManyToMany(targetEntity=ChildClass::class, mappedBy="parents", orphanRemoval=true, cascade={"persist"}) * @var Collection|ChildClass[] * @phpstan-var Collection */ + #[ManyToMany(targetEntity: ChildClass::class, mappedBy: 'parents', orphanRemoval: true, cascade: ['persist'])] public $children; - public function __construct(int $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(name: 'id', type: 'integer')] + public int $id, + ) { $this->children = new ArrayCollection(); } } diff --git a/tests/Tests/Models/MixedToOneIdentity/CompositeToOneKeyState.php b/tests/Tests/Models/MixedToOneIdentity/CompositeToOneKeyState.php index 4e092d4b5d6..f3f0d9a1289 100644 --- a/tests/Tests/Models/MixedToOneIdentity/CompositeToOneKeyState.php +++ b/tests/Tests/Models/MixedToOneIdentity/CompositeToOneKeyState.php @@ -11,22 +11,18 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class CompositeToOneKeyState { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $state; - /** - * @var Country - * @Id - * @ManyToOne(targetEntity="Country", cascade={"MERGE"}) - * @JoinColumn(referencedColumnName="country") - */ + /** @var Country */ + #[Id] + #[ManyToOne(targetEntity: 'Country', cascade: [])] + #[JoinColumn(referencedColumnName: 'country')] public $country; } diff --git a/tests/Tests/Models/MixedToOneIdentity/Country.php b/tests/Tests/Models/MixedToOneIdentity/Country.php index 0f699fee83e..5beb998d839 100644 --- a/tests/Tests/Models/MixedToOneIdentity/Country.php +++ b/tests/Tests/Models/MixedToOneIdentity/Country.php @@ -9,14 +9,12 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class Country { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $country; } diff --git a/tests/Tests/Models/Navigation/NavCountry.php b/tests/Tests/Models/Navigation/NavCountry.php index 276d08e17b2..016c2d69f09 100644 --- a/tests/Tests/Models/Navigation/NavCountry.php +++ b/tests/Tests/Models/Navigation/NavCountry.php @@ -12,35 +12,23 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="navigation_countries") - */ +#[Table(name: 'navigation_countries')] +#[Entity] class NavCountry { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="NavPointOfInterest", mappedBy="country") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'NavPointOfInterest', mappedBy: 'country')] private $pois; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { } public function getId(): int diff --git a/tests/Tests/Models/Navigation/NavPhotos.php b/tests/Tests/Models/Navigation/NavPhotos.php index f8bc4f9186b..34203eb2e5e 100644 --- a/tests/Tests/Models/Navigation/NavPhotos.php +++ b/tests/Tests/Models/Navigation/NavPhotos.php @@ -9,44 +9,26 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="navigation_photos") - */ +#[Table(name: 'navigation_photos')] +#[Entity] class NavPhotos { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var NavPointOfInterest - * @ManyToOne(targetEntity="NavPointOfInterest") - * @JoinColumns({ - * @JoinColumn(name="poi_long", referencedColumnName="nav_long"), - * @JoinColumn(name="poi_lat", referencedColumnName="nav_lat") - * }) - */ - private $poi; - - /** - * @var string - * @Column(type="string", length=255, name="file_name") - */ - private $file; - - public function __construct(NavPointOfInterest $poi, string $file) - { - $this->poi = $poi; - $this->file = $file; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + public function __construct( + #[JoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')] + #[JoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')] + #[ManyToOne(targetEntity: 'NavPointOfInterest')] + private NavPointOfInterest $poi, + #[Column(type: 'string', length: 255, name: 'file_name')] + private string $file, + ) { } public function getId(): int diff --git a/tests/Tests/Models/Navigation/NavPointOfInterest.php b/tests/Tests/Models/Navigation/NavPointOfInterest.php index d2ff469cede..7bbec91fb13 100644 --- a/tests/Tests/Models/Navigation/NavPointOfInterest.php +++ b/tests/Tests/Models/Navigation/NavPointOfInterest.php @@ -9,63 +9,43 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="navigation_pois") - */ +#[Table(name: 'navigation_pois')] +#[Entity] class NavPointOfInterest { - /** - * @var int - * @Id - * @Column(type="integer", name="nav_long") - */ - private $long; + #[Id] + #[Column(type: 'integer', name: 'nav_long')] + private int $long; - /** - * @var int - * @Id - * @Column(type="integer", name="nav_lat") - */ - private $lat; + #[Id] + #[Column(type: 'integer', name: 'nav_lat')] + private int $lat; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - /** - * @var NavCountry - * @ManyToOne(targetEntity="NavCountry", inversedBy="pois") - */ - private $country; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="NavUser", cascade={"persist"}) - * @JoinTable(name="navigation_pois_visitors", - * inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * joinColumns={ - * @JoinColumn(name="poi_long", referencedColumnName="nav_long"), - * @JoinColumn(name="poi_lat", referencedColumnName="nav_lat") - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'navigation_pois_visitors')] + #[JoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')] + #[JoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')] + #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'NavUser', cascade: ['persist'])] private $visitors; - public function __construct(int $lat, int $long, string $name, NavCountry $country) - { + public function __construct( + int $lat, + int $long, + #[Column(type: 'string', length: 255)] + private string $name, + #[ManyToOne(targetEntity: 'NavCountry', inversedBy: 'pois')] + private NavCountry $country, + ) { $this->lat = $lat; $this->long = $long; - $this->name = $name; - $this->country = $country; $this->visitors = new ArrayCollection(); } diff --git a/tests/Tests/Models/Navigation/NavTour.php b/tests/Tests/Models/Navigation/NavTour.php index 9aa3d42fae6..5e42f509b78 100644 --- a/tests/Tests/Models/Navigation/NavTour.php +++ b/tests/Tests/Models/Navigation/NavTour.php @@ -10,47 +10,33 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="navigation_tours") - */ +#[Table(name: 'navigation_tours')] +#[Entity] class NavTour { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - /** - * @var Collection - * @ManyToMany(targetEntity="NavPointOfInterest") - * @JoinTable(name="navigation_tour_pois", - * joinColumns={@JoinColumn(name="tour_id", referencedColumnName="id")}, - * inverseJoinColumns={ - * @JoinColumn(name="poi_long", referencedColumnName="nav_long"), - * @JoinColumn(name="poi_lat", referencedColumnName="nav_lat") - * } - * ) - */ - private $pois; - - public function __construct(string $name) - { - $this->name = $name; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + /** @var Collection */ + #[JoinTable(name: 'navigation_tour_pois')] + #[JoinColumn(name: 'tour_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'poi_long', referencedColumnName: 'nav_long')] + #[InverseJoinColumn(name: 'poi_lat', referencedColumnName: 'nav_lat')] + #[ManyToMany(targetEntity: 'NavPointOfInterest')] + private Collection $pois; + + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { $this->pois = new ArrayCollection(); } diff --git a/tests/Tests/Models/Navigation/NavUser.php b/tests/Tests/Models/Navigation/NavUser.php index 0b179ba6876..d87555508ac 100644 --- a/tests/Tests/Models/Navigation/NavUser.php +++ b/tests/Tests/Models/Navigation/NavUser.php @@ -10,28 +10,18 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="navigation_users") - */ +#[Table(name: 'navigation_users')] +#[Entity] class NavUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { } } diff --git a/tests/Tests/Models/NonPublicSchemaJoins/User.php b/tests/Tests/Models/NonPublicSchemaJoins/User.php index a808127260f..d86470bc031 100644 --- a/tests/Tests/Models/NonPublicSchemaJoins/User.php +++ b/tests/Tests/Models/NonPublicSchemaJoins/User.php @@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -14,34 +15,24 @@ /** * Doctrine\Tests\Models\NonPublicSchemaJoins\User - * - * @Entity - * @Table(name="readers.user") */ +#[Table(name: 'readers.user')] +#[Entity] class User { - /** - * @var int - * @Column(type="integer") - * @Id - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] public $id; - /** - * @ManyToMany(targetEntity="Doctrine\Tests\Models\NonPublicSchemaJoins\User", inversedBy="authors") - * @JoinTable( - * name="author_reader", - * schema="readers", - * joinColumns={@JoinColumn(name="author_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="reader_id", referencedColumnName="id")} - * ) - * @var User[] - */ + /** @var User[] */ + #[JoinTable(name: 'author_reader', schema: 'readers')] + #[JoinColumn(name: 'author_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'reader_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'Doctrine\Tests\Models\NonPublicSchemaJoins\User', inversedBy: 'authors')] public $readers; - /** - * @ManyToMany(targetEntity="Doctrine\Tests\Models\NonPublicSchemaJoins\User", mappedBy="readers") - * @var User[] - */ + /** @var User[] */ + #[ManyToMany(targetEntity: 'Doctrine\Tests\Models\NonPublicSchemaJoins\User', mappedBy: 'readers')] public $authors; } diff --git a/tests/Tests/Models/NullDefault/NullDefaultColumn.php b/tests/Tests/Models/NullDefault/NullDefaultColumn.php index 308adccba86..9d75c7609dc 100644 --- a/tests/Tests/Models/NullDefault/NullDefaultColumn.php +++ b/tests/Tests/Models/NullDefault/NullDefaultColumn.php @@ -9,20 +9,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class NullDefaultColumn { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var mixed - * @Column(options={"default":NULL}) - */ + /** @var mixed */ + #[Column(options: ['default' => null])] public $nullDefault; } diff --git a/tests/Tests/Models/OneToOneInverseSideLoad/InverseSide.php b/tests/Tests/Models/OneToOneInverseSideLoad/InverseSide.php index 69d988f3492..e13bc077dd0 100644 --- a/tests/Tests/Models/OneToOneInverseSideLoad/InverseSide.php +++ b/tests/Tests/Models/OneToOneInverseSideLoad/InverseSide.php @@ -11,23 +11,17 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity() - * @Table(name="one_to_one_inverse_side_load_inverse") - */ +#[Table(name: 'one_to_one_inverse_side_load_inverse')] +#[Entity] class InverseSide { - /** - * @var string - * @Id() - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $id; - /** - * @var OwningSide - * @OneToOne(targetEntity=OwningSide::class, mappedBy="inverse") - */ + /** @var OwningSide */ + #[OneToOne(targetEntity: OwningSide::class, mappedBy: 'inverse')] public $owning; } diff --git a/tests/Tests/Models/OneToOneInverseSideLoad/OwningSide.php b/tests/Tests/Models/OneToOneInverseSideLoad/OwningSide.php index f9099165f06..fc58b6b8b05 100644 --- a/tests/Tests/Models/OneToOneInverseSideLoad/OwningSide.php +++ b/tests/Tests/Models/OneToOneInverseSideLoad/OwningSide.php @@ -12,26 +12,22 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity() - * @Table(name="one_to_one_inverse_side_load_owning") - */ +#[Table(name: 'one_to_one_inverse_side_load_owning')] +#[Entity] class OwningSide { - /** - * @var string - * @Id() - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $id; /** * Owning side * * @var InverseSide - * @OneToOne(targetEntity=InverseSide::class, inversedBy="owning") - * @JoinColumn(nullable=false, name="inverse") */ + #[OneToOne(targetEntity: InverseSide::class, inversedBy: 'owning')] + #[JoinColumn(nullable: false, name: 'inverse')] public $inverse; } diff --git a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSide.php b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSide.php index 0dcb9a93a1b..2bb15b8cb29 100644 --- a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSide.php +++ b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSide.php @@ -10,25 +10,16 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity() - * @Table(name="one_to_one_inverse_side_assoc_id_load_inverse") - */ +#[Entity] +#[Table(name: 'one_to_one_inverse_side_assoc_id_load_inverse')] class InverseSide { - /** - * Associative id (owning identifier) - * - * @var InverseSideIdTarget - * @Id() - * @OneToOne(targetEntity=InverseSideIdTarget::class, inversedBy="inverseSide") - * @JoinColumn(nullable=false, name="associativeId") - */ - public $associativeId; + /** Associative id (owning identifier) */ + #[Id] + #[OneToOne(targetEntity: InverseSideIdTarget::class, inversedBy: 'inverseSide')] + #[JoinColumn(nullable: false, name: 'associativeId')] + public InverseSideIdTarget $associativeId; - /** - * @var OwningSide - * @OneToOne(targetEntity=OwningSide::class, mappedBy="inverse") - */ - public $owning; + #[OneToOne(targetEntity: OwningSide::class, mappedBy: 'inverse')] + public OwningSide $owning; } diff --git a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSideIdTarget.php b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSideIdTarget.php index 5746abc8c76..0be6262daf5 100644 --- a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSideIdTarget.php +++ b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/InverseSideIdTarget.php @@ -11,23 +11,15 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity() - * @Table(name="one_to_one_inverse_side_assoc_id_load_inverse_id_target") - */ +#[Entity] +#[Table(name: 'one_to_one_inverse_side_assoc_id_load_inverse_id_target')] class InverseSideIdTarget { - /** - * @var string - * @Id() - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - public $id; + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + public string $id; - /** - * @var InverseSide - * @OneToOne(targetEntity=InverseSide::class, mappedBy="associativeId") - */ - public $inverseSide; + #[OneToOne(targetEntity: InverseSide::class, mappedBy: 'associativeId')] + public InverseSide $inverseSide; } diff --git a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/OwningSide.php b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/OwningSide.php index 0b3fc7861a3..be1fc5e5db5 100644 --- a/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/OwningSide.php +++ b/tests/Tests/Models/OneToOneInverseSideWithAssociativeIdLoad/OwningSide.php @@ -12,26 +12,17 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity() - * @Table(name="one_to_one_inverse_side_assoc_id_load_owning") - */ +#[Entity] +#[Table(name: 'one_to_one_inverse_side_assoc_id_load_owning')] class OwningSide { - /** - * @var string - * @Id() - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - public $id; + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + public string $id; - /** - * Owning side - * - * @var InverseSide - * @OneToOne(targetEntity=InverseSide::class, inversedBy="owning") - * @JoinColumn(name="inverse", referencedColumnName="associativeId") - */ - public $inverse; + /** Owning side */ + #[OneToOne(targetEntity: InverseSide::class, inversedBy: 'owning')] + #[JoinColumn(name: 'inverse', referencedColumnName: 'associativeId')] + public InverseSide $inverse; } diff --git a/tests/Tests/Models/OneToOneSingleTableInheritance/Cat.php b/tests/Tests/Models/OneToOneSingleTableInheritance/Cat.php index adc0a218a05..f63f165c84f 100644 --- a/tests/Tests/Models/OneToOneSingleTableInheritance/Cat.php +++ b/tests/Tests/Models/OneToOneSingleTableInheritance/Cat.php @@ -7,12 +7,10 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\OneToOne; -/** @Entity */ +#[Entity] class Cat extends Pet { - /** - * @var LitterBox - * @OneToOne(targetEntity="LitterBox") - */ + /** @var LitterBox */ + #[OneToOne(targetEntity: 'LitterBox')] public $litterBox; } diff --git a/tests/Tests/Models/OneToOneSingleTableInheritance/LitterBox.php b/tests/Tests/Models/OneToOneSingleTableInheritance/LitterBox.php index e00b3176d05..c90cbe43d85 100644 --- a/tests/Tests/Models/OneToOneSingleTableInheritance/LitterBox.php +++ b/tests/Tests/Models/OneToOneSingleTableInheritance/LitterBox.php @@ -10,17 +10,13 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="one_to_one_single_table_inheritance_litter_box") - */ +#[Table(name: 'one_to_one_single_table_inheritance_litter_box')] +#[Entity] class LitterBox { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/Models/OneToOneSingleTableInheritance/Pet.php b/tests/Tests/Models/OneToOneSingleTableInheritance/Pet.php index 597706e5c99..f9bf67d0d17 100644 --- a/tests/Tests/Models/OneToOneSingleTableInheritance/Pet.php +++ b/tests/Tests/Models/OneToOneSingleTableInheritance/Pet.php @@ -12,19 +12,15 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="one_to_one_single_table_inheritance_pet") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"cat" = "Cat"}) - */ +#[Table(name: 'one_to_one_single_table_inheritance_pet')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['cat' => 'Cat'])] abstract class Pet { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/Models/OrnementalOrphanRemoval/Person.php b/tests/Tests/Models/OrnementalOrphanRemoval/Person.php deleted file mode 100644 index fcf7cc7891a..00000000000 --- a/tests/Tests/Models/OrnementalOrphanRemoval/Person.php +++ /dev/null @@ -1,11 +0,0 @@ - - * @OneToMany(targetEntity="Department", mappedBy="company", cascade={"persist"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Department', mappedBy: 'company', cascade: ['persist'], orphanRemoval: true)] public $departments; } diff --git a/tests/Tests/Models/Pagination/Department.php b/tests/Tests/Models/Pagination/Department.php index d3139dd8c4c..9d906bd9970 100644 --- a/tests/Tests/Models/Pagination/Department.php +++ b/tests/Tests/Models/Pagination/Department.php @@ -13,29 +13,22 @@ /** * Department - * - * @Entity - * @Table(name="pagination_department") */ +#[Table(name: 'pagination_department')] +#[Entity] class Department { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var Company - * @ManyToOne(targetEntity="Company", inversedBy="departments", cascade={"persist"}) - */ + /** @var Company */ + #[ManyToOne(targetEntity: 'Company', inversedBy: 'departments', cascade: ['persist'])] public $company; } diff --git a/tests/Tests/Models/Pagination/Logo.php b/tests/Tests/Models/Pagination/Logo.php index 14358c09aee..413070c5d56 100644 --- a/tests/Tests/Models/Pagination/Logo.php +++ b/tests/Tests/Models/Pagination/Logo.php @@ -14,42 +14,31 @@ /** * Logo - * - * @Entity - * @Table(name="pagination_logo") */ +#[Table(name: 'pagination_logo')] +#[Entity] class Logo { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $image; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $imageHeight; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $imageWidth; - /** - * @var Company - * @OneToOne(targetEntity="Company", inversedBy="logo", cascade={"persist"}) - * @JoinColumn(name="company_id") - */ + /** @var Company */ + #[OneToOne(targetEntity: 'Company', inversedBy: 'logo', cascade: ['persist'])] + #[JoinColumn(name: 'company_id')] public $company; } diff --git a/tests/Tests/Models/Pagination/User.php b/tests/Tests/Models/Pagination/User.php index 450c4728ad2..3600c857e4a 100644 --- a/tests/Tests/Models/Pagination/User.php +++ b/tests/Tests/Models/Pagination/User.php @@ -13,26 +13,19 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="pagination_user") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string", length=255) - * @DiscriminatorMap({"user1"="User1"}) - */ +#[Table(name: 'pagination_user')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string', length: 255)] +#[DiscriminatorMap(['user1' => 'User1'])] abstract class User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; } diff --git a/tests/Tests/Models/Pagination/User1.php b/tests/Tests/Models/Pagination/User1.php index e05eb9342f1..2d73598d147 100644 --- a/tests/Tests/Models/Pagination/User1.php +++ b/tests/Tests/Models/Pagination/User1.php @@ -7,12 +7,10 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** @Entity() */ +#[Entity] class User1 extends User { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $email; } diff --git a/tests/Tests/Models/PersistentObject/PersistentCollectionContent.php b/tests/Tests/Models/PersistentObject/PersistentCollectionContent.php index 1ced97b1afa..2c960587d25 100644 --- a/tests/Tests/Models/PersistentObject/PersistentCollectionContent.php +++ b/tests/Tests/Models/PersistentObject/PersistentCollectionContent.php @@ -10,14 +10,12 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class PersistentCollectionContent extends PersistentObject { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; } diff --git a/tests/Tests/Models/PersistentObject/PersistentCollectionHolder.php b/tests/Tests/Models/PersistentObject/PersistentCollectionHolder.php index 9ac2456c1d6..c155e9be03f 100644 --- a/tests/Tests/Models/PersistentObject/PersistentCollectionHolder.php +++ b/tests/Tests/Models/PersistentObject/PersistentCollectionHolder.php @@ -13,21 +13,17 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; -/** @Entity */ +#[Entity] class PersistentCollectionHolder extends PersistentObject { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var Collection - * @ManyToMany(targetEntity="PersistentCollectionContent", cascade={"all"}, fetch="EXTRA_LAZY") - */ + /** @var Collection */ + #[ManyToMany(targetEntity: 'PersistentCollectionContent', cascade: ['all'], fetch: 'EXTRA_LAZY')] protected $collection; public function __construct() @@ -40,13 +36,11 @@ public function addElement(PersistentCollectionContent $element): void $this->collection->add($element); } - /** @return Collection */ public function getCollection(): Collection { return clone $this->collection; } - /** @return Collection */ public function getRawCollection(): Collection { return $this->collection; diff --git a/tests/Tests/Models/PersistentObject/PersistentEntity.php b/tests/Tests/Models/PersistentObject/PersistentEntity.php index 25813bf54be..ed55f4bb7a0 100644 --- a/tests/Tests/Models/PersistentObject/PersistentEntity.php +++ b/tests/Tests/Models/PersistentObject/PersistentEntity.php @@ -11,26 +11,20 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class PersistentEntity extends PersistentObject { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @Column(type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 255)] protected $name; - /** - * @ManyToOne(targetEntity="PersistentEntity") - * @var PersistentEntity - */ + /** @var PersistentEntity */ + #[ManyToOne(targetEntity: 'PersistentEntity')] protected $parent; } diff --git a/tests/Tests/Models/Project/Project.php b/tests/Tests/Models/Project/Project.php index fc64b43561a..afc5545a02c 100644 --- a/tests/Tests/Models/Project/Project.php +++ b/tests/Tests/Models/Project/Project.php @@ -6,14 +6,10 @@ class Project { - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $name; public function __construct(string $id, string $name) diff --git a/tests/Tests/Models/Project/ProjectId.php b/tests/Tests/Models/Project/ProjectId.php index cedc0e5bd9d..872edb8c927 100644 --- a/tests/Tests/Models/Project/ProjectId.php +++ b/tests/Tests/Models/Project/ProjectId.php @@ -6,9 +6,7 @@ class ProjectId { - /** - * @var string - */ + /** @var string */ private $id; public function __construct(string $id) diff --git a/tests/Tests/Models/Project/ProjectInvalidMapping.php b/tests/Tests/Models/Project/ProjectInvalidMapping.php index ceecd48f754..a3e56f34b9b 100644 --- a/tests/Tests/Models/Project/ProjectInvalidMapping.php +++ b/tests/Tests/Models/Project/ProjectInvalidMapping.php @@ -6,14 +6,10 @@ class ProjectInvalidMapping { - /** - * @var string - */ + /** @var string */ private $id; - /** - * @var string - */ + /** @var string */ private $name; public function __construct(string $id, string $name) diff --git a/tests/Tests/Models/Project/ProjectName.php b/tests/Tests/Models/Project/ProjectName.php index ea2177996d6..8ddd5ff4ca9 100644 --- a/tests/Tests/Models/Project/ProjectName.php +++ b/tests/Tests/Models/Project/ProjectName.php @@ -6,9 +6,7 @@ final class ProjectName { - /** - * @var string - */ + /** @var string */ private $name; public function __construct(string $name) diff --git a/tests/Tests/Models/Quote/Address.php b/tests/Tests/Models/Quote/Address.php index b5f3fd2d95c..bffefdd3b09 100644 --- a/tests/Tests/Models/Quote/Address.php +++ b/tests/Tests/Models/Quote/Address.php @@ -15,34 +15,26 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`quote-address`") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string", length=255) - * @DiscriminatorMap({"simple" = Address::class, "full" = FullAddress::class}) - */ +#[Table(name: '`quote-address`')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string', length: 255)] +#[DiscriminatorMap(['simple' => Address::class, 'full' => FullAddress::class])] class Address { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="`address-id`") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: '`address-id`')] public $id; - /** - * @var string - * @Column(name="`address-zip`") - */ + /** @var string */ + #[Column(name: '`address-zip`')] public $zip; - /** - * @var User - * @OneToOne(targetEntity="User", inversedBy="address") - * @JoinColumn(name="`user-id`", referencedColumnName="`user-id`") - */ + /** @var User */ + #[OneToOne(targetEntity: 'User', inversedBy: 'address')] + #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')] public $user; public function setUser(User $user): void diff --git a/tests/Tests/Models/Quote/City.php b/tests/Tests/Models/Quote/City.php index 79f48d3f63a..07b8b9d897c 100644 --- a/tests/Tests/Models/Quote/City.php +++ b/tests/Tests/Models/Quote/City.php @@ -10,28 +10,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`quote-city`") - */ +#[Table(name: '`quote-city`')] +#[Entity] class City { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="`city-id`") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: '`city-id`')] public $id; - /** - * @var string - * @Column(name="`city-name`") - */ - public $name; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(name: '`city-name`')] + public string $name, + ) { } } diff --git a/tests/Tests/Models/Quote/FullAddress.php b/tests/Tests/Models/Quote/FullAddress.php index 7e84abd7625..5e13a2734e6 100644 --- a/tests/Tests/Models/Quote/FullAddress.php +++ b/tests/Tests/Models/Quote/FullAddress.php @@ -8,13 +8,11 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; -/** @Entity */ +#[Entity] class FullAddress extends Address { - /** - * @var City - * @OneToOne(targetEntity=City::class, cascade={"persist"}) - * @JoinColumn(name="`city-id`", referencedColumnName="`city-id`") - */ + /** @var City */ + #[OneToOne(targetEntity: City::class, cascade: ['persist'])] + #[JoinColumn(name: '`city-id`', referencedColumnName: '`city-id`')] public $city; } diff --git a/tests/Tests/Models/Quote/Group.php b/tests/Tests/Models/Quote/Group.php index 956a47ffd41..c57108b76f7 100644 --- a/tests/Tests/Models/Quote/Group.php +++ b/tests/Tests/Models/Quote/Group.php @@ -14,42 +14,26 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`quote-group`") - */ +#[Table(name: '`quote-group`')] +#[Entity] class Group { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="`group-id`") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: '`group-id`')] public $id; - /** - * @var string|null - * @Column(name="`group-name`") - */ - public $name; - - /** - * @var Group|null - * @ManyToOne(targetEntity="Group", cascade={"persist"}) - * @JoinColumn(name="`parent-id`", referencedColumnName="`group-id`") - */ - public $parent; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="User", mappedBy="groups") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'User', mappedBy: 'groups')] public $users; - public function __construct(?string $name = null, ?Group $parent = null) - { - $this->name = $name; - $this->parent = $parent; + public function __construct( + #[Column(name: '`group-name`')] + public string|null $name = null, + #[ManyToOne(targetEntity: 'Group', cascade: ['persist'])] + #[JoinColumn(name: '`parent-id`', referencedColumnName: '`group-id`')] + public Group|null $parent = null, + ) { } } diff --git a/tests/Tests/Models/Quote/NumericEntity.php b/tests/Tests/Models/Quote/NumericEntity.php index 86bdf8d14fb..bb6aadab937 100644 --- a/tests/Tests/Models/Quote/NumericEntity.php +++ b/tests/Tests/Models/Quote/NumericEntity.php @@ -10,28 +10,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="table") - */ +#[Table(name: 'table')] +#[Entity] class NumericEntity { - /** - * @var int - * @Id - * @Column(type="integer", name="`1:1`") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer', name: '`1:1`')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255, name="`2:2`") - */ - public $value; - - public function __construct(string $value) - { - $this->value = $value; + public function __construct( + #[Column(type: 'string', length: 255, name: '`2:2`')] + public string $value, + ) { } } diff --git a/tests/Tests/Models/Quote/Phone.php b/tests/Tests/Models/Quote/Phone.php index 0102a01e04c..daf159182c5 100644 --- a/tests/Tests/Models/Quote/Phone.php +++ b/tests/Tests/Models/Quote/Phone.php @@ -11,23 +11,17 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`quote-phone`") - */ +#[Table(name: '`quote-phone`')] +#[Entity] class Phone { - /** - * @var string - * @Id - * @Column(name="`phone-number`") - */ + /** @var string */ + #[Id] + #[Column(name: '`phone-number`')] public $number; - /** - * @var User - * @ManyToOne(targetEntity="User", inversedBy="phones") - * @JoinColumn(name="`user-id`", referencedColumnName="`user-id`") - */ + /** @var User */ + #[ManyToOne(targetEntity: 'User', inversedBy: 'phones')] + #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')] public $user; } diff --git a/tests/Tests/Models/Quote/User.php b/tests/Tests/Models/Quote/User.php index 2724f96818b..5a31d9b1303 100644 --- a/tests/Tests/Models/Quote/User.php +++ b/tests/Tests/Models/Quote/User.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -17,56 +18,33 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="`quote-user`") - */ +#[Table(name: '`quote-user`')] +#[Entity] class User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="`user-id`") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: '`user-id`')] public $id; - /** - * @var string - * @Column(type="string", length=255, name="`user-name`") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: '`user-name`')] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Phone", mappedBy="user", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Phone', mappedBy: 'user', cascade: ['persist'])] public $phones; - /** - * @var Address - * @OneToOne(targetEntity="Address", mappedBy="user", cascade={"persist"}, fetch="EAGER") - */ + /** @var Address */ + #[OneToOne(targetEntity: 'Address', mappedBy: 'user', cascade: ['persist'], fetch: 'EAGER')] public $address; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Group", inversedBy="users", cascade={"all"}, fetch="EXTRA_LAZY") - * @JoinTable(name="`quote-users-groups`", - * joinColumns={ - * @JoinColumn( - * name="`user-id`", - * referencedColumnName="`user-id`" - * ) - * }, - * inverseJoinColumns={ - * @JoinColumn( - * name="`group-id`", - * referencedColumnName="`group-id`" - * ) - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: '`quote-users-groups`')] + #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`')] + #[InverseJoinColumn(name: '`group-id`', referencedColumnName: '`group-id`')] + #[ManyToMany(targetEntity: 'Group', inversedBy: 'users', cascade: ['all'], fetch: 'EXTRA_LAZY')] public $groups; public function __construct() @@ -81,7 +59,7 @@ public function getPhones(): Collection return $this->phones; } - public function getAddress(): ?Address + public function getAddress(): Address|null { return $this->address; } diff --git a/tests/Tests/Models/Reflection/AbstractEmbeddable.php b/tests/Tests/Models/Reflection/AbstractEmbeddable.php index 670baa00086..2240c6fcb9f 100644 --- a/tests/Tests/Models/Reflection/AbstractEmbeddable.php +++ b/tests/Tests/Models/Reflection/AbstractEmbeddable.php @@ -9,6 +9,5 @@ */ abstract class AbstractEmbeddable { - /** @var string */ - private $propertyInAbstractClass; + private string $propertyInAbstractClass; } diff --git a/tests/Tests/Models/Reflection/ClassWithMixedProperties.php b/tests/Tests/Models/Reflection/ClassWithMixedProperties.php index 346f095a43e..307eeba22e6 100644 --- a/tests/Tests/Models/Reflection/ClassWithMixedProperties.php +++ b/tests/Tests/Models/Reflection/ClassWithMixedProperties.php @@ -15,9 +15,7 @@ class ClassWithMixedProperties extends ParentClass /** @var string */ protected $protectedProperty = 'protectedProperty'; - /** @var string */ - private $privateProperty = 'privateProperty'; + private string $privateProperty = 'privateProperty'; - /** @var string */ - private $privatePropertyOverride = 'privatePropertyOverride'; + private string $privatePropertyOverride = 'privatePropertyOverride'; } diff --git a/tests/Tests/Models/Reflection/ConcreteEmbeddable.php b/tests/Tests/Models/Reflection/ConcreteEmbeddable.php index 76c0ceea7f9..63c38dbefa8 100644 --- a/tests/Tests/Models/Reflection/ConcreteEmbeddable.php +++ b/tests/Tests/Models/Reflection/ConcreteEmbeddable.php @@ -9,6 +9,5 @@ */ class ConcreteEmbeddable extends AbstractEmbeddable { - /** @var string */ - private $propertyInConcreteClass; + private string $propertyInConcreteClass; } diff --git a/tests/Tests/Models/Reflection/ParentClass.php b/tests/Tests/Models/Reflection/ParentClass.php index da95ee61503..7bd063b6058 100644 --- a/tests/Tests/Models/Reflection/ParentClass.php +++ b/tests/Tests/Models/Reflection/ParentClass.php @@ -6,6 +6,5 @@ class ParentClass { - /** @var string */ - private $privatePropertyOverride = 'privatePropertyOverride'; + private string $privatePropertyOverride = 'privatePropertyOverride'; } diff --git a/tests/Tests/Models/Routing/RoutingLeg.php b/tests/Tests/Models/Routing/RoutingLeg.php index ee8808c2eda..c02d35b4e41 100644 --- a/tests/Tests/Models/Routing/RoutingLeg.php +++ b/tests/Tests/Models/Routing/RoutingLeg.php @@ -11,40 +11,30 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class RoutingLeg { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var RoutingLocation - * @ManyToOne(targetEntity="RoutingLocation") - * @JoinColumn(name="from_id", referencedColumnName="id") - */ + /** @var RoutingLocation */ + #[ManyToOne(targetEntity: 'RoutingLocation')] + #[JoinColumn(name: 'from_id', referencedColumnName: 'id')] public $fromLocation; - /** - * @var RoutingLocation - * @ManyToOne(targetEntity="RoutingLocation") - * @JoinColumn(name="to_id", referencedColumnName="id") - */ + /** @var RoutingLocation */ + #[ManyToOne(targetEntity: 'RoutingLocation')] + #[JoinColumn(name: 'to_id', referencedColumnName: 'id')] public $toLocation; - /** - * @var DateTime - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Column(type: 'datetime')] public $departureDate; - /** - * @var DateTime - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Column(type: 'datetime')] public $arrivalDate; } diff --git a/tests/Tests/Models/Routing/RoutingLocation.php b/tests/Tests/Models/Routing/RoutingLocation.php index 38ff74ce4c4..8c70dbfb612 100644 --- a/tests/Tests/Models/Routing/RoutingLocation.php +++ b/tests/Tests/Models/Routing/RoutingLocation.php @@ -9,21 +9,17 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** @Entity */ +#[Entity] class RoutingLocation { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; public function getName(): string diff --git a/tests/Tests/Models/Routing/RoutingRoute.php b/tests/Tests/Models/Routing/RoutingRoute.php index e95a54da6ba..8720d7016ed 100644 --- a/tests/Tests/Models/Routing/RoutingRoute.php +++ b/tests/Tests/Models/Routing/RoutingRoute.php @@ -10,39 +10,33 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OrderBy; -/** @Entity */ +#[Entity] class RoutingRoute { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var Collection - * @ManyToMany(targetEntity="RoutingLeg", cascade={"all"}) - * @JoinTable(name="RoutingRouteLegs", - * joinColumns={@JoinColumn(name="route_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="leg_id", referencedColumnName="id", unique=true)} - * ) - * @OrderBy({"departureDate" = "ASC"}) - */ + /** @var Collection */ + #[JoinTable(name: 'RoutingRouteLegs')] + #[JoinColumn(name: 'route_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'leg_id', referencedColumnName: 'id', unique: true)] + #[ManyToMany(targetEntity: 'RoutingLeg', cascade: ['all'])] + #[OrderBy(['departureDate' => 'ASC'])] public $legs; - /** - * @var Collection - * @OneToMany(targetEntity="RoutingRouteBooking", mappedBy="route") - * @OrderBy({"passengerName" = "ASC"}) - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'RoutingRouteBooking', mappedBy: 'route')] + #[OrderBy(['passengerName' => 'ASC'])] public $bookings = []; public function __construct() diff --git a/tests/Tests/Models/Routing/RoutingRouteBooking.php b/tests/Tests/Models/Routing/RoutingRouteBooking.php index 4d769bc5a9f..12d185b8517 100644 --- a/tests/Tests/Models/Routing/RoutingRouteBooking.php +++ b/tests/Tests/Models/Routing/RoutingRouteBooking.php @@ -11,28 +11,22 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; -/** @Entity */ +#[Entity] class RoutingRouteBooking { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var RoutingRoute - * @ManyToOne(targetEntity="RoutingRoute", inversedBy="bookings") - * @JoinColumn(name="route_id", referencedColumnName="id") - */ + /** @var RoutingRoute */ + #[ManyToOne(targetEntity: 'RoutingRoute', inversedBy: 'bookings')] + #[JoinColumn(name: 'route_id', referencedColumnName: 'id')] public $route; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $passengerName = null; public function getPassengerName(): string diff --git a/tests/Tests/Models/StockExchange/Bond.php b/tests/Tests/Models/StockExchange/Bond.php index 601aa9348eb..d913aa431bb 100644 --- a/tests/Tests/Models/StockExchange/Bond.php +++ b/tests/Tests/Models/StockExchange/Bond.php @@ -15,36 +15,25 @@ /** * Bonds have many stocks. This uses a many to many association and fails to model how many of a * particular stock a bond has. But i Need a many-to-many association, so please bear with my modelling skills ;) - * - * @Entity - * @Table(name="exchange_bonds") */ +#[Table(name: 'exchange_bonds')] +#[Entity] class Bond { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ - private $id; - - /** - * @Column(type="string", length=255) - * @var string - */ - private $name; - - /** - * @ManyToMany(targetEntity="Stock", indexBy="symbol") - * @JoinTable(name="exchange_bonds_stocks") - * @var Stock[] - */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; + + /** @var Stock[] */ + #[JoinTable(name: 'exchange_bonds_stocks')] + #[ManyToMany(targetEntity: 'Stock', indexBy: 'symbol')] public $stocks; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { } public function getId(): int diff --git a/tests/Tests/Models/StockExchange/Market.php b/tests/Tests/Models/StockExchange/Market.php index 6753e99874f..ed0902d4adc 100644 --- a/tests/Tests/Models/StockExchange/Market.php +++ b/tests/Tests/Models/StockExchange/Market.php @@ -12,35 +12,23 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="exchange_markets") - */ +#[Table(name: 'exchange_markets')] +#[Entity] class Market { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @Column(type="string", length=255) - * @var string - */ - private $name; - - /** - * @OneToMany(targetEntity="Stock", mappedBy="market", indexBy="symbol") - * @phpstan-var ArrayCollection - */ + /** @phpstan-var ArrayCollection */ + #[OneToMany(targetEntity: 'Stock', mappedBy: 'market', indexBy: 'symbol')] public $stocks; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { $this->stocks = new ArrayCollection(); } diff --git a/tests/Tests/Models/StockExchange/Stock.php b/tests/Tests/Models/StockExchange/Stock.php index 5c01c8a8e83..ea2e7067b62 100644 --- a/tests/Tests/Models/StockExchange/Stock.php +++ b/tests/Tests/Models/StockExchange/Stock.php @@ -11,44 +11,29 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="exchange_stocks") - */ +#[Table(name: 'exchange_stocks')] +#[Entity] class Stock { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ - private $id; - - /** - * @var string - * For real this column would have to be unique=true. But I want to test behavior of non-unique overrides. - * @Column(type="string", length=255) - */ - private $symbol; - - /** - * @var float - * @Column(type="decimal", precision=10) - */ - private $price; - - /** - * @ManyToOne(targetEntity="Market", inversedBy="stocks") - * @var Market - */ - private $market; - - public function __construct(string $symbol, float $initialOfferingPrice, Market $market) - { - $this->symbol = $symbol; - $this->price = $initialOfferingPrice; - $this->market = $market; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; + + #[Column(type: 'decimal', precision: 10)] + private string $price; + + public function __construct( + /** + * For real this column would have to be unique=true. But I want to test behavior of non-unique overrides. + */ + #[Column(type: 'string', length: 255)] + private string $symbol, + float $price, + #[ManyToOne(targetEntity: 'Market', inversedBy: 'stocks')] + private Market $market, + ) { + $this->price = (string) $price; $market->addStock($this); } diff --git a/tests/Tests/Models/Taxi/Car.php b/tests/Tests/Models/Taxi/Car.php index acbe7211590..54a0804f7c2 100644 --- a/tests/Tests/Models/Taxi/Car.php +++ b/tests/Tests/Models/Taxi/Car.php @@ -12,36 +12,24 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="taxi_car") - */ +#[Table(name: 'taxi_car')] +#[Entity] class Car { - /** - * @var string - * @Id - * @Column(type="string", length=25) - * @GeneratedValue(strategy="NONE") - */ - private $brand; - - /** - * @var string - * @Column(type="string", length=255); - */ - private $model; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Ride", mappedBy="car") - */ + #[Id] + #[Column(type: 'string', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + private string|null $brand = null; + + #[Column(type: 'string', length: 255)] + private string|null $model = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Ride', mappedBy: 'car')] private $freeCarRides; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="PaidRide", mappedBy="car") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'PaidRide', mappedBy: 'car')] private $carRides; public function getBrand(): string diff --git a/tests/Tests/Models/Taxi/Driver.php b/tests/Tests/Models/Taxi/Driver.php index 643d2884d31..63353abd7bc 100644 --- a/tests/Tests/Models/Taxi/Driver.php +++ b/tests/Tests/Models/Taxi/Driver.php @@ -12,36 +12,24 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="taxi_driver") - */ +#[Table(name: 'taxi_driver')] +#[Entity] class Driver { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255); - */ - private $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Ride", mappedBy="driver") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', length: 255)] + private string|null $name = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Ride', mappedBy: 'driver')] private $freeDriverRides; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="PaidRide", mappedBy="driver") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'PaidRide', mappedBy: 'driver')] private $driverRides; public function getId(): int diff --git a/tests/Tests/Models/Taxi/PaidRide.php b/tests/Tests/Models/Taxi/PaidRide.php index a66008a3426..c6686aed73f 100644 --- a/tests/Tests/Models/Taxi/PaidRide.php +++ b/tests/Tests/Models/Taxi/PaidRide.php @@ -13,38 +13,25 @@ /** * Same as Ride but with an extra column that is not part of the composite primary key - * - * @Entity - * @Table(name="taxi_paid_ride") */ +#[Table(name: 'taxi_paid_ride')] +#[Entity] class PaidRide { - /** - * @var Driver - * @Id - * @ManyToOne(targetEntity="Driver", inversedBy="driverRides") - * @JoinColumn(name="driver_id", referencedColumnName="id") - */ - private $driver; - - /** - * @var Car - * @Id - * @ManyToOne(targetEntity="Car", inversedBy="carRides") - * @JoinColumn(name="car", referencedColumnName="brand") - */ - private $car; - - /** - * @var float - * @Column(type="decimal", precision=6, scale=2) - */ + /** @var float */ + #[Column(type: 'decimal', precision: 6, scale: 2)] private $fare; - public function __construct(Driver $driver, Car $car) - { - $this->driver = $driver; - $this->car = $car; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'Driver', inversedBy: 'driverRides')] + #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')] + private Driver $driver, + #[Id] + #[ManyToOne(targetEntity: 'Car', inversedBy: 'carRides')] + #[JoinColumn(name: 'car', referencedColumnName: 'brand')] + private Car $car, + ) { } public function setFare($fare): void diff --git a/tests/Tests/Models/Taxi/Ride.php b/tests/Tests/Models/Taxi/Ride.php index cbbb5360995..878447fa741 100644 --- a/tests/Tests/Models/Taxi/Ride.php +++ b/tests/Tests/Models/Taxi/Ride.php @@ -12,31 +12,20 @@ /** * Test model that contains only Id-columns - * - * @Entity - * @Table(name="taxi_ride") */ +#[Table(name: 'taxi_ride')] +#[Entity] class Ride { - /** - * @var Driver - * @Id - * @ManyToOne(targetEntity="Driver", inversedBy="freeDriverRides") - * @JoinColumn(name="driver_id", referencedColumnName="id") - */ - private $driver; - - /** - * @var Car - * @Id - * @ManyToOne(targetEntity="Car", inversedBy="freeCarRides") - * @JoinColumn(name="car", referencedColumnName="brand") - */ - private $car; - - public function __construct(Driver $driver, Car $car) - { - $this->driver = $driver; - $this->car = $car; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'Driver', inversedBy: 'freeDriverRides')] + #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')] + private Driver $driver, + #[Id] + #[ManyToOne(targetEntity: 'Car', inversedBy: 'freeCarRides')] + #[JoinColumn(name: 'car', referencedColumnName: 'brand')] + private Car $car, + ) { } } diff --git a/tests/Tests/Models/Tweet/Tweet.php b/tests/Tests/Models/Tweet/Tweet.php index f53ad2f1aa3..8d180d61552 100644 --- a/tests/Tests/Models/Tweet/Tweet.php +++ b/tests/Tests/Models/Tweet/Tweet.php @@ -11,30 +11,22 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="tweet_tweet") - */ +#[Table(name: 'tweet_tweet')] +#[Entity] class Tweet { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $content; - /** - * @var User - * @ManyToOne(targetEntity="User", inversedBy="tweets") - */ + /** @var User */ + #[ManyToOne(targetEntity: 'User', inversedBy: 'tweets')] public $author; public function setAuthor(User $user): void diff --git a/tests/Tests/Models/Tweet/User.php b/tests/Tests/Models/Tweet/User.php index 9a3cfa948e3..649906eb36c 100644 --- a/tests/Tests/Models/Tweet/User.php +++ b/tests/Tests/Models/Tweet/User.php @@ -13,36 +13,26 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="tweet_user") - */ +#[Table(name: 'tweet_user')] +#[Entity] class User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Tweet", mappedBy="author", cascade={"persist"}, fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Tweet', mappedBy: 'author', cascade: ['persist'], fetch: 'EXTRA_LAZY')] public $tweets; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="UserList", mappedBy="owner", fetch="EXTRA_LAZY", orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'UserList', mappedBy: 'owner', fetch: 'EXTRA_LAZY', orphanRemoval: true)] public $userLists; public function __construct() diff --git a/tests/Tests/Models/Tweet/UserList.php b/tests/Tests/Models/Tweet/UserList.php index 2695e723f7d..55db602e2bb 100644 --- a/tests/Tests/Models/Tweet/UserList.php +++ b/tests/Tests/Models/Tweet/UserList.php @@ -11,29 +11,21 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="tweet_user_list") - */ +#[Table(name: 'tweet_user_list')] +#[Entity] class UserList { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $listName; - /** - * @var User - * @ManyToOne(targetEntity="User", inversedBy="userLists") - */ + /** @var User */ + #[ManyToOne(targetEntity: 'User', inversedBy: 'userLists')] public $owner; } diff --git a/tests/Tests/Models/TypedProperties/Contact.php b/tests/Tests/Models/TypedProperties/Contact.php index 01c50ecf50f..0229cec95c9 100644 --- a/tests/Tests/Models/TypedProperties/Contact.php +++ b/tests/Tests/Models/TypedProperties/Contact.php @@ -5,13 +5,10 @@ namespace Doctrine\Tests\Models\TypedProperties; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\Column; -/** @ORM\Embeddable() */ #[ORM\Embeddable] class Contact { - /** @Column() */ #[ORM\Column] - public ?string $email = null; + public string|null $email = null; } diff --git a/tests/Tests/Models/TypedProperties/UserTyped.php b/tests/Tests/Models/TypedProperties/UserTyped.php index 531a145389c..22ac2127ac2 100644 --- a/tests/Tests/Models/TypedProperties/UserTyped.php +++ b/tests/Tests/Models/TypedProperties/UserTyped.php @@ -9,95 +9,63 @@ use DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Embedded; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\ManyToOne; -use Doctrine\ORM\Mapping\OneToOne; -use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\Models\CMS\CmsEmail; -/** - * @Entity - * @Table(name="cms_users_typed") - */ #[ORM\Entity] #[ORM\Table(name: 'cms_users_typed')] class UserTyped { - /** - * @Id - * @Column - * @GeneratedValue - */ #[ORM\Id] #[ORM\Column] #[ORM\GeneratedValue] public int $id; - /** @Column(length=50) */ #[ORM\Column(length: 50)] - public ?string $status; + public string|null $status = null; - /** @Column(length=255, unique=true) */ #[ORM\Column(length: 255, unique: true)] public string $username; - /** @Column */ #[ORM\Column] public DateInterval $dateInterval; - /** @Column */ #[ORM\Column] public DateTime $dateTime; - /** @Column */ #[ORM\Column] public DateTimeImmutable $dateTimeImmutable; - /** @Column */ #[ORM\Column] public array $array; - /** @Column */ #[ORM\Column] public bool $boolean; - /** @Column */ #[ORM\Column] public float $float; - /** - * @OneToOne(cascade={"persist"}, orphanRemoval=true) - * @JoinColumn - */ #[ORM\OneToOne(cascade: ['persist'], orphanRemoval: true)] #[ORM\JoinColumn] public CmsEmail $email; - /** @ManyToOne */ #[ORM\ManyToOne] - public ?CmsEmail $mainEmail; + public CmsEmail|null $mainEmail = null; - /** @Embedded */ #[ORM\Embedded] - public ?Contact $contact = null; + public Contact|null $contact = null; public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); $metadata->setPrimaryTable( - ['name' => 'cms_users_typed'] + ['name' => 'cms_users_typed'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -105,32 +73,32 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'fieldName' => 'status', 'length' => 50, - ] + ], ); $metadata->mapField( [ 'fieldName' => 'username', 'length' => 255, 'unique' => true, - ] + ], ); $metadata->mapField( - ['fieldName' => 'dateInterval'] + ['fieldName' => 'dateInterval'], ); $metadata->mapField( - ['fieldName' => 'dateTime'] + ['fieldName' => 'dateTime'], ); $metadata->mapField( - ['fieldName' => 'dateTimeImmutable'] + ['fieldName' => 'dateTimeImmutable'], ); $metadata->mapField( - ['fieldName' => 'array'] + ['fieldName' => 'array'], ); $metadata->mapField( - ['fieldName' => 'boolean'] + ['fieldName' => 'boolean'], ); $metadata->mapField( - ['fieldName' => 'float'] + ['fieldName' => 'float'], ); $metadata->mapOneToOne( @@ -141,14 +109,14 @@ public static function loadMetadata(ClassMetadata $metadata): void 'joinColumns' => [ 0 => - [], + ['referencedColumnName' => 'id'], ], 'orphanRemoval' => true, - ] + ], ); $metadata->mapManyToOne( - ['fieldName' => 'mainEmail'] + ['fieldName' => 'mainEmail'], ); $metadata->mapEmbedded(['fieldName' => 'contact']); diff --git a/tests/Tests/Models/TypedProperties/UserTypedWithCustomTypedField.php b/tests/Tests/Models/TypedProperties/UserTypedWithCustomTypedField.php index 28b116ea3b9..84f83fcd456 100644 --- a/tests/Tests/Models/TypedProperties/UserTypedWithCustomTypedField.php +++ b/tests/Tests/Models/TypedProperties/UserTypedWithCustomTypedField.php @@ -6,36 +6,20 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\CustomIdObject; -/** - * @Entity - * @Table(name="cms_users_typed_with_custom_typed_field") - */ #[ORM\Entity] #[ORM\Table(name: 'cms_users_typed_with_custom_typed_field')] class UserTypedWithCustomTypedField { - /** - * @Id - * @Column - * @GeneratedValue - */ #[ORM\Id] #[ORM\Column] #[ORM\GeneratedValue] public int $id; - /** @Column */ #[ORM\Column] public CustomIdObject $customId; - /** @Column */ #[ORM\Column] public int $customIntTypedField; @@ -43,23 +27,23 @@ public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); $metadata->setPrimaryTable( - ['name' => 'cms_users_typed_with_custom_typed_field'] + ['name' => 'cms_users_typed_with_custom_typed_field'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); $metadata->mapField( - ['fieldName' => 'customId'] + ['fieldName' => 'customId'], ); $metadata->mapField( - ['fieldName' => 'customIntTypedField'] + ['fieldName' => 'customIntTypedField'], ); } } diff --git a/tests/Tests/Models/Upsertable/Insertable.php b/tests/Tests/Models/Upsertable/Insertable.php index 98bd1ccc3f6..4d281c6f530 100644 --- a/tests/Tests/Models/Upsertable/Insertable.php +++ b/tests/Tests/Models/Upsertable/Insertable.php @@ -11,50 +11,35 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="insertable_column") - */ #[Entity] #[Table(name: 'insertable_column')] class Insertable { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255, insertable=false, options={"default": "1234"}, generated="INSERT") - */ + /** @var string */ #[Column(type: 'string', insertable: false, options: ['default' => '1234'], generated: 'INSERT')] public $nonInsertableContent; - /** - * @var string - * @Column(type="string", length=255, insertable=true) - */ + /** @var string */ #[Column(type: 'string', insertable: true)] public $insertableContent; public static function loadMetadata(ClassMetadata $metadata): ClassMetadata { $metadata->setPrimaryTable( - ['name' => 'insertable_column'] + ['name' => 'insertable_column'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -64,10 +49,10 @@ public static function loadMetadata(ClassMetadata $metadata): ClassMetadata 'notInsertable' => true, 'options' => ['default' => '1234'], 'generated' => ClassMetadata::GENERATED_INSERT, - ] + ], ); $metadata->mapField( - ['fieldName' => 'insertableContent'] + ['fieldName' => 'insertableContent'], ); return $metadata; diff --git a/tests/Tests/Models/Upsertable/Updatable.php b/tests/Tests/Models/Upsertable/Updatable.php index e4f18a60119..cf0ff5304be 100644 --- a/tests/Tests/Models/Upsertable/Updatable.php +++ b/tests/Tests/Models/Upsertable/Updatable.php @@ -11,50 +11,35 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="updatable_column") - */ #[Entity] #[Table(name: 'updatable_column')] class Updatable { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255, name="non_updatable_content", updatable=false, generated="ALWAYS") - */ + /** @var string */ #[Column(name: 'non_updatable_content', type: 'string', length: 255, updatable: false, generated: 'ALWAYS')] public $nonUpdatableContent; - /** - * @var string - * @Column(type="string", length=255, updatable=true) - */ + /** @var string */ #[Column(type: 'string', length: 255, updatable: true)] public $updatableContent; public static function loadMetadata(ClassMetadata $metadata): ClassMetadata { $metadata->setPrimaryTable( - ['name' => 'updatable_column'] + ['name' => 'updatable_column'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -63,10 +48,10 @@ public static function loadMetadata(ClassMetadata $metadata): ClassMetadata 'fieldName' => 'nonUpdatableContent', 'notUpdatable' => true, 'generated' => ClassMetadata::GENERATED_ALWAYS, - ] + ], ); $metadata->mapField( - ['fieldName' => 'updatableContent'] + ['fieldName' => 'updatableContent'], ); return $metadata; diff --git a/tests/Tests/Models/ValueConversionType/AuxiliaryEntity.php b/tests/Tests/Models/ValueConversionType/AuxiliaryEntity.php index 2aa60cc8e63..0b8918b07ca 100644 --- a/tests/Tests/Models/ValueConversionType/AuxiliaryEntity.php +++ b/tests/Tests/Models/ValueConversionType/AuxiliaryEntity.php @@ -9,16 +9,12 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_auxiliary") - */ +#[Table(name: 'vct_auxiliary')] +#[Entity] class AuxiliaryEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id4; } diff --git a/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php index 9f2e4d0d438..72b57a811e7 100644 --- a/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdEntity.php @@ -12,30 +12,22 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_manytomany_compositeid") - */ +#[Table(name: 'vct_inversed_manytomany_compositeid')] +#[Entity] class InversedManyToManyCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="OwningManyToManyCompositeIdEntity", mappedBy="associatedEntities") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'OwningManyToManyCompositeIdEntity', mappedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php index 06c7cdbfb3b..456d08b9e1b 100644 --- a/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedManyToManyCompositeIdForeignKeyEntity.php @@ -14,31 +14,23 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_manytomany_compositeid_foreignkey") - */ +#[Table(name: 'vct_inversed_manytomany_compositeid_foreignkey')] +#[Entity] class InversedManyToManyCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var AuxiliaryEntity - * @ManyToOne(targetEntity="AuxiliaryEntity") - * @JoinColumn(name="foreign_id", referencedColumnName="id4") - * @Id - */ + /** @var AuxiliaryEntity */ + #[ManyToOne(targetEntity: 'AuxiliaryEntity')] + #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')] + #[Id] public $foreignEntity; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="OwningManyToManyCompositeIdForeignKeyEntity", mappedBy="associatedEntities") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'OwningManyToManyCompositeIdForeignKeyEntity', mappedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedManyToManyEntity.php b/tests/Tests/Models/ValueConversionType/InversedManyToManyEntity.php index c34226b7843..50e575e3a56 100644 --- a/tests/Tests/Models/ValueConversionType/InversedManyToManyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedManyToManyEntity.php @@ -12,23 +12,17 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_manytomany") - */ +#[Table(name: 'vct_inversed_manytomany')] +#[Entity] class InversedManyToManyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="OwningManyToManyEntity", mappedBy="associatedEntities") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'OwningManyToManyEntity', mappedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php b/tests/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php index b949fad6328..8dd18be20d9 100644 --- a/tests/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedManyToManyExtraLazyEntity.php @@ -12,28 +12,17 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_manytomany_extralazy") - */ +#[Table(name: 'vct_inversed_manytomany_extralazy')] +#[Entity] class InversedManyToManyExtraLazyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var Collection - * @ManyToMany( - * targetEntity="OwningManyToManyExtraLazyEntity", - * mappedBy="associatedEntities", - * fetch="EXTRA_LAZY", - * indexBy="id2" - * ) - */ + /** @var Collection */ + #[ManyToMany(targetEntity: 'OwningManyToManyExtraLazyEntity', mappedBy: 'associatedEntities', fetch: 'EXTRA_LAZY', indexBy: 'id2')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php index 6068ebc812a..caecd4dcf28 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdEntity.php @@ -12,36 +12,26 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetomany_compositeid") - */ +#[Table(name: 'vct_inversed_onetomany_compositeid')] +#[Entity] class InversedOneToManyCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var string - * @Column(type="string", length=255, name="some_property") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'some_property')] public $someProperty; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="OwningManyToOneCompositeIdEntity", mappedBy="associatedEntity") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'OwningManyToOneCompositeIdEntity', mappedBy: 'associatedEntity')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php index 07987f201a4..b79f1ea4001 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToManyCompositeIdForeignKeyEntity.php @@ -14,37 +14,27 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetomany_compositeid_foreignkey") - */ +#[Table(name: 'vct_inversed_onetomany_compositeid_foreignkey')] +#[Entity] class InversedOneToManyCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var AuxiliaryEntity - * @ManyToOne(targetEntity="AuxiliaryEntity") - * @JoinColumn(name="foreign_id", referencedColumnName="id4") - * @Id - */ + /** @var AuxiliaryEntity */ + #[ManyToOne(targetEntity: 'AuxiliaryEntity')] + #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')] + #[Id] public $foreignEntity; - /** - * @var string - * @Column(type="string", length=255, name="some_property") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'some_property')] public $someProperty; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="OwningManyToOneCompositeIdForeignKeyEntity", mappedBy="associatedEntity") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'OwningManyToOneCompositeIdForeignKeyEntity', mappedBy: 'associatedEntity')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToManyEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToManyEntity.php index bb586e2b07d..9ffbf32b677 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToManyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToManyEntity.php @@ -12,29 +12,21 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetomany") - */ +#[Table(name: 'vct_inversed_onetomany')] +#[Entity] class InversedOneToManyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="OwningManyToOneEntity", mappedBy="associatedEntity") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'OwningManyToOneEntity', mappedBy: 'associatedEntity')] public $associatedEntities; - /** - * @var string - * @Column(type="string", name="some_property", length=255) - */ + /** @var string */ + #[Column(type: 'string', name: 'some_property', length: 255)] public $someProperty; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php index d748fb77489..b7522a1905d 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToManyExtraLazyEntity.php @@ -12,28 +12,17 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetomany_extralazy") - */ +#[Table(name: 'vct_inversed_onetomany_extralazy')] +#[Entity] class InversedOneToManyExtraLazyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var Collection - * @OneToMany( - * targetEntity="OwningManyToOneExtraLazyEntity", - * mappedBy="associatedEntity", - * fetch="EXTRA_LAZY", - * indexBy="id2" - * ) - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'OwningManyToOneExtraLazyEntity', mappedBy: 'associatedEntity', fetch: 'EXTRA_LAZY', indexBy: 'id2')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php index 9d7d071ce9e..7478950ff35 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdEntity.php @@ -10,35 +10,25 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetoone_compositeid") - */ +#[Table(name: 'vct_inversed_onetoone_compositeid')] +#[Entity] class InversedOneToOneCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var string - * @Column(type="string", length=255, name="some_property") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'some_property')] public $someProperty; - /** - * @var OwningOneToOneCompositeIdEntity - * @OneToOne(targetEntity="OwningOneToOneCompositeIdEntity", mappedBy="associatedEntity") - */ + /** @var OwningOneToOneCompositeIdEntity */ + #[OneToOne(targetEntity: 'OwningOneToOneCompositeIdEntity', mappedBy: 'associatedEntity')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php index 18b40c7f7d3..ac1abe1ceeb 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToOneCompositeIdForeignKeyEntity.php @@ -12,36 +12,26 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetoone_compositeid_foreignkey") - */ +#[Table(name: 'vct_inversed_onetoone_compositeid_foreignkey')] +#[Entity] class InversedOneToOneCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var AuxiliaryEntity - * @ManyToOne(targetEntity="AuxiliaryEntity") - * @JoinColumn(name="foreign_id", referencedColumnName="id4") - * @Id - */ + /** @var AuxiliaryEntity */ + #[ManyToOne(targetEntity: 'AuxiliaryEntity')] + #[JoinColumn(name: 'foreign_id', referencedColumnName: 'id4')] + #[Id] public $foreignEntity; - /** - * @var string - * @Column(type="string", length=255, name="some_property") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'some_property')] public $someProperty; - /** - * @var OwningOneToOneCompositeIdForeignKeyEntity - * @OneToOne(targetEntity="OwningOneToOneCompositeIdForeignKeyEntity", mappedBy="associatedEntity") - */ + /** @var OwningOneToOneCompositeIdForeignKeyEntity */ + #[OneToOne(targetEntity: 'OwningOneToOneCompositeIdForeignKeyEntity', mappedBy: 'associatedEntity')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/InversedOneToOneEntity.php b/tests/Tests/Models/ValueConversionType/InversedOneToOneEntity.php index 9b856b6e32c..36096592a77 100644 --- a/tests/Tests/Models/ValueConversionType/InversedOneToOneEntity.php +++ b/tests/Tests/Models/ValueConversionType/InversedOneToOneEntity.php @@ -10,28 +10,20 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_inversed_onetoone") - */ +#[Table(name: 'vct_inversed_onetoone')] +#[Entity] class InversedOneToOneEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id1; - /** - * @var string - * @Column(type="string", length=255, name="some_property") - */ + /** @var string */ + #[Column(type: 'string', length: 255, name: 'some_property')] public $someProperty; - /** - * @var OwningOneToOneEntity - * @OneToOne(targetEntity="OwningOneToOneEntity", mappedBy="associatedEntity") - */ + /** @var OwningOneToOneEntity */ + #[OneToOne(targetEntity: 'OwningOneToOneEntity', mappedBy: 'associatedEntity')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php index cda302a420a..26bb0bade33 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdEntity.php @@ -9,36 +9,27 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytomany_compositeid") - */ +#[Table(name: 'vct_owning_manytomany_compositeid')] +#[Entity] class OwningManyToManyCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id3; - /** - * @var Collection - * @ManyToMany(targetEntity="InversedManyToManyCompositeIdEntity", inversedBy="associatedEntities") - * @JoinTable( - * name="vct_xref_manytomany_compositeid", - * joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id3")}, - * inverseJoinColumns={ - * @JoinColumn(name="inversed_id1", referencedColumnName="id1"), - * @JoinColumn(name="inversed_id2", referencedColumnName="id2") - * } - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'vct_xref_manytomany_compositeid')] + #[JoinColumn(name: 'owning_id', referencedColumnName: 'id3')] + #[InverseJoinColumn(name: 'inversed_id1', referencedColumnName: 'id1')] + #[InverseJoinColumn(name: 'inversed_id2', referencedColumnName: 'id2')] + #[ManyToMany(targetEntity: 'InversedManyToManyCompositeIdEntity', inversedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php index 823df4958a5..d132d350704 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToManyCompositeIdForeignKeyEntity.php @@ -9,36 +9,27 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytomany_compositeid_foreignkey") - */ +#[Table(name: 'vct_owning_manytomany_compositeid_foreignkey')] +#[Entity] class OwningManyToManyCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var Collection - * @ManyToMany(targetEntity="InversedManyToManyCompositeIdForeignKeyEntity", inversedBy="associatedEntities") - * @JoinTable( - * name="vct_xref_manytomany_compositeid_foreignkey", - * joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")}, - * inverseJoinColumns={ - * @JoinColumn(name="associated_id", referencedColumnName="id1"), - * @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id") - * } - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'vct_xref_manytomany_compositeid_foreignkey')] + #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')] + #[InverseJoinColumn(name: 'associated_id', referencedColumnName: 'id1')] + #[InverseJoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')] + #[ManyToMany(targetEntity: 'InversedManyToManyCompositeIdForeignKeyEntity', inversedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToManyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToManyEntity.php index 4b037be3fca..c2b9534a0b1 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToManyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToManyEntity.php @@ -9,33 +9,26 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytomany") - */ +#[Table(name: 'vct_owning_manytomany')] +#[Entity] class OwningManyToManyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var Collection - * @ManyToMany(targetEntity="InversedManyToManyEntity", inversedBy="associatedEntities") - * @JoinTable( - * name="vct_xref_manytomany", - * joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")}, - * inverseJoinColumns={@JoinColumn(name="inversed_id", referencedColumnName="id1")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'vct_xref_manytomany')] + #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')] + #[InverseJoinColumn(name: 'inversed_id', referencedColumnName: 'id1')] + #[ManyToMany(targetEntity: 'InversedManyToManyEntity', inversedBy: 'associatedEntities')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php index 13b12402dce..d8a5d5092df 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToManyExtraLazyEntity.php @@ -9,38 +9,26 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytomany_extralazy") - */ +#[Table(name: 'vct_owning_manytomany_extralazy')] +#[Entity] class OwningManyToManyExtraLazyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var Collection - * @ManyToMany( - * targetEntity="InversedManyToManyExtraLazyEntity", - * inversedBy="associatedEntities", - * fetch="EXTRA_LAZY", - * indexBy="id1" - * ) - * @JoinTable( - * name="vct_xref_manytomany_extralazy", - * joinColumns={@JoinColumn(name="owning_id", referencedColumnName="id2")}, - * inverseJoinColumns={@JoinColumn(name="inversed_id", referencedColumnName="id1")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'vct_xref_manytomany_extralazy')] + #[JoinColumn(name: 'owning_id', referencedColumnName: 'id2')] + #[InverseJoinColumn(name: 'inversed_id', referencedColumnName: 'id1')] + #[ManyToMany(targetEntity: 'InversedManyToManyExtraLazyEntity', inversedBy: 'associatedEntities', fetch: 'EXTRA_LAZY', indexBy: 'id1')] public $associatedEntities; public function __construct() diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php index 6dee5c8be53..89a5432ec14 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdEntity.php @@ -8,30 +8,21 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytoone_compositeid") - */ +#[Table(name: 'vct_owning_manytoone_compositeid')] +#[Entity] class OwningManyToOneCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id3; - /** - * @var InversedOneToManyCompositeIdEntity - * @ManyToOne(targetEntity="InversedOneToManyCompositeIdEntity", inversedBy="associatedEntities") - * @JoinColumns({ - * @JoinColumn(name="associated_id1", referencedColumnName="id1"), - * @JoinColumn(name="associated_id2", referencedColumnName="id2") - * }) - */ + /** @var InversedOneToManyCompositeIdEntity */ + #[JoinColumn(name: 'associated_id1', referencedColumnName: 'id1')] + #[JoinColumn(name: 'associated_id2', referencedColumnName: 'id2')] + #[ManyToOne(targetEntity: 'InversedOneToManyCompositeIdEntity', inversedBy: 'associatedEntities')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php index 0a1c6761468..bfa3a03974b 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToOneCompositeIdForeignKeyEntity.php @@ -8,30 +8,21 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytoone_compositeid_foreignkey") - */ +#[Table(name: 'vct_owning_manytoone_compositeid_foreignkey')] +#[Entity] class OwningManyToOneCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var InversedOneToManyCompositeIdForeignKeyEntity - * @ManyToOne(targetEntity="InversedOneToManyCompositeIdForeignKeyEntity", inversedBy="associatedEntities") - * @JoinColumns({ - * @JoinColumn(name="associated_id", referencedColumnName="id1"), - * @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id") - * }) - */ + /** @var InversedOneToManyCompositeIdForeignKeyEntity */ + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')] + #[JoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')] + #[ManyToOne(targetEntity: 'InversedOneToManyCompositeIdForeignKeyEntity', inversedBy: 'associatedEntities')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToOneEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToOneEntity.php index 283d95f50ff..7896465571d 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToOneEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToOneEntity.php @@ -11,23 +11,17 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytoone") - */ +#[Table(name: 'vct_owning_manytoone')] +#[Entity] class OwningManyToOneEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var InversedOneToManyEntity - * @ManyToOne(targetEntity="InversedOneToManyEntity", inversedBy="associatedEntities") - * @JoinColumn(name="associated_id", referencedColumnName="id1") - */ + /** @var InversedOneToManyEntity */ + #[ManyToOne(targetEntity: 'InversedOneToManyEntity', inversedBy: 'associatedEntities')] + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php index 500d7e57865..8e4dbfb5f96 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToOneExtraLazyEntity.php @@ -11,23 +11,17 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytoone_extralazy") - */ +#[Table(name: 'vct_owning_manytoone_extralazy')] +#[Entity] class OwningManyToOneExtraLazyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var InversedOneToManyExtraLazyEntity - * @ManyToOne(targetEntity="InversedOneToManyExtraLazyEntity", inversedBy="associatedEntities") - * @JoinColumn(name="associated_id", referencedColumnName="id1") - */ + /** @var InversedOneToManyExtraLazyEntity */ + #[ManyToOne(targetEntity: 'InversedOneToManyExtraLazyEntity', inversedBy: 'associatedEntities')] + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningManyToOneIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/OwningManyToOneIdForeignKeyEntity.php index acd92bbab15..286c8241ad7 100644 --- a/tests/Tests/Models/ValueConversionType/OwningManyToOneIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningManyToOneIdForeignKeyEntity.php @@ -10,17 +10,13 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_manytoone_foreignkey") - */ +#[Table(name: 'vct_owning_manytoone_foreignkey')] +#[Entity] class OwningManyToOneIdForeignKeyEntity { - /** - * @var associatedEntities - * @Id - * @ManyToOne(targetEntity=AuxiliaryEntity::class, inversedBy="associatedEntities") - * @JoinColumn(name="associated_id", referencedColumnName="id4") - */ + /** @var associatedEntities */ + #[Id] + #[ManyToOne(targetEntity: AuxiliaryEntity::class, inversedBy: 'associatedEntities')] + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id4')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php b/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php index 1ce91c02ed3..9ba6ab24ca0 100644 --- a/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdEntity.php @@ -8,30 +8,21 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_onetoone_compositeid") - */ +#[Table(name: 'vct_owning_onetoone_compositeid')] +#[Entity] class OwningOneToOneCompositeIdEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id3; - /** - * @var InversedOneToOneCompositeIdEntity - * @OneToOne(targetEntity="InversedOneToOneCompositeIdEntity", inversedBy="associatedEntity") - * @JoinColumns({ - * @JoinColumn(name="associated_id1", referencedColumnName="id1"), - * @JoinColumn(name="associated_id2", referencedColumnName="id2") - * }) - */ + /** @var InversedOneToOneCompositeIdEntity */ + #[JoinColumn(name: 'associated_id1', referencedColumnName: 'id1')] + #[JoinColumn(name: 'associated_id2', referencedColumnName: 'id2')] + #[OneToOne(targetEntity: 'InversedOneToOneCompositeIdEntity', inversedBy: 'associatedEntity')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php b/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php index d315724b6b2..8050bb413c2 100644 --- a/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningOneToOneCompositeIdForeignKeyEntity.php @@ -8,36 +8,23 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\UniqueConstraint; -/** - * @Entity - * @Table( - * name="vct_owning_onetoone_compositeid_foreignkey", - * uniqueConstraints={ - * @UniqueConstraint(name="associated_entity_uniq", columns={"associated_id", "associated_foreign_id"}) - * } - * ) - */ +#[Table(name: 'vct_owning_onetoone_compositeid_foreignkey')] +#[UniqueConstraint(name: 'associated_entity_uniq', columns: ['associated_id', 'associated_foreign_id'])] +#[Entity] class OwningOneToOneCompositeIdForeignKeyEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var InversedOneToOneCompositeIdForeignKeyEntity - * @OneToOne(targetEntity="InversedOneToOneCompositeIdForeignKeyEntity", inversedBy="associatedEntity") - * @JoinColumns({ - * @JoinColumn(name="associated_id", referencedColumnName="id1"), - * @JoinColumn(name="associated_foreign_id", referencedColumnName="foreign_id") - * }) - */ + /** @var InversedOneToOneCompositeIdForeignKeyEntity */ + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')] + #[JoinColumn(name: 'associated_foreign_id', referencedColumnName: 'foreign_id')] + #[OneToOne(targetEntity: 'InversedOneToOneCompositeIdForeignKeyEntity', inversedBy: 'associatedEntity')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueConversionType/OwningOneToOneEntity.php b/tests/Tests/Models/ValueConversionType/OwningOneToOneEntity.php index df81a30abe0..7ea304d6777 100644 --- a/tests/Tests/Models/ValueConversionType/OwningOneToOneEntity.php +++ b/tests/Tests/Models/ValueConversionType/OwningOneToOneEntity.php @@ -11,23 +11,17 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="vct_owning_onetoone") - */ +#[Table(name: 'vct_owning_onetoone')] +#[Entity] class OwningOneToOneEntity { - /** - * @var string - * @Column(type="rot13", length=255) - * @Id - */ + /** @var string */ + #[Column(type: 'rot13', length: 255)] + #[Id] public $id2; - /** - * @var InversedOneToOneEntity - * @OneToOne(targetEntity="InversedOneToOneEntity", inversedBy="associatedEntity") - * @JoinColumn(name="associated_id", referencedColumnName="id1") - */ + /** @var InversedOneToOneEntity */ + #[OneToOne(targetEntity: 'InversedOneToOneEntity', inversedBy: 'associatedEntity')] + #[JoinColumn(name: 'associated_id', referencedColumnName: 'id1')] public $associatedEntity; } diff --git a/tests/Tests/Models/ValueObjects/Name.php b/tests/Tests/Models/ValueObjects/Name.php index dddd7d6c194..63bf85a88a7 100644 --- a/tests/Tests/Models/ValueObjects/Name.php +++ b/tests/Tests/Models/ValueObjects/Name.php @@ -6,9 +6,7 @@ class Name { - /** @var string */ - private $firstName; + private string $firstName; - /** @var string */ - private $lastName; + private string $lastName; } diff --git a/tests/Tests/Models/ValueObjects/Person.php b/tests/Tests/Models/ValueObjects/Person.php index 01060c9cf3a..92d5e281b38 100644 --- a/tests/Tests/Models/ValueObjects/Person.php +++ b/tests/Tests/Models/ValueObjects/Person.php @@ -6,9 +6,7 @@ class Person { - /** @var int */ - private $id; + private int $id; - /** @var string */ - private $name; + private string $name; } diff --git a/tests/Tests/Models/VersionedManyToOne/Article.php b/tests/Tests/Models/VersionedManyToOne/Article.php index 76d7a342828..8c57945ca7c 100644 --- a/tests/Tests/Models/VersionedManyToOne/Article.php +++ b/tests/Tests/Models/VersionedManyToOne/Article.php @@ -12,38 +12,30 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\Version; -/** - * @Entity - * @Table(name="versioned_many_to_one_article") - */ +#[Table(name: 'versioned_many_to_one_article')] +#[Entity] class Article { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="name") - */ + /** @var string */ + #[Column(name: 'name')] public $name; - /** - * @var Category - * @ManyToOne(targetEntity="Category", cascade={"merge", "persist"}) - */ + /** @var Category */ + #[ManyToOne(targetEntity: 'Category', cascade: ['persist'])] public $category; /** * Version column * * @var int - * @Column(type="integer", name="version") - * @Version */ + #[Column(type: 'integer', name: 'version')] + #[Version] public $version; } diff --git a/tests/Tests/Models/VersionedManyToOne/Category.php b/tests/Tests/Models/VersionedManyToOne/Category.php index de854878e34..148de2e3fed 100644 --- a/tests/Tests/Models/VersionedManyToOne/Category.php +++ b/tests/Tests/Models/VersionedManyToOne/Category.php @@ -11,26 +11,22 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\Version; -/** - * @Entity - * @Table(name="versioned_many_to_one_category") - */ +#[Table(name: 'versioned_many_to_one_category')] +#[Entity] class Category { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; /** * Version column * * @var int - * @Column(type="integer", name="version") - * @Version */ + #[Column(type: 'integer', name: 'version')] + #[Version] public $version; } diff --git a/tests/Tests/Models/VersionedOneToOne/FirstRelatedEntity.php b/tests/Tests/Models/VersionedOneToOne/FirstRelatedEntity.php index 5714d03242f..3c30def93a9 100644 --- a/tests/Tests/Models/VersionedOneToOne/FirstRelatedEntity.php +++ b/tests/Tests/Models/VersionedOneToOne/FirstRelatedEntity.php @@ -12,31 +12,25 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\Version; -/** - * @Entity - * @Table(name="first_entity") - */ +#[Table(name: 'first_entity')] +#[Entity] class FirstRelatedEntity { - /** - * @var SecondRelatedEntity - * @Id - * @OneToOne(targetEntity="SecondRelatedEntity", fetch="EAGER") - * @JoinColumn(name="second_entity_id", referencedColumnName="id") - */ + /** @var SecondRelatedEntity */ + #[Id] + #[OneToOne(targetEntity: 'SecondRelatedEntity', fetch: 'EAGER')] + #[JoinColumn(name: 'second_entity_id', referencedColumnName: 'id')] public $secondEntity; - /** - * @var string - * @Column(name="name") - */ + /** @var string */ + #[Column(name: 'name')] public $name; /** * @var int * Version column - * @Column(type="integer", name="version") - * @Version */ + #[Column(type: 'integer', name: 'version')] + #[Version] public $version; } diff --git a/tests/Tests/Models/VersionedOneToOne/SecondRelatedEntity.php b/tests/Tests/Models/VersionedOneToOne/SecondRelatedEntity.php index 1030b4be284..a1e85ba6373 100644 --- a/tests/Tests/Models/VersionedOneToOne/SecondRelatedEntity.php +++ b/tests/Tests/Models/VersionedOneToOne/SecondRelatedEntity.php @@ -11,31 +11,25 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\Version; -/** - * @Entity - * @Table(name="second_entity") - */ +#[Table(name: 'second_entity')] +#[Entity] class SecondRelatedEntity { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="name") - */ + /** @var string */ + #[Column(name: 'name')] public $name; /** * @var int * Version column - * @Column(type="integer", name="version") - * @Version */ + #[Column(type: 'integer', name: 'version')] + #[Version] public $version; } diff --git a/tests/Tests/ORM/AbstractQueryTest.php b/tests/Tests/ORM/AbstractQueryTest.php index efab438ab69..a0360decbeb 100644 --- a/tests/Tests/ORM/AbstractQueryTest.php +++ b/tests/Tests/ORM/AbstractQueryTest.php @@ -4,41 +4,16 @@ namespace Doctrine\Tests\ORM; -use Doctrine\Common\Cache\Psr6\CacheAdapter; use Doctrine\DBAL\Cache\QueryCacheProfile; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; +use Doctrine\DBAL\Result; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; -use stdClass; final class AbstractQueryTest extends TestCase { - use VerifyDeprecations; - - /** - * @requires function Doctrine\DBAL\Cache\QueryCacheProfile::getResultCacheDriver - */ - public function testItMakesHydrationCacheProfilesAwareOfTheResultCacheDriver(): void - { - $cache = $this->createMock(CacheItemPoolInterface::class); - - $configuration = new Configuration(); - $configuration->setHydrationCache($cache); - $entityManager = $this->createMock(EntityManagerInterface::class); - $entityManager->method('getConfiguration')->willReturn($configuration); - $query = $this->getMockForAbstractClass(AbstractQuery::class, [$entityManager]); - $cacheProfile = new QueryCacheProfile(); - - $query->setHydrationCacheProfile($cacheProfile); - self::assertSame($cache, CacheAdapter::wrap($query->getHydrationCacheProfile()->getResultCacheDriver())); - } - - /** - * @requires function Doctrine\DBAL\Cache\QueryCacheProfile::getResultCache - */ public function testItMakesHydrationCacheProfilesAwareOfTheResultCache(): void { $cache = $this->createMock(CacheItemPoolInterface::class); @@ -47,12 +22,11 @@ public function testItMakesHydrationCacheProfilesAwareOfTheResultCache(): void $configuration->setHydrationCache($cache); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->method('getConfiguration')->willReturn($configuration); - $query = $this->getMockForAbstractClass(AbstractQuery::class, [$entityManager]); + $query = new TestQuery($entityManager); $cacheProfile = new QueryCacheProfile(); $query->setHydrationCacheProfile($cacheProfile); self::assertSame($cache, $query->getHydrationCacheProfile()->getResultCache()); - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/4620'); } public function testItMakesResultCacheProfilesAwareOfTheResultCache(): void @@ -63,57 +37,39 @@ public function testItMakesResultCacheProfilesAwareOfTheResultCache(): void $configuration->setResultCache($cache); $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->method('getConfiguration')->willReturn($configuration); - $query = $this->getMockForAbstractClass(AbstractQuery::class, [$entityManager]); - $cacheProfile = new QueryCacheProfile(); + $query = new TestQuery($entityManager); + $query->setResultCacheProfile(new QueryCacheProfile()); - $query->setResultCacheProfile($cacheProfile); - self::assertSame($cache, CacheAdapter::wrap($query->getResultCacheDriver())); - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/4620'); + self::assertSame($cache, $query->getResultCache()); } - /** @dataProvider provideSettersWithDeprecatedDefault */ - public function testCallingSettersWithoutArgumentsIsDeprecated(string $setter): void + public function testSettingTheResultCacheIsPossibleWithoutCallingDeprecatedMethods(): void { + $cache = $this->createMock(CacheItemPoolInterface::class); + $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->method('getConfiguration')->willReturn(new Configuration()); - $query = $this->getMockForAbstractClass(AbstractQuery::class, [$entityManager]); + $query = new TestQuery($entityManager); + $query->setResultCache($cache); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9791'); - $query->$setter(); + self::assertSame($cache, $query->getResultCache()); } +} - /** @return array */ - public static function provideSettersWithDeprecatedDefault(): array +class TestQuery extends AbstractQuery +{ + public function getSQL(): string { - return [ - 'setHydrationCacheProfile' => ['setHydrationCacheProfile'], - 'setResultCache' => ['setResultCache'], - 'setResultCacheProfile' => ['setResultCacheProfile'], - ]; + return ''; } - public function testSettingTheResultCacheIsPossibleWithoutCallingDeprecatedMethods(): void + protected function _doExecute(): Result|int { - $cache = $this->createMock(CacheItemPoolInterface::class); - - $entityManager = $this->createMock(EntityManagerInterface::class); - $entityManager->method('getConfiguration')->willReturn(new Configuration()); - $query = $this->getMockForAbstractClass(AbstractQuery::class, [$entityManager]); - - $query->setResultCache($cache); - self::assertSame($cache, CacheAdapter::wrap($query->getResultCacheDriver())); - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/dbal/pull/4620'); + return 0; } - public function testSettingTheFetchModeToRandomIntegersIsDeprecated(): void + public function getResultCache(): CacheItemPoolInterface|null { - $query = $this->getMockForAbstractClass( - AbstractQuery::class, - [], - '', - false // no need to call the constructor - ); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9777'); - $query->setFetchMode(stdClass::class, 'foo', 42); + return $this->queryCacheProfile->getResultCache(); } } diff --git a/tests/Tests/ORM/Cache/CacheConfigTest.php b/tests/Tests/ORM/Cache/CacheConfigTest.php index 041da75629a..453f828000c 100644 --- a/tests/Tests/ORM/Cache/CacheConfigTest.php +++ b/tests/Tests/ORM/Cache/CacheConfigTest.php @@ -10,16 +10,15 @@ use Doctrine\ORM\Cache\QueryCacheValidator; use Doctrine\ORM\Cache\TimestampQueryCacheValidator; use Doctrine\ORM\Cache\TimestampRegion; -use Doctrine\Tests\DoctrineTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; -/** - * @group DDC-2183 - * @covers \Doctrine\ORM\Cache\CacheConfiguration - */ -class CacheConfigTest extends DoctrineTestCase +#[CoversClass(CacheConfiguration::class)] +#[Group('DDC-2183')] +class CacheConfigTest extends TestCase { - /** @var CacheConfiguration */ - private $config; + private CacheConfiguration $config; protected function setUp(): void { diff --git a/tests/Tests/ORM/Cache/CacheKeyTest.php b/tests/Tests/ORM/Cache/CacheKeyTest.php index 4dc6c23d0c7..ee51e3fc9b5 100644 --- a/tests/Tests/ORM/Cache/CacheKeyTest.php +++ b/tests/Tests/ORM/Cache/CacheKeyTest.php @@ -4,17 +4,15 @@ namespace Doctrine\Tests\ORM\Cache; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\Cache\CacheKey; use Doctrine\ORM\Cache\CollectionCacheKey; use Doctrine\ORM\Cache\EntityCacheKey; -use Doctrine\Tests\DoctrineTestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; -/** @group DDC-2183 */ -class CacheKeyTest extends DoctrineTestCase +#[Group('DDC-2183')] +class CacheKeyTest extends TestCase { - use VerifyDeprecations; - public function testEntityCacheKeyIdentifierCollision(): void { $key1 = new EntityCacheKey('Foo', ['id' => 1]); @@ -78,20 +76,4 @@ public function testConstructor(): void self::assertSame('my-hash', $key->hash); } - - public function testDeprecatedConstructor(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10212'); - - $key = new class extends CacheKey { - public function __construct() - { - $this->hash = 'my-hash'; - - parent::__construct(); - } - }; - - self::assertSame('my-hash', $key->hash); - } } diff --git a/tests/Tests/ORM/Cache/CacheLoggerChainTest.php b/tests/Tests/ORM/Cache/CacheLoggerChainTest.php index d2ddc7f4a11..0edb3faf5f7 100644 --- a/tests/Tests/ORM/Cache/CacheLoggerChainTest.php +++ b/tests/Tests/ORM/Cache/CacheLoggerChainTest.php @@ -9,18 +9,16 @@ use Doctrine\ORM\Cache\Logging\CacheLogger; use Doctrine\ORM\Cache\Logging\CacheLoggerChain; use Doctrine\ORM\Cache\QueryCacheKey; -use Doctrine\Tests\DoctrineTestCase; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -/** @group DDC-2183 */ -class CacheLoggerChainTest extends DoctrineTestCase +#[Group('DDC-2183')] +class CacheLoggerChainTest extends TestCase { - /** @var CacheLoggerChain */ - private $logger; - - /** @var CacheLogger&MockObject */ - private $mock; + private CacheLoggerChain $logger; + private CacheLogger&MockObject $mock; protected function setUp(): void { diff --git a/tests/Tests/ORM/Cache/DefaultCacheFactoryTest.php b/tests/Tests/ORM/Cache/DefaultCacheFactoryTest.php index 0a3680d7140..bdb3d2ae9f9 100644 --- a/tests/Tests/ORM/Cache/DefaultCacheFactoryTest.php +++ b/tests/Tests/ORM/Cache/DefaultCacheFactoryTest.php @@ -26,25 +26,18 @@ use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; use InvalidArgumentException; use LogicException; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use Psr\Cache\CacheItemPoolInterface; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class DefaultCacheFactoryTest extends OrmTestCase { - use MockBuilderCompatibilityTools; - - /** @var CacheFactory&MockObject */ - private $factory; - - /** @var EntityManagerMock */ - private $em; - - /** @var RegionsConfiguration */ - private $regionsConfig; + private CacheFactory&MockObject $factory; + private EntityManagerMock $em; + private RegionsConfiguration $regionsConfig; protected function setUp(): void { @@ -55,7 +48,8 @@ protected function setUp(): void $this->em = $this->getTestEntityManager(); $this->regionsConfig = new RegionsConfiguration(); $arguments = [$this->regionsConfig, $this->getSharedSecondLevelCache()]; - $this->factory = $this->getMockBuilderWithOnlyMethods(DefaultCacheFactory::class, ['getRegion']) + $this->factory = $this->getMockBuilder(DefaultCacheFactory::class) + ->onlyMethods(['getRegion']) ->setConstructorArgs($arguments) ->getMock(); } @@ -133,11 +127,11 @@ public function testBuildCachedCollectionPersisterReadOnly(): void $persister = new OneToManyPersister($em); $region = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache())); - $mapping['cache']['usage'] = ClassMetadata::CACHE_USAGE_READ_ONLY; + $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_ONLY; $this->factory->expects(self::once()) ->method('getRegion') - ->with(self::equalTo($mapping['cache'])) + ->with(self::equalTo($mapping->cache)) ->willReturn($region); $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping); @@ -154,11 +148,11 @@ public function testBuildCachedCollectionPersisterReadWrite(): void $persister = new OneToManyPersister($em); $region = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache())); - $mapping['cache']['usage'] = ClassMetadata::CACHE_USAGE_READ_WRITE; + $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_READ_WRITE; $this->factory->expects(self::once()) ->method('getRegion') - ->with(self::equalTo($mapping['cache'])) + ->with(self::equalTo($mapping->cache)) ->willReturn($region); $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping); @@ -175,11 +169,11 @@ public function testBuildCachedCollectionPersisterNonStrictReadWrite(): void $persister = new OneToManyPersister($em); $region = new ConcurrentRegionMock(new DefaultRegion('regionName', $this->getSharedSecondLevelCache())); - $mapping['cache']['usage'] = ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE; + $mapping->cache['usage'] = ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE; $this->factory->expects(self::once()) ->method('getRegion') - ->with(self::equalTo($mapping['cache'])) + ->with(self::equalTo($mapping->cache)) ->willReturn($region); $cachedPersister = $this->factory->buildCachedCollectionPersister($em, $persister, $mapping); @@ -247,7 +241,7 @@ public function testBuildCachedCollectionPersisterException(): void $mapping = $metadata->associationMappings['cities']; $persister = new OneToManyPersister($em); - $mapping['cache']['usage'] = -1; + $mapping->cache['usage'] = -1; $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Unrecognized access strategy type [-1]'); @@ -263,14 +257,14 @@ public function testInvalidFileLockRegionDirectoryException(): void $this->expectExceptionMessage( 'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" ' . 'is required, The default implementation provided by doctrine is ' - . '"Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory' + . '"Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory', ); $factory->getRegion( [ 'usage' => ClassMetadata::CACHE_USAGE_READ_WRITE, 'region' => 'foo', - ] + ], ); } @@ -284,14 +278,14 @@ public function testInvalidFileLockRegionDirectoryExceptionWithEmptyString(): vo $this->expectExceptionMessage( 'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" ' . 'is required, The default implementation provided by doctrine is ' - . '"Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory' + . '"Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory', ); $factory->getRegion( [ 'usage' => ClassMetadata::CACHE_USAGE_READ_WRITE, 'region' => 'foo', - ] + ], ); } @@ -305,8 +299,8 @@ public function testBuildsDefaultCacheRegionFromGenericCacheRegion(): void [ 'region' => 'bar', 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, - ] - ) + ], + ), ); } } diff --git a/tests/Tests/ORM/Cache/DefaultCacheTest.php b/tests/Tests/ORM/Cache/DefaultCacheTest.php index 389de9255c8..cd30af1322b 100644 --- a/tests/Tests/ORM/Cache/DefaultCacheTest.php +++ b/tests/Tests/ORM/Cache/DefaultCacheTest.php @@ -10,24 +10,24 @@ use Doctrine\ORM\Cache\DefaultCache; use Doctrine\ORM\Cache\EntityCacheEntry; use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCache; +use Doctrine\ORM\Cache\Region; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use ReflectionMethod; use ReflectionProperty; use function array_merge; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class DefaultCacheTest extends OrmTestCase { - /** @var DefaultCache */ - private $cache; - - /** @var EntityManagerMock */ - private $em; + private DefaultCache $cache; + private EntityManagerMock $em; protected function setUp(): void { @@ -66,13 +66,13 @@ public function testImplementsCache(): void public function testGetEntityCacheRegionAccess(): void { - self::assertInstanceOf(Cache\Region::class, $this->cache->getEntityCacheRegion(State::class)); + self::assertInstanceOf(Region::class, $this->cache->getEntityCacheRegion(State::class)); self::assertNull($this->cache->getEntityCacheRegion(CmsUser::class)); } public function testGetCollectionCacheRegionAccess(): void { - self::assertInstanceOf(Cache\Region::class, $this->cache->getCollectionCacheRegion(State::class, 'cities')); + self::assertInstanceOf(Region::class, $this->cache->getCollectionCacheRegion(State::class, 'cities')); self::assertNull($this->cache->getCollectionCacheRegion(CmsUser::class, 'phonenumbers')); } @@ -221,8 +221,8 @@ public function testQueryCache(): void $defaultQueryCache = $this->cache->getQueryCache(); $fooQueryCache = $this->cache->getQueryCache('foo'); - self::assertInstanceOf(Cache\QueryCache::class, $defaultQueryCache); - self::assertInstanceOf(Cache\QueryCache::class, $fooQueryCache); + self::assertInstanceOf(QueryCache::class, $defaultQueryCache); + self::assertInstanceOf(QueryCache::class, $fooQueryCache); self::assertSame($defaultQueryCache, $this->cache->getQueryCache()); self::assertSame($fooQueryCache, $this->cache->getQueryCache('foo')); @@ -244,8 +244,6 @@ public function testToIdentifierArrayShouldLookupForEntityIdentifier(): void $method = new ReflectionMethod($this->cache, 'toIdentifierArray'); $property = new ReflectionProperty($entity, 'id'); - $property->setAccessible(true); - $method->setAccessible(true); $property->setValue($entity, $identifier); self::assertEquals(['id' => $identifier], $method->invoke($this->cache, $metadata, $identifier)); diff --git a/tests/Tests/ORM/Cache/DefaultCollectionHydratorTest.php b/tests/Tests/ORM/Cache/DefaultCollectionHydratorTest.php index 339f1f8ffb5..b5d0217657d 100644 --- a/tests/Tests/ORM/Cache/DefaultCollectionHydratorTest.php +++ b/tests/Tests/ORM/Cache/DefaultCollectionHydratorTest.php @@ -15,12 +15,12 @@ use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class DefaultCollectionHydratorTest extends OrmFunctionalTestCase { - /** @var DefaultCollectionHydrator */ - private $structure; + private DefaultCollectionHydrator $structure; protected function setUp(): void { @@ -43,7 +43,7 @@ public function testLoadCacheCollection(): void [ new EntityCacheKey(City::class, ['id' => 31]), new EntityCacheKey(City::class, ['id' => 32]), - ] + ], ); $targetRegion->put(new EntityCacheKey(City::class, ['id' => 31]), new EntityCacheEntry(City::class, ['id' => 31, 'name' => 'Foo'])); diff --git a/tests/Tests/ORM/Cache/DefaultEntityHydratorTest.php b/tests/Tests/ORM/Cache/DefaultEntityHydratorTest.php index 471aae7830c..e3271be38cc 100644 --- a/tests/Tests/ORM/Cache/DefaultEntityHydratorTest.php +++ b/tests/Tests/ORM/Cache/DefaultEntityHydratorTest.php @@ -10,20 +10,18 @@ use Doctrine\ORM\Cache\EntityCacheEntry; use Doctrine\ORM\Cache\EntityCacheKey; use Doctrine\ORM\Cache\EntityHydrator; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\UnitOfWork; +use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class DefaultEntityHydratorTest extends OrmTestCase { - /** @var EntityHydrator */ - private $structure; - - /** @var EntityManagerInterface */ - private $em; + private DefaultEntityHydrator $structure; + private EntityManagerMock $em; protected function setUp(): void { @@ -35,7 +33,7 @@ protected function setUp(): void public function testImplementsEntityEntryStructure(): void { - self::assertInstanceOf('\Doctrine\ORM\Cache\EntityHydrator', $this->structure); + self::assertInstanceOf(EntityHydrator::class, $this->structure); } public function testCreateEntity(): void @@ -91,7 +89,7 @@ public function testBuildCacheEntry(): void 'id' => 1, 'name' => 'Foo', ], - $cache->data + $cache->data, ); } @@ -125,7 +123,7 @@ public function testBuildCacheEntryAssociation(): void 'name' => 'Bar', 'country' => new AssociationCacheEntry(Country::class, ['id' => 11]), ], - $cache->data + $cache->data, ); } @@ -156,7 +154,7 @@ public function testBuildCacheEntryNonInitializedAssocProxy(): void 'name' => 'Bar', 'country' => new AssociationCacheEntry(Country::class, ['id' => 11]), ], - $cache->data + $cache->data, ); } @@ -188,7 +186,7 @@ public function testCacheEntryWithWrongIdentifierType(): void 'name' => 'Bar', 'country' => new AssociationCacheEntry(Country::class, ['id' => 11]), ], - $cache->data + $cache->data, ); } } diff --git a/tests/Tests/ORM/Cache/DefaultQueryCacheTest.php b/tests/Tests/ORM/Cache/DefaultQueryCacheTest.php index 2e0d768a3fe..2edc63afd81 100644 --- a/tests/Tests/ORM/Cache/DefaultQueryCacheTest.php +++ b/tests/Tests/ORM/Cache/DefaultQueryCacheTest.php @@ -6,6 +6,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\DefaultCacheFactory; use Doctrine\ORM\Cache\DefaultQueryCache; use Doctrine\ORM\Cache\EntityCacheEntry; use Doctrine\ORM\Cache\EntityCacheKey; @@ -25,22 +26,18 @@ use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\Models\Generic\BooleanModel; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use ReflectionMethod; use function microtime; use function sprintf; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class DefaultQueryCacheTest extends OrmTestCase { - /** @var DefaultQueryCache */ - private $queryCache; - - /** @var EntityManagerMock */ - private $em; - - /** @var CacheRegionMock */ - private $region; + private DefaultQueryCache $queryCache; + private EntityManagerMock $em; + private CacheRegionMock $region; protected function setUp(): void { @@ -273,7 +270,7 @@ public function testGetBasicQueryResult(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], ); $data = [ @@ -288,7 +285,7 @@ public function testGetBasicQueryResult(): void [ new EntityCacheEntry(Country::class, $data[0]), new EntityCacheEntry(Country::class, $data[1]), - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -312,7 +309,7 @@ public function testGetWithAssociation(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], ); $data = [ @@ -327,7 +324,7 @@ public function testGetWithAssociation(): void [ new EntityCacheEntry(Country::class, $data[0]), new EntityCacheEntry(Country::class, $data[1]), - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -351,7 +348,7 @@ public function testGetWithAssociationCacheMiss(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], ); $this->region->addReturn('get', $entry); @@ -361,7 +358,7 @@ public function testGetWithAssociationCacheMiss(): void [ new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']), false, - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -468,7 +465,7 @@ public function testIgnoreCacheNonGetMode(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -507,15 +504,14 @@ public function testGetShouldIgnoreOldQueryCacheEntryResult(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], + microtime(true) - 100, ); $entities = [ ['id' => 1, 'name' => 'Foo'], ['id' => 2, 'name' => 'Bar'], ]; - $entry->time = microtime(true) - 100; - $this->region->addReturn('get', $entry); $this->region->addReturn( @@ -523,7 +519,7 @@ public function testGetShouldIgnoreOldQueryCacheEntryResult(): void [ new EntityCacheEntry(Country::class, $entities[0]), new EntityCacheEntry(Country::class, $entities[1]), - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -552,7 +548,7 @@ public function testGetShouldIgnoreNonQueryCacheEntryResult(): void [ new EntityCacheEntry(Country::class, $data[0]), new EntityCacheEntry(Country::class, $data[1]), - ] + ], ); $rsm->addRootEntityFromClassMetadata(Country::class, 'c'); @@ -568,7 +564,7 @@ public function testGetShouldIgnoreMissingEntityQueryCacheEntry(): void [ ['identifier' => ['id' => 1]], ['identifier' => ['id' => 2]], - ] + ], ); $this->region->addReturn('get', $entry); @@ -585,8 +581,6 @@ public function testGetAssociationValue(): void $rsm = new ResultSetMappingBuilder($this->em); $key = new QueryCacheKey('query.key1', 0); - $reflection->setAccessible(true); - $germany = new Country('Germany'); $bavaria = new State('Bavaria', $germany); $wurzburg = new City('WΓΌrzburg', $bavaria); @@ -674,21 +668,15 @@ public function testNotCacheableEntityException(): void } } -class CacheFactoryDefaultQueryCacheTest extends Cache\DefaultCacheFactory +class CacheFactoryDefaultQueryCacheTest extends DefaultCacheFactory { - /** @var DefaultQueryCache */ - private $queryCache; - - /** @var CacheRegionMock */ - private $region; - - public function __construct(DefaultQueryCache $queryCache, CacheRegionMock $region) - { - $this->queryCache = $queryCache; - $this->region = $region; + public function __construct( + private DefaultQueryCache $queryCache, + private CacheRegionMock $region, + ) { } - public function buildQueryCache(EntityManagerInterface $em, $regionName = null): DefaultQueryCache + public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): DefaultQueryCache { return $this->queryCache; } diff --git a/tests/Tests/ORM/Cache/DefaultRegionLegacyTest.php b/tests/Tests/ORM/Cache/DefaultRegionLegacyTest.php deleted file mode 100644 index 54f07d4d652..00000000000 --- a/tests/Tests/ORM/Cache/DefaultRegionLegacyTest.php +++ /dev/null @@ -1,22 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9322'); - - return new DefaultRegion('default.region.test', DoctrineProvider::wrap($this->cacheItemPool)); - } -} diff --git a/tests/Tests/ORM/Cache/DefaultRegionTest.php b/tests/Tests/ORM/Cache/DefaultRegionTest.php index 74bb8c00daf..b9f21e36209 100644 --- a/tests/Tests/ORM/Cache/DefaultRegionTest.php +++ b/tests/Tests/ORM/Cache/DefaultRegionTest.php @@ -4,23 +4,20 @@ namespace Doctrine\Tests\ORM\Cache; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\ORM\Cache\CollectionCacheEntry; -use Doctrine\ORM\Cache\Region; use Doctrine\ORM\Cache\Region\DefaultRegion; use Doctrine\Tests\Mocks\CacheEntryMock; use Doctrine\Tests\Mocks\CacheKeyMock; -use Symfony\Component\Cache\Adapter\ArrayAdapter; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use function array_map; -/** - * @extends RegionTestCase - * @group DDC-2183 - */ +/** @extends RegionTestCase */ +#[Group('DDC-2183')] class DefaultRegionTest extends RegionTestCase { - protected function createRegion(): Region + protected function createRegion(): DefaultRegion { return new DefaultRegion('default.region.test', $this->cacheItemPool); } @@ -28,7 +25,6 @@ protected function createRegion(): Region public function testGetters(): void { self::assertEquals('default.region.test', $this->region->getName()); - self::assertSame($this->cacheItemPool, $this->region->getCache()->getPool()); } public function testSharedRegion(): void @@ -53,18 +49,6 @@ public function testSharedRegion(): void self::assertTrue($region2->contains($key)); } - public function testDoesNotModifyCacheNamespace(): void - { - $cache = DoctrineProvider::wrap(new ArrayAdapter()); - - $cache->setNamespace('foo'); - - new DefaultRegion('bar', $cache); - new DefaultRegion('baz', $cache); - - self::assertSame('foo', $cache->getNamespace()); - } - public function testGetMulti(): void { $key1 = new CacheKeyMock('key.1'); @@ -101,7 +85,7 @@ public function testGetMultiPreservesOrderAndKeys(): void $actual = array_map( 'iterator_to_array', - $this->region->getMultiple(new CollectionCacheEntry(['one' => $key1, 'two' => $key2])) + $this->region->getMultiple(new CollectionCacheEntry(['one' => $key1, 'two' => $key2])), ); self::assertSame([ @@ -110,34 +94,30 @@ public function testGetMultiPreservesOrderAndKeys(): void ], $actual); } - /** - * @test - * @group GH7266 - */ + #[Test] + #[Group('GH7266')] public function corruptedDataDoesNotLeakIntoApplicationWhenGettingSingleEntry(): void { $key1 = new CacheKeyMock('key.1'); $this->cacheItemPool->save( $this->cacheItemPool ->getItem('DC2_REGION_' . $this->region->getName() . '_' . $key1->hash) - ->set('a-very-invalid-value') + ->set('a-very-invalid-value'), ); self::assertTrue($this->region->contains($key1)); self::assertNull($this->region->get($key1)); } - /** - * @test - * @group GH7266 - */ + #[Test] + #[Group('GH7266')] public function corruptedDataDoesNotLeakIntoApplicationWhenGettingMultipleEntries(): void { $key1 = new CacheKeyMock('key.1'); $this->cacheItemPool->save( $this->cacheItemPool ->getItem('DC2_REGION_' . $this->region->getName() . '_' . $key1->hash) - ->set('a-very-invalid-value') + ->set('a-very-invalid-value'), ); self::assertTrue($this->region->contains($key1)); diff --git a/tests/Tests/ORM/Cache/FileLockRegionTest.php b/tests/Tests/ORM/Cache/FileLockRegionTest.php index 79d8a3401e0..bf6fe97ab94 100644 --- a/tests/Tests/ORM/Cache/FileLockRegionTest.php +++ b/tests/Tests/ORM/Cache/FileLockRegionTest.php @@ -6,36 +6,27 @@ use Doctrine\ORM\Cache\CacheKey; use Doctrine\ORM\Cache\Lock; -use Doctrine\ORM\Cache\Region; use Doctrine\ORM\Cache\Region\DefaultRegion; use Doctrine\ORM\Cache\Region\FileLockRegion; use Doctrine\Tests\Mocks\CacheEntryMock; use Doctrine\Tests\Mocks\CacheKeyMock; +use PHPUnit\Framework\Attributes\Group; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use ReflectionMethod; -use ReflectionProperty; use function file_put_contents; use function is_dir; -use function restore_error_handler; use function rmdir; -use function set_error_handler; -use function str_repeat; use function sys_get_temp_dir; use function uniqid; use function unlink; -use const E_WARNING; - -/** - * @extends RegionTestCase - * @group DDC-2183 - */ +/** @extends RegionTestCase */ +#[Group('DDC-2183')] class FileLockRegionTest extends RegionTestCase { - /** @var string */ - protected $directory; + protected string $directory; public function tearDown(): void { @@ -46,18 +37,16 @@ private function getFileName(FileLockRegion $region, CacheKey $key): string { $reflection = new ReflectionMethod($region, 'getLockFileName'); - $reflection->setAccessible(true); - return $reflection->invoke($region, $key); } - protected function createRegion(): Region + protected function createRegion(int $lockLifetime = 60): FileLockRegion { $this->directory = sys_get_temp_dir() . '/doctrine_lock_' . uniqid(); $region = new DefaultRegion('concurren_region_test', $this->cacheItemPool); - return new FileLockRegion($region, $this->directory, 60); + return new FileLockRegion($region, $this->directory, $lockLifetime); } public function testGetRegionName(): void @@ -222,13 +211,10 @@ public function testLockedEvictAll(): void public function testLockLifetime(): void { - $key = new CacheKeyMock('key'); - $entry = new CacheEntryMock(['foo' => 'bar']); - $file = $this->getFileName($this->region, $key); - $property = new ReflectionProperty($this->region, 'lockLifetime'); - - $property->setAccessible(true); - $property->setValue($this->region, -10); + $this->region = $this->createRegion(-10); + $key = new CacheKeyMock('key'); + $entry = new CacheEntryMock(['foo' => 'bar']); + $file = $this->getFileName($this->region, $key); self::assertFalse($this->region->contains($key)); self::assertTrue($this->region->put($key, $entry)); @@ -244,29 +230,7 @@ public function testLockLifetime(): void self::assertFileDoesNotExist($file); } - /** - * @group 1072 - * @group DDC-3191 - */ - public function testHandlesScanErrorsGracefullyOnEvictAll(): void - { - $region = $this->createRegion(); - $reflectionDirectory = new ReflectionProperty($region, 'directory'); - - $reflectionDirectory->setAccessible(true); - $reflectionDirectory->setValue($region, str_repeat('a', 10000)); - - set_error_handler(static function (): bool { - return true; - }, E_WARNING); - try { - self::assertTrue($region->evictAll()); - } finally { - restore_error_handler(); - } - } - - private function cleanTestDirectory(?string $path): void + private function cleanTestDirectory(string|null $path): void { $path = $path ?: $this->directory; @@ -276,7 +240,7 @@ private function cleanTestDirectory(?string $path): void $directoryIterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path), - RecursiveIteratorIterator::CHILD_FIRST + RecursiveIteratorIterator::CHILD_FIRST, ); foreach ($directoryIterator as $file) { diff --git a/tests/Tests/ORM/Cache/MultiGetRegionTest.php b/tests/Tests/ORM/Cache/MultiGetRegionTest.php deleted file mode 100644 index 89f3e36dc61..00000000000 --- a/tests/Tests/ORM/Cache/MultiGetRegionTest.php +++ /dev/null @@ -1,60 +0,0 @@ - */ -class MultiGetRegionTest extends RegionTestCase -{ - protected function createRegion(): Region - { - return new DefaultMultiGetRegion('default.region.test', $this->cacheItemPool); - } - - public function testGetMulti(): void - { - $key1 = new CacheKeyMock('key.1'); - $value1 = new CacheEntryMock(['id' => 1, 'name' => 'bar']); - - $key2 = new CacheKeyMock('key.2'); - $value2 = new CacheEntryMock(['id' => 2, 'name' => 'bar']); - - self::assertFalse($this->region->contains($key1)); - self::assertFalse($this->region->contains($key2)); - - $this->region->put($key1, $value1); - $this->region->put($key2, $value2); - - self::assertTrue($this->region->contains($key1)); - self::assertTrue($this->region->contains($key2)); - - $actual = $this->region->getMultiple(new CollectionCacheEntry([$key1, $key2])); - - self::assertEquals($value1, $actual[0]); - self::assertEquals($value2, $actual[1]); - } - - /** - * @test - * @group GH7266 - */ - public function corruptedDataDoesNotLeakIntoApplication(): void - { - $key1 = new CacheKeyMock('key.1'); - $this->cacheItemPool->save( - $this->cacheItemPool - ->getItem('DC2_REGION_' . $this->region->getName() . '_' . $key1->hash) - ->set('a-very-invalid-value') - ); - - self::assertTrue($this->region->contains($key1)); - self::assertNull($this->region->getMultiple(new CollectionCacheEntry([$key1]))); - } -} diff --git a/tests/Tests/ORM/Cache/Persister/Collection/CollectionPersisterTestCase.php b/tests/Tests/ORM/Cache/Persister/Collection/CollectionPersisterTestCase.php index f9818a7603e..5318f96dff0 100644 --- a/tests/Tests/ORM/Cache/Persister/Collection/CollectionPersisterTestCase.php +++ b/tests/Tests/ORM/Cache/Persister/Collection/CollectionPersisterTestCase.php @@ -10,26 +10,28 @@ use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister; use Doctrine\ORM\Cache\Region; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; -/** @group DDC-2183 */ +#[Group('DDC-2183')] abstract class CollectionPersisterTestCase extends OrmTestCase { - /** @var Region&MockObject */ - protected $region; + protected Region&MockObject $region; + protected CollectionPersister&MockObject $collectionPersister; + protected EntityManagerMock $em; - /** @var CollectionPersister&MockObject */ - protected $collectionPersister; - - /** @var EntityManagerMock */ - protected $em; - - abstract protected function createPersister(EntityManagerInterface $em, CollectionPersister $persister, Region $region, array $mapping): AbstractCollectionPersister; + abstract protected function createPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + Region $region, + AssociationMapping $mapping, + ): AbstractCollectionPersister; protected function setUp(): void { @@ -43,14 +45,12 @@ protected function setUp(): void $this->collectionPersister = $this->createMock(CollectionPersister::class); } - /** @return Region&MockObject */ - protected function createRegion(): Region + protected function createRegion(): Region&MockObject { return $this->createMock(Region::class); } - /** @param object $owner */ - protected function createCollection($owner): PersistentCollection + protected function createCollection(object $owner): PersistentCollection { $class = $this->em->getClassMetadata(State::class); $assoc = $class->associationMappings['cities']; diff --git a/tests/Tests/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersisterTest.php index 27bb54af970..4b79d021000 100644 --- a/tests/Tests/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersisterTest.php @@ -8,16 +8,19 @@ use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister; use Doctrine\ORM\Cache\Region; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class NonStrictReadWriteCachedCollectionPersisterTest extends CollectionPersisterTestCase { - /** - * {@inheritDoc} - */ - protected function createPersister(EntityManagerInterface $em, CollectionPersister $persister, Region $region, array $mapping): AbstractCollectionPersister - { + protected function createPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + Region $region, + AssociationMapping $mapping, + ): AbstractCollectionPersister { return new NonStrictReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); } } diff --git a/tests/Tests/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersisterTest.php index 34f4b003400..8fb07fa4b3e 100644 --- a/tests/Tests/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersisterTest.php @@ -8,13 +8,19 @@ use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister; use Doctrine\ORM\Cache\Region; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ReadOnlyCachedCollectionPersisterTest extends CollectionPersisterTestCase { - protected function createPersister(EntityManagerInterface $em, CollectionPersister $persister, Region $region, array $mapping): AbstractCollectionPersister - { + protected function createPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + Region $region, + AssociationMapping $mapping, + ): AbstractCollectionPersister { return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping); } } diff --git a/tests/Tests/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersisterTest.php index f06845db9f2..2b87882fa60 100644 --- a/tests/Tests/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersisterTest.php @@ -11,19 +11,26 @@ use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister; use Doctrine\ORM\Cache\Region; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\MockObject\MockObject; use ReflectionProperty; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ReadWriteCachedCollectionPersisterTest extends CollectionPersisterTestCase { - protected function createPersister(EntityManagerInterface $em, CollectionPersister $persister, Region $region, array $mapping): AbstractCollectionPersister - { + protected function createPersister( + EntityManagerInterface $em, + CollectionPersister $persister, + Region $region, + AssociationMapping $mapping, + ): AbstractCollectionPersister { return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); } - protected function createRegion(): Region + protected function createRegion(): ConcurrentRegion&MockObject { return $this->createMock(ConcurrentRegion::class); } @@ -121,8 +128,6 @@ public function testTransactionRollBackDeleteShouldClearQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -153,8 +158,6 @@ public function testTransactionRollBackUpdateShouldClearQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -185,8 +188,6 @@ public function testTransactionRollCommitDeleteShouldClearQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -217,8 +218,6 @@ public function testTransactionRollCommitUpdateShouldClearQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -248,8 +247,6 @@ public function testDeleteLockFailureShouldIgnoreQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -273,8 +270,6 @@ public function testUpdateLockFailureShouldIgnoreQueue(): void $key = new CollectionCacheKey(State::class, 'cities', ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedCollectionPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) diff --git a/tests/Tests/ORM/Cache/Persister/Entity/EntityPersisterTestCase.php b/tests/Tests/ORM/Cache/Persister/Entity/EntityPersisterTestCase.php index 9294df28e15..c590f3a04e1 100644 --- a/tests/Tests/ORM/Cache/Persister/Entity/EntityPersisterTestCase.php +++ b/tests/Tests/ORM/Cache/Persister/Entity/EntityPersisterTestCase.php @@ -13,25 +13,22 @@ use Doctrine\ORM\Cache\Region; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\OneToOneInverseSideMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Persisters\Entity\EntityPersister; use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; -/** @group DDC-2183 */ +#[Group('DDC-2183')] abstract class EntityPersisterTestCase extends OrmTestCase { - /** @var Region&MockObject */ - protected $region; - - /** @var EntityPersister&MockObject */ - protected $entityPersister; - - /** @var EntityManagerMock */ - protected $em; + protected Region&MockObject $region; + protected EntityPersister&MockObject $entityPersister; + protected EntityManagerMock $em; abstract protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister; @@ -47,8 +44,7 @@ protected function setUp(): void $this->entityPersister = $this->createMock(EntityPersister::class); } - /** @return Region&MockObject */ - protected function createRegion(): Region + protected function createRegion(): Region&MockObject { return $this->createMock(Region::class); } @@ -95,25 +91,27 @@ public function testInvokeGetSelectSQL(): void { $persister = $this->createPersisterDefault(); + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('getSelectSQL') ->with( self::identicalTo(['name' => 'Foo']), - self::identicalTo([0]), + self::identicalTo($associationMapping), self::identicalTo(LockMode::OPTIMISTIC), self::identicalTo(2), self::identicalTo(3), - self::identicalTo([4]) + self::identicalTo([4]), ) ->willReturn('SELECT * FROM foo WERE name = ?'); self::assertSame('SELECT * FROM foo WERE name = ?', $persister->getSelectSQL( ['name' => 'Foo'], - [0], + $associationMapping, LockMode::OPTIMISTIC, 2, 3, - [4] + [4], )); } @@ -157,12 +155,18 @@ public function testInvokeSelectConditionStatementSQL(): void { $persister = $this->createPersisterDefault(); + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('getSelectConditionStatementSQL') - ->with(self::identicalTo('id'), self::identicalTo(1), self::identicalTo([]), self::identicalTo('=')) - ->willReturn('name = 1'); + ->with( + self::identicalTo('id'), + self::identicalTo(1), + self::identicalTo($associationMapping), + self::identicalTo('='), + )->willReturn('name = 1'); - self::assertSame('name = 1', $persister->getSelectConditionStatementSQL('id', 1, [], '=')); + self::assertSame('name = 1', $persister->getSelectConditionStatementSQL('id', 1, $associationMapping, '=')); } public function testInvokeExecuteInserts(): void @@ -170,10 +174,9 @@ public function testInvokeExecuteInserts(): void $persister = $this->createPersisterDefault(); $this->entityPersister->expects(self::once()) - ->method('executeInserts') - ->willReturn(['id' => 1]); + ->method('executeInserts'); - self::assertSame(['id' => 1], $persister->executeInserts()); + $persister->executeInserts(); } public function testInvokeUpdate(): void @@ -222,27 +225,29 @@ public function testInvokeLoad(): void $persister = $this->createPersisterDefault(); $entity = new Country('Foo'); + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('load') ->with( self::identicalTo(['id' => 1]), self::identicalTo($entity), - self::identicalTo([0]), + self::identicalTo($associationMapping), self::identicalTo([1]), self::identicalTo(LockMode::PESSIMISTIC_READ), self::identicalTo(3), - self::identicalTo([4]) + self::identicalTo([4]), ) ->willReturn($entity); self::assertSame($entity, $persister->load( ['id' => 1], $entity, - [0], + $associationMapping, [1], LockMode::PESSIMISTIC_READ, 3, - [4] + [4], )); } @@ -288,12 +293,14 @@ public function testInvokeLoadOneToOneEntity(): void $entity = new Country('Foo'); $owner = (object) []; + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('loadOneToOneEntity') - ->with(self::identicalTo([]), self::identicalTo($owner), self::identicalTo(['id' => 11])) + ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(['id' => 11])) ->willReturn($entity); - self::assertSame($entity, $persister->loadOneToOneEntity([], $owner, ['id' => 11])); + self::assertSame($entity, $persister->loadOneToOneEntity($associationMapping, $owner, ['id' => 11])); } public function testInvokeRefresh(): void @@ -337,12 +344,14 @@ public function testInvokeGetManyToManyCollection(): void $entity = new Country('Foo'); $owner = (object) []; + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('getManyToManyCollection') - ->with(self::identicalTo([]), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2)) + ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2)) ->willReturn([$entity]); - self::assertSame([$entity], $persister->getManyToManyCollection([], $owner, 1, 2)); + self::assertSame([$entity], $persister->getManyToManyCollection($associationMapping, $owner, 1, 2)); } public function testInvokeGetOneToManyCollection(): void @@ -351,18 +360,20 @@ public function testInvokeGetOneToManyCollection(): void $entity = new Country('Foo'); $owner = (object) []; + $associationMapping = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); + $this->entityPersister->expects(self::once()) ->method('getOneToManyCollection') - ->with(self::identicalTo([]), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2)) + ->with(self::identicalTo($associationMapping), self::identicalTo($owner), self::identicalTo(1), self::identicalTo(2)) ->willReturn([$entity]); - self::assertSame([$entity], $persister->getOneToManyCollection([], $owner, 1, 2)); + self::assertSame([$entity], $persister->getOneToManyCollection($associationMapping, $owner, 1, 2)); } public function testInvokeLoadManyToManyCollection(): void { $mapping = $this->em->getClassMetadata(Country::class); - $assoc = ['type' => 1]; + $assoc = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); $coll = new PersistentCollection($this->em, $mapping, new ArrayCollection()); $persister = $this->createPersisterDefault(); $entity = new Country('Foo'); @@ -379,7 +390,7 @@ public function testInvokeLoadManyToManyCollection(): void public function testInvokeLoadOneToManyCollection(): void { $mapping = $this->em->getClassMetadata(Country::class); - $assoc = ['type' => 1]; + $assoc = new OneToOneInverseSideMapping('foo', 'bar', 'baz'); $coll = new PersistentCollection($this->em, $mapping, new ArrayCollection()); $persister = $this->createPersisterDefault(); $entity = new Country('Foo'); diff --git a/tests/Tests/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersisterTest.php index 35ea9ca86b6..fe6c050bc71 100644 --- a/tests/Tests/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersisterTest.php @@ -13,9 +13,10 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Entity\EntityPersister; use Doctrine\Tests\Models\Cache\Country; +use PHPUnit\Framework\Attributes\Group; use ReflectionProperty; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class NonStrictReadWriteCachedEntityPersisterTest extends EntityPersisterTestCase { protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister @@ -29,8 +30,6 @@ public function testTransactionRollBackShouldClearQueue(): void $persister = $this->createPersisterDefault(); $property = new ReflectionProperty($persister, 'queuedCache'); - $property->setAccessible(true); - $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']); $persister->update($entity); @@ -51,8 +50,6 @@ public function testInsertTransactionCommitShouldPutCache(): void $entry = new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']); $property = new ReflectionProperty($persister, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('put') ->with(self::equalTo($key), self::equalTo($entry)); @@ -88,8 +85,6 @@ public function testUpdateTransactionCommitShouldPutCache(): void $entry = new EntityCacheEntry(Country::class, ['id' => 1, 'name' => 'Foo']); $property = new ReflectionProperty($persister, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('put') ->with(self::equalTo($key), self::equalTo($entry)); @@ -116,8 +111,6 @@ public function testDeleteTransactionCommitShouldEvictCache(): void $key = new EntityCacheKey(Country::class, ['id' => 1]); $property = new ReflectionProperty($persister, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('evict') ->with(self::equalTo($key)); diff --git a/tests/Tests/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersisterTest.php index c537d66ffb0..9ced1accddd 100644 --- a/tests/Tests/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersisterTest.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Entity\EntityPersister; use Doctrine\Tests\Models\Cache\Country; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ReadOnlyCachedEntityPersisterTest extends EntityPersisterTestCase { protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister diff --git a/tests/Tests/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersisterTest.php b/tests/Tests/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersisterTest.php index e8f2e38db4c..40fbd20a7c4 100644 --- a/tests/Tests/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersisterTest.php +++ b/tests/Tests/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersisterTest.php @@ -14,9 +14,11 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Persisters\Entity\EntityPersister; use Doctrine\Tests\Models\Cache\Country; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\MockObject\MockObject; use ReflectionProperty; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ReadWriteCachedEntityPersisterTest extends EntityPersisterTestCase { protected function createPersister(EntityManagerInterface $em, EntityPersister $persister, Region $region, ClassMetadata $metadata): AbstractEntityPersister @@ -24,7 +26,7 @@ protected function createPersister(EntityManagerInterface $em, EntityPersister $ return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata); } - protected function createRegion(): Region + protected function createRegion(): ConcurrentRegion&MockObject { return $this->createMock(ConcurrentRegion::class); } @@ -78,7 +80,7 @@ public function testUpdateTransactionRollBackShouldEvictItem(): void $this->region->expects(self::once()) ->method('evict') ->with(self::equalTo($key)) - ->willReturn($lock); + ->willReturn(true); $this->em->getUnitOfWork()->registerManaged($entity, ['id' => 1], ['id' => 1, 'name' => 'Foo']); @@ -116,8 +118,6 @@ public function testTransactionRollBackShouldClearQueue(): void $key = new EntityCacheKey(Country::class, ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::exactly(2)) ->method('lock') ->with(self::equalTo($key)) @@ -147,8 +147,6 @@ public function testTransactionCommitShouldClearQueue(): void $key = new EntityCacheKey(Country::class, ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::exactly(2)) ->method('lock') ->with(self::equalTo($key)) @@ -177,8 +175,6 @@ public function testDeleteLockFailureShouldIgnoreQueue(): void $key = new EntityCacheKey(Country::class, ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) @@ -201,8 +197,6 @@ public function testUpdateLockFailureShouldIgnoreQueue(): void $key = new EntityCacheKey(Country::class, ['id' => 1]); $property = new ReflectionProperty(ReadWriteCachedEntityPersister::class, 'queuedCache'); - $property->setAccessible(true); - $this->region->expects(self::once()) ->method('lock') ->with(self::equalTo($key)) diff --git a/tests/Tests/ORM/Cache/RegionTestCase.php b/tests/Tests/ORM/Cache/RegionTestCase.php index 7f7379ecc34..110a560aa18 100644 --- a/tests/Tests/ORM/Cache/RegionTestCase.php +++ b/tests/Tests/ORM/Cache/RegionTestCase.php @@ -10,23 +10,18 @@ use Doctrine\Tests\Mocks\CacheEntryMock; use Doctrine\Tests\Mocks\CacheKeyMock; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; -/** - * @template TRegion of Region - * @group DDC-2183 - */ +/** @template TRegion of Region */ +#[Group('DDC-2183')] abstract class RegionTestCase extends OrmFunctionalTestCase { - /** - * @var Region - * @phpstan-var TRegion - */ - protected $region; - - /** @var CacheItemPoolInterface */ - protected $cacheItemPool; + /** @phpstan-var TRegion */ + protected Region $region; + protected CacheItemPoolInterface $cacheItemPool; protected function setUp(): void { @@ -48,7 +43,7 @@ public static function dataProviderCacheValues(): array ]; } - /** @dataProvider dataProviderCacheValues */ + #[DataProvider('dataProviderCacheValues')] public function testPutGetContainsEvict(CacheKey $key, CacheEntry $value): void { self::assertFalse($this->region->contains($key)); diff --git a/tests/Tests/ORM/Cache/StatisticsCacheLoggerTest.php b/tests/Tests/ORM/Cache/StatisticsCacheLoggerTest.php index 185069636bb..e0a6b2922a4 100644 --- a/tests/Tests/ORM/Cache/StatisticsCacheLoggerTest.php +++ b/tests/Tests/ORM/Cache/StatisticsCacheLoggerTest.php @@ -8,14 +8,14 @@ use Doctrine\ORM\Cache\EntityCacheKey; use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger; use Doctrine\ORM\Cache\QueryCacheKey; -use Doctrine\Tests\DoctrineTestCase; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; -/** @group DDC-2183 */ -class StatisticsCacheLoggerTest extends DoctrineTestCase +#[Group('DDC-2183')] +class StatisticsCacheLoggerTest extends TestCase { - /** @var StatisticsCacheLogger */ - private $logger; + private StatisticsCacheLogger $logger; protected function setUp(): void { diff --git a/tests/Tests/ORM/ConfigurationTest.php b/tests/Tests/ORM/ConfigurationTest.php index 397666e3e52..269228e72ea 100644 --- a/tests/Tests/ORM/ConfigurationTest.php +++ b/tests/Tests/ORM/ConfigurationTest.php @@ -4,51 +4,28 @@ namespace Doctrine\Tests\ORM; -use Doctrine\Common\Annotations\SimpleAnnotationReader; -use Doctrine\Common\Cache\ArrayCache; -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Common\Persistence\PersistentObject; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\Cache\CacheConfiguration; -use Doctrine\ORM\Cache\Exception\MetadataCacheNotConfigured; -use Doctrine\ORM\Cache\Exception\MetadataCacheUsesNonPersistentCache; -use Doctrine\ORM\Cache\Exception\QueryCacheNotConfigured; -use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Exception\InvalidEntityRepository; -use Doctrine\ORM\Exception\NotSupported; -use Doctrine\ORM\Exception\ORMException; -use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating; -use Doctrine\ORM\Mapping as AnnotationNamespace; +use Doctrine\ORM\Mapping as MappingNamespace; use Doctrine\ORM\Mapping\DefaultTypedFieldMapper; use Doctrine\ORM\Mapping\EntityListenerResolver; use Doctrine\ORM\Mapping\NamingStrategy; -use Doctrine\ORM\Mapping\PrePersist; use Doctrine\ORM\Mapping\QuoteStrategy; use Doctrine\ORM\Proxy\ProxyFactory; -use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Persistence\ObjectRepository; -use Doctrine\Tests\DoctrineTestCase; use Doctrine\Tests\Models\DDC753\DDC753CustomRepository; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; -use ReflectionClass; -use stdClass; -use Symfony\Component\Cache\Adapter\ArrayAdapter; - -use function class_exists; /** * Tests for the Configuration object */ -class ConfigurationTest extends DoctrineTestCase +class ConfigurationTest extends TestCase { - use VerifyDeprecations; - - /** @var Configuration */ - private $configuration; + private Configuration $configuration; protected function setUp(): void { @@ -96,81 +73,12 @@ public function testSetGetMetadataDriverImpl(): void self::assertSame($metadataDriver, $this->configuration->getMetadataDriverImpl()); } - public function testNewDefaultAnnotationDriver(): void - { - $paths = [__DIR__]; - $reflectionClass = new ReflectionClass(ConfigurationTestAnnotationReaderChecker::class); - - $annotationDriver = $this->configuration->newDefaultAnnotationDriver($paths, false, true); - $reader = $annotationDriver->getReader(); - $annotation = $reader->getMethodAnnotation( - $reflectionClass->getMethod('namespacedAnnotationMethod'), - AnnotationNamespace\PrePersist::class - ); - self::assertInstanceOf(AnnotationNamespace\PrePersist::class, $annotation); - } - - public function testNewDefaultAnnotationDriverWithSimpleAnnotationReader(): void - { - if (! class_exists(SimpleAnnotationReader::class)) { - self::markTestSkipped('Requires doctrine/annotations 1.x'); - } - - $paths = [__DIR__]; - $reflectionClass = new ReflectionClass(ConfigurationTestAnnotationReaderChecker::class); - - $annotationDriver = $this->configuration->newDefaultAnnotationDriver($paths); - $reader = $annotationDriver->getReader(); - $annotation = $reader->getMethodAnnotation( - $reflectionClass->getMethod('simpleAnnotationMethod'), - AnnotationNamespace\PrePersist::class - ); - self::assertInstanceOf(AnnotationNamespace\PrePersist::class, $annotation); - } - - public function testSetGetEntityNamespace(): void - { - if (class_exists(PersistentObject::class)) { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8818'); - } else { - $this->expectException(NotSupported::class); - } - - $this->configuration->addEntityNamespace('TestNamespace', __NAMESPACE__); - self::assertSame(__NAMESPACE__, $this->configuration->getEntityNamespace('TestNamespace')); - $namespaces = ['OtherNamespace' => __NAMESPACE__]; - $this->configuration->setEntityNamespaces($namespaces); - self::assertSame($namespaces, $this->configuration->getEntityNamespaces()); - $this->expectException(ORMException::class); - $this->configuration->getEntityNamespace('NonExistingNamespace'); - } - - public function testSetGetQueryCacheImpl(): void - { - self::assertNull($this->configuration->getQueryCacheImpl()); // defaults - self::assertNull($this->configuration->getQueryCache()); // defaults - $queryCacheImpl = $this->createMock(Cache::class); - $this->configuration->setQueryCacheImpl($queryCacheImpl); - self::assertSame($queryCacheImpl, $this->configuration->getQueryCacheImpl()); - self::assertNotNull($this->configuration->getQueryCache()); - } - public function testSetGetQueryCache(): void { self::assertNull($this->configuration->getQueryCache()); // defaults $queryCache = $this->createMock(CacheItemPoolInterface::class); $this->configuration->setQueryCache($queryCache); self::assertSame($queryCache, $this->configuration->getQueryCache()); - self::assertSame($queryCache, CacheAdapter::wrap($this->configuration->getQueryCacheImpl())); - } - - public function testSetGetHydrationCacheImpl(): void - { - self::assertNull($this->configuration->getHydrationCacheImpl()); // defaults - $hydrationCacheImpl = $this->createMock(Cache::class); - $this->configuration->setHydrationCacheImpl($hydrationCacheImpl); - self::assertSame($hydrationCacheImpl, $this->configuration->getHydrationCacheImpl()); - self::assertNotNull($this->configuration->getHydrationCache()); } public function testSetGetHydrationCache(): void @@ -179,16 +87,6 @@ public function testSetGetHydrationCache(): void $hydrationCache = $this->createStub(CacheItemPoolInterface::class); $this->configuration->setHydrationCache($hydrationCache); self::assertSame($hydrationCache, $this->configuration->getHydrationCache()); - self::assertSame($hydrationCache, CacheAdapter::wrap($this->configuration->getHydrationCacheImpl())); - } - - public function testSetGetMetadataCacheImpl(): void - { - self::assertNull($this->configuration->getMetadataCacheImpl()); // defaults - $queryCacheImpl = $this->createMock(Cache::class); - $this->configuration->setMetadataCacheImpl($queryCacheImpl); - self::assertSame($queryCacheImpl, $this->configuration->getMetadataCacheImpl()); - self::assertNotNull($this->configuration->getMetadataCache()); } public function testSetGetMetadataCache(): void @@ -197,151 +95,6 @@ public function testSetGetMetadataCache(): void $cache = $this->createStub(CacheItemPoolInterface::class); $this->configuration->setMetadataCache($cache); self::assertSame($cache, $this->configuration->getMetadataCache()); - self::assertSame($cache, CacheAdapter::wrap($this->configuration->getMetadataCacheImpl())); - } - - public function testAddGetNamedQuery(): void - { - $dql = 'SELECT u FROM User u'; - $this->configuration->addNamedQuery('QueryName', $dql); - self::assertSame($dql, $this->configuration->getNamedQuery('QueryName')); - $this->expectException(ORMException::class); - $this->expectExceptionMessage('a named query'); - $this->configuration->getNamedQuery('NonExistingQuery'); - } - - public function testAddGetNamedNativeQuery(): void - { - $sql = 'SELECT * FROM user'; - $rsm = $this->createMock(ResultSetMapping::class); - $this->configuration->addNamedNativeQuery('QueryName', $sql, $rsm); - $fetched = $this->configuration->getNamedNativeQuery('QueryName'); - self::assertSame($sql, $fetched[0]); - self::assertSame($rsm, $fetched[1]); - $this->expectException(ORMException::class); - $this->expectExceptionMessage('a named native query'); - $this->configuration->getNamedNativeQuery('NonExistingQuery'); - } - - /** - * Configures $this->configuration to use production settings. - * - * @param string|null $skipCache Do not configure a cache of this type, either "query" or "metadata". - */ - protected function setProductionSettings(?string $skipCache = null): void - { - $this->configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_NEVER); - - $cache = $this->createMock(Cache::class); - - if ($skipCache !== 'query') { - $this->configuration->setQueryCacheImpl($cache); - } - - if ($skipCache !== 'metadata') { - $this->configuration->setMetadataCacheImpl($cache); - } - } - - public function testEnsureProductionSettings(): void - { - $this->setProductionSettings(); - $this->configuration->ensureProductionSettings(); - - $this->addToAssertionCount(1); - } - - public function testEnsureProductionSettingsWithNewMetadataCache(): void - { - $this->setProductionSettings('metadata'); - $this->configuration->setMetadataCache(new ArrayAdapter()); - - $this->configuration->ensureProductionSettings(); - - $this->addToAssertionCount(1); - } - - public function testEnsureProductionSettingsMissingQueryCache(): void - { - $this->setProductionSettings('query'); - - $this->expectException(QueryCacheNotConfigured::class); - $this->expectExceptionMessage('Query Cache is not configured.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsMissingMetadataCache(): void - { - $this->setProductionSettings('metadata'); - - $this->expectException(MetadataCacheNotConfigured::class); - $this->expectExceptionMessage('Metadata Cache is not configured.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsQueryArrayCache(): void - { - if (! class_exists(ArrayCache::class)) { - self::markTestSkipped('Test only applies with doctrine/cache 1.x'); - } - - $this->setProductionSettings(); - $this->configuration->setQueryCacheImpl(new ArrayCache()); - - $this->expectException(QueryCacheUsesNonPersistentCache::class); - $this->expectExceptionMessage('Query Cache uses a non-persistent cache driver, Doctrine\Common\Cache\ArrayCache.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsLegacyMetadataArrayCache(): void - { - if (! class_exists(ArrayCache::class)) { - self::markTestSkipped('Test only applies with doctrine/cache 1.x'); - } - - $this->setProductionSettings(); - $this->configuration->setMetadataCacheImpl(new ArrayCache()); - - $this->expectException(MetadataCacheUsesNonPersistentCache::class); - $this->expectExceptionMessage('Metadata Cache uses a non-persistent cache driver, Doctrine\Common\Cache\ArrayCache.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsAutoGenerateProxyClassesAlways(): void - { - $this->setProductionSettings(); - $this->configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_ALWAYS); - - $this->expectException(ProxyClassesAlwaysRegenerating::class); - $this->expectExceptionMessage('Proxy Classes are always regenerating.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsAutoGenerateProxyClassesFileNotExists(): void - { - $this->setProductionSettings(); - $this->configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS); - - $this->expectException(ORMException::class); - $this->expectExceptionMessage('Proxy Classes are always regenerating.'); - - $this->configuration->ensureProductionSettings(); - } - - public function testEnsureProductionSettingsAutoGenerateProxyClassesEval(): void - { - $this->setProductionSettings(); - $this->configuration->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL); - - $this->expectException(ORMException::class); - $this->expectExceptionMessage('Proxy Classes are always regenerating.'); - - $this->configuration->ensureProductionSettings(); } public function testAddGetCustomStringFunction(): void @@ -384,7 +137,7 @@ public function testSetCustomHydrationModes(): void self::assertSame(self::class, $this->configuration->getCustomHydrationMode('HydrationModeName')); $this->configuration->setCustomHydrationModes( - ['AnotherHydrationModeName' => self::class] + ['AnotherHydrationModeName' => self::class], ); self::assertNull($this->configuration->getCustomHydrationMode('HydrationModeName')); @@ -393,7 +146,7 @@ public function testSetCustomHydrationModes(): void public function testSetGetClassMetadataFactoryName(): void { - self::assertSame(AnnotationNamespace\ClassMetadataFactory::class, $this->configuration->getClassMetadataFactoryName()); + self::assertSame(MappingNamespace\ClassMetadataFactory::class, $this->configuration->getClassMetadataFactoryName()); $this->configuration->setClassMetadataFactoryName(self::class); self::assertSame(self::class, $this->configuration->getClassMetadataFactoryName()); } @@ -415,14 +168,6 @@ public function setDefaultRepositoryClassName(): void $this->configuration->setDefaultRepositoryClassName(self::class); } - public function testSetDeprecatedDefaultRepositoryClassName(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9533'); - - $this->configuration->setDefaultRepositoryClassName(DeprecatedRepository::class); - self::assertSame(DeprecatedRepository::class, $this->configuration->getDefaultRepositoryClassName()); - } - public function testSetGetNamingStrategy(): void { self::assertInstanceOf(NamingStrategy::class, $this->configuration->getNamingStrategy()); @@ -439,17 +184,17 @@ public function testSetGetQuoteStrategy(): void self::assertSame($quoteStrategy, $this->configuration->getQuoteStrategy()); } - /** @group DDC-1955 */ + #[Group('DDC-1955')] public function testSetGetEntityListenerResolver(): void { self::assertInstanceOf(EntityListenerResolver::class, $this->configuration->getEntityListenerResolver()); - self::assertInstanceOf(AnnotationNamespace\DefaultEntityListenerResolver::class, $this->configuration->getEntityListenerResolver()); + self::assertInstanceOf(MappingNamespace\DefaultEntityListenerResolver::class, $this->configuration->getEntityListenerResolver()); $resolver = $this->createMock(EntityListenerResolver::class); $this->configuration->setEntityListenerResolver($resolver); self::assertSame($resolver, $this->configuration->getEntityListenerResolver()); } - /** @group DDC-2183 */ + #[Group('DDC-2183')] public function testSetGetSecondLevelCacheConfig(): void { $mockClass = $this->createMock(CacheConfiguration::class); @@ -459,7 +204,7 @@ public function testSetGetSecondLevelCacheConfig(): void self::assertEquals($mockClass, $this->configuration->getSecondLevelCacheConfiguration()); } - /** @group GH10313 */ + #[Group('GH10313')] public function testSetGetTypedFieldMapper(): void { self::assertEmpty($this->configuration->getTypedFieldMapper()); @@ -468,50 +213,3 @@ public function testSetGetTypedFieldMapper(): void self::assertSame($defaultTypedFieldMapper, $this->configuration->getTypedFieldMapper()); } } - -class ConfigurationTestAnnotationReaderChecker -{ - /** @PrePersist */ - public function simpleAnnotationMethod(): void - { - } - - /** @AnnotationNamespace\PrePersist */ - public function namespacedAnnotationMethod(): void - { - } -} - -class DeprecatedRepository implements ObjectRepository -{ - /** - * {@inheritDoc} - */ - public function find($id) - { - return null; - } - - public function findAll(): array - { - return []; - } - - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array - { - return []; - } - - /** - * {@inheritDoc} - */ - public function findOneBy(array $criteria) - { - return null; - } - - public function getClassName(): string - { - return stdClass::class; - } -} diff --git a/tests/Tests/ORM/Decorator/EntityManagerDecoratorTest.php b/tests/Tests/ORM/Decorator/EntityManagerDecoratorTest.php deleted file mode 100644 index 6d67a2dfcba..00000000000 --- a/tests/Tests/ORM/Decorator/EntityManagerDecoratorTest.php +++ /dev/null @@ -1,136 +0,0 @@ -wrapped = $this->createMock(EntityManagerInterface::class); - } - - /** @phpstan-return Generator */ - public static function getMethodParameters(): Generator - { - $class = new ReflectionClass(EntityManagerInterface::class); - - foreach ($class->getMethods() as $method) { - if ($method->isConstructor() || $method->isStatic() || ! $method->isPublic()) { - continue; - } - - yield $method->getName() => self::getParameters($method); - } - } - - /** @return mixed[] */ - private static function getParameters(ReflectionMethod $method): array - { - /** Special case EntityManager::createNativeQuery() */ - if ($method->getName() === 'createNativeQuery') { - return [$method->getName(), ['name', new ResultSetMapping()]]; - } - - if ($method->getName() === 'wrapInTransaction') { - return [ - $method->getName(), - [ - static function (): void { - }, - ], - ]; - } - - $parameters = []; - - foreach ($method->getParameters() as $parameter) { - if ($parameter->getType() === null) { - $parameters[] = 'mixed'; - continue; - } - - $type = $parameter->getType(); - assert($type instanceof ReflectionNamedType); - switch ($type->getName()) { - case 'string': - $parameters[] = 'parameter'; - break; - - case 'object': - $parameters[] = new stdClass(); - break; - - default: - throw new LogicException(sprintf( - 'Type %s is not handled yet', - (string) $parameter->getType() - )); - } - } - - return [$method->getName(), $parameters]; - } - - /** @dataProvider getMethodParameters */ - public function testAllMethodCallsAreDelegatedToTheWrappedInstance($method, array $parameters): void - { - $return = ! in_array($method, self::VOID_METHODS, true) ? 'INNER VALUE FROM ' . $method : null; - - $this->wrapped->expects(self::once()) - ->method($method) - ->with(...$parameters) - ->willReturn($return); - - $decorator = new class ($this->wrapped) extends EntityManagerDecorator { - }; - - self::assertSame($return, $decorator->$method(...$parameters)); - } - - public function testGetPartialReferenceIsDeprecated(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10987'); - $decorator = new class ($this->wrapped) extends EntityManagerDecorator { - }; - $decorator->getPartialReference(stdClass::class, 1); - } -} diff --git a/tests/Tests/ORM/Entity/ConstructorTest.php b/tests/Tests/ORM/Entity/ConstructorTest.php index d39071c37ec..73e01a87661 100644 --- a/tests/Tests/ORM/Entity/ConstructorTest.php +++ b/tests/Tests/ORM/Entity/ConstructorTest.php @@ -17,13 +17,12 @@ public function testFieldInitializationInConstructor(): void class ConstructorTestEntity1 { - /** @var int */ - private $id; + private int $id; /** @var string|null */ public $username; - public function __construct(?string $username = null) + public function __construct(string|null $username = null) { if ($username !== null) { $this->username = $username; diff --git a/tests/Tests/ORM/EntityManagerTest.php b/tests/Tests/ORM/EntityManagerTest.php index 98ad1cc01d3..e724d9ac632 100644 --- a/tests/Tests/ORM/EntityManagerTest.php +++ b/tests/Tests/ORM/EntityManagerTest.php @@ -6,45 +6,33 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; +use Doctrine\DBAL\Driver; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\EntityManagerClosed; use Doctrine\ORM\Mapping\ClassMetadataFactory; -use Doctrine\ORM\NativeQuery; -use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Query; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\UnitOfWork; -use Doctrine\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Persistence\Mapping\MappingException; -use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\CMS\CmsUser; -use Doctrine\Tests\Models\GeoNames\Country; use Doctrine\Tests\OrmTestCase; use Exception; use Generator; -use InvalidArgumentException; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use ReflectionProperty; use stdClass; +use Symfony\Component\VarExporter\LazyGhostTrait; use TypeError; -use function get_class; -use function random_int; -use function sprintf; -use function sys_get_temp_dir; -use function uniqid; - class EntityManagerTest extends OrmTestCase { - use VerifyDeprecations; - - /** @var EntityManager */ - private $entityManager; + private EntityManagerMock $entityManager; protected function setUp(): void { @@ -53,7 +41,7 @@ protected function setUp(): void $this->entityManager = $this->getTestEntityManager(); } - /** @group DDC-899 */ + #[Group('DDC-899')] public function testIsOpen(): void { self::assertTrue($this->entityManager->isOpen()); @@ -99,17 +87,6 @@ public function testCreateNativeQuery(): void self::assertSame('SELECT foo', $query->getSql()); } - /** @covers \Doctrine\ORM\EntityManager::createNamedNativeQuery */ - public function testCreateNamedNativeQuery(): void - { - $rsm = new ResultSetMapping(); - $this->entityManager->getConfiguration()->addNamedNativeQuery('foo', 'SELECT foo', $rsm); - - $query = $this->entityManager->createNamedNativeQuery('foo'); - - self::assertInstanceOf(NativeQuery::class, $query); - } - public function testCreateQueryBuilder(): void { self::assertInstanceOf(QueryBuilder::class, $this->entityManager->createQueryBuilder()); @@ -134,15 +111,6 @@ public function testCreateQueryDqlIsOptional(): void self::assertInstanceOf(Query::class, $this->entityManager->createQuery()); } - public function testGetPartialReference(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10987'); - $user = $this->entityManager->getPartialReference(CmsUser::class, 42); - self::assertTrue($this->entityManager->contains($user)); - self::assertEquals(42, $user->id); - self::assertNull($user->getName()); - } - public function testCreateQuery(): void { $q = $this->entityManager->createQuery('SELECT 1'); @@ -150,37 +118,6 @@ public function testCreateQuery(): void self::assertEquals('SELECT 1', $q->getDql()); } - /** @covers Doctrine\ORM\EntityManager::createNamedQuery */ - public function testCreateNamedQuery(): void - { - $this->entityManager->getConfiguration()->addNamedQuery('foo', 'SELECT 1'); - - $query = $this->entityManager->createNamedQuery('foo'); - self::assertInstanceOf(Query::class, $query); - self::assertEquals('SELECT 1', $query->getDql()); - } - - /** @phpstan-return list */ - public static function dataMethodsAffectedByNoObjectArguments(): array - { - return [ - ['persist'], - ['remove'], - ['merge'], - ['refresh'], - ['detach'], - ]; - } - - /** @dataProvider dataMethodsAffectedByNoObjectArguments */ - public function testThrowsExceptionOnNonObjectValues($methodName): void - { - $this->expectException(ORMInvalidArgumentException::class); - $this->expectExceptionMessage('EntityManager#' . $methodName . '() expects parameter 1 to be an entity object, NULL given.'); - - $this->entityManager->$methodName(null); - } - /** @phpstan-return list */ public static function dataAffectedByErrorIfClosedException(): array { @@ -188,12 +125,11 @@ public static function dataAffectedByErrorIfClosedException(): array ['flush'], ['persist'], ['remove'], - ['merge'], ['refresh'], ]; } - /** @dataProvider dataAffectedByErrorIfClosedException */ + #[DataProvider('dataAffectedByErrorIfClosedException')] public function testAffectedByErrorIfClosedException(string $methodName): void { $this->expectException(EntityManagerClosed::class); @@ -216,95 +152,18 @@ public static function dataToBeReturnedByWrapInTransaction(): Generator yield ['foo']; } - /** - * @param mixed $expectedValue - * - * @dataProvider dataToBeReturnedByWrapInTransaction - * @group DDC-1125 - */ - public function testWrapInTransactionAcceptsReturn($expectedValue): void + #[DataProvider('dataToBeReturnedByWrapInTransaction')] + #[Group('DDC-1125')] + public function testWrapInTransactionAcceptsReturn(mixed $expectedValue): void { $return = $this->entityManager->wrapInTransaction( - /** @return mixed */ - static function (EntityManagerInterface $em) use ($expectedValue) { - return $expectedValue; - } + static fn (EntityManagerInterface $em): mixed => $expectedValue, ); $this->assertSame($expectedValue, $return); } - /** @group DDC-1125 */ - public function testTransactionalAcceptsReturn(): void - { - $return = $this->entityManager->transactional(static function ($em) { - return 'foo'; - }); - - self::assertEquals('foo', $return); - } - - public function testTransactionalAcceptsVariousCallables(): void - { - self::assertSame('callback', $this->entityManager->transactional([$this, 'transactionalCallback'])); - } - - public function testTransactionalThrowsInvalidArgumentExceptionIfNonCallablePassed(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected argument of type "callable", got "object"'); - - $this->entityManager->transactional($this); - } - - public function transactionalCallback($em): string - { - self::assertSame($this->entityManager, $em); - - return 'callback'; - } - - public function testCreateInvalidConnection(): void - { - $config = new Configuration(); - $config->setMetadataDriverImpl($this->createMock(MappingDriver::class)); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid $connection argument of type int given: "1".'); - EntityManager::create(1, $config); - } - - public function testNamedConstructorDeprecation(): void - { - $config = new Configuration(); - $config->setMetadataDriverImpl($this->createMock(MappingDriver::class)); - $config->setProxyDir(sys_get_temp_dir()); - $config->setProxyNamespace(__NAMESPACE__ . '\\MyProxies'); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9961'); - - $em = EntityManager::create(['driver' => 'pdo_sqlite', 'memory' => true], $config); - - self::assertInstanceOf(Connection::class, $em->getConnection()); - } - - /** @group #5796 */ - public function testTransactionalReThrowsThrowables(): void - { - try { - $this->entityManager->transactional(static function (): void { - (static function (array $value): void { - // this only serves as an IIFE that throws a `TypeError` - })(null); - }); - - self::fail('TypeError expected to be thrown'); - } catch (TypeError $ignored) { - self::assertFalse($this->entityManager->isOpen()); - } - } - - /** @group #5796 */ + #[Group('#5796')] public function testWrapInTransactionReThrowsThrowables(): void { try { @@ -315,91 +174,58 @@ public function testWrapInTransactionReThrowsThrowables(): void }); self::fail('TypeError expected to be thrown'); - } catch (TypeError $ignored) { + } catch (TypeError) { self::assertFalse($this->entityManager->isOpen()); } } - /** @group 6017 */ - public function testClearManagerWithObject(): void - { - $entity = new Country(456, 'United Kingdom'); - - $this->expectException(ORMInvalidArgumentException::class); - - $this->entityManager->clear($entity); - } - - /** @group 6017 */ - public function testClearManagerWithUnknownEntityName(): void - { - $this->expectException(MappingException::class); - - $this->entityManager->clear(uniqid('nonExisting', true)); - } - - /** @group 6017 */ - public function testClearManagerWithProxyClassName(): void - { - $proxy = $this->entityManager->getReference(Country::class, ['id' => random_int(457, 100000)]); - - $entity = new Country(456, 'United Kingdom'); - - $this->entityManager->persist($entity); - - self::assertTrue($this->entityManager->contains($entity)); - - $this->entityManager->clear(get_class($proxy)); - - self::assertFalse($this->entityManager->contains($entity)); - } - - /** @group 6017 */ - public function testClearManagerWithNullValue(): void + /** Resetting the EntityManager relies on lazy objects until https://github.com/doctrine/orm/issues/5933 is resolved */ + public function testLazyGhostEntityManager(): void { - $entity = new Country(456, 'United Kingdom'); + $em = new class () extends EntityManager { + use LazyGhostTrait; - $this->entityManager->persist($entity); - - self::assertTrue($this->entityManager->contains($entity)); - - $this->entityManager->clear(null); + public function __construct() + { + } + }; - self::assertFalse($this->entityManager->contains($entity)); - } + $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 testDeprecatedClearWithArguments(): void - { - $entity = new Country(456, 'United Kingdom'); - $this->entityManager->persist($entity); + public function clear(): void + { + } + }); + }); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8460'); + $this->assertTrue($em->isOpen()); + $em->close(); + $this->assertFalse($em->isOpen()); - $this->entityManager->clear(Country::class); + $em->resetLazyObject(); + $this->assertTrue($em->isOpen()); } - public function testDeprecatedFlushWithArguments(): void + public function testItPreservesTheOriginalExceptionOnRollbackFailure(): void { - $entity = new Country(456, 'United Kingdom'); - $this->entityManager->persist($entity); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8459'); + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($this->createMock(Driver\Connection::class)); - $this->entityManager->flush($entity); - } - - /** @dataProvider entityManagerMethodNames */ - public function testItPreservesTheOriginalExceptionOnRollbackFailure(string $methodName): void - { - $entityManager = new EntityManagerMock(new class extends ConnectionMock { - public function rollBack(): bool + $entityManager = new EntityManagerMock(new class ([], $driver) extends Connection { + public function rollBack(): void { throw new Exception('Rollback exception'); } }); try { - $entityManager->$methodName(static function (): void { + $entityManager->wrapInTransaction(static function (): void { throw new Exception('Original exception'); }); self::fail('Exception expected'); @@ -410,17 +236,20 @@ public function rollBack(): bool } } - /** @dataProvider entityManagerMethodNames */ - public function testItDoesNotAttemptToRollbackIfNoTransactionIsActive(string $methodName): void + public function testItDoesNotAttemptToRollbackIfNoTransactionIsActive(): void { + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($this->createMock(Driver\Connection::class)); + $entityManager = new EntityManagerMock( - new class extends ConnectionMock { - public function commit(): bool + new class ([], $driver) extends Connection { + public function commit(): void { throw new Exception('Commit exception that happens after doing the actual commit'); } - public function rollBack(): bool + public function rollBack(): void { Assert::fail('Should not attempt to rollback if no transaction is active'); } @@ -429,19 +258,11 @@ public function isTransactionActive(): bool { return false; } - } + }, ); $this->expectExceptionMessage('Commit exception'); - $entityManager->$methodName(static function (): void { + $entityManager->wrapInTransaction(static function (): void { }); } - - /** @return Generator */ - public function entityManagerMethodNames(): Generator - { - foreach (['transactional', 'wrapInTransaction'] as $methodName) { - yield sprintf('%s()', $methodName) => [$methodName]; - } - } } diff --git a/tests/Tests/ORM/EntityNotFoundExceptionTest.php b/tests/Tests/ORM/EntityNotFoundExceptionTest.php index 8cb401c8a57..59d413b4696 100644 --- a/tests/Tests/ORM/EntityNotFoundExceptionTest.php +++ b/tests/Tests/ORM/EntityNotFoundExceptionTest.php @@ -5,20 +5,20 @@ namespace Doctrine\Tests\ORM; use Doctrine\ORM\EntityNotFoundException; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; /** * Tests for {@see \Doctrine\ORM\EntityNotFoundException} - * - * @covers \Doctrine\ORM\EntityNotFoundException */ +#[CoversClass(EntityNotFoundException::class)] class EntityNotFoundExceptionTest extends TestCase { public function testFromClassNameAndIdentifier(): void { $exception = EntityNotFoundException::fromClassNameAndIdentifier( 'foo', - ['foo' => 'bar'] + ['foo' => 'bar'], ); self::assertInstanceOf(EntityNotFoundException::class, $exception); @@ -26,7 +26,7 @@ public function testFromClassNameAndIdentifier(): void $exception = EntityNotFoundException::fromClassNameAndIdentifier( 'foo', - [] + [], ); self::assertInstanceOf(EntityNotFoundException::class, $exception); diff --git a/tests/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php b/tests/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php index e80ba2ff75f..1412f6f0c78 100644 --- a/tests/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php +++ b/tests/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php @@ -7,15 +7,15 @@ use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use function assert; /** * Tests for {@see \Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs} - * - * @covers \Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs */ +#[CoversClass(OnClassMetadataNotFoundEventArgs::class)] class OnClassMetadataNotFoundEventArgsTest extends TestCase { public function testEventArgsMutability(): void diff --git a/tests/Tests/ORM/Functional/AbstractFetchEagerTest.php b/tests/Tests/ORM/Functional/AbstractFetchEagerTest.php index 24e100a684c..c8166c3d03d 100644 --- a/tests/Tests/ORM/Functional/AbstractFetchEagerTest.php +++ b/tests/Tests/ORM/Functional/AbstractFetchEagerTest.php @@ -15,7 +15,7 @@ public function testWithAbstractFetchEager(): void { $this->createSchemaForModels( AbstractRemoteControl::class, - User::class + User::class, ); $control = new MobileRemoteControl('smart'); diff --git a/tests/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php b/tests/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php index b8aae160788..0a658bbfac8 100644 --- a/tests/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php +++ b/tests/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php @@ -47,14 +47,12 @@ protected function countForeignKeys($firstId, $secondId): int $this->firstField, $this->table, $this->firstField, - $this->secondField + $this->secondField, ), [$firstId, $secondId])); } public function assertCollectionEquals(Collection $first, Collection $second): bool { - return $first->forAll(static function ($k, $e) use ($second): bool { - return $second->contains($e); - }); + return $first->forAll(static fn ($k, $e): bool => $second->contains($e)); } } diff --git a/tests/Tests/ORM/Functional/AdvancedAssociationTest.php b/tests/Tests/ORM/Functional/AdvancedAssociationTest.php index 0c66b857b05..a05e7e70eff 100644 --- a/tests/Tests/ORM/Functional/AdvancedAssociationTest.php +++ b/tests/Tests/ORM/Functional/AdvancedAssociationTest.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -38,7 +39,7 @@ protected function setUp(): void PhraseType::class, Definition::class, Lemma::class, - Type::class + Type::class, ); } @@ -149,33 +150,22 @@ public function testManyToMany(): void } } -/** - * @Entity - * @Table(name="lemma") - */ +#[Table(name: 'lemma')] +#[Entity] class Lemma { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="lemma_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="lemma_name", unique=true, length=255) - */ - private $lemma; - - /** - * @var Collection - * @ManyToMany(targetEntity="Type", mappedBy="lemmas", cascade={"persist"}) - */ - private $types; + #[Id] + #[Column(type: 'integer', name: 'lemma_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'lemma_name', unique: true, length: 255)] + private string|null $lemma = null; + + #[ManyToMany(targetEntity: 'Type', mappedBy: 'lemmas', cascade: ['persist'])] + private Collection $types; public function __construct() { @@ -219,43 +209,28 @@ public function getTypes(): Collection } } -/** - * @Entity - * @Table(name="type") - */ +#[Table(name: 'type')] +#[Entity] class Type { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="type_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="type_name", unique=true) - */ - private $type; - - /** - * @var string - * @Column(type="string", name="type_abbreviation", unique=true) - */ - private $abbreviation; - - /** - * @var Collection - * @ManyToMany(targetEntity="Lemma") - * @JoinTable(name="lemma_type", - * joinColumns={@JoinColumn(name="type_id", referencedColumnName="type_id")}, - * inverseJoinColumns={@JoinColumn(name="lemma_id", referencedColumnName="lemma_id")} - * ) - */ - private $lemmas; + #[Id] + #[Column(type: 'integer', name: 'type_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'type_name', unique: true)] + private string|null $type = null; + + #[Column(type: 'string', name: 'type_abbreviation', unique: true)] + private string|null $abbreviation = null; + + #[JoinTable(name: 'lemma_type')] + #[JoinColumn(name: 'type_id', referencedColumnName: 'type_id')] + #[InverseJoinColumn(name: 'lemma_id', referencedColumnName: 'lemma_id')] + #[ManyToMany(targetEntity: 'Lemma')] + private Collection $lemmas; public function __construct() { @@ -310,39 +285,26 @@ public function getCategories(): Collection } -/** - * @Entity - * @Table(name="phrase") - */ +#[Table(name: 'phrase')] +#[Entity] class Phrase { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="phrase_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="phrase_name", unique=true, length=255) - */ - private $phrase; - - /** - * @var PhraseType - * @ManyToOne(targetEntity="PhraseType") - * @JoinColumn(name="phrase_type_id", referencedColumnName="phrase_type_id") - */ - private $type; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Definition", mappedBy="phrase", cascade={"persist"}) - */ + #[Id] + #[Column(type: 'integer', name: 'phrase_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'phrase_name', unique: true, length: 255)] + private string|null $phrase = null; + + #[ManyToOne(targetEntity: 'PhraseType')] + #[JoinColumn(name: 'phrase_type_id', referencedColumnName: 'phrase_type_id')] + private PhraseType|null $type = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Definition', mappedBy: 'phrase', cascade: ['persist'])] private $definitions; public function __construct() @@ -387,38 +349,25 @@ public function getDefinitions(): Collection } } -/** - * @Entity - * @Table(name="phrase_type") - */ +#[Table(name: 'phrase_type')] +#[Entity] class PhraseType { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="phrase_type_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="phrase_type_name", unique=true) - */ - private $type; - - /** - * @var string - * @Column(type="string", name="phrase_type_abbreviation", unique=true) - */ - private $abbreviation; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Phrase", mappedBy="type") - */ + #[Id] + #[Column(type: 'integer', name: 'phrase_type_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'phrase_type_name', unique: true)] + private string|null $type = null; + + #[Column(type: 'string', name: 'phrase_type_abbreviation', unique: true)] + private string|null $abbreviation = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Phrase', mappedBy: 'type')] private $phrases; public function __construct() @@ -462,34 +411,23 @@ public function getPhrases(): Collection } } -/** - * @Entity - * @Table(name="definition") - */ +#[Table(name: 'definition')] +#[Entity] class Definition { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="definition_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var Phrase - * @ManyToOne(targetEntity="Phrase") - * @JoinColumn(name="definition_phrase_id", referencedColumnName="phrase_id") - */ - private $phrase; - - /** - * @var string - * @Column(type="text", name="definition_text") - */ - private $definition; + #[Id] + #[Column(type: 'integer', name: 'definition_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[ManyToOne(targetEntity: 'Phrase')] + #[JoinColumn(name: 'definition_phrase_id', referencedColumnName: 'phrase_id')] + private Phrase|null $phrase = null; + + #[Column(type: 'text', name: 'definition_text')] + private string|null $definition = null; public function getId(): int { diff --git a/tests/Tests/ORM/Functional/AdvancedDqlQueryTest.php b/tests/Tests/ORM/Functional/AdvancedDqlQueryTest.php index 2ccf262155d..f410a4a84f2 100644 --- a/tests/Tests/ORM/Functional/AdvancedDqlQueryTest.php +++ b/tests/Tests/ORM/Functional/AdvancedDqlQueryTest.php @@ -211,7 +211,7 @@ public function testUpdateAs(): void $this->_em->createQuery($dql)->execute(); $query = $this->_em->createQuery( - 'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1' + 'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1', ); self::assertGreaterThan(0, $query->getResult()); diff --git a/tests/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Tests/ORM/Functional/BasicFunctionalTest.php index cfd60ab8cdc..fe03a864060 100644 --- a/tests/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Tests/ORM/Functional/BasicFunctionalTest.php @@ -4,7 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\ORM\EntityNotFoundException; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\EntityIdentityCollisionException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\ORMInvalidArgumentException; @@ -21,8 +21,7 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; use InvalidArgumentException; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; class BasicFunctionalTest extends OrmFunctionalTestCase { @@ -134,7 +133,7 @@ public function testBasicOneToOne(): void // Check that the foreign key has been set $userId = $this->_em->getConnection()->executeQuery( 'SELECT user_id FROM cms_addresses WHERE id=?', - [$address->id] + [$address->id], )->fetchOne(); self::assertIsNumeric($userId); @@ -149,7 +148,7 @@ public function testBasicOneToOne(): void self::assertFalse($this->isUninitializedObject($user2->address)); } - /** @group DDC-1230 */ + #[Group('DDC-1230')] public function testRemove(): void { $user = new CmsUser(); @@ -338,7 +337,7 @@ public function testBasicRefresh(): void self::assertEquals('developer', $user->status); } - /** @group DDC-833 */ + #[Group('DDC-833')] public function testRefreshResetsCollection(): void { $user = new CmsUser(); @@ -368,7 +367,7 @@ public function testRefreshResetsCollection(): void self::assertCount(1, $user->phonenumbers); } - /** @group DDC-833 */ + #[Group('DDC-833')] public function testDqlRefreshResetsCollection(): void { $user = new CmsUser(); @@ -402,7 +401,7 @@ public function testDqlRefreshResetsCollection(): void self::assertCount(1, $user->phonenumbers); } - /** @group DDC-833 */ + #[Group('DDC-833')] public function testCreateEntityOfProxy(): void { $user = new CmsUser(); @@ -524,6 +523,24 @@ public function testSetToOneAssociationWithGetReference(): void $user->username = 'gblanco'; $user->status = 'developer'; $this->_em->persist($user); + + $address = new CmsAddress(); + $address->country = 'Germany'; + $address->city = 'Berlin'; + $address->zip = '12345'; + $this->_em->persist($address); + + $this->_em->flush(); + $userId = $user->getId(); + $this->_em->clear(); + $user = $this->_em->find(CmsUser::class, $userId); + + // Assume we only got the identifier of the address and now want to attach + // that address to the user without actually loading it, using getReference(). + $addressRef = $this->_em->getReference(CmsAddress::class, $address->getId()); + + $user->setAddress($addressRef); // Ugh! Initializes address 'cause of $address->setUser($user)! + $this->_em->flush(); $this->_em->clear(); @@ -615,12 +632,12 @@ public function testOneToManyCascadeRemove(): void $this->_em->clear(); self::assertEquals(0, $this->_em->createQuery( - 'select count(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p' + 'select count(p.phonenumber) from Doctrine\Tests\Models\CMS\CmsPhonenumber p', ) ->getSingleScalarResult()); self::assertEquals(0, $this->_em->createQuery( - 'select count(u.id) from Doctrine\Tests\Models\CMS\CmsUser u' + 'select count(u.id) from Doctrine\Tests\Models\CMS\CmsUser u', ) ->getSingleScalarResult()); } @@ -646,7 +663,7 @@ public function testTextColumnSaveAndRetrieve(): void $this->_em->clear(); // test find() with leading backslash at the same time - $articleNew = $this->_em->find('\Doctrine\Tests\Models\CMS\CmsArticle', $articleId); + $articleNew = $this->_em->find(CmsArticle::class, $articleId); self::assertTrue($this->_em->contains($articleNew)); self::assertEquals('Lorem ipsum dolor sunt.', $articleNew->text); @@ -733,7 +750,7 @@ public function testQueryEntityByReference(): void $user->setAddress($address); - $this->_em->transactional(static function ($em) use ($user): void { + $this->_em->wrapInTransaction(static function (EntityManagerInterface $em) use ($user): void { $em->persist($user); }); $this->_em->clear(); @@ -776,10 +793,8 @@ public function testOneToOneNullUpdate(): void self::assertNotEquals(1, $this->_em->getConnection()->fetchOne('select 1 from cms_addresses where user_id = ' . $user->id)); } - /** - * @group DDC-600 - * @group DDC-455 - */ + #[Group('DDC-600')] + #[Group('DDC-455')] public function testNewAssociatedEntityDuringFlushThrowsException(): void { $user = new CmsUser(); @@ -801,10 +816,8 @@ public function testNewAssociatedEntityDuringFlushThrowsException(): void $this->_em->flush(); } - /** - * @group DDC-600 - * @group DDC-455 - */ + #[Group('DDC-600')] + #[Group('DDC-455')] public function testNewAssociatedEntityDuringFlushThrowsException2(): void { $user = new CmsUser(); @@ -834,10 +847,8 @@ public function testNewAssociatedEntityDuringFlushThrowsException2(): void $this->_em->flush(); } - /** - * @group DDC-600 - * @group DDC-455 - */ + #[Group('DDC-600')] + #[Group('DDC-455')] public function testNewAssociatedEntityDuringFlushThrowsException3(): void { $art = new CmsArticle(); @@ -905,119 +916,7 @@ public function testOneToOneOrphanRemoval(): void self::assertEquals(1, $this->_em->getConnection()->fetchOne('select count(*) from cms_addresses')); } - public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - $this->_em->persist($user); - $this->_em->flush(); - $userId = $user->id; - $this->_em->clear(); - - $user = $this->_em->getPartialReference(CmsUser::class, $userId); - self::assertTrue($this->_em->contains($user)); - self::assertNull($user->getName()); - self::assertEquals($userId, $user->id); - - $user->name = 'Stephan'; - $this->_em->flush(); - $this->_em->clear(); - - self::assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name); - } - - public function testMergePersistsNewEntities(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - - $managedUser = $this->_em->merge($user); - self::assertEquals('beberlei', $managedUser->username); - self::assertEquals('Benjamin E.', $managedUser->name); - self::assertEquals('active', $managedUser->status); - - self::assertTrue($user !== $managedUser); - self::assertTrue($this->_em->contains($managedUser)); - - $this->_em->flush(); - $userId = $managedUser->id; - $this->_em->clear(); - - $user2 = $this->_em->find(get_class($managedUser), $userId); - self::assertInstanceOf(CmsUser::class, $user2); - } - - public function testMergeNonPersistedProperties(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - $user->nonPersistedProperty = 'test'; - $user->nonPersistedPropertyObject = new CmsPhonenumber(); - - $managedUser = $this->_em->merge($user); - self::assertEquals('test', $managedUser->nonPersistedProperty); - self::assertSame($user->nonPersistedProperty, $managedUser->nonPersistedProperty); - self::assertSame($user->nonPersistedPropertyObject, $managedUser->nonPersistedPropertyObject); - - self::assertTrue($user !== $managedUser); - self::assertTrue($this->_em->contains($managedUser)); - - $this->_em->flush(); - $userId = $managedUser->id; - $this->_em->clear(); - - $user2 = $this->_em->find(get_class($managedUser), $userId); - self::assertNull($user2->nonPersistedProperty); - self::assertNull($user2->nonPersistedPropertyObject); - self::assertEquals('active', $user2->status); - } - - public function testMergeThrowsExceptionIfEntityWithGeneratedIdentifierDoesNotExist(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - $user->id = 42; - - $this->expectException(EntityNotFoundException::class); - $this->_em->merge($user); - } - - /** @group DDC-634 */ - public function testOneToOneMergeSetNull(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - - $ph = new CmsPhonenumber(); - $ph->phonenumber = '12345'; - $user->addPhonenumber($ph); - - $this->_em->persist($user); - $this->_em->persist($ph); - $this->_em->flush(); - - $this->_em->clear(); - - $ph->user = null; - $managedPh = $this->_em->merge($ph); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertNull($this->_em->find(get_class($ph), $ph->phonenumber)->getUser()); - } - - /** @group DDC-952 */ + #[Group('DDC-952')] public function testManyToOneFetchModeQuery(): void { $user = new CmsUser(); @@ -1046,85 +945,7 @@ public function testManyToOneFetchModeQuery(): void $this->assertQueryCount(2); } - /** @group DDC-1278 */ - public function testClearWithEntityName(): void - { - $user = new CmsUser(); - $user->name = 'Dominik'; - $user->username = 'domnikl'; - $user->status = 'developer'; - - $address = new CmsAddress(); - $address->city = 'Springfield'; - $address->zip = '12354'; - $address->country = 'Germany'; - $address->street = 'Foo Street'; - $address->user = $user; - $user->address = $address; - - $article1 = new CmsArticle(); - $article1->topic = 'Foo'; - $article1->text = 'Foo Text'; - - $article2 = new CmsArticle(); - $article2->topic = 'Bar'; - $article2->text = 'Bar Text'; - - $user->addArticle($article1); - $user->addArticle($article2); - - $this->_em->persist($article1); - $this->_em->persist($article2); - $this->_em->persist($address); - $this->_em->persist($user); - $this->_em->flush(); - - $unitOfWork = $this->_em->getUnitOfWork(); - - $this->_em->clear(CmsUser::class); - - self::assertEquals(UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($user)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($article1)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($article2)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($address)); - - $this->_em->clear(); - - self::assertEquals(UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address)); - } - - public function testFlushManyExplicitEntities(): void - { - $userA = new CmsUser(); - $userA->username = 'UserA'; - $userA->name = 'UserA'; - - $userB = new CmsUser(); - $userB->username = 'UserB'; - $userB->name = 'UserB'; - - $userC = new CmsUser(); - $userC->username = 'UserC'; - $userC->name = 'UserC'; - - $this->_em->persist($userA); - $this->_em->persist($userB); - $this->_em->persist($userC); - - $this->_em->flush([$userA, $userB, $userB]); - - $userC->name = 'changed name'; - - $this->_em->flush([$userA, $userB]); - $this->_em->refresh($userC); - - self::assertTrue($userA->id > 0, 'user a has an id'); - self::assertTrue($userB->id > 0, 'user b has an id'); - self::assertTrue($userC->id > 0, 'user c has an id'); - self::assertEquals('UserC', $userC->name, 'name has not changed because we did not flush it'); - } - - /** @group DDC-720 */ + #[Group('DDC-720')] public function testFlushSingleManagedEntity(): void { $user = new CmsUser(); @@ -1136,53 +957,14 @@ public function testFlushSingleManagedEntity(): void $this->_em->flush(); $user->status = 'administrator'; - $this->_em->flush($user); + $this->_em->flush(); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); self::assertEquals('administrator', $user->status); } - /** @group DDC-720 */ - public function testFlushSingleUnmanagedEntity(): void - { - $user = new CmsUser(); - $user->name = 'Dominik'; - $user->username = 'domnikl'; - $user->status = 'developer'; - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Entity has to be managed or scheduled for removal for single computation'); - - $this->_em->flush($user); - } - - /** @group DDC-720 */ - public function testFlushSingleAndNewEntity(): void - { - $user = new CmsUser(); - $user->name = 'Dominik'; - $user->username = 'domnikl'; - $user->status = 'developer'; - - $this->_em->persist($user); - $this->_em->flush(); - - $otherUser = new CmsUser(); - $otherUser->name = 'Dominik2'; - $otherUser->username = 'domnikl2'; - $otherUser->status = 'developer'; - - $user->status = 'administrator'; - - $this->_em->persist($otherUser); - $this->_em->flush($user); - - self::assertTrue($this->_em->contains($otherUser), 'Other user is contained in EntityManager'); - self::assertTrue($otherUser->id > 0, 'other user has an id'); - } - - /** @group DDC-720 */ + #[Group('DDC-720')] public function testFlushAndCascadePersist(): void { $user = new CmsUser(); @@ -1201,13 +983,13 @@ public function testFlushAndCascadePersist(): void $address->user = $user; $user->address = $address; - $this->_em->flush($user); + $this->_em->flush(); self::assertTrue($this->_em->contains($address), 'Other user is contained in EntityManager'); self::assertTrue($address->id > 0, 'other user has an id'); } - /** @group DDC-720 */ + #[Group('DDC-720')] public function testFlushSingleAndNoCascade(): void { $user = new CmsUser(); @@ -1227,14 +1009,12 @@ public function testFlushSingleAndNoCascade(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("A new entity was found through the relationship 'Doctrine\Tests\Models\CMS\CmsUser#articles'"); - $this->_em->flush($user); + $this->_em->flush(); } - /** - * @group DDC-720 - * @group DDC-1612 - * @group DDC-2267 - */ + #[Group('DDC-720')] + #[Group('DDC-1612')] + #[Group('DDC-2267')] public function testFlushSingleNewEntityThenRemove(): void { $user = new CmsUser(); @@ -1243,71 +1023,18 @@ public function testFlushSingleNewEntityThenRemove(): void $user->status = 'developer'; $this->_em->persist($user); - $this->_em->flush($user); + $this->_em->flush(); $userId = $user->id; $this->_em->remove($user); - $this->_em->flush($user); - $this->_em->clear(); - - self::assertNull($this->_em->find(get_class($user), $userId)); - } - - /** @group DDC-720 */ - public function testProxyIsIgnored(): void - { - $user = new CmsUser(); - $user->name = 'Dominik'; - $user->username = 'domnikl'; - $user->status = 'developer'; - - $this->_em->persist($user); - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->getReference(get_class($user), $user->id); - - $otherUser = new CmsUser(); - $otherUser->name = 'Dominik2'; - $otherUser->username = 'domnikl2'; - $otherUser->status = 'developer'; - - $this->_em->persist($otherUser); - $this->_em->flush($user); - - self::assertTrue($this->_em->contains($otherUser), 'Other user is contained in EntityManager'); - self::assertTrue($otherUser->id > 0, 'other user has an id'); - } - - /** @group DDC-720 */ - public function testFlushSingleSaveOnlySingle(): void - { - $user = new CmsUser(); - $user->name = 'Dominik'; - $user->username = 'domnikl'; - $user->status = 'developer'; - $this->_em->persist($user); - - $user2 = new CmsUser(); - $user2->name = 'Dominik'; - $user2->username = 'domnikl2'; - $user2->status = 'developer'; - $this->_em->persist($user2); - $this->_em->flush(); - - $user->status = 'admin'; - $user2->status = 'admin'; - - $this->_em->flush($user); $this->_em->clear(); - $user2 = $this->_em->find(get_class($user2), $user2->id); - self::assertEquals('developer', $user2->status); + self::assertNull($this->_em->find($user::class, $userId)); } - /** @group DDC-1585 */ + #[Group('DDC-1585')] public function testWrongAssociationInstance(): void { $user = new CmsUser(); @@ -1319,7 +1046,7 @@ public function testWrongAssociationInstance(): void $this->expectException(ORMInvalidArgumentException::class); $this->expectExceptionMessage( 'Expected value of type "Doctrine\Tests\Models\CMS\CmsAddress" for association field ' . - '"Doctrine\Tests\Models\CMS\CmsUser#$address", got "Doctrine\Tests\Models\CMS\CmsUser" instead.' + '"Doctrine\Tests\Models\CMS\CmsUser#$address", got "Doctrine\Tests\Models\CMS\CmsUser" instead.', ); $this->_em->persist($user); @@ -1329,8 +1056,6 @@ public function testWrongAssociationInstance(): void public function testItThrowsWhenReferenceUsesIdAssignedByDatabase(): void { - $this->_em->getConfiguration()->setRejectIdCollisionInIdentityMap(true); - $user = new CmsUser(); $user->name = 'test'; $user->username = 'test'; diff --git a/tests/Tests/ORM/Functional/CascadeRemoveOrderTest.php b/tests/Tests/ORM/Functional/CascadeRemoveOrderTest.php index 5d17a875bb1..b151791587c 100644 --- a/tests/Tests/ORM/Functional/CascadeRemoveOrderTest.php +++ b/tests/Tests/ORM/Functional/CascadeRemoveOrderTest.php @@ -14,8 +14,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group CascadeRemoveOrderTest */ +#[Group('CascadeRemoveOrderTest')] class CascadeRemoveOrderTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +25,7 @@ protected function setUp(): void $this->createSchemaForModels( CascadeRemoveOrderEntityO::class, - CascadeRemoveOrderEntityG::class + CascadeRemoveOrderEntityG::class, ); } @@ -69,32 +70,20 @@ public function testMany(): void } } -/** @Entity */ +#[Entity] class CascadeRemoveOrderEntityO { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var CascadeRemoveOrderEntityG - * @OneToOne(targetEntity="Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityG") - * @JoinColumn(nullable=true, onDelete="SET NULL") - */ - private $oneToOneG; - - /** - * @phpstan-var Collection - * @OneToMany( - * targetEntity="Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityG", - * mappedBy="ownerO", - * cascade={"persist", "remove"} - * ) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + #[OneToOne(targetEntity: 'Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityG')] + #[JoinColumn(nullable: true, onDelete: 'SET NULL')] + private CascadeRemoveOrderEntityG|null $oneToOneG = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityG', mappedBy: 'ownerO', cascade: ['persist', 'remove'])] private $oneToManyG; public function __construct() @@ -129,33 +118,19 @@ public function getOneToManyGs(): array } } -/** @Entity */ +#[Entity] class CascadeRemoveOrderEntityG { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var CascadeRemoveOrderEntityO - * @ManyToOne( - * targetEntity="Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityO", - * inversedBy="oneToMany" - * ) - */ - private $ownerO; - - /** @var int */ - private $position; - - public function __construct(CascadeRemoveOrderEntityO $eO, $position = 1) - { - $this->position = $position; - $this->ownerO = $eO; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + public function __construct( + #[ManyToOne(targetEntity: 'Doctrine\Tests\ORM\Functional\CascadeRemoveOrderEntityO', inversedBy: 'oneToMany')] + private CascadeRemoveOrderEntityO $ownerO, + private int $position = 1, + ) { $this->ownerO->addOneToManyG($this); } diff --git a/tests/Tests/ORM/Functional/ClassTableInheritanceSecondTest.php b/tests/Tests/ORM/Functional/ClassTableInheritanceSecondTest.php index 9bf1b81b590..4f55a05fb31 100644 --- a/tests/Tests/ORM/Functional/ClassTableInheritanceSecondTest.php +++ b/tests/Tests/ORM/Functional/ClassTableInheritanceSecondTest.php @@ -20,7 +20,6 @@ use Doctrine\Tests\OrmFunctionalTestCase; use function count; -use function get_class; /** * Functional tests for the Class Table Inheritance mapping strategy. @@ -35,7 +34,7 @@ protected function setUp(): void CTIParent::class, CTIChild::class, CTIRelated::class, - CTIRelated2::class + CTIRelated2::class, ); } @@ -77,7 +76,7 @@ public function testManyToManyToCTIHierarchy(): void $this->_em->flush(); $this->_em->clear(); - $mmrel2 = $this->_em->find(get_class($mmrel), $mmrel->getId()); + $mmrel2 = $this->_em->find($mmrel::class, $mmrel->getId()); self::assertFalse($mmrel2->getCTIChildren()->isInitialized()); self::assertEquals(1, count($mmrel2->getCTIChildren())); self::assertTrue($mmrel2->getCTIChildren()->isInitialized()); @@ -85,28 +84,20 @@ public function testManyToManyToCTIHierarchy(): void } } -/** - * @Entity - * @Table(name="cti_parents") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"parent" = "CTIParent", "child" = "CTIChild"}) - */ +#[Table(name: 'cti_parents')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['parent' => 'CTIParent', 'child' => 'CTIChild'])] class CTIParent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var CTIRelated - * @OneToOne(targetEntity="CTIRelated", mappedBy="ctiParent") - */ - private $related; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[OneToOne(targetEntity: 'CTIRelated', mappedBy: 'ctiParent')] + private CTIRelated|null $related = null; public function getId(): int { @@ -125,17 +116,12 @@ public function setRelated(CTIRelated $related): void } } -/** - * @Entity - * @Table(name="cti_children") - */ +#[Table(name: 'cti_children')] +#[Entity] class CTIChild extends CTIParent { - /** - * @var string - * @Column(type="string", length=255) - */ - private $data; + #[Column(type: 'string', length: 255)] + private string|null $data = null; public function getData(): string { @@ -148,23 +134,17 @@ public function setData(string $data): void } } -/** @Entity */ +#[Entity] class CTIRelated { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var CTIParent - * @OneToOne(targetEntity="CTIParent") - * @JoinColumn(name="ctiparent_id", referencedColumnName="id") - */ - private $ctiParent; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[OneToOne(targetEntity: 'CTIParent')] + #[JoinColumn(name: 'ctiparent_id', referencedColumnName: 'id')] + private CTIParent|null $ctiParent = null; public function getId(): int { @@ -182,21 +162,16 @@ public function setCTIParent(CTIParent $ctiParent): void } } -/** @Entity */ +#[Entity] class CTIRelated2 { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="CTIChild") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'CTIChild')] private $ctiChildren; public function __construct() diff --git a/tests/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Tests/ORM/Functional/ClassTableInheritanceTest.php index 489d9779e67..94f817a2353 100644 --- a/tests/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -16,8 +16,9 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\Models\Company\CompanyRaffle; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\Group; -use function get_class; use function get_debug_type; use function sprintf; @@ -87,7 +88,7 @@ public function testCRUD(): void $this->_em->clear(); - $guilherme = $this->_em->getRepository(get_class($employee))->findOneBy(['name' => 'Guilherme Blanco']); + $guilherme = $this->_em->getRepository($employee::class)->findOneBy(['name' => 'Guilherme Blanco']); self::assertInstanceOf(CompanyEmployee::class, $guilherme); self::assertEquals('Guilherme Blanco', $guilherme->getName()); @@ -306,7 +307,7 @@ public function testLazyLoading2(): void IterableTester::assertResultsAreTheSame($q); } - /** @group DDC-368 */ + #[Group('DDC-368')] public function testBulkUpdateIssueDDC368(): void { $this->_em->createQuery('UPDATE ' . CompanyEmployee::class . ' AS p SET p.salary = 1') @@ -322,18 +323,17 @@ public function testBulkUpdateIssueDDC368(): void IterableTester::assertResultsAreTheSame($query); } - /** @group DDC-1341 */ + #[Group('DDC-1341')] + #[DoesNotPerformAssertions] public function testBulkUpdateNonScalarParameterDDC1341(): void { $this->_em->createQuery('UPDATE ' . CompanyEmployee::class . ' AS p SET p.startDate = ?0 WHERE p.department = ?1') ->setParameter(0, new DateTime()) ->setParameter(1, 'IT') ->execute(); - - $this->addToAssertionCount(1); } - /** @group DDC-130 */ + #[Group('DDC-130')] public function testDeleteJoinTableRecords(): void { $employee1 = new CompanyEmployee(); @@ -357,10 +357,10 @@ public function testDeleteJoinTableRecords(): void $this->_em->remove($employee1); $this->_em->flush(); - self::assertNull($this->_em->find(get_class($employee1), $employee1Id)); + self::assertNull($this->_em->find($employee1::class, $employee1Id)); } - /** @group DDC-728 */ + #[Group('DDC-728')] public function testQueryForInheritedSingleValuedAssociation(): void { $manager = new CompanyManager(); @@ -387,7 +387,7 @@ public function testQueryForInheritedSingleValuedAssociation(): void self::assertEquals($person->getId(), $dqlManager->getSpouse()->getId()); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindByAssociation(): void { $manager = new CompanyManager(); @@ -417,7 +417,7 @@ public function testFindByAssociation(): void self::assertEquals($manager->getId(), $pmanager->getId()); } - /** @group DDC-834 */ + #[Group('DDC-834')] public function testGetReferenceEntityWithSubclasses(): void { $manager = new CompanyManager(); @@ -440,7 +440,7 @@ public function testGetReferenceEntityWithSubclasses(): void self::assertTrue($this->isUninitializedObject($ref), 'A proxy can be generated only if no subclasses exists for the requested reference.'); } - /** @group DDC-992 */ + #[Group('DDC-992')] public function testGetSubClassManyToManyCollection(): void { $manager = new CompanyManager(); @@ -464,7 +464,7 @@ public function testGetSubClassManyToManyCollection(): void self::assertCount(1, $manager->getFriends()); } - /** @group DDC-1777 */ + #[Group('DDC-1777')] public function testExistsSubclass(): void { $manager = new CompanyManager(); @@ -473,15 +473,15 @@ public function testExistsSubclass(): void $manager->setTitle('Awesome!'); $manager->setDepartment('IT'); - self::assertFalse($this->_em->getUnitOfWork()->getEntityPersister(get_class($manager))->exists($manager)); + self::assertFalse($this->_em->getUnitOfWork()->getEntityPersister($manager::class)->exists($manager)); $this->_em->persist($manager); $this->_em->flush(); - self::assertTrue($this->_em->getUnitOfWork()->getEntityPersister(get_class($manager))->exists($manager)); + self::assertTrue($this->_em->getUnitOfWork()->getEntityPersister($manager::class)->exists($manager)); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatching(): void { $manager = new CompanyManager(); @@ -495,13 +495,13 @@ public function testMatching(): void $repository = $this->_em->getRepository(CompanyEmployee::class); $users = $repository->matching(new Criteria( - Criteria::expr()->eq('department', 'IT') + Criteria::expr()->eq('department', 'IT'), )); self::assertCount(1, $users); $repository = $this->_em->getRepository(CompanyManager::class); $users = $repository->matching(new Criteria( - Criteria::expr()->eq('department', 'IT') + Criteria::expr()->eq('department', 'IT'), )); self::assertCount(1, $users); } diff --git a/tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php b/tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php index 88a87505fb3..955d6acaaaf 100644 --- a/tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php +++ b/tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php @@ -55,7 +55,7 @@ private function findEntity(string $companyCode, string $invoiceNumber): Invoice { return $this->_em->find( InvoiceClass::class, - ['companyCode' => $companyCode, 'invoiceNumber' => $invoiceNumber] + ['companyCode' => $companyCode, 'invoiceNumber' => $invoiceNumber], ); } } diff --git a/tests/Tests/ORM/Functional/CompositePrimaryKeyTest.php b/tests/Tests/ORM/Functional/CompositePrimaryKeyTest.php index c1dbc8f3aeb..0892c78d49e 100644 --- a/tests/Tests/ORM/Functional/CompositePrimaryKeyTest.php +++ b/tests/Tests/ORM/Functional/CompositePrimaryKeyTest.php @@ -14,6 +14,7 @@ use Doctrine\Tests\Models\Navigation\NavTour; use Doctrine\Tests\Models\Navigation\NavUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class CompositePrimaryKeyTest extends OrmFunctionalTestCase { @@ -60,7 +61,7 @@ public function testPersistCompositePkEntity(): void self::assertEquals('Brandenburger Tor', $poi->getName()); } - /** @group DDC-1651 */ + #[Group('DDC-1651')] public function testSetParameterCompositeKeyObject(): void { $this->putGermanysBrandenburderTor(); @@ -167,7 +168,7 @@ public function testUnrecognizedIdentifierFieldsOnGetReference(): void $poi = $this->_em->getReference(NavPointOfInterest::class, ['lat' => 10, 'long' => 20, 'key1' => 100]); } - /** @group DDC-1939 */ + #[Group('DDC-1939')] public function testDeleteCompositePersistentCollection(): void { $this->putGermanysBrandenburderTor(); diff --git a/tests/Tests/ORM/Functional/CustomFunctionsTest.php b/tests/Tests/ORM/Functional/CustomFunctionsTest.php index 284aee8c68a..8c71f8d1f43 100644 --- a/tests/Tests/ORM/Functional/CustomFunctionsTest.php +++ b/tests/Tests/ORM/Functional/CustomFunctionsTest.php @@ -4,7 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\ORM\Query; +use Doctrine\ORM\Query\AST\AggregateExpression; use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\AST\PathExpression; use Doctrine\ORM\Query\Parser; @@ -33,12 +33,8 @@ public function testCustomFunctionDefinedWithCallback(): void $this->_em->flush(); // Instead of defining the function with the class name, we use a callback - $this->_em->getConfiguration()->addCustomStringFunction('FOO', static function ($funcName) { - return new NoOp($funcName); - }); - $this->_em->getConfiguration()->addCustomNumericFunction('BAR', static function ($funcName) { - return new NoOp($funcName); - }); + $this->_em->getConfiguration()->addCustomStringFunction('FOO', static fn ($funcName) => new NoOp($funcName)); + $this->_em->getConfiguration()->addCustomNumericFunction('BAR', static fn ($funcName) => new NoOp($funcName)); $query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u' . ' WHERE FOO(u.name) = \'Bob\'' @@ -89,8 +85,7 @@ public function getSql(SqlWalker $sqlWalker): string class CustomCount extends FunctionNode { - /** @var Query\AST\AggregateExpression */ - private $aggregateExpression; + private AggregateExpression|null $aggregateExpression = null; public function parse(Parser $parser): void { diff --git a/tests/Tests/ORM/Functional/CustomIdObjectTypeTest.php b/tests/Tests/ORM/Functional/CustomIdObjectTypeTest.php index 7cc731b81b5..c5fbcc751b5 100644 --- a/tests/Tests/ORM/Functional/CustomIdObjectTypeTest.php +++ b/tests/Tests/ORM/Functional/CustomIdObjectTypeTest.php @@ -10,6 +10,7 @@ use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild; use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class CustomIdObjectTypeTest extends OrmFunctionalTestCase { @@ -38,10 +39,8 @@ public function testFindByCustomIdObject(): void self::assertSame($parent, $result); } - /** - * @group DDC-3622 - * @group 1336 - */ + #[Group('DDC-3622')] + #[Group('1336')] public function testFetchJoinCustomIdObject(): void { $parent = new CustomIdObjectTypeParent(new CustomIdObject('foo')); @@ -56,7 +55,7 @@ public function testFetchJoinCustomIdObject(): void ->createQuery( 'SELECT parent, children FROM ' . CustomIdObjectTypeParent::class - . ' parent LEFT JOIN parent.children children' + . ' parent LEFT JOIN parent.children children', ) ->getResult(); @@ -64,10 +63,8 @@ public function testFetchJoinCustomIdObject(): void self::assertSame($parent, $result[0]); } - /** - * @group DDC-3622 - * @group 1336 - */ + #[Group('DDC-3622')] + #[Group('1336')] public function testFetchJoinWhereCustomIdObject(): void { $parent = new CustomIdObjectTypeParent(new CustomIdObject('foo')); @@ -84,7 +81,7 @@ public function testFetchJoinWhereCustomIdObject(): void 'SELECT parent, children FROM ' . CustomIdObjectTypeParent::class . ' parent LEFT JOIN parent.children children ' - . 'WHERE children.id = ?1' + . 'WHERE children.id = ?1', ) ->setParameter(1, $parent->children->first()->id) ->getResult(); diff --git a/tests/Tests/ORM/Functional/CustomRepositoryTest.php b/tests/Tests/ORM/Functional/CustomRepositoryTest.php deleted file mode 100644 index 2aae9545daf..00000000000 --- a/tests/Tests/ORM/Functional/CustomRepositoryTest.php +++ /dev/null @@ -1,145 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9533'); - $this->createSchemaForModels(MinimalEntity::class, OtherMinimalEntity::class); - } - - public function testMinimalRepository(): void - { - $this->_em->persist(new MinimalEntity('foo')); - $this->_em->persist(new MinimalEntity('bar')); - $this->_em->flush(); - $this->_em->clear(); - - $repository = $this->_em->getRepository(MinimalEntity::class); - self::assertInstanceOf(MinimalRepository::class, $repository); - self::assertSame('foo', $repository->find('foo')->id); - } - - public function testMinimalDefaultRepository(): void - { - $this->_em->getConfiguration()->setDefaultRepositoryClassName(MinimalRepository::class); - - $this->_em->persist(new OtherMinimalEntity('foo')); - $this->_em->persist(new OtherMinimalEntity('bar')); - $this->_em->flush(); - $this->_em->clear(); - - $repository = $this->_em->getRepository(OtherMinimalEntity::class); - self::assertInstanceOf(MinimalRepository::class, $repository); - self::assertSame('foo', $repository->find('foo')->id); - } -} - -/** @ORM\Entity(repositoryClass="MinimalRepository") */ -class MinimalEntity -{ - /** - * @ORM\Column - * @ORM\Id - * - * @var string - */ - public $id; - - public function __construct(string $id) - { - $this->id = $id; - } -} - -/** @ORM\Entity */ -class OtherMinimalEntity -{ - /** - * @ORM\Column - * @ORM\Id - * - * @var string - */ - public $id; - - public function __construct(string $id) - { - $this->id = $id; - } -} - -/** - * @template TEntity of object - * @implements ObjectRepository - */ -class MinimalRepository implements ObjectRepository -{ - /** @var EntityManagerInterface */ - private $em; - /** @var ClassMetadata */ - private $class; - - /** @phpstan-param ClassMetadata $class */ - public function __construct(EntityManagerInterface $em, ClassMetadata $class) - { - $this->em = $em; - $this->class = $class; - } - - /** - * {@inheritDoc} - */ - public function find($id) - { - return $this->em->find($this->class->name, $id); - } - - /** - * {@inheritDoc} - */ - public function findAll(): array - { - return $this->findBy([]); - } - - /** - * {@inheritDoc} - */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array - { - $persister = $this->em->getUnitOfWork()->getEntityPersister($this->class->name); - - return $persister->loadAll($criteria, $orderBy, $limit, $offset); - } - - /** - * {@inheritDoc} - */ - public function findOneBy(array $criteria) - { - $persister = $this->em->getUnitOfWork()->getEntityPersister($this->class->name); - - return $persister->load($criteria, null, null, [], null, 1); - } - - public function getClassName(): string - { - return $this->class->name; - } -} diff --git a/tests/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Tests/ORM/Functional/DatabaseDriverTest.php index 41c24518677..f2ef9e58f62 100644 --- a/tests/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Tests/ORM/Functional/DatabaseDriverTest.php @@ -9,7 +9,7 @@ use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Table; -use Doctrine\ORM\Mapping\ClassMetadata; +use PHPUnit\Framework\Attributes\Group; use function array_change_key_case; use function count; @@ -31,13 +31,9 @@ protected function setUp(): void $this->schemaManager = $this->createSchemaManager(); } - /** @group DDC-2059 */ + #[Group('DDC-2059')] public function testIssue2059(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $user = new Table('ddc2059_user'); $user->addColumn('id', 'integer'); $user->setPrimaryKey(['id']); @@ -56,10 +52,6 @@ public function testIssue2059(): void public function testLoadMetadataFromDatabase(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $table = new Table('dbdriver_foo'); $table->addColumn('id', 'integer'); $table->setPrimaryKey(['id']); @@ -73,24 +65,20 @@ public function testLoadMetadataFromDatabase(): void $metadata = $metadatas['DbdriverFoo']; self::assertArrayHasKey('id', $metadata->fieldMappings); - self::assertEquals('id', $metadata->fieldMappings['id']['fieldName']); - self::assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName'])); - self::assertEquals('integer', (string) $metadata->fieldMappings['id']['type']); + self::assertEquals('id', $metadata->fieldMappings['id']->fieldName); + self::assertEquals('id', strtolower($metadata->fieldMappings['id']->columnName)); + self::assertEquals('integer', (string) $metadata->fieldMappings['id']->type); self::assertArrayHasKey('bar', $metadata->fieldMappings); - self::assertEquals('bar', $metadata->fieldMappings['bar']['fieldName']); - self::assertEquals('bar', strtolower($metadata->fieldMappings['bar']['columnName'])); - self::assertEquals('string', (string) $metadata->fieldMappings['bar']['type']); - self::assertEquals(200, $metadata->fieldMappings['bar']['length']); - self::assertTrue($metadata->fieldMappings['bar']['nullable']); + self::assertEquals('bar', $metadata->fieldMappings['bar']->fieldName); + self::assertEquals('bar', strtolower($metadata->fieldMappings['bar']->columnName)); + self::assertEquals('string', (string) $metadata->fieldMappings['bar']->type); + self::assertEquals(200, $metadata->fieldMappings['bar']->length); + self::assertTrue($metadata->fieldMappings['bar']->nullable); } public function testLoadMetadataWithForeignKeyFromDatabase(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $tableB = new Table('dbdriver_bar'); $tableB->addColumn('id', 'integer'); $tableB->setPrimaryKey(['id']); @@ -116,15 +104,11 @@ public function testLoadMetadataWithForeignKeyFromDatabase(): void $bazMetadata->associationMappings = array_change_key_case($bazMetadata->associationMappings, CASE_LOWER); self::assertArrayHasKey('bar', $bazMetadata->associationMappings); - self::assertEquals(ClassMetadata::MANY_TO_ONE, $bazMetadata->associationMappings['bar']['type']); + self::assertTrue($bazMetadata->associationMappings['bar']->isManyToOne()); } public function testDetectManyToManyTables(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $metadatas = $this->extractClassMetadata(['CmsUsers', 'CmsGroups', 'CmsTags']); self::assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers entity was not detected.'); @@ -161,10 +145,6 @@ public function testIgnoreManyToManyTableWithoutFurtherForeignKeyDetails(): void public function testLoadMetadataFromDatabaseDetail(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $table = new Table('dbdriver_foo'); $table->addColumn('id', 'integer', ['unsigned' => true]); @@ -191,35 +171,35 @@ public function testLoadMetadataFromDatabaseDetail(): void $metadata = $metadatas['DbdriverFoo']; self::assertArrayHasKey('id', $metadata->fieldMappings); - self::assertEquals('id', $metadata->fieldMappings['id']['fieldName']); - self::assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName'])); - self::assertEquals('integer', (string) $metadata->fieldMappings['id']['type']); + self::assertEquals('id', $metadata->fieldMappings['id']->fieldName); + self::assertEquals('id', strtolower($metadata->fieldMappings['id']->columnName)); + self::assertEquals('integer', (string) $metadata->fieldMappings['id']->type); if (self::supportsUnsignedInteger($this->_em->getConnection()->getDatabasePlatform())) { self::assertArrayHasKey('columnUnsigned', $metadata->fieldMappings); - self::assertTrue($metadata->fieldMappings['columnUnsigned']['options']['unsigned']); + self::assertTrue($metadata->fieldMappings['columnUnsigned']->options['unsigned']); } self::assertArrayHasKey('columnComment', $metadata->fieldMappings); - self::assertEquals('test_comment', $metadata->fieldMappings['columnComment']['options']['comment']); + self::assertEquals('test_comment', $metadata->fieldMappings['columnComment']->options['comment']); self::assertArrayHasKey('columnDefault', $metadata->fieldMappings); - self::assertEquals('test_default', $metadata->fieldMappings['columnDefault']['options']['default']); + self::assertEquals('test_default', $metadata->fieldMappings['columnDefault']->options['default']); self::assertArrayHasKey('columnDecimal', $metadata->fieldMappings); - self::assertEquals(4, $metadata->fieldMappings['columnDecimal']['precision']); - self::assertEquals(3, $metadata->fieldMappings['columnDecimal']['scale']); + self::assertEquals(4, $metadata->fieldMappings['columnDecimal']->precision); + self::assertEquals(3, $metadata->fieldMappings['columnDecimal']->scale); self::assertNotEmpty($metadata->table['indexes']['index1']['columns']); self::assertEquals( ['column_index1', 'column_index2'], - $metadata->table['indexes']['index1']['columns'] + $metadata->table['indexes']['index1']['columns'], ); self::assertNotEmpty($metadata->table['uniqueConstraints']['unique_index1']['columns']); self::assertEquals( ['column_unique_index1', 'column_unique_index2'], - $metadata->table['uniqueConstraints']['unique_index1']['columns'] + $metadata->table['uniqueConstraints']['unique_index1']['columns'], ); } diff --git a/tests/Tests/ORM/Functional/DefaultValuesTest.php b/tests/Tests/ORM/Functional/DefaultValuesTest.php index 313eaa0c30c..3a46b00a406 100644 --- a/tests/Tests/ORM/Functional/DefaultValuesTest.php +++ b/tests/Tests/ORM/Functional/DefaultValuesTest.php @@ -12,8 +12,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * Tests basic operations on entities with default values. @@ -26,11 +25,11 @@ protected function setUp(): void $this->createSchemaForModels( DefaultValueUser::class, - DefaultValueAddress::class + DefaultValueAddress::class, ); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testSimpleDetachMerge(): void { $user = new DefaultValueUser(); @@ -40,7 +39,7 @@ public function testSimpleDetachMerge(): void $this->_em->clear(); $userId = $user->id; // e.g. from $_REQUEST - $user2 = $this->_em->getReference(get_class($user), $userId); + $user2 = $this->_em->getReference($user::class, $userId); $this->_em->flush(); self::assertTrue($this->isUninitializedObject($user2)); @@ -58,63 +57,31 @@ public function testSimpleDetachMerge(): void self::assertTrue($this->isUninitializedObject($user2)); $this->_em->clear(); - $a2 = $this->_em->find(get_class($a), $a->id); + $a2 = $this->_em->find($a::class, $a->id); self::assertInstanceOf(DefaultValueUser::class, $a2->getUser()); self::assertEquals($userId, $a2->getUser()->getId()); self::assertEquals('Poweruser', $a2->getUser()->type); } - - /** @group DDC-1386 */ - public function testGetPartialReferenceWithDefaultValueNotEvaluatedInFlush(): void - { - $user = new DefaultValueUser(); - $user->name = 'romanb'; - $user->type = 'Normaluser'; - - $this->_em->persist($user); - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->getPartialReference(DefaultValueUser::class, $user->id); - self::assertTrue($this->_em->getUnitOfWork()->isReadOnly($user)); - - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(DefaultValueUser::class, $user->id); - - self::assertEquals('Normaluser', $user->type); - } } -/** - * @Entity - * @Table(name="defaultvalueuser") - */ +#[Table(name: 'defaultvalueuser')] +#[Entity] class DefaultValueUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name = ''; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $type = 'Poweruser'; - /** - * @var DefaultValueAddress - * @OneToOne(targetEntity="DefaultValueAddress", mappedBy="user", cascade={"persist"}) - */ + /** @var DefaultValueAddress */ + #[OneToOne(targetEntity: 'DefaultValueAddress', mappedBy: 'user', cascade: ['persist'])] public $address; public function getId(): int @@ -125,36 +92,27 @@ public function getId(): int /** * CmsAddress - * - * @Entity - * @Table(name="defaultvalueaddresses") */ +#[Table(name: 'defaultvalueaddresses')] +#[Entity] class DefaultValueAddress { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=50) - */ + /** @var string */ + #[Column(type: 'string', length: 50)] public $country; - /** - * @var string - * @Column(type="string", length=50) - */ + /** @var string */ + #[Column(type: 'string', length: 50)] public $zip; - /** - * @var string - * @Column(type="string", length=50) - */ + /** @var string */ + #[Column(type: 'string', length: 50)] public $city; /** @@ -163,11 +121,9 @@ class DefaultValueAddress */ public $street; - /** - * @var DefaultValueUser - * @OneToOne(targetEntity="DefaultValueUser") - * @JoinColumn(name="user_id", referencedColumnName="id") - */ + /** @var DefaultValueUser */ + #[OneToOne(targetEntity: 'DefaultValueUser')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] public $user; public function getUser(): DefaultValueUser diff --git a/tests/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Tests/ORM/Functional/DetachedEntityTest.php index 1fef49dd952..b7fcb209d54 100644 --- a/tests/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Tests/ORM/Functional/DetachedEntityTest.php @@ -5,17 +5,10 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use Doctrine\ORM\OptimisticLockException; -use Doctrine\Tests\Models\CMS\CmsAddress; -use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; - -use function count; -use function get_class; -use function serialize; -use function unserialize; +use PHPUnit\Framework\Attributes\Group; /** * Description of DetachedEntityTest @@ -29,90 +22,7 @@ protected function setUp(): void parent::setUp(); } - public function testSimpleDetachMerge(): void - { - $user = new CmsUser(); - $user->name = 'Roman'; - $user->username = 'romanb'; - $user->status = 'dev'; - $this->_em->persist($user); - $this->_em->flush(); - $this->_em->clear(); - - // $user is now detached - self::assertFalse($this->_em->contains($user)); - - $user->name = 'Roman B.'; - - $user2 = $this->_em->merge($user); - - self::assertFalse($user === $user2); - self::assertTrue($this->_em->contains($user2)); - self::assertEquals('Roman B.', $user2->name); - } - - public function testSerializeUnserializeModifyMerge(): void - { - $user = new CmsUser(); - $user->name = 'Guilherme'; - $user->username = 'gblanco'; - $user->status = 'developer'; - - $ph1 = new CmsPhonenumber(); - $ph1->phonenumber = '1234'; - $user->addPhonenumber($ph1); - - $this->_em->persist($user); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user)); - self::assertTrue($user->phonenumbers->isInitialized()); - - $serialized = serialize($user); - - $this->_em->clear(); - - self::assertFalse($this->_em->contains($user)); - - unset($user); - - $user = unserialize($serialized); - - self::assertEquals(1, count($user->getPhonenumbers()), 'Pre-Condition: 1 Phonenumber'); - - $ph2 = new CmsPhonenumber(); - - $ph2->phonenumber = '56789'; - $user->addPhonenumber($ph2); - - $oldPhonenumbers = $user->getPhonenumbers(); - - self::assertEquals(2, count($oldPhonenumbers), 'Pre-Condition: 2 Phonenumbers'); - self::assertFalse($this->_em->contains($user)); - - $this->_em->persist($ph2); - - // Merge back in - $user = $this->_em->merge($user); // merge cascaded to phonenumbers - self::assertInstanceOf(CmsUser::class, $user->phonenumbers[0]->user); - self::assertInstanceOf(CmsUser::class, $user->phonenumbers[1]->user); - $im = $this->_em->getUnitOfWork()->getIdentityMap(); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user), 'Failed to assert that merged user is contained inside EntityManager persistence context.'); - $phonenumbers = $user->getPhonenumbers(); - self::assertNotSame($oldPhonenumbers, $phonenumbers, 'Merge should replace the Detached Collection with a new PersistentCollection.'); - self::assertEquals(2, count($phonenumbers), 'Failed to assert that two phonenumbers are contained in the merged users phonenumber collection.'); - - self::assertInstanceOf(CmsPhonenumber::class, $phonenumbers[1]); - self::assertTrue($this->_em->contains($phonenumbers[1]), 'Failed to assert that second phonenumber in collection is contained inside EntityManager persistence context.'); - - self::assertInstanceOf(CmsPhonenumber::class, $phonenumbers[0]); - self::assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($phonenumbers[0])); - self::assertTrue($this->_em->contains($phonenumbers[0]), 'Failed to assert that first phonenumber in collection is contained inside EntityManager persistence context.'); - } - - /** @group DDC-203 */ + #[Group('DDC-203')] public function testDetachedEntityThrowsExceptionOnFlush(): void { $ph = new CmsPhonenumber(); @@ -129,36 +39,7 @@ public function testDetachedEntityThrowsExceptionOnFlush(): void $this->_em->flush(); } - public function testUninitializedLazyAssociationsAreIgnoredOnMerge(): void - { - $user = new CmsUser(); - $user->name = 'Guilherme'; - $user->username = 'gblanco'; - $user->status = 'developer'; - - $address = new CmsAddress(); - $address->city = 'Berlin'; - $address->country = 'Germany'; - $address->street = 'Sesamestreet'; - $address->zip = 12345; - $address->setUser($user); - $this->_em->persist($address); - $this->_em->persist($user); - - $this->_em->flush(); - $this->_em->clear(); - - $address2 = $this->_em->find(get_class($address), $address->id); - self::assertTrue($this->isUninitializedObject($address2->user)); - $detachedAddress2 = unserialize(serialize($address2)); - self::assertTrue($this->isUninitializedObject($detachedAddress2->user)); - - $managedAddress2 = $this->_em->merge($detachedAddress2); - self::assertFalse($managedAddress2->user === $detachedAddress2->user); - self::assertTrue($this->isUninitializedObject($managedAddress2->user)); - } - - /** @group DDC-822 */ + #[Group('DDC-822')] public function testUseDetachedEntityAsQueryParameter(): void { $user = new CmsUser(); @@ -181,7 +62,7 @@ public function testUseDetachedEntityAsQueryParameter(): void self::assertEquals('gblanco', $newUser->username); } - /** @group DDC-920 */ + #[Group('DDC-920')] public function testDetachManagedUnpersistedEntity(): void { $user = new CmsUser(); @@ -197,25 +78,4 @@ public function testDetachManagedUnpersistedEntity(): void self::assertFalse($this->_em->contains($user)); self::assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user)); } - - /** @group DDC-1340 */ - public function testMergeArticleWrongVersion(): void - { - $article = new CmsArticle(); - $article->topic = 'test'; - $article->text = 'test'; - - $this->_em->persist($article); - $this->_em->flush(); - - $this->_em->detach($article); - - $sql = 'UPDATE cms_articles SET version = version + 1 WHERE id = ' . $article->id; - $this->_em->getConnection()->executeStatement($sql); - - $this->expectException(OptimisticLockException::class); - $this->expectExceptionMessage('The optimistic lock failed, version 1 was expected, but is actually 2'); - - $this->_em->merge($article); - } } diff --git a/tests/Tests/ORM/Functional/EagerFetchCollectionTest.php b/tests/Tests/ORM/Functional/EagerFetchCollectionTest.php index 88397c6a12f..86fedaff06a 100644 --- a/tests/Tests/ORM/Functional/EagerFetchCollectionTest.php +++ b/tests/Tests/ORM/Functional/EagerFetchCollectionTest.php @@ -5,11 +5,14 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Tests\OrmFunctionalTestCase; use function count; +use function iterator_to_array; class EagerFetchCollectionTest extends OrmFunctionalTestCase { @@ -96,6 +99,16 @@ public function testSubselectFetchJoinWithAllowedWhenOverriddenNotEager(): void $this->assertIsString($query->getSql()); } + public function testSubselectFetchJoinWithAllowedWhenOverriddenNotEagerPaginator(): void + { + $query = $this->_em->createQuery('SELECT o, c FROM ' . EagerFetchOwner::class . ' o JOIN o.children c WITH c.id = 1'); + $query->setMaxResults(1); + $query->setFetchMode(EagerFetchChild::class, 'owner', ORM\ClassMetadata::FETCH_LAZY); + + $paginator = new Paginator($query, true); + $this->assertIsArray(iterator_to_array($paginator)); + } + public function testEagerFetchWithIterable(): void { $this->createOwnerWithChildren(2); @@ -128,26 +141,17 @@ protected function createOwnerWithChildren(int $children): EagerFetchOwner } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class EagerFetchOwner { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToMany(targetEntity="EagerFetchChild", mappedBy="owner", fetch="EAGER") - * - * @var ArrayCollection - */ - public $children; + #[ORM\OneToMany(mappedBy: 'owner', targetEntity: EagerFetchChild::class, fetch: 'EAGER')] + public Collection $children; public function __construct() { @@ -155,24 +159,16 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class EagerFetchChild { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\ManyToOne(targetEntity="EagerFetchOwner", inversedBy="children") - * - * @var EagerFetchOwner - */ + /** @var EagerFetchOwner */ + #[ORM\ManyToOne(targetEntity: EagerFetchOwner::class, inversedBy: 'children')] public $owner; } diff --git a/tests/Tests/ORM/Functional/EntityListenersTest.php b/tests/Tests/ORM/Functional/EntityListenersTest.php index 908cb31059d..097b2577658 100644 --- a/tests/Tests/ORM/Functional/EntityListenersTest.php +++ b/tests/Tests/ORM/Functional/EntityListenersTest.php @@ -16,12 +16,12 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1955 */ +#[Group('DDC-1955')] class EntityListenersTest extends OrmFunctionalTestCase { - /** @var CompanyContractListener */ - private $listener; + private CompanyContractListener $listener; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php b/tests/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php index fd8a79c04ea..51e1dc3b557 100644 --- a/tests/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php +++ b/tests/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php @@ -67,7 +67,7 @@ public function testLteDateComparison(): void $repository = $this->_em->getRepository(DateTimeModel::class); $dates = $repository->matching(new Criteria( - Criteria::expr()->lte('datetime', new DateTime('today')) + Criteria::expr()->lte('datetime', new DateTime('today')), )); self::assertCount(2, $dates); @@ -99,7 +99,7 @@ public function testIsNullComparison(): void $repository = $this->_em->getRepository(DateTimeModel::class); $dates = $repository->matching(new Criteria( - Criteria::expr()->isNull('time') + Criteria::expr()->isNull('time'), )); self::assertCount(1, $dates); @@ -111,7 +111,7 @@ public function testEqNullComparison(): void $repository = $this->_em->getRepository(DateTimeModel::class); $dates = $repository->matching(new Criteria( - Criteria::expr()->eq('time', null) + Criteria::expr()->eq('time', null), )); self::assertCount(1, $dates); @@ -123,7 +123,7 @@ public function testNotEqNullComparison(): void $repository = $this->_em->getRepository(DateTimeModel::class); $dates = $repository->matching(new Criteria( - Criteria::expr()->neq('time', null) + Criteria::expr()->neq('time', null), )); self::assertCount(1, $dates); @@ -142,7 +142,7 @@ public function testCanCountWithoutLoadingCollection(): void // Test it can work even with a constraint $dates = $repository->matching(new Criteria( - Criteria::expr()->lte('datetime', new DateTime('today')) + Criteria::expr()->lte('datetime', new DateTime('today')), )); self::assertFalse($dates->isInitialized()); diff --git a/tests/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Tests/ORM/Functional/EntityRepositoryTest.php index 2fb65358c7a..4ea391669f5 100644 --- a/tests/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Tests/ORM/Functional/EntityRepositoryTest.php @@ -7,18 +7,14 @@ use BadMethodCallException; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\EntityRepository; -use Doctrine\ORM\Exception\NotSupported; use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\UnrecognizedIdentifierFields; -use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\Persisters\Exception\InvalidOrientation; use Doctrine\ORM\Persisters\Exception\UnrecognizedField; -use Doctrine\ORM\Query; +use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\Repository\Exception\InvalidFindByCall; use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall; use Doctrine\ORM\TransactionRequiredException; @@ -30,15 +26,13 @@ use Doctrine\Tests\Models\DDC753\DDC753EntityWithCustomRepository; use Doctrine\Tests\Models\DDC753\DDC753EntityWithDefaultCustomRepository; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_values; -use function class_exists; use function reset; class EntityRepositoryTest extends OrmFunctionalTestCase { - use VerifyDeprecations; - protected function setUp(): void { $this->useModelSet('cms'); @@ -276,25 +270,6 @@ public function testFindAll(): void self::assertCount(4, $users); } - public function testFindByAlias(): void - { - if (! class_exists(PersistentObject::class)) { - $this->markTestSkipped('This test requires doctrine/persistence 2'); - } - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8818'); - - $user1Id = $this->loadFixture(); - $repos = $this->_em->getRepository(CmsUser::class); - - $this->_em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS'); - - $repos = $this->_em->getRepository('CMS:CmsUser'); - - $users = $repos->findAll(); - self::assertCount(4, $users); - } - public function testCount(): void { $this->loadFixture(); @@ -333,10 +308,8 @@ public function testExceptionIsThrownWhenUsingInvalidFieldName(): void ->findByThisFieldDoesNotExist('testvalue'); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticReadLockWithoutTransactionThrowsException(): void { $this->expectException(TransactionRequiredException::class); @@ -345,10 +318,8 @@ public function testPessimisticReadLockWithoutTransactionThrowsException(): void ->find(1, LockMode::PESSIMISTIC_READ); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticWriteLockWithoutTransactionThrowsException(): void { $this->expectException(TransactionRequiredException::class); @@ -357,10 +328,8 @@ public function testPessimisticWriteLockWithoutTransactionThrowsException(): voi ->find(1, LockMode::PESSIMISTIC_WRITE); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testOptimisticLockUnversionedEntityThrowsException(): void { $this->expectException(OptimisticLockException::class); @@ -369,10 +338,8 @@ public function testOptimisticLockUnversionedEntityThrowsException(): void ->find(1, LockMode::OPTIMISTIC); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testIdentityMappedOptimisticLockUnversionedEntityThrowsException(): void { $user = new CmsUser(); @@ -391,7 +358,7 @@ public function testIdentityMappedOptimisticLockUnversionedEntityThrowsException $this->_em->find(CmsUser::class, $userId, LockMode::OPTIMISTIC); } - /** @group DDC-819 */ + #[Group('DDC-819')] public function testFindMagicCallByNullValue(): void { $this->loadFixture(); @@ -402,7 +369,7 @@ public function testFindMagicCallByNullValue(): void self::assertCount(1, $users); } - /** @group DDC-819 */ + #[Group('DDC-819')] public function testInvalidMagicCall(): void { $this->expectException(BadMethodCallException::class); @@ -411,7 +378,7 @@ public function testInvalidMagicCall(): void $repos->foo(); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindByAssociationKeyExceptionOnInverseSide(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -423,7 +390,7 @@ public function testFindByAssociationKeyExceptionOnInverseSide(): void $user = $repos->findBy(['address' => $addressId]); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindOneByAssociationKey(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -434,7 +401,7 @@ public function testFindOneByAssociationKey(): void self::assertEquals($addressId, $address->id); } - /** @group DDC-1241 */ + #[Group('DDC-1241')] public function testFindOneByOrderBy(): void { $this->loadFixture(); @@ -446,7 +413,7 @@ public function testFindOneByOrderBy(): void self::assertNotSame($userAsc, $userDesc); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindByAssociationKey(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -458,7 +425,7 @@ public function testFindByAssociationKey(): void self::assertEquals($addressId, $addresses[0]->id); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindAssociationByMagicCall(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -470,7 +437,7 @@ public function testFindAssociationByMagicCall(): void self::assertEquals($addressId, $addresses[0]->id); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindOneAssociationByMagicCall(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -481,28 +448,7 @@ public function testFindOneAssociationByMagicCall(): void self::assertEquals($addressId, $address->id); } - public function testValidNamedQueryRetrieval(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8592'); - - $repos = $this->_em->getRepository(CmsUser::class); - - $query = $repos->createNamedQuery('all'); - - self::assertInstanceOf(Query::class, $query); - self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $query->getDQL()); - } - - public function testInvalidNamedQueryRetrieval(): void - { - $repos = $this->_em->getRepository(CmsUser::class); - - $this->expectException(MappingException::class); - - $repos->createNamedQuery('invalidNamedQuery'); - } - - /** @group DDC-1087 */ + #[Group('DDC-1087')] public function testIsNullCriteriaDoesNotGenerateAParameter(): void { $repos = $this->_em->getRepository(CmsUser::class); @@ -523,7 +469,7 @@ public function testIsNullCriteria(): void self::assertCount(1, $users); } - /** @group DDC-1094 */ + #[Group('DDC-1094')] public function testFindByLimitOffset(): void { $this->loadFixture(); @@ -539,7 +485,7 @@ public function testFindByLimitOffset(): void self::assertNotSame($users1[0], $users2[0]); } - /** @group DDC-1094 */ + #[Group('DDC-1094')] public function testFindByOrderBy(): void { $this->loadFixture(); @@ -554,7 +500,7 @@ public function testFindByOrderBy(): void self::assertSame($usersAsc[3], $usersDesc[0]); } - /** @group DDC-1376 */ + #[Group('DDC-1376')] public function testFindByOrderByAssociation(): void { $this->loadFixtureUserEmail(); @@ -570,7 +516,7 @@ public function testFindByOrderByAssociation(): void self::assertEquals($resultAsc[2]->getEmail()->getId(), $resultDesc[0]->getEmail()->getId()); } - /** @group DDC-1426 */ + #[Group('DDC-1426')] public function testFindFieldByMagicCallOrderBy(): void { $this->loadFixture(); @@ -590,7 +536,7 @@ public function testFindFieldByMagicCallOrderBy(): void self::assertSame($usersAsc[1], $usersDesc[0]); } - /** @group DDC-1426 */ + #[Group('DDC-1426')] public function testFindFieldByMagicCallLimitOffset(): void { $this->loadFixture(); @@ -604,7 +550,7 @@ public function testFindFieldByMagicCallLimitOffset(): void self::assertNotSame($users1[0], $users2[0]); } - /** @group DDC-753 */ + #[Group('DDC-753')] public function testDefaultRepositoryClassName(): void { self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), EntityRepository::class); @@ -624,35 +570,16 @@ public function testDefaultRepositoryClassName(): void self::assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), EntityRepository::class); } - /** @group DDC-3257 */ - public function testSingleRepositoryInstanceForDifferentEntityAliases(): void - { - if (! class_exists(PersistentObject::class)) { - $this->markTestSkipped('This test requires doctrine/persistence 2'); - } - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8818'); - $config = $this->_em->getConfiguration(); - - $config->addEntityNamespace('Aliased', 'Doctrine\Tests\Models\CMS'); - $config->addEntityNamespace('AliasedAgain', 'Doctrine\Tests\Models\CMS'); - - $repository = $this->_em->getRepository(CmsUser::class); - - self::assertSame($repository, $this->_em->getRepository('Aliased:CmsUser')); - self::assertSame($repository, $this->_em->getRepository('AliasedAgain:CmsUser')); - } - - /** @group DDC-3257 */ + #[Group('DDC-3257')] public function testCanRetrieveRepositoryFromClassNameWithLeadingBackslash(): void { self::assertSame( $this->_em->getRepository('\\' . CmsUser::class), - $this->_em->getRepository(CmsUser::class) + $this->_em->getRepository(CmsUser::class), ); } - /** @group DDC-1376 */ + #[Group('DDC-1376')] public function testInvalidOrderByAssociation(): void { $this->expectException(InvalidFindByCall::class); @@ -661,7 +588,7 @@ public function testInvalidOrderByAssociation(): void ->findBy(['status' => 'test'], ['address' => 'ASC']); } - /** @group DDC-1500 */ + #[Group('DDC-1500')] public function testInvalidOrientation(): void { $this->expectException(InvalidOrientation::class); @@ -671,7 +598,7 @@ public function testInvalidOrientation(): void $repo->findBy(['status' => 'test'], ['username' => 'INVALID']); } - /** @group DDC-1713 */ + #[Group('DDC-1713')] public function testFindByAssociationArray(): void { $address1 = new CmsAddress(); @@ -728,7 +655,7 @@ public function testFindByAssociationArray(): void } } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingEmptyCriteria(): void { $this->loadFixture(); @@ -739,111 +666,111 @@ public function testMatchingEmptyCriteria(): void self::assertCount(4, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaEqComparison(): void { $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->eq('username', 'beberlei') + Criteria::expr()->eq('username', 'beberlei'), )); self::assertCount(1, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaNeqComparison(): void { $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->neq('username', 'beberlei') + Criteria::expr()->neq('username', 'beberlei'), )); self::assertCount(3, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaInComparison(): void { $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->in('username', ['beberlei', 'gblanco']) + Criteria::expr()->in('username', ['beberlei', 'gblanco']), )); self::assertCount(2, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaNotInComparison(): void { $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->notIn('username', ['beberlei', 'gblanco', 'asm89']) + Criteria::expr()->notIn('username', ['beberlei', 'gblanco', 'asm89']), )); self::assertCount(1, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaLtComparison(): void { $firstUserId = $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->lt('id', $firstUserId + 1) + Criteria::expr()->lt('id', $firstUserId + 1), )); self::assertCount(1, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaLeComparison(): void { $firstUserId = $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->lte('id', $firstUserId + 1) + Criteria::expr()->lte('id', $firstUserId + 1), )); self::assertCount(2, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaGtComparison(): void { $firstUserId = $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->gt('id', $firstUserId) + Criteria::expr()->gt('id', $firstUserId), )); self::assertCount(3, $users); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatchingCriteriaGteComparison(): void { $firstUserId = $this->loadFixture(); $repository = $this->_em->getRepository(CmsUser::class); $users = $repository->matching(new Criteria( - Criteria::expr()->gte('id', $firstUserId) + Criteria::expr()->gte('id', $firstUserId), )); self::assertCount(4, $users); } - /** @group DDC-2430 */ + #[Group('DDC-2430')] public function testMatchingCriteriaAssocationByObjectInMemory(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -851,7 +778,7 @@ public function testMatchingCriteriaAssocationByObjectInMemory(): void $user = $this->_em->find(CmsUser::class, $userId); $criteria = new Criteria( - Criteria::expr()->eq('user', $user) + Criteria::expr()->eq('user', $user), ); $repository = $this->_em->getRepository(CmsAddress::class); @@ -864,7 +791,7 @@ public function testMatchingCriteriaAssocationByObjectInMemory(): void self::assertCount(1, $addresses->matching($criteria)); } - /** @group DDC-2430 */ + #[Group('DDC-2430')] public function testMatchingCriteriaAssocationInWithArray(): void { [$userId, $addressId] = $this->loadAssociatedFixture(); @@ -872,7 +799,7 @@ public function testMatchingCriteriaAssocationInWithArray(): void $user = $this->_em->find(CmsUser::class, $userId); $criteria = new Criteria( - Criteria::expr()->in('user', [$user]) + Criteria::expr()->in('user', [$user]), ); $repository = $this->_em->getRepository(CmsAddress::class); @@ -933,7 +860,7 @@ public function testMatchingCriteriaEndsWithComparison(): void self::assertCount(2, $users); } - /** @group DDC-2478 */ + #[Group('DDC-2478')] public function testMatchingCriteriaNullAssocComparison(): void { $fixtures = $this->loadFixtureUserEmail(); @@ -960,17 +887,17 @@ public function testMatchingCriteriaNullAssocComparison(): void self::assertNull($usersEqNull[0]->getEmail()); } - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testCreateResultSetMappingBuilder(): void { $repository = $this->_em->getRepository(CmsUser::class); $rsm = $repository->createResultSetMappingBuilder('u'); - self::assertInstanceOf(Query\ResultSetMappingBuilder::class, $rsm); + self::assertInstanceOf(ResultSetMappingBuilder::class, $rsm); self::assertEquals(['u' => CmsUser::class], $rsm->aliasMap); } - /** @group DDC-3045 */ + #[Group('DDC-3045')] public function testFindByFieldInjectionPrevented(): void { $this->expectException(UnrecognizedField::class); @@ -980,7 +907,7 @@ public function testFindByFieldInjectionPrevented(): void $repository->findBy(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test']); } - /** @group DDC-3045 */ + #[Group('DDC-3045')] public function testFindOneByFieldInjectionPrevented(): void { $this->expectException(ORMException::class); @@ -990,7 +917,7 @@ public function testFindOneByFieldInjectionPrevented(): void $repository->findOneBy(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test']); } - /** @group DDC-3045 */ + #[Group('DDC-3045')] public function testMatchingInjectionPrevented(): void { $this->expectException(UnrecognizedField::class); @@ -998,14 +925,14 @@ public function testMatchingInjectionPrevented(): void $repository = $this->_em->getRepository(CmsUser::class); $result = $repository->matching(new Criteria( - Criteria::expr()->eq('username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1', 'beberlei') + Criteria::expr()->eq('username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1', 'beberlei'), )); // Because repository returns a lazy collection, we call toArray to force initialization $result->toArray(); } - /** @group DDC-3045 */ + #[Group('DDC-3045')] public function testFindInjectionPrevented(): void { $this->expectException(UnrecognizedIdentifierFields::class); @@ -1015,7 +942,7 @@ public function testFindInjectionPrevented(): void $repository->find(['username = ?; DELETE FROM cms_users; SELECT 1 WHERE 1' => 'test', 'id' => 1]); } - /** @group DDC-3056 */ + #[Group('DDC-3056')] public function testFindByNullValueInInCondition(): void { $user1 = new CmsUser(); @@ -1038,7 +965,7 @@ public function testFindByNullValueInInCondition(): void self::assertSame($user1, reset($users)); } - /** @group DDC-3056 */ + #[Group('DDC-3056')] public function testFindByNullValueInMultipleInCriteriaValues(): void { $user1 = new CmsUser(); @@ -1064,7 +991,7 @@ public function testFindByNullValueInMultipleInCriteriaValues(): void self::assertSame($user1, reset($users)); } - /** @group DDC-3056 */ + #[Group('DDC-3056')] public function testFindMultipleByNullValueInMultipleInCriteriaValues(): void { $user1 = new CmsUser(); @@ -1092,18 +1019,4 @@ public function testFindMultipleByNullValueInMultipleInCriteriaValues(): void self::assertContains($user, [$user1, $user2]); } } - - public function testDeprecatedClear(): void - { - $repository = $this->_em->getRepository(CmsAddress::class); - - if (class_exists(PersistentObject::class)) { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8460'); - } else { - $this->expectException(NotSupported::class); - $this->expectExceptionMessage(CmsAddress::class); - } - - $repository->clear(); - } } diff --git a/tests/Tests/ORM/Functional/EnumTest.php b/tests/Tests/ORM/Functional/EnumTest.php index 3062c707ac6..ba84a921240 100644 --- a/tests/Tests/ORM/Functional/EnumTest.php +++ b/tests/Tests/ORM/Functional/EnumTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\DBAL\Types\EnumType; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Driver\AttributeDriver; @@ -13,6 +14,7 @@ use Doctrine\Tests\Models\DataTransferObjects\DtoWithArrayOfEnums; use Doctrine\Tests\Models\DataTransferObjects\DtoWithEnum; use Doctrine\Tests\Models\Enums\Card; +use Doctrine\Tests\Models\Enums\CardNativeEnum; use Doctrine\Tests\Models\Enums\CardWithDefault; use Doctrine\Tests\Models\Enums\CardWithNullable; use Doctrine\Tests\Models\Enums\Product; @@ -20,16 +22,16 @@ use Doctrine\Tests\Models\Enums\Scale; use Doctrine\Tests\Models\Enums\Suit; use Doctrine\Tests\Models\Enums\TypedCard; +use Doctrine\Tests\Models\Enums\TypedCardNativeEnum; use Doctrine\Tests\Models\Enums\Unit; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use function class_exists; use function dirname; use function sprintf; use function uniqid; -/** - * @requires PHP 8.1 - */ class EnumTest extends OrmFunctionalTestCase { public function setUp(): void @@ -44,11 +46,8 @@ public function setUp(): void } } - /** - * @param class-string $cardClass - * - * @dataProvider provideCardClasses - */ + /** @param class-string $cardClass */ + #[DataProvider('provideCardClasses')] public function testEnumMapping(string $cardClass): void { $this->setUpEntitySchema([$cardClass]); @@ -60,7 +59,7 @@ public function testEnumMapping(string $cardClass): void $this->_em->flush(); $this->_em->clear(); - $fetchedCard = $this->_em->find(Card::class, $card->id); + $fetchedCard = $this->_em->find($cardClass, $card->id); $this->assertInstanceOf(Suit::class, $fetchedCard->suit); $this->assertEquals(Suit::Clubs, $fetchedCard->suit); @@ -275,7 +274,7 @@ public function testEnumSingleEntityChangeSetsSimpleObjectHydrator(): void $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet( $this->_em->getClassMetadata(Card::class), - $result + $result, ); self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result)); @@ -284,7 +283,7 @@ public function testEnumSingleEntityChangeSetsSimpleObjectHydrator(): void $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet( $this->_em->getClassMetadata(Card::class), - $result + $result, ); self::assertTrue($this->_em->getUnitOfWork()->isScheduledForUpdate($result)); @@ -305,7 +304,7 @@ public function testEnumSingleEntityChangeSetsObjectHydrator(): void $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet( $this->_em->getClassMetadata(Card::class), - $result + $result, ); self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result)); @@ -326,7 +325,7 @@ public function testEnumArraySingleEntityChangeSets(): void $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet( $this->_em->getClassMetadata(Scale::class), - $result + $result, ); self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result)); @@ -418,13 +417,14 @@ public function testFindByEnum(): void } } - /** - * @param class-string $cardClass - * - * @dataProvider provideCardClasses - */ + /** @param class-string $cardClass */ + #[DataProvider('provideCardClasses')] public function testEnumWithNonMatchingDatabaseValueThrowsException(string $cardClass): void { + if ($cardClass === TypedCardNativeEnum::class) { + self::markTestSkipped('MySQL won\'t allow us to insert invalid values in this case.'); + } + $this->setUpEntitySchema([$cardClass]); $card = new $cardClass(); @@ -437,31 +437,34 @@ public function testEnumWithNonMatchingDatabaseValueThrowsException(string $card $metadata = $this->_em->getClassMetadata($cardClass); $this->_em->getConnection()->update( $metadata->table['name'], - [$metadata->fieldMappings['suit']['columnName'] => 'invalid'], - [$metadata->fieldMappings['id']['columnName'] => $card->id] + [$metadata->fieldMappings['suit']->columnName => 'Z'], + [$metadata->fieldMappings['id']->columnName => $card->id], ); $this->expectException(MappingException::class); $this->expectExceptionMessage(sprintf( <<<'EXCEPTION' Context: Trying to hydrate enum property "%s::$suit" -Problem: Case "invalid" is not listed in enum "Doctrine\Tests\Models\Enums\Suit" +Problem: Case "Z" is not listed in enum "Doctrine\Tests\Models\Enums\Suit" Solution: Either add the case to the enum type or migrate the database column to use another case of the enum EXCEPTION , - $cardClass + $cardClass, )); $this->_em->find($cardClass, $card->id); } - /** @return array */ - public static function provideCardClasses(): array + /** @return iterable */ + public static function provideCardClasses(): iterable { - return [ - Card::class => [Card::class], - TypedCard::class => [TypedCard::class], - ]; + yield Card::class => [Card::class]; + yield TypedCard::class => [TypedCard::class]; + + if (class_exists(EnumType::class)) { + yield CardNativeEnum::class => [CardNativeEnum::class]; + yield TypedCardNativeEnum::class => [TypedCardNativeEnum::class]; + } } public function testItAllowsReadingAttributes(): void diff --git a/tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php index e422d2cde78..569ea135009 100644 --- a/tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -12,6 +12,7 @@ use Doctrine\Tests\Models\DDC2504\DDC2504ChildClass; use Doctrine\Tests\Models\DDC2504\DDC2504OtherClass; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_shift; use function assert; @@ -21,32 +22,23 @@ */ class ExtraLazyCollectionTest extends OrmFunctionalTestCase { - /** @var int */ - private $userId; + private int|null $userId = null; - /** @var int */ - private $userId2; + private int|null $userId2 = null; - /** @var int */ - private $groupId; + private int|null $groupId = null; - /** @var int */ - private $articleId; + private int|null $articleId = null; - /** @var int */ - private $ddc2504OtherClassId; + private int|null $ddc2504OtherClassId = null; - /** @var int */ - private $ddc2504ChildClassId; + private int|null $ddc2504ChildClassId = null; - /** @var string */ - private $username; + private string|null $username = null; - /** @var string */ - private $groupname; + private string|null $groupname = null; - /** @var string */ - private $topic; + private string|null $topic = null; /** @var CmsPhonenumber */ private $phonenumber; @@ -62,25 +54,25 @@ protected function setUp(): void parent::setUp(); - $class = $this->_em->getClassMetadata(CmsUser::class); - $class->associationMappings['groups']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['groups']['indexBy'] = 'name'; - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['articles']['indexBy'] = 'topic'; - $class->associationMappings['phonenumbers']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['phonenumbers']['indexBy'] = 'phonenumber'; + $class = $this->_em->getClassMetadata(CmsUser::class); + $class->associationMappings['groups']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['groups']->indexBy = 'name'; + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['articles']->indexBy = 'topic'; + $class->associationMappings['phonenumbers']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['phonenumbers']->indexBy = 'phonenumber'; foreach (['phonenumbers', 'articles', 'users'] as $field) { - if (isset($class->associationMappings[$field]['cache'])) { - $this->previousCacheConfig[$field] = $class->associationMappings[$field]['cache']; + if (isset($class->associationMappings[$field]->cache)) { + $this->previousCacheConfig[$field] = $class->associationMappings[$field]->cache; } - unset($class->associationMappings[$field]['cache']); + unset($class->associationMappings[$field]->cache); } - $class = $this->_em->getClassMetadata(CmsGroup::class); - $class->associationMappings['users']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['users']['indexBy'] = 'username'; + $class = $this->_em->getClassMetadata(CmsGroup::class); + $class->associationMappings['users']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['users']->indexBy = 'username'; $this->loadFixture(); } @@ -89,32 +81,30 @@ public function tearDown(): void { parent::tearDown(); - $class = $this->_em->getClassMetadata(CmsUser::class); - $class->associationMappings['groups']['fetch'] = ClassMetadata::FETCH_LAZY; - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_LAZY; - $class->associationMappings['phonenumbers']['fetch'] = ClassMetadata::FETCH_LAZY; + $class = $this->_em->getClassMetadata(CmsUser::class); + $class->associationMappings['groups']->fetch = ClassMetadata::FETCH_LAZY; + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_LAZY; + $class->associationMappings['phonenumbers']->fetch = ClassMetadata::FETCH_LAZY; foreach (['phonenumbers', 'articles', 'users'] as $field) { if (isset($this->previousCacheConfig[$field])) { - $class->associationMappings[$field]['cache'] = $this->previousCacheConfig[$field]; + $class->associationMappings[$field]->cache = $this->previousCacheConfig[$field]; unset($this->previousCacheConfig[$field]); } } - unset($class->associationMappings['groups']['indexBy']); - unset($class->associationMappings['articles']['indexBy']); - unset($class->associationMappings['phonenumbers']['indexBy']); + unset($class->associationMappings['groups']->indexBy); + unset($class->associationMappings['articles']->indexBy); + unset($class->associationMappings['phonenumbers']->indexBy); - $class = $this->_em->getClassMetadata(CmsGroup::class); - $class->associationMappings['users']['fetch'] = ClassMetadata::FETCH_LAZY; + $class = $this->_em->getClassMetadata(CmsGroup::class); + $class->associationMappings['users']->fetch = ClassMetadata::FETCH_LAZY; - unset($class->associationMappings['users']['indexBy']); + unset($class->associationMappings['users']->indexBy); } - /** - * @group DDC-546 - * @group non-cacheable - */ + #[Group('DDC-546')] + #[Group('non-cacheable')] public function testCountNotInitializesCollection(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -130,7 +120,7 @@ public function testCountNotInitializesCollection(): void $this->assertQueryCount(2, 'Expecting two queries to be fired for count, then iteration.'); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testCountWhenNewEntityPresent(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -146,10 +136,8 @@ public function testCountWhenNewEntityPresent(): void self::assertFalse($user->groups->isInitialized()); } - /** - * @group DDC-546 - * @group non-cacheable - */ + #[Group('DDC-546')] + #[Group('non-cacheable')] public function testCountWhenInitialized(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -163,7 +151,7 @@ public function testCountWhenInitialized(): void $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for count() more.'); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testCountInverseCollection(): void { $group = $this->_em->find(CmsGroup::class, $this->groupId); @@ -173,7 +161,7 @@ public function testCountInverseCollection(): void self::assertFalse($group->users->isInitialized(), 'Extra Lazy collection should not be initialized by counting the collection.'); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testCountOneToMany(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -182,7 +170,7 @@ public function testCountOneToMany(): void self::assertCount(2, $user->articles); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testCountOneToManyJoinedInheritance(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -191,7 +179,64 @@ public function testCountOneToManyJoinedInheritance(): void self::assertCount(2, $otherClass->childClasses); } - /** @group DDC-546 */ + #[Group('non-cacheable')] + public function testFirstWhenInitialized(): void + { + $user = $this->_em->find(CmsUser::class, $this->userId); + $this->getQueryLog()->reset()->enable(); + $user->groups->toArray(); + + self::assertTrue($user->groups->isInitialized()); + self::assertInstanceOf(CmsGroup::class, $user->groups->first()); + $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().'); + } + + public function testFirstOnEmptyCollectionWhenInitialized(): void + { + foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) { + $this->_em->remove($group); + } + + $this->_em->flush(); + + $user = $this->_em->find(CmsUser::class, $this->userId); + $this->getQueryLog()->reset()->enable(); + $user->groups->toArray(); + + self::assertTrue($user->groups->isInitialized()); + self::assertFalse($user->groups->first()); + $this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().'); + } + + public function testFirstWhenNotInitialized(): void + { + $user = $this->_em->find(CmsUser::class, $this->userId); + $this->getQueryLog()->reset()->enable(); + + self::assertFalse($user->groups->isInitialized()); + self::assertInstanceOf(CmsGroup::class, $user->groups->first()); + self::assertFalse($user->groups->isInitialized()); + $this->assertQueryCount(1, 'Should only execute one query for first().'); + } + + public function testFirstOnEmptyCollectionWhenNotInitialized(): void + { + foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) { + $this->_em->remove($group); + } + + $this->_em->flush(); + + $user = $this->_em->find(CmsUser::class, $this->userId); + $this->getQueryLog()->reset()->enable(); + + self::assertFalse($user->groups->isInitialized()); + self::assertFalse($user->groups->first()); + self::assertFalse($user->groups->isInitialized()); + $this->assertQueryCount(1, 'Should only execute one query for first().'); + } + + #[Group('DDC-546')] public function testFullSlice(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -201,10 +246,8 @@ public function testFullSlice(): void self::assertCount(3, $someGroups); } - /** - * @group DDC-546 - * @group non-cacheable - */ + #[Group('DDC-546')] + #[Group('non-cacheable')] public function testSlice(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -233,10 +276,8 @@ public function testSlice(): void $this->assertQueryCount(3); } - /** - * @group DDC-546 - * @group non-cacheable - */ + #[Group('DDC-546')] + #[Group('non-cacheable')] public function testSliceInitializedCollection(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -254,7 +295,7 @@ public function testSliceInitializedCollection(): void self::assertTrue($user->groups->contains(array_shift($someGroups))); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testSliceInverseCollection(): void { $group = $this->_em->find(CmsGroup::class, $this->groupId); @@ -273,7 +314,7 @@ public function testSliceInverseCollection(): void $this->assertQueryCount(2, 'Slicing two parts should only execute two additional queries.'); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testSliceOneToMany(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -287,7 +328,7 @@ public function testSliceOneToMany(): void $this->assertQueryCount(2); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testContainsOneToMany(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -333,7 +374,7 @@ public function testContainsOneToMany(): void self::assertFalse($user->articles->isInitialized(), 'Post-Condition: Collection is not initialized.'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testLazyOneToManyJoinedInheritanceIsLazilyInitialized(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -341,7 +382,7 @@ public function testLazyOneToManyJoinedInheritanceIsLazilyInitialized(): void self::assertFalse($otherClass->childClasses->isInitialized(), 'Collection is not initialized.'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWhenMatchingItemIsFound(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -355,7 +396,7 @@ public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollect $this->assertQueryCount(1, 'Search operation was performed via SQL'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testContainsOnOneToManyJoinedInheritanceWillNotCauseQueriesWhenNonPersistentItemIsMatched(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -365,7 +406,7 @@ public function testContainsOnOneToManyJoinedInheritanceWillNotCauseQueriesWhenN $this->assertQueryCount(0, 'Checking for contains of new entity should cause no query to be executed.'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWithClearStateMatchingItem(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -381,7 +422,7 @@ public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollect self::assertFalse($otherClass->childClasses->isInitialized(), 'Post-Condition: Collection is not initialized.'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollectionWithNewStateNotMatchingItem(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -396,7 +437,7 @@ public function testContainsOnOneToManyJoinedInheritanceWillNotInitializeCollect self::assertFalse($otherClass->childClasses->isInitialized(), 'Post-Condition: Collection is not initialized.'); } - /** @group DDC-2504 */ + #[Group('DDC-2504')] public function testCountingOnOneToManyJoinedInheritanceWillNotInitializeCollection(): void { $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -406,7 +447,7 @@ public function testCountingOnOneToManyJoinedInheritanceWillNotInitializeCollect self::assertFalse($otherClass->childClasses->isInitialized()); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testContainsManyToMany(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -453,7 +494,7 @@ public function testContainsManyToMany(): void self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.'); } - /** @group DDC-546 */ + #[Group('DDC-546')] public function testContainsManyToManyInverse(): void { $group = $this->_em->find(CmsGroup::class, $this->groupId); @@ -475,7 +516,7 @@ public function testContainsManyToManyInverse(): void self::assertFalse($user->groups->isInitialized(), 'Post-Condition: Collection is not initialized.'); } - /** @group DDC-1399 */ + #[Group('DDC-1399')] public function testCountAfterAddThenFlush(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -495,10 +536,8 @@ public function testCountAfterAddThenFlush(): void self::assertCount(4, $user->groups); } - /** - * @group DDC-1462 - * @group non-cacheable - */ + #[Group('DDC-1462')] + #[Group('non-cacheable')] public function testSliceOnDirtyCollection(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -517,10 +556,8 @@ public function testSliceOnDirtyCollection(): void $this->assertQueryCount(1); } - /** - * @group DDC-1398 - * @group non-cacheable - */ + #[Group('DDC-1398')] + #[Group('non-cacheable')] public function testGetIndexByIdentifier(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -537,7 +574,7 @@ public function testGetIndexByIdentifier(): void $this->assertQueryCount(1, 'Getting the same entity should not cause an extra query to be executed'); } - /** @group DDC-1398 */ + #[Group('DDC-1398')] public function testGetIndexByOneToMany(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -552,7 +589,7 @@ public function testGetIndexByOneToMany(): void self::assertSame($article, $this->_em->find(CmsArticle::class, $this->articleId)); } - /** @group DDC-1398 */ + #[Group('DDC-1398')] public function testGetIndexByManyToManyInverseSide(): void { $group = $this->_em->find(CmsGroup::class, $this->groupId); @@ -567,7 +604,7 @@ public function testGetIndexByManyToManyInverseSide(): void self::assertSame($user, $this->_em->find(CmsUser::class, $this->userId)); } - /** @group DDC-1398 */ + #[Group('DDC-1398')] public function testGetIndexByManyToManyOwningSide(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -582,7 +619,7 @@ public function testGetIndexByManyToManyOwningSide(): void self::assertSame($group, $this->_em->find(CmsGroup::class, $this->groupId)); } - /** @group DDC-1398 */ + #[Group('DDC-1398')] public function testGetNonExistentIndexBy(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -606,8 +643,8 @@ public function testContainsKeyIndexByOneToMany(): void public function testContainsKeyIndexByOneToManyJoinedInheritance(): void { - $class = $this->_em->getClassMetadata(DDC2504OtherClass::class); - $class->associationMappings['childClasses']['indexBy'] = 'id'; + $class = $this->_em->getClassMetadata(DDC2504OtherClass::class); + $class->associationMappings['childClasses']->indexBy = 'id'; $otherClass = $this->_em->find(DDC2504OtherClass::class, $this->ddc2504OtherClassId); @@ -651,8 +688,8 @@ public function testContainsKeyIndexByManyToManyNonOwning(): void public function testContainsKeyIndexByWithPkManyToMany(): void { - $class = $this->_em->getClassMetadata(CmsUser::class); - $class->associationMappings['groups']['indexBy'] = 'id'; + $class = $this->_em->getClassMetadata(CmsUser::class); + $class->associationMappings['groups']->indexBy = 'id'; $user = $this->_em->find(CmsUser::class, $this->userId2); @@ -667,8 +704,8 @@ public function testContainsKeyIndexByWithPkManyToMany(): void public function testContainsKeyIndexByWithPkManyToManyNonOwning(): void { - $class = $this->_em->getClassMetadata(CmsGroup::class); - $class->associationMappings['users']['indexBy'] = 'id'; + $class = $this->_em->getClassMetadata(CmsGroup::class); + $class->associationMappings['users']->indexBy = 'id'; $group = $this->_em->find(CmsGroup::class, $this->groupId); diff --git a/tests/Tests/ORM/Functional/FlushEventTest.php b/tests/Tests/ORM/Functional/FlushEventTest.php index 8900ea0748c..114fe3396b2 100644 --- a/tests/Tests/ORM/Functional/FlushEventTest.php +++ b/tests/Tests/ORM/Functional/FlushEventTest.php @@ -9,8 +9,7 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * FlushEventTest @@ -51,7 +50,7 @@ public function testPersistNewEntitiesOnPreFlush(): void //$this->_em->flush(); } - /** @group DDC-2173 */ + #[Group('DDC-2173')] public function testPreAndOnFlushCalledAlways(): void { $listener = new OnFlushCalledListener(); @@ -92,7 +91,7 @@ public function onFlush(OnFlushEventArgs $args): void $em->persist($phone); // Explicitly calculate the changeset since onFlush is raised // after changeset calculation! - $uow->computeChangeSet($em->getClassMetadata(get_class($phone)), $phone); + $uow->computeChangeSet($em->getClassMetadata($phone::class), $phone); // Take a snapshot because the UoW wont do this for us, because // the UoW did not visit this collection. diff --git a/tests/Tests/ORM/Functional/GH7877Test.php b/tests/Tests/ORM/Functional/GH7877Test.php index 6461c686527..c61d3112114 100644 --- a/tests/Tests/ORM/Functional/GH7877Test.php +++ b/tests/Tests/ORM/Functional/GH7877Test.php @@ -9,9 +9,6 @@ use function uniqid; -/** - * @group GH7877 - */ class GH7877Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -20,7 +17,7 @@ protected function setUp(): void $this->createSchemaForModels( GH7877ApplicationGeneratedIdEntity::class, - GH7877EntityWithNullableAssociation::class + GH7877EntityWithNullableAssociation::class, ); } @@ -76,29 +73,18 @@ public function testNullableForeignKeysMakeInsertOrderLessRelevant(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH7877ApplicationGeneratedIdEntity { - /** - * @ORM\Id - * @ORM\Column(type="string") - * @ORM\GeneratedValue(strategy="NONE") - * - * @var string - */ - public $id; - - /** - * (!) Note this uses "nullable=false" - * - * @ORM\ManyToOne(targetEntity="GH7877ApplicationGeneratedIdEntity") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false) - * - * @var self - */ - public $parent; + #[ORM\Id] + #[ORM\Column(type: 'string', length: 32)] + #[ORM\GeneratedValue(strategy: 'NONE')] + public string $id; + + /** (!) Note this uses "nullable=false" */ + #[ORM\ManyToOne(targetEntity: self::class)] + #[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id', nullable: false)] + public self $parent; public function __construct() { @@ -106,27 +92,17 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH7877EntityWithNullableAssociation { - /** - * @ORM\Id - * @ORM\Column(type="string") - * @ORM\GeneratedValue(strategy="NONE") - * - * @var string - */ - public $id; - - /** - * @ORM\ManyToOne(targetEntity="GH7877EntityWithNullableAssociation") - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true) - * - * @var self - */ - public $parent; + #[ORM\Id] + #[ORM\Column(type: 'string', length: 32)] + #[ORM\GeneratedValue(strategy: 'NONE')] + public string $id; + + #[ORM\ManyToOne(targetEntity: self::class)] + #[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id', nullable: true)] + public self $parent; public function __construct() { diff --git a/tests/Tests/ORM/Functional/HydrationCacheTest.php b/tests/Tests/ORM/Functional/HydrationCacheTest.php index d04fcbee192..5cd5b6cb906 100644 --- a/tests/Tests/ORM/Functional/HydrationCacheTest.php +++ b/tests/Tests/ORM/Functional/HydrationCacheTest.php @@ -4,15 +4,13 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\Tests\Models\Cms\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use function method_exists; - -/** @group DDC-1766 */ +#[Group('DDC-1766')] class HydrationCacheTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -33,8 +31,7 @@ protected function setUp(): void public function testHydrationCache(): void { - $arrayAdapter = new ArrayAdapter(); - $cache = method_exists(QueryCacheProfile::class, 'getResultCache') ? $arrayAdapter : DoctrineProvider::wrap($arrayAdapter); + $cache = new ArrayAdapter(); $dql = 'SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u'; @@ -65,7 +62,7 @@ public function testHydrationCache(): void ->setHydrationCacheProfile(new QueryCacheProfile(0, 'cachekey', $cache)) ->getArrayResult(); - self::assertTrue($arrayAdapter->hasItem('cachekey'), 'Explicit cache key'); + self::assertTrue($cache->hasItem('cachekey'), 'Explicit cache key'); $this->_em->createQuery($dql) ->setHydrationCacheProfile(new QueryCacheProfile(0, 'cachekey', $cache)) @@ -76,13 +73,10 @@ public function testHydrationCache(): void public function testHydrationParametersSerialization(): void { $cache = new ArrayAdapter(); - if (! method_exists(QueryCacheProfile::class, 'getResultCache')) { - $cache = DoctrineProvider::wrap($cache); - } $dql = 'SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1'; $query = $this->_em->createQuery($dql) - ->setParameter(1, $userId = 1) + ->setParameter(1, 1) ->setHydrationCacheProfile(new QueryCacheProfile(0, null, $cache)); $query->getResult(); diff --git a/tests/Tests/ORM/Functional/IdentityMapTest.php b/tests/Tests/ORM/Functional/IdentityMapTest.php index e6a28eb4eca..de3b44e1f16 100644 --- a/tests/Tests/ORM/Functional/IdentityMapTest.php +++ b/tests/Tests/ORM/Functional/IdentityMapTest.php @@ -9,8 +9,7 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * IdentityMapTest @@ -45,14 +44,14 @@ public function testBasicIdentityManagement(): void $this->_em->flush(); $this->_em->clear(); - $user2 = $this->_em->find(get_class($user), $user->getId()); + $user2 = $this->_em->find($user::class, $user->getId()); self::assertNotSame($user2, $user); - $user3 = $this->_em->find(get_class($user), $user->getId()); + $user3 = $this->_em->find($user::class, $user->getId()); self::assertSame($user2, $user3); - $address2 = $this->_em->find(get_class($address), $address->getId()); + $address2 = $this->_em->find($address::class, $address->getId()); self::assertNotSame($address2, $address); - $address3 = $this->_em->find(get_class($address), $address->getId()); + $address3 = $this->_em->find($address::class, $address->getId()); self::assertSame($address2, $address3); self::assertSame($user2->getAddress(), $address2); // !!! @@ -208,7 +207,7 @@ public function testCollectionValuedAssociationIdentityMapBehaviorWithRefreshQue self::assertCount(4, $user3->getPhonenumbers()); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh(): void { $user = new CmsUser(); diff --git a/tests/Tests/ORM/Functional/IndexByAssociationTest.php b/tests/Tests/ORM/Functional/IndexByAssociationTest.php index 9bccadeee9c..ab8a3eba74a 100644 --- a/tests/Tests/ORM/Functional/IndexByAssociationTest.php +++ b/tests/Tests/ORM/Functional/IndexByAssociationTest.php @@ -8,17 +8,16 @@ use Doctrine\Tests\Models\StockExchange\Market; use Doctrine\Tests\Models\StockExchange\Stock; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -/** @group DDC-250 */ +#[Group('DDC-250')] class IndexByAssociationTest extends OrmFunctionalTestCase { - /** @var Market */ - private $market; + private Market|null $market = null; - /** @var Bond */ - private $bond; + private Bond|null $bond = null; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php b/tests/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php index 3b492a2a5e6..9a27bd7efa6 100644 --- a/tests/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php +++ b/tests/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php @@ -7,6 +7,7 @@ use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass; use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedRootClass; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class JoinedTableCompositeKeyTest extends OrmFunctionalTestCase { @@ -29,7 +30,7 @@ public function testInsertWithCompositeKey(): void self::assertEquals($childEntity, $entity); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testUpdateWithCompositeKey(): void { $childEntity = new JoinedChildClass(); @@ -53,7 +54,7 @@ private function findEntity(): JoinedChildClass { return $this->_em->find( JoinedRootClass::class, - ['keyPart1' => 'part-1', 'keyPart2' => 'part-2'] + ['keyPart1' => 'part-1', 'keyPart2' => 'part-2'], ); } } diff --git a/tests/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Tests/ORM/Functional/LifecycleCallbackTest.php index bff77353b58..3dcee0e3712 100644 --- a/tests/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -34,10 +34,10 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function count; use function current; -use function get_class; use function iterator_to_array; use function sprintf; @@ -51,7 +51,7 @@ protected function setUp(): void LifecycleCallbackEventArgEntity::class, LifecycleCallbackTestEntity::class, LifecycleCallbackTestUser::class, - LifecycleCallbackCascader::class + LifecycleCallbackCascader::class, ); } @@ -114,13 +114,13 @@ public function testChangesDontGetLost(): void $this->_em->clear(); - $user2 = $this->_em->find(get_class($user), $user->getId()); + $user2 = $this->_em->find($user::class, $user->getId()); self::assertEquals('Alice', $user2->getName()); self::assertEquals('Hello World', $user2->getValue()); } - /** @group DDC-194 */ + #[Group('DDC-194')] public function testGetReferenceWithPostLoadEventIsDelayedUntilProxyTrigger(): void { $entity = new LifecycleCallbackTestEntity(); @@ -138,7 +138,7 @@ public function testGetReferenceWithPostLoadEventIsDelayedUntilProxyTrigger(): v self::assertTrue($reference->postLoadCallbackInvoked); } - /** @group DDC-958 */ + #[Group('DDC-958')] public function testPostLoadTriggeredOnRefresh(): void { $entity = new LifecycleCallbackTestEntity(); @@ -157,7 +157,7 @@ public function testPostLoadTriggeredOnRefresh(): void self::assertTrue($reference->postLoadCallbackInvoked, 'postLoad should be invoked when refresh() is called.'); } - /** @group DDC-113 */ + #[Group('DDC-113')] public function testCascadedEntitiesCallsPrePersist(): void { $e1 = new LifecycleCallbackTestEntity(); @@ -178,10 +178,8 @@ public function testCascadedEntitiesCallsPrePersist(): void self::assertTrue($e2->prePersistCallbackInvoked); } - /** - * @group DDC-54 - * @group DDC-3005 - */ + #[Group('DDC-54')] + #[Group('DDC-3005')] public function testCascadedEntitiesLoadedInPostLoad(): void { $e1 = new LifecycleCallbackTestEntity(); @@ -220,10 +218,8 @@ public function testCascadedEntitiesLoadedInPostLoad(): void self::assertEquals(current($entities)->cascader->postLoadEntitiesCount, 2); } - /** - * @group DDC-54 - * @group DDC-3005 - */ + #[Group('DDC-54')] + #[Group('DDC-3005')] public function testCascadedEntitiesNotLoadedInPostLoadDuringIteration(): void { $e1 = new LifecycleCallbackTestEntity(); @@ -253,15 +249,6 @@ public function testCascadedEntitiesNotLoadedInPostLoadDuringIteration(): void $query = $this->_em->createQuery(sprintf($dql, $e1->getId(), $e2->getId())); - $result = iterator_to_array($query->iterate()); - - foreach ($result as $entity) { - self::assertTrue($entity[0]->postLoadCallbackInvoked); - self::assertFalse($entity[0]->postLoadCascaderNotNull); - - break; - } - $iterableResult = iterator_to_array($query->toIterable()); foreach ($iterableResult as $entity) { @@ -272,10 +259,8 @@ public function testCascadedEntitiesNotLoadedInPostLoadDuringIteration(): void } } - /** - * @group DDC-54 - * @group DDC-3005 - */ + #[Group('DDC-54')] + #[Group('DDC-3005')] public function testCascadedEntitiesNotLoadedInPostLoadDuringIterationWithSimpleObjectHydrator(): void { $this->_em->persist(new LifecycleCallbackTestEntity()); @@ -285,18 +270,9 @@ public function testCascadedEntitiesNotLoadedInPostLoadDuringIterationWithSimple $this->_em->clear(); $query = $this->_em->createQuery( - 'SELECT e FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e' + 'SELECT e FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e', ); - $result = iterator_to_array($query->iterate(null, Query::HYDRATE_SIMPLEOBJECT)); - - foreach ($result as $entity) { - self::assertTrue($entity[0]->postLoadCallbackInvoked); - self::assertFalse($entity[0]->postLoadCascaderNotNull); - - break; - } - $result = iterator_to_array($query->toIterable([], Query::HYDRATE_SIMPLEOBJECT)); foreach ($result as $entity) { @@ -380,7 +356,7 @@ public function testLifecycleListenerChangeUpdateChangeSet(): void self::assertEquals('Bob', $bob->getName()); } - /** @group DDC-1955 */ + #[Group('DDC-1955')] public function testLifecycleCallbackEventArgs(): void { $e = new LifecycleCallbackEventArgEntity(); @@ -418,31 +394,20 @@ public function testLifecycleCallbackEventArgs(): void } } -/** - * @Entity - * @HasLifecycleCallbacks - */ +#[Entity] +#[HasLifecycleCallbacks] class LifecycleCallbackTestUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $value; + #[Column(type: 'string', length: 255)] + private string|null $value = null; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; + #[Column(type: 'string', length: 255)] + private string|null $name = null; public function getId(): int { @@ -469,18 +434,16 @@ public function setName(string $name): void $this->name = $name; } - /** @PreUpdate */ + #[PreUpdate] public function testCallback(): void { $this->value = 'Hello World'; } } -/** - * @Entity - * @HasLifecycleCallbacks - * @Table(name="lc_cb_test_entity") - */ +#[Table(name: 'lc_cb_test_entity')] +#[Entity] +#[HasLifecycleCallbacks] class LifecycleCallbackTestEntity { /* test stuff */ @@ -500,25 +463,18 @@ class LifecycleCallbackTestEntity /** @var bool */ public $preFlushCallbackInvoked = false; - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(type="string", nullable=true) - */ + /** @var string */ + #[Column(type: 'string', nullable: true)] public $value; - /** - * @var LifecycleCallbackCascader - * @ManyToOne(targetEntity="LifecycleCallbackCascader") - * @JoinColumn(name="cascader_id", referencedColumnName="id") - */ + /** @var LifecycleCallbackCascader */ + #[ManyToOne(targetEntity: 'LifecycleCallbackCascader')] + #[JoinColumn(name: 'cascader_id', referencedColumnName: 'id')] public $cascader; public function getId(): int @@ -531,43 +487,41 @@ public function getValue(): string return $this->value; } - /** @PrePersist */ + #[PrePersist] public function doStuffOnPrePersist(): void { $this->prePersistCallbackInvoked = true; } - /** @PostPersist */ + #[PostPersist] public function doStuffOnPostPersist(): void { $this->postPersistCallbackInvoked = true; } - /** @PostLoad */ + #[PostLoad] public function doStuffOnPostLoad(): void { $this->postLoadCallbackInvoked = true; $this->postLoadCascaderNotNull = isset($this->cascader); } - /** @PreUpdate */ + #[PreUpdate] public function doStuffOnPreUpdate(): void { $this->value = 'changed from preUpdate callback!'; } - /** @PreFlush */ + #[PreFlush] public function doStuffOnPreFlush(): void { $this->preFlushCallbackInvoked = true; } } -/** - * @Entity - * @HasLifecycleCallbacks - * @Table(name="lc_cb_test_cascade") - */ +#[Table(name: 'lc_cb_test_cascade')] +#[Entity] +#[HasLifecycleCallbacks] class LifecycleCallbackCascader { /* test stuff */ @@ -577,18 +531,13 @@ class LifecycleCallbackCascader /** @var int */ public $postLoadEntitiesCount = 0; - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="LifecycleCallbackTestEntity", mappedBy="cascader", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'LifecycleCallbackTestEntity', mappedBy: 'cascader', cascade: ['persist'])] public $entities; public function __construct() @@ -596,7 +545,7 @@ public function __construct() $this->entities = new ArrayCollection(); } - /** @PostLoad */ + #[PostLoad] public function doStuffOnPostLoad(): void { $this->postLoadCallbackInvoked = true; @@ -609,31 +558,24 @@ public function getId(): int } } -/** - * @MappedSuperclass - * @HasLifecycleCallbacks - */ +#[MappedSuperclass] +#[HasLifecycleCallbacks] class LifecycleCallbackParentEntity { - /** @PrePersist */ + #[PrePersist] public function doStuff(): void { } } -/** - * @Entity - * @Table(name="lc_cb_childentity") - */ +#[Table(name: 'lc_cb_childentity')] +#[Entity] class LifecycleCallbackChildEntity extends LifecycleCallbackParentEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; } class LifecycleListenerPreUpdate @@ -644,72 +586,66 @@ public function preUpdate(PreUpdateEventArgs $eventArgs): void } } -/** - * @Entity - * @HasLifecycleCallbacks - */ +#[Entity] +#[HasLifecycleCallbacks] class LifecycleCallbackEventArgEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column() - */ + /** @var string */ + #[Column] public $value; /** @var array */ public $calls = []; - /** @PostPersist */ + #[PostPersist] public function postPersistHandler(PostPersistEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PrePersist */ + #[PrePersist] public function prePersistHandler(PrePersistEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PostUpdate */ + #[PostUpdate] public function postUpdateHandler(PostUpdateEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PreUpdate */ + #[PreUpdate] public function preUpdateHandler(PreUpdateEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PostRemove */ + #[PostRemove] public function postRemoveHandler(PostRemoveEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PreRemove */ + #[PreRemove] public function preRemoveHandler(PreRemoveEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PreFlush */ + #[PreFlush] public function preFlushHandler(PreFlushEventArgs $event): void { $this->calls[__FUNCTION__] = $event; } - /** @PostLoad */ + #[PostLoad] public function postLoadHandler(PostLoadEventArgs $event): void { $this->calls[__FUNCTION__] = $event; diff --git a/tests/Tests/ORM/Functional/Locking/GearmanLockTest.php b/tests/Tests/ORM/Functional/Locking/GearmanLockTest.php index 66bc56c293b..e84f412e058 100644 --- a/tests/Tests/ORM/Functional/Locking/GearmanLockTest.php +++ b/tests/Tests/ORM/Functional/Locking/GearmanLockTest.php @@ -8,22 +8,20 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\OrmFunctionalTestCase; use GearmanClient; +use PHPUnit\Framework\Attributes\Group; use function class_exists; use function max; use function serialize; -/** @group locking_functional */ +#[Group('locking_functional')] class GearmanLockTest extends OrmFunctionalTestCase { - /** @var GearmanClient */ - private $gearman = null; + private GearmanClient|null $gearman = null; - /** @var int $maxRunTime */ - private $maxRunTime = 0; + private int $maxRunTime = 0; - /** @var int */ - private $articleId; + private int $articleId; protected function setUp(): void { @@ -40,7 +38,7 @@ protected function setUp(): void $this->gearman = new GearmanClient(); $this->gearman->addServer( $_SERVER['GEARMAN_HOST'] ?? null, - $_SERVER['GEARMAN_PORT'] ?? 4730 + $_SERVER['GEARMAN_PORT'] ?? 4730, ); $this->gearman->setCompleteCallback([$this, 'gearmanTaskCompleted']); @@ -147,12 +145,12 @@ protected function assertLockWorked($forTime = 2, $notLongerThan = null): void self::assertTrue( $this->maxRunTime > $forTime, 'Because of locking this tests should have run at least ' . $forTime . ' seconds, ' . - 'but only did for ' . $this->maxRunTime . ' seconds.' + 'but only did for ' . $this->maxRunTime . ' seconds.', ); self::assertTrue( $this->maxRunTime < $notLongerThan, 'The longest task should not run longer than ' . $notLongerThan . ' seconds, ' . - 'but did for ' . $this->maxRunTime . ' seconds.' + 'but did for ' . $this->maxRunTime . ' seconds.', ); } @@ -189,7 +187,7 @@ protected function startJob($fn, $fixture): void [ 'conn' => $this->_em->getConnection()->getParams(), 'fixture' => $fixture, - ] + ], )); self::assertEquals(GEARMAN_SUCCESS, $this->gearman->returnCode()); diff --git a/tests/Tests/ORM/Functional/Locking/LockAgentWorker.php b/tests/Tests/ORM/Functional/Locking/LockAgentWorker.php index bab1335e317..bd26efd07b0 100644 --- a/tests/Tests/ORM/Functional/Locking/LockAgentWorker.php +++ b/tests/Tests/ORM/Functional/Locking/LockAgentWorker.php @@ -9,7 +9,7 @@ use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\ORMSetup; +use Doctrine\Tests\ORM\Functional\Locking\Doctrine\ORM\Query; use Doctrine\Tests\TestUtil; use GearmanWorker; use InvalidArgumentException; @@ -23,8 +23,7 @@ class LockAgentWorker { - /** @var EntityManagerInterface */ - private $em; + private EntityManagerInterface|null $em = null; public static function run(): void { @@ -33,7 +32,7 @@ public static function run(): void $worker = new GearmanWorker(); $worker->addServer( $_SERVER['GEARMAN_HOST'] ?? null, - $_SERVER['GEARMAN_PORT'] ?? 4730 + $_SERVER['GEARMAN_PORT'] ?? 4730, ); $worker->addFunction('findWithLock', [$lockAgent, 'findWithLock']); $worker->addFunction('dqlWithLock', [$lockAgent, 'dqlWithLock']); @@ -75,7 +74,7 @@ public function dqlWithLock($job): float { return $this->process($job, static function ($fixture, $em): void { $query = $em->createQuery($fixture['dql']); - assert($query instanceof Doctrine\ORM\Query); + assert($query instanceof Query); $query->setLockMode($fixture['lockMode']); $query->setParameters($fixture['dqlParams']); $result = $query->getResult(); @@ -117,7 +116,7 @@ protected function createEntityManager(Connection $conn): EntityManagerInterface TestUtil::configureProxies($config); $config->setAutoGenerateProxyClasses(true); - $annotDriver = ORMSetup::createDefaultAnnotationDriver([__DIR__ . '/../../../Models/']); + $annotDriver = new AttributeDriver([__DIR__ . '/../../../Models/']); $config->setMetadataDriverImpl($annotDriver); $config->setMetadataCache(new ArrayAdapter()); diff --git a/tests/Tests/ORM/Functional/Locking/LockTest.php b/tests/Tests/ORM/Functional/Locking/LockTest.php index 2b5b398ce8d..63e3799d4ec 100644 --- a/tests/Tests/ORM/Functional/Locking/LockTest.php +++ b/tests/Tests/ORM/Functional/Locking/LockTest.php @@ -14,8 +14,10 @@ use Doctrine\Tests\OrmFunctionalTestCase; use Exception; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\Group; -/** @group locking */ +#[Group('locking')] class LockTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -25,13 +27,10 @@ protected function setUp(): void parent::setUp(); } - /** - * @group DDC-178 - * @group locking - * @testWith [false] - * [true] - */ - public function testLockVersionedEntity(bool $useStringVersion): void + #[Group('DDC-178')] + #[Group('locking')] + #[DoesNotPerformAssertions] + public function testLockVersionedEntity(): void { $article = new CmsArticle(); $article->text = 'my article'; @@ -40,26 +39,12 @@ public function testLockVersionedEntity(bool $useStringVersion): void $this->_em->persist($article); $this->_em->flush(); - $lockVersion = $article->version; - if ($useStringVersion) { - // NOTE: Officially, the lock method (and callers) do not accept a string argument. Calling code should - // cast the version to (int) as per the docs. However, this is not currently enforced. This may change in - // a future release. - $lockVersion = (string) $lockVersion; - } - - $this->_em->lock($article, LockMode::OPTIMISTIC, $lockVersion); - - $this->addToAssertionCount(1); + $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version); } - /** - * @group DDC-178 - * @group locking - * @testWith [false] - * [true] - */ - public function testLockVersionedEntityMismatchThrowsException(bool $useStringVersion): void + #[Group('DDC-178')] + #[Group('locking')] + public function testLockVersionedEntityMismatchThrowsException(): void { $article = new CmsArticle(); $article->text = 'my article'; @@ -69,18 +54,11 @@ public function testLockVersionedEntityMismatchThrowsException(bool $useStringVe $this->_em->flush(); $this->expectException(OptimisticLockException::class); - $lockVersion = $article->version + 1; - if ($useStringVersion) { - $lockVersion = (string) $lockVersion; - } - - $this->_em->lock($article, LockMode::OPTIMISTIC, $lockVersion); + $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version + 1); } - /** - * @group DDC-178 - * @group locking - */ + #[Group('DDC-178')] + #[Group('locking')] public function testLockUnversionedEntityThrowsException(): void { $user = new CmsUser(); @@ -96,10 +74,8 @@ public function testLockUnversionedEntityThrowsException(): void $this->_em->lock($user, LockMode::OPTIMISTIC); } - /** - * @group DDC-178 - * @group locking - */ + #[Group('DDC-178')] + #[Group('locking')] public function testLockUnmanagedEntityThrowsException(): void { $article = new CmsArticle(); @@ -110,10 +86,8 @@ public function testLockUnmanagedEntityThrowsException(): void $this->_em->lock($article, LockMode::OPTIMISTIC, $article->version + 1); } - /** - * @group DDC-178 - * @group locking - */ + #[Group('DDC-178')] + #[Group('locking')] public function testLockPessimisticReadNoTransactionThrowsException(): void { $article = new CmsArticle(); @@ -128,10 +102,8 @@ public function testLockPessimisticReadNoTransactionThrowsException(): void $this->_em->lock($article, LockMode::PESSIMISTIC_READ); } - /** - * @group DDC-178 - * @group locking - */ + #[Group('DDC-178')] + #[Group('locking')] public function testLockPessimisticWriteNoTransactionThrowsException(): void { $article = new CmsArticle(); @@ -146,9 +118,7 @@ public function testLockPessimisticWriteNoTransactionThrowsException(): void $this->_em->lock($article, LockMode::PESSIMISTIC_WRITE); } - /** - * @group locking - */ + #[Group('locking')] public function testRefreshWithLockPessimisticWriteNoTransactionThrowsException(): void { $article = new CmsArticle(); @@ -163,10 +133,8 @@ public function testRefreshWithLockPessimisticWriteNoTransactionThrowsException( $this->_em->refresh($article, LockMode::PESSIMISTIC_WRITE); } - /** - * @group DDC-178 - * @group locking - */ + #[Group('DDC-178')] + #[Group('locking')] public function testLockPessimisticWrite(): void { if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { @@ -184,7 +152,7 @@ public function testLockPessimisticWrite(): void try { $this->_em->lock($article, LockMode::PESSIMISTIC_WRITE); $this->_em->commit(); - } catch (Exception $e) { + } catch (Exception) { $this->_em->rollback(); } @@ -197,9 +165,7 @@ public function testLockPessimisticWrite(): void self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery); } - /** - * @group locking - */ + #[Group('locking')] public function testRefreshWithLockPessimisticWrite(): void { if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { @@ -217,7 +183,7 @@ public function testRefreshWithLockPessimisticWrite(): void try { $this->_em->refresh($article, LockMode::PESSIMISTIC_WRITE); $this->_em->commit(); - } catch (Exception $e) { + } catch (Exception) { $this->_em->rollback(); } @@ -230,7 +196,7 @@ public function testRefreshWithLockPessimisticWrite(): void self::assertStringContainsString('FOR UPDATE', $lastLoggedQuery); } - /** @group DDC-178 */ + #[Group('DDC-178')] public function testLockPessimisticRead(): void { if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { @@ -249,7 +215,7 @@ public function testLockPessimisticRead(): void try { $this->_em->lock($article, LockMode::PESSIMISTIC_READ); $this->_em->commit(); - } catch (Exception $e) { + } catch (Exception) { $this->_em->rollback(); } @@ -262,11 +228,11 @@ public function testLockPessimisticRead(): void self::assertThat($lastLoggedQuery, self::logicalOr( self::stringContains('FOR UPDATE'), self::stringContains('FOR SHARE'), - self::stringContains('LOCK IN SHARE MODE') + self::stringContains('LOCK IN SHARE MODE'), )); } - /** @group DDC-1693 */ + #[Group('DDC-1693')] public function testLockOptimisticNonVersionedThrowsExceptionInDQL(): void { $dql = 'SELECT u FROM ' . CmsUser::class . " u WHERE u.username = 'gblanco'"; diff --git a/tests/Tests/ORM/Functional/Locking/OptimisticTest.php b/tests/Tests/ORM/Functional/Locking/OptimisticTest.php index efc063ff849..bb88657665b 100644 --- a/tests/Tests/ORM/Functional/Locking/OptimisticTest.php +++ b/tests/Tests/ORM/Functional/Locking/OptimisticTest.php @@ -18,14 +18,15 @@ use Doctrine\ORM\Mapping\Version; use Doctrine\ORM\OptimisticLockException; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use function date; use function strtotime; class OptimisticTest extends OrmFunctionalTestCase { - /** @var Connection */ - private $_conn; + private Connection $_conn; protected function setUp(): void { @@ -40,7 +41,7 @@ private function createSchema(): void OptimisticJoinedParent::class, OptimisticJoinedChild::class, OptimisticStandard::class, - OptimisticTimestamp::class + OptimisticTimestamp::class, ); } @@ -60,7 +61,7 @@ public function testJoinedChildInsertSetsInitialVersionValue(): OptimisticJoined return $test; } - /** @depends testJoinedChildInsertSetsInitialVersionValue */ + #[Depends('testJoinedChildInsertSetsInitialVersionValue')] public function testJoinedChildFailureThrowsException(OptimisticJoinedChild $child): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.id = :id'); @@ -99,7 +100,7 @@ public function testJoinedParentInsertSetsInitialVersionValue(): OptimisticJoine return $test; } - /** @depends testJoinedParentInsertSetsInitialVersionValue */ + #[Depends('testJoinedParentInsertSetsInitialVersionValue')] public function testJoinedParentFailureThrowsException(OptimisticJoinedParent $parent): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.id = :id'); @@ -157,7 +158,7 @@ public function testStandardInsertSetsInitialVersionValue(): OptimisticStandard return $test; } - /** @depends testStandardInsertSetsInitialVersionValue */ + #[Depends('testStandardInsertSetsInitialVersionValue')] public function testStandardFailureThrowsException(OptimisticStandard $entity): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.id = :id'); @@ -181,6 +182,7 @@ public function testStandardFailureThrowsException(OptimisticStandard $entity): } } + #[DoesNotPerformAssertions] public function testLockWorksWithProxy(): void { $this->createSchema(); @@ -194,8 +196,6 @@ public function testLockWorksWithProxy(): void $proxy = $this->_em->getReference(OptimisticStandard::class, $test->id); $this->_em->lock($proxy, LockMode::OPTIMISTIC, 1); - - $this->addToAssertionCount(1); } public function testOptimisticTimestampSetsDefaultValue(): OptimisticTimestamp @@ -210,12 +210,12 @@ public function testOptimisticTimestampSetsDefaultValue(): OptimisticTimestamp $this->_em->persist($test); $this->_em->flush(); - self::assertInstanceOf('DateTime', $test->version); + self::assertInstanceOf(DateTime::class, $test->version); return $test; } - /** @depends testOptimisticTimestampSetsDefaultValue */ + #[Depends('testOptimisticTimestampSetsDefaultValue')] public function testOptimisticTimestampFailureThrowsException(OptimisticTimestamp $entity): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id'); @@ -224,7 +224,7 @@ public function testOptimisticTimestampFailureThrowsException(OptimisticTimestam $test = $q->getSingleResult(); - self::assertInstanceOf('DateTime', $test->version); + self::assertInstanceOf(DateTime::class, $test->version); // Manually increment the version datetime column $format = $this->_em->getConnection()->getDatabasePlatform()->getDateTimeFormatString(); @@ -245,7 +245,7 @@ public function testOptimisticTimestampFailureThrowsException(OptimisticTimestam self::assertSame($test, $caughtException->getEntity()); } - /** @depends testOptimisticTimestampSetsDefaultValue */ + #[Depends('testOptimisticTimestampSetsDefaultValue')] public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id'); @@ -254,7 +254,7 @@ public function testOptimisticTimestampLockFailureThrowsException(OptimisticTime $test = $q->getSingleResult(); - self::assertInstanceOf('DateTime', $test->version); + self::assertInstanceOf(DateTime::class, $test->version); // Try to lock the record with an older timestamp and it should throw an exception $caughtException = null; @@ -262,7 +262,7 @@ public function testOptimisticTimestampLockFailureThrowsException(OptimisticTime try { $expectedVersionExpired = DateTime::createFromFormat( 'U', - (string) ($test->version->getTimestamp() - 3600) + (string) ($test->version->getTimestamp() - 3600), ); $this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired); @@ -275,77 +275,56 @@ public function testOptimisticTimestampLockFailureThrowsException(OptimisticTime } } -/** - * @Entity - * @Table(name="optimistic_joined_parent") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string", length=255) - * @DiscriminatorMap({"parent" = "OptimisticJoinedParent", "child" = "OptimisticJoinedChild"}) - */ +#[Table(name: 'optimistic_joined_parent')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['parent' => 'OptimisticJoinedParent', 'child' => 'OptimisticJoinedChild'])] class OptimisticJoinedParent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; } -/** - * @Entity - * @Table(name="optimistic_joined_child") - */ +#[Table(name: 'optimistic_joined_child')] +#[Entity] class OptimisticJoinedChild extends OptimisticJoinedParent { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $whatever; } -/** - * @Entity - * @Table(name="optimistic_standard") - */ +#[Table(name: 'optimistic_standard')] +#[Entity] class OptimisticStandard { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var int - * @Version - * @Column(type="integer") - */ - private $version; + #[Version] + #[Column(type: 'integer')] + private int $version; public function getVersion(): int { @@ -353,30 +332,22 @@ public function getVersion(): int } } -/** - * @Entity - * @Table(name="optimistic_timestamp") - */ +#[Table(name: 'optimistic_timestamp')] +#[Entity] class OptimisticTimestamp { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var DateTime - * @Version - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Version] + #[Column(type: 'datetime')] public $version; } diff --git a/tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index afe0c8e62e7..352bffd3b24 100644 --- a/tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -7,16 +7,17 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Order; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsTag; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; use function class_exists; -use function get_class; /** * Basic many-to-many association tests. @@ -84,7 +85,7 @@ public function testManyToManyAddRemove(): void $user = $this->addCmsUserGblancoWithGroups(2); $this->_em->clear(); - $uRep = $this->_em->getRepository(get_class($user)); + $uRep = $this->_em->getRepository($user::class); // Get user $user = $uRep->findOneById($user->getId()); @@ -131,7 +132,7 @@ public function testManyToManyInverseSideIgnored(): void $this->_em->clear(); // Association should not exist - $user2 = $this->_em->find(get_class($user), $user->getId()); + $user2 = $this->_em->find($user::class, $user->getId()); self::assertNotNull($user2, 'Has to return exactly one entry.'); self::assertEquals(0, $user2->getGroups()->count()); @@ -184,7 +185,7 @@ public function assertGblancoGroupCountIs(int $expectedGroupCount): void self::assertEquals( $expectedGroupCount, $this->_em->createQuery($countDql)->getSingleScalarResult(), - "Failed to verify that CmsUser with username 'gblanco' has a group count of 10 with a DQL count query." + "Failed to verify that CmsUser with username 'gblanco' has a group count of 10 with a DQL count query.", ); } @@ -219,7 +220,7 @@ public function testRetrieveManyToManyAndAddMore(): void self::assertCount(3, $freshUser->getGroups()); } - /** @group DDC-130 */ + #[Group('DDC-130')] public function testRemoveUserWithManyGroups(): void { $user = $this->addCmsUserGblancoWithGroups(10); @@ -236,11 +237,11 @@ public function testRemoveUserWithManyGroups(): void $this->removeTransactionCommandsFromQueryLog(); self::assertQueryCount(3); - $newUser = $this->_em->find(get_class($user), $userId); + $newUser = $this->_em->find($user::class, $userId); self::assertNull($newUser); } - /** @group DDC-130 */ + #[Group('DDC-130')] public function testRemoveGroupWithUser(): void { $user = $this->addCmsUserGblancoWithGroups(5); @@ -277,7 +278,7 @@ public function testRemoveGroupWithUser(): void $this->_em->clear(); // Changes have been made to the database - $newUser = $this->_em->find(get_class($user), $user->getId()); + $newUser = $this->_em->find($user::class, $user->getId()); self::assertCount(0, $newUser->getGroups()); } @@ -295,11 +296,11 @@ public function testDereferenceCollectionDelete(): void $this->_em->clear(); - $newUser = $this->_em->find(get_class($user), $user->getId()); + $newUser = $this->_em->find($user::class, $user->getId()); self::assertCount(0, $newUser->getGroups()); } - /** @group DDC-839 */ + #[Group('DDC-839')] public function testWorkWithDqlHydratedEmptyCollection(): void { $user = $this->addCmsUserGblancoWithGroups(0); @@ -314,14 +315,14 @@ public function testWorkWithDqlHydratedEmptyCollection(): void ->setParameter(1, $user->getId()) ->getSingleResult(); self::assertCount(0, $newUser->groups); - self::assertIsArray($newUser->groups->getMapping()); + self::assertInstanceOf(AssociationMapping::class, $newUser->groups->getMapping()); $newUser->addGroup($group); $this->_em->flush(); $this->_em->clear(); - $newUser = $this->_em->find(get_class($user), $user->getId()); + $newUser = $this->_em->find($user::class, $user->getId()); self::assertCount(1, $newUser->groups); } @@ -346,7 +347,7 @@ public function addCmsUserGblancoWithGroups(int $groupCount = 1): CmsUser return $user; } - /** @group DDC-978 */ + #[Group('DDC-978')] public function testClearAndResetCollection(): void { $user = $this->addCmsUserGblancoWithGroups(2); @@ -360,7 +361,7 @@ public function testClearAndResetCollection(): void $this->_em->flush(); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $coll = new ArrayCollection([$group1, $group2]); $user->groups = $coll; @@ -368,52 +369,50 @@ public function testClearAndResetCollection(): void self::assertInstanceOf( PersistentCollection::class, $user->groups, - 'UnitOfWork should have replaced ArrayCollection with PersistentCollection.' + 'UnitOfWork should have replaced ArrayCollection with PersistentCollection.', ); $this->_em->flush(); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); self::assertCount(2, $user->groups); self::assertEquals('Developers_New1', $user->groups[0]->name); self::assertEquals('Developers_New2', $user->groups[1]->name); } - /** @group DDC-733 */ + #[Group('DDC-733')] public function testInitializePersistentCollection(): void { $user = $this->addCmsUserGblancoWithGroups(2); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection'); $this->_em->getUnitOfWork()->initializeObject($user->groups); self::assertTrue($user->groups->isInitialized(), 'Collection should be initialized after calling UnitOfWork::initializeObject()'); } - /** - * @group DDC-1189 - * @group DDC-956 - */ + #[Group('DDC-1189')] + #[Group('DDC-956')] public function testClearBeforeLazyLoad(): void { $user = $this->addCmsUserGblancoWithGroups(4); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $user->groups->clear(); self::assertCount(0, $user->groups); $this->_em->flush(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); self::assertCount(0, $user->groups); } - /** @group DDC-3952 */ + #[Group('DDC-3952')] public function testManyToManyOrderByIsNotIgnored(): void { $user = $this->addCmsUserGblancoWithGroups(1); @@ -435,7 +434,7 @@ public function testManyToManyOrderByIsNotIgnored(): void $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $criteria = Criteria::create() ->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]); @@ -445,14 +444,12 @@ public function testManyToManyOrderByIsNotIgnored(): void $user ->getGroups() ->matching($criteria) - ->map(static function (CmsGroup $group) { - return $group->getName(); - }) - ->toArray() + ->map(static fn (CmsGroup $group) => $group->getName()) + ->toArray(), ); } - /** @group DDC-3952 */ + #[Group('DDC-3952')] public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void { $user = new CmsUser(); @@ -477,7 +474,7 @@ public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $criteria = Criteria::create() ->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]); @@ -487,10 +484,8 @@ public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void $user ->getTags() ->matching($criteria) - ->map(static function (CmsTag $tag) { - return $tag->getName(); - }) - ->toArray() + ->map(static fn (CmsTag $tag) => $tag->getName()) + ->toArray(), ); } @@ -499,7 +494,7 @@ public function testMatchingWithLimit(): void $user = $this->addCmsUserGblancoWithGroups(2); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $groups = $user->groups; self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection'); @@ -517,7 +512,7 @@ public function testMatchingWithOffset(): void $user = $this->addCmsUserGblancoWithGroups(2); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $groups = $user->groups; self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection'); @@ -538,7 +533,7 @@ public function testMatchingWithLimitAndOffset(): void $user = $this->addCmsUserGblancoWithGroups(5); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $groups = $user->groups; self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection'); @@ -562,7 +557,7 @@ public function testMatching(): void $user = $this->addCmsUserGblancoWithGroups(2); $this->_em->clear(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); $groups = $user->groups; self::assertFalse($user->groups->isInitialized(), 'Pre-condition: lazy collection'); diff --git a/tests/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php index a1b681d3262..a2f48df25e5 100644 --- a/tests/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php @@ -22,17 +22,13 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati /** @var string */ protected $table = 'ecommerce_products_categories'; - /** @var ECommerceProduct */ - private $firstProduct; + private ECommerceProduct $firstProduct; - /** @var ECommerceProduct */ - private $secondProduct; + private ECommerceProduct $secondProduct; - /** @var ECommerceCategory */ - private $firstCategory; + private ECommerceCategory $firstCategory; - /** @var ECommerceCategory */ - private $secondCategory; + private ECommerceCategory $secondCategory; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/ManyToManyEventTest.php b/tests/Tests/ORM/Functional/ManyToManyEventTest.php index d9436acb23f..d599e65d26f 100644 --- a/tests/Tests/ORM/Functional/ManyToManyEventTest.php +++ b/tests/Tests/ORM/Functional/ManyToManyEventTest.php @@ -14,8 +14,7 @@ */ class ManyToManyEventTest extends OrmFunctionalTestCase { - /** @var PostUpdateListener */ - private $listener; + private PostUpdateListener $listener; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php b/tests/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php index ccc66052a88..927af3d3e13 100644 --- a/tests/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php +++ b/tests/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php @@ -23,17 +23,13 @@ class ManyToManySelfReferentialAssociationTest extends AbstractManyToManyAssocia /** @var string */ protected $table = 'ecommerce_products_related'; - /** @var ECommerceProduct */ - private $firstProduct; + private ECommerceProduct $firstProduct; - /** @var ECommerceProduct */ - private $secondProduct; + private ECommerceProduct $secondProduct; - /** @var ECommerceProduct */ - private $firstRelated; + private ECommerceProduct $firstRelated; - /** @var ECommerceProduct */ - private $secondRelated; + private ECommerceProduct $secondRelated; protected function setUp(): void { @@ -58,11 +54,11 @@ public function testSavesAManyToManyAssociationWithCascadeSaveSet(): void $this->assertForeignKeysContain( $this->firstProduct->getId(), - $this->firstRelated->getId() + $this->firstRelated->getId(), ); $this->assertForeignKeysContain( $this->firstProduct->getId(), - $this->secondRelated->getId() + $this->secondRelated->getId(), ); } @@ -77,11 +73,11 @@ public function testRemovesAManyToManyAssociation(): void $this->assertForeignKeysNotContain( $this->firstProduct->getId(), - $this->firstRelated->getId() + $this->firstRelated->getId(), ); $this->assertForeignKeysContain( $this->firstProduct->getId(), - $this->secondRelated->getId() + $this->secondRelated->getId(), ); } @@ -96,8 +92,8 @@ public function testLazyLoadsOwningSide(): void { $this->createLoadingFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceProduct::class); - $metadata->associationMappings['related']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceProduct::class); + $metadata->associationMappings['related']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); $products = $query->getResult(); diff --git a/tests/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php index a850c2504b4..6f210a7c815 100644 --- a/tests/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php @@ -23,17 +23,13 @@ class ManyToManyUnidirectionalAssociationTest extends AbstractManyToManyAssociat /** @var string */ protected $table = 'ecommerce_carts_products'; - /** @var ECommerceProduct */ - private $firstProduct; + private ECommerceProduct $firstProduct; - /** @var ECommerceProduct */ - private $secondProduct; + private ECommerceProduct $secondProduct; - /** @var ECommerceCart */ - private $firstCart; + private ECommerceCart $firstCart; - /** @var ECommerceCart */ - private $secondCart; + private ECommerceCart $secondCart; protected function setUp(): void { @@ -93,8 +89,8 @@ public function testEagerLoad(): void public function testLazyLoadsCollection(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceCart::class); - $metadata->associationMappings['products']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceCart::class); + $metadata->associationMappings['products']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\ECommerce\ECommerceCart c'); $result = $query->getResult(); diff --git a/tests/Tests/ORM/Functional/ManyToOneOrphanRemovalTest.php b/tests/Tests/ORM/Functional/ManyToOneOrphanRemovalTest.php deleted file mode 100644 index e0a96994ef0..00000000000 --- a/tests/Tests/ORM/Functional/ManyToOneOrphanRemovalTest.php +++ /dev/null @@ -1,95 +0,0 @@ -> */ - protected static $modelSets = [ - 'ornemental_orphan_removal' => [ - Person::class, - PhoneNumber::class, - ], - ]; - - protected function setUp(): void - { - $this->useModelSet('ornemental_orphan_removal'); - - parent::setUp(); - - $person = new Person(); - $person->id = 'ca41a293-799f-4d68-bf79-626c3ad223ec'; - - $phone1 = new PhoneNumber(); - $phone1->id = 'f4132478-c492-4dfe-aab5-a5b79ae129e7'; - $phone1->phonenumber = '123456'; - - $phone2 = new PhoneNumber(); - $phone2->id = '7faa4cd3-a155-4fbf-bc42-aa4269a4454d'; - $phone2->phonenumber = '234567'; - - $phone1->person = $person; - $phone2->person = $person; - - $this->_em->persist($phone1); - $this->_em->persist($phone2); - $this->_em->persist($person); - $this->_em->flush(); - - $this->personId = $person->id; - $this->_em->clear(); - } - - public function testOrphanRemovalIsPurelyOrnemental(): void - { - $person = $this->_em->getReference(Person::class, $this->personId); - - $this->_em->remove($person); - $this->_em->flush(); - $this->_em->clear(); - - $query = $this->_em->createQuery( - 'SELECT u FROM Doctrine\Tests\Models\OrnementalOrphanRemoval\Person u' - ); - $result = $query->getResult(); - - self::assertEquals(0, count($result), 'Person should be removed by EntityManager'); - - $query = $this->_em->createQuery( - 'SELECT p FROM Doctrine\Tests\Models\OrnementalOrphanRemoval\PhoneNumber p' - ); - $result = $query->getResult(); - - self::assertEquals(2, count($result), 'Orphan removal should not kick in'); - } - - protected function getEntityManager( - ?Connection $connection = null, - ?MappingDriver $mappingDriver = null - ): EntityManagerInterface { - return parent::getEntityManager($connection, new XmlDriver( - __DIR__ . DIRECTORY_SEPARATOR . 'xml' - )); - } -} diff --git a/tests/Tests/ORM/Functional/MappedSuperclassTest.php b/tests/Tests/ORM/Functional/MappedSuperclassTest.php index 03c14f2e03f..ab5d0cbb7ff 100644 --- a/tests/Tests/ORM/Functional/MappedSuperclassTest.php +++ b/tests/Tests/ORM/Functional/MappedSuperclassTest.php @@ -8,8 +8,6 @@ use Doctrine\Tests\Models\DirectoryTree\File; use Doctrine\Tests\OrmFunctionalTestCase; -use function get_class; - /** * MappedSuperclassTest */ @@ -42,7 +40,7 @@ public function testCRUD(): void $this->_em->flush(); $this->_em->clear(); - $cleanFile = $this->_em->find(get_class($file), $file->getId()); + $cleanFile = $this->_em->find($file::class, $file->getId()); self::assertInstanceOf(Directory::class, $cleanFile->getParent()); self::assertTrue($this->isUninitializedObject($cleanFile->getParent())); diff --git a/tests/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php b/tests/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php deleted file mode 100644 index dc628a1ef03..00000000000 --- a/tests/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php +++ /dev/null @@ -1,46 +0,0 @@ -createSchemaForModels( - Country::class, - CompositeToOneKeyState::class - ); - } - - /** - * @group DDC-3378 - * @group 1176 - */ - public function testMergingOfEntityWithCompositeIdentifierContainingToOneAssociation(): void - { - $country = new Country(); - $country->country = 'US'; - - $state = new CompositeToOneKeyState(); - $state->state = 'CA'; - $state->country = $country; - - $merged = $this->_em->merge($state); - assert($merged instanceof CompositeToOneKeyState); - - self::assertInstanceOf(CompositeToOneKeyState::class, $state); - self::assertNotSame($state, $merged); - self::assertInstanceOf(Country::class, $merged->country); - self::assertNotSame($country, $merged->country); - } -} diff --git a/tests/Tests/ORM/Functional/MergeProxiesTest.php b/tests/Tests/ORM/Functional/MergeProxiesTest.php deleted file mode 100644 index 312c5f975ca..00000000000 --- a/tests/Tests/ORM/Functional/MergeProxiesTest.php +++ /dev/null @@ -1,273 +0,0 @@ -useModelSet('generic'); - - parent::setUp(); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeDetachedUnInitializedProxy(): void - { - $detachedUninitialized = $this->_em->getReference(DateTimeModel::class, 123); - - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame($managed, $this->_em->merge($detachedUninitialized)); - - self::assertTrue($this->isUninitializedObject($managed)); - self::assertTrue($this->isUninitializedObject($detachedUninitialized)); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeUnserializedUnInitializedProxy(): void - { - $detachedUninitialized = $this->_em->getReference(DateTimeModel::class, 123); - - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame( - $managed, - $this->_em->merge(unserialize(serialize($this->_em->merge($detachedUninitialized)))) - ); - - self::assertTrue($this->isUninitializedObject($managed)); - self::assertTrue($this->isUninitializedObject($detachedUninitialized)); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeManagedProxy(): void - { - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame($managed, $this->_em->merge($managed)); - - self::assertTrue($this->isUninitializedObject($managed)); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - * - * Bug discovered while working on DDC-2704 - merging towards un-initialized proxies does not initialize them, - * causing merged data to be lost when they are actually initialized - */ - public function testMergeWithExistingUninitializedManagedProxy(): void - { - $date = new DateTimeModel(); - - $this->_em->persist($date); - $this->_em->flush($date); - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, $date->id); - - self::assertTrue($this->isUninitializedObject($managed)); - - $date->date = $dateTime = new DateTime(); - - self::assertSame($managed, $this->_em->merge($date)); - self::assertFalse($this->isUninitializedObject($managed)); - self::assertSame($dateTime, $managed->date, 'Data was merged into the proxy after initialization'); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergingProxyFromDifferentEntityManagerWithExistingManagedInstanceDoesNotReplaceInitializer(): void - { - $em1 = $this->createEntityManager(); - $em2 = $this->createEntityManager(); - - $file1 = new DateTimeModel(); - $file2 = new DateTimeModel(); - - $em1->persist($file1); - $em2->persist($file2); - $em1->flush(); - $em2->flush(); - $em1->clear(); - $em2->clear(); - - $logger1 = $this->getResetQueryLogFromEntityManager($em1); - $logger2 = $this->getResetQueryLogFromEntityManager($em2); - - $proxy1 = $em1->getReference(DateTimeModel::class, $file1->id); - $proxy2 = $em2->getReference(DateTimeModel::class, $file1->id); - $merged2 = $em2->merge($proxy1); - - self::assertNotSame($proxy1, $merged2); - self::assertSame($proxy2, $merged2); - - self::assertTrue($this->isUninitializedObject($proxy1)); - self::assertTrue($this->isUninitializedObject($proxy2)); - - $proxy1->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the first proxy was done through the first entity manager' - ); - self::assertCount( - 0, - $logger2->queries, - 'No queries were executed on the second entity manager, as it is unrelated with the first proxy' - ); - - $proxy2->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the second proxy does not affect the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'Loading of the second proxy instance was done through the second entity manager' - ); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergingUnInitializedProxyDoesNotInitializeIt(): void - { - $em1 = $this->createEntityManager(); - $em2 = $this->createEntityManager(); - - $file1 = new DateTimeModel(); - $file2 = new DateTimeModel(); - - $em1->persist($file1); - $em2->persist($file2); - $em1->flush(); - $em2->flush(); - $em1->clear(); - $em2->clear(); - - $logger1 = $this->getResetQueryLogFromEntityManager($em1); - $logger2 = $this->getResetQueryLogFromEntityManager($em2); - - $unManagedProxy = $em1->getReference(DateTimeModel::class, $file1->id); - $mergedInstance = $em2->merge($unManagedProxy); - - self::assertFalse($this->isUninitializedObject($mergedInstance)); - self::assertTrue($this->isUninitializedObject($unManagedProxy)); - - self::assertCount( - 0, - $logger1->queries, - 'Loading the merged instance affected only the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'Loading the merged instance was done via the second entity manager' - ); - - $unManagedProxy->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the first proxy was done through the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'No queries were executed on the second entity manager, as it is unrelated with the first proxy' - ); - } - - private function createEntityManager(): EntityManagerInterface - { - $config = new Configuration(); - - TestUtil::configureProxies($config); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver( - [realpath(__DIR__ . '/../../Models/Cache')], - null, - true - )); - - // always runs on sqlite to prevent multi-connection race-conditions with the test suite - // multi-connection is not relevant for the purpose of checking locking here, but merely - // to stub out DB-level access and intercept it - $connection = DriverManager::getConnection( - [ - 'driver' => 'pdo_sqlite', - 'memory' => true, - 'wrapperClass' => Connection::class, - ], - $config - ); - - $entityManager = new EntityManager($connection, $config); - - (new SchemaTool($entityManager))->createSchema([$entityManager->getClassMetadata(DateTimeModel::class)]); - - return $entityManager; - } - - private function getResetQueryLogFromEntityManager(EntityManagerInterface $entityManager): QueryLog - { - $connection = $entityManager->getConnection(); - assert($connection instanceof Connection); - - return $connection->queryLog->reset()->enable(); - } -} diff --git a/tests/Tests/ORM/Functional/MergeSharedEntitiesTest.php b/tests/Tests/ORM/Functional/MergeSharedEntitiesTest.php deleted file mode 100644 index cceb8df02ec..00000000000 --- a/tests/Tests/ORM/Functional/MergeSharedEntitiesTest.php +++ /dev/null @@ -1,159 +0,0 @@ -createSchemaForModels(MSEFile::class, MSEPicture::class); - } - - public function testMergeSharedNewEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $picture = $this->_em->merge($picture); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - public function testMergeSharedManagedEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $this->_em->persist($file); - $this->_em->persist($picture); - $this->_em->flush(); - $this->_em->clear(); - - $picture = $this->_em->merge($picture); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - public function testMergeSharedDetachedSerializedEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $serializedPicture = serialize($picture); - - $this->_em->persist($file); - $this->_em->persist($picture); - $this->_em->flush(); - $this->_em->clear(); - - $picture = $this->_em->merge(unserialize($serializedPicture)); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - /** @group DDC-2704 */ - public function testMergeInheritedTransientPrivateProperties(): void - { - $admin1 = new MSEAdmin(); - $admin2 = new MSEAdmin(); - - $admin1->id = 123; - $admin2->id = 123; - - $this->_em->persist($admin1); - - $admin2->setSession('zeh current session data'); - - self::assertSame($admin1, $this->_em->merge($admin2)); - self::assertSame('zeh current session data', $admin1->getSession()); - } -} - -/** @Entity */ -class MSEPicture -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - public $id; - - /** - * @var MSEFile - * @ManyToOne(targetEntity="MSEFile", cascade={"merge"}) - */ - public $file; - - /** - * @var MSEFile - * @ManyToOne(targetEntity="MSEFile", cascade={"merge"}) - */ - public $otherFile; -} - -/** @Entity */ -class MSEFile -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $id; -} - -/** @MappedSuperclass */ -abstract class MSEUser -{ - /** @var string */ - private $session; // intentionally transient property - - public function getSession(): string - { - return $this->session; - } - - public function setSession(string $session): void - { - $this->session = $session; - } -} - -/** @Entity */ -class MSEAdmin extends MSEUser -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="NONE") - */ - public $id; -} diff --git a/tests/Tests/ORM/Functional/MergeVersionedManyToOneTest.php b/tests/Tests/ORM/Functional/MergeVersionedManyToOneTest.php deleted file mode 100644 index 200384d5257..00000000000 --- a/tests/Tests/ORM/Functional/MergeVersionedManyToOneTest.php +++ /dev/null @@ -1,44 +0,0 @@ -useModelSet('versioned_many_to_one'); - - parent::setUp(); - } - - /** - * This test case asserts that a detached and unmodified entity could be merge without firing - * OptimisticLockException. - */ - public function testSetVersionOnCreate(): void - { - $category = new Category(); - $article = new Article(); - - $article->name = 'Article'; - $article->category = $category; - - $this->_em->persist($article); - $this->_em->flush(); - $this->_em->clear(); - - $articleMerged = $this->_em->merge($article); - - $articleMerged->name = 'Article Merged'; - - $this->_em->flush(); - self::assertEquals(2, $articleMerged->version); - } -} diff --git a/tests/Tests/ORM/Functional/NativeQueryTest.php b/tests/Tests/ORM/Functional/NativeQueryTest.php index e998a15c837..c9f665387b7 100644 --- a/tests/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Tests/ORM/Functional/NativeQueryTest.php @@ -6,10 +6,8 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type as DBALType; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\Internal\Hydration\HydrationException; use Doctrine\ORM\Internal\SQLResultCasing; use Doctrine\ORM\PersistentCollection; @@ -23,25 +21,19 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsUserDTO; use Doctrine\Tests\Models\Company\CompanyContract; -use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\Models\Company\CompanyFixContract; -use Doctrine\Tests\Models\Company\CompanyFlexContract; -use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\Models\CustomType\CustomTypeUpperCase; use Doctrine\Tests\Models\DDC3899\DDC3899FixContract; use Doctrine\Tests\Models\DDC3899\DDC3899User; use Doctrine\Tests\OrmFunctionalTestCase; use InvalidArgumentException; - -use function class_exists; +use PHPUnit\Framework\Attributes\Group; class NativeQueryTest extends OrmFunctionalTestCase { use SQLResultCasing; - use VerifyDeprecations; - /** @var AbstractPlatform */ - private $platform = null; + private AbstractPlatform|null $platform = null; protected function setUp(): void { @@ -106,7 +98,7 @@ public function testNativeQueryWithArrayParameter(): void $rsm->addFieldResult('u', $this->getSQLResultCasing($this->platform, 'name'), 'name'); $query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username IN (?) ORDER BY username', $rsm); - $query->setParameter(1, ['wshatner', 'lnimoy'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY); + $query->setParameter(1, ['wshatner', 'lnimoy'], ArrayParameterType::STRING); $users = $query->getResult(); @@ -255,7 +247,7 @@ public function testMappingAsDto(): void WHERE username = ? SQL , - $rsm + $rsm, ); $query->setParameter(1, 'romanb'); @@ -330,7 +322,6 @@ public function testFluentInterface(): void ->setHint('foo', 'bar') ->setParameter(1, 'foo') ->setParameters($parameters) - ->setResultCacheDriver(null) ->setResultCacheLifetime(3500); self::assertSame($q, $q2); @@ -433,7 +424,7 @@ public function testJoinedOneToOneNativeQueryWithRSMBuilder(): void self::assertEquals($user->name, $address->getUser()->getName()); } - /** @group rsm-sti */ + #[Group('rsm-sti')] public function testConcreteClassInSingleTableInheritanceSchemaWithRSMBuilderIsFine(): void { $rsm = new ResultSetMappingBuilder($this->_em); @@ -442,7 +433,7 @@ public function testConcreteClassInSingleTableInheritanceSchemaWithRSMBuilderIsF self::assertSame(CompanyFixContract::class, $rsm->getClassName('c')); } - /** @group rsm-sti */ + #[Group('rsm-sti')] public function testAbstractClassInSingleTableInheritanceSchemaWithRSMBuilderThrowsException(): void { $this->expectException(InvalidArgumentException::class); @@ -454,13 +445,13 @@ public function testAbstractClassInSingleTableInheritanceSchemaWithRSMBuilderThr public function testRSMBuilderThrowsExceptionOnColumnConflict(): void { - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $rsm = new ResultSetMappingBuilder($this->_em); $rsm->addRootEntityFromClassMetadata(CmsUser::class, 'u'); $rsm->addJoinedEntityFromClassMetadata(CmsAddress::class, 'a', 'u', 'address'); } - /** @group PR-39 */ + #[Group('PR-39')] public function testUnknownParentAliasThrowsException(): void { $rsm = new ResultSetMappingBuilder($this->_em); @@ -476,343 +467,7 @@ public function testUnknownParentAliasThrowsException(): void $users = $query->getResult(); } - /** @group DDC-1663 */ - public function testBasicNativeNamedQueryWithSqlResultSetMapping(): void - { - $user = new CmsUser(); - $user->name = 'Fabio B. Silva'; - $user->username = 'FabioBatSilva'; - $user->status = 'dev'; - - $addr = new CmsAddress(); - $addr->country = 'Brazil'; - $addr->zip = 10827; - $addr->city = 'SΓ£o Paulo'; - - $user->setAddress($addr); - - $this->_em->clear(); - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - - $repository = $this->_em->getRepository(CmsAddress::class); - $query = $repository->createNativeNamedQuery('find-all'); - $result = $query->getResult(); - - self::assertCount(1, $result); - self::assertInstanceOf(CmsAddress::class, $result[0]); - self::assertEquals($addr->id, $result[0]->id); - self::assertEquals($addr->city, $result[0]->city); - self::assertEquals($addr->country, $result[0]->country); - } - - /** @group DDC-1663 */ - public function testBasicNativeNamedQueryWithResultClass(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8592'); - - $user = new CmsUser(); - $user->name = 'Fabio B. Silva'; - $user->username = 'FabioBatSilva'; - $user->status = 'dev'; - - $email = new CmsEmail(); - $email->email = 'fabio.bat.silva@gmail.com'; - - $user->setEmail($email); - - $this->_em->clear(); - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - - $repository = $this->_em->getRepository(CmsUser::class); - - $result = $repository->createNativeNamedQuery('fetchIdAndUsernameWithResultClass') - ->setParameter(1, 'FabioBatSilva') - ->getResult(); - - self::assertCount(1, $result); - self::assertInstanceOf(CmsUser::class, $result[0]); - self::assertNull($result[0]->name); - self::assertNull($result[0]->email); - self::assertEquals($user->id, $result[0]->id); - self::assertEquals('FabioBatSilva', $result[0]->username); - - $this->_em->clear(); - - $result = $repository->createNativeNamedQuery('fetchAllColumns') - ->setParameter(1, 'FabioBatSilva') - ->getResult(); - - self::assertCount(1, $result); - self::assertInstanceOf(CmsUser::class, $result[0]); - self::assertEquals($user->id, $result[0]->id); - self::assertEquals('Fabio B. Silva', $result[0]->name); - self::assertEquals('FabioBatSilva', $result[0]->username); - self::assertEquals('dev', $result[0]->status); - self::assertInstanceOf(CmsEmail::class, $result[0]->email); - } - - /** @group DDC-1663 */ - public function testJoinedOneToOneNativeNamedQueryWithResultSetMapping(): void - { - $user = new CmsUser(); - $user->name = 'Fabio B. Silva'; - $user->username = 'FabioBatSilva'; - $user->status = 'dev'; - - $addr = new CmsAddress(); - $addr->country = 'Brazil'; - $addr->zip = 10827; - $addr->city = 'SΓ£o Paulo'; - - $user->setAddress($addr); - - $this->_em->persist($user); - $this->_em->flush(); - $this->_em->clear(); - - $result = $this->_em->getRepository(CmsUser::class) - ->createNativeNamedQuery('fetchJoinedAddress') - ->setParameter(1, 'FabioBatSilva') - ->getResult(); - - self::assertCount(1, $result); - self::assertInstanceOf(CmsUser::class, $result[0]); - self::assertEquals('Fabio B. Silva', $result[0]->name); - self::assertInstanceOf(PersistentCollection::class, $result[0]->getPhonenumbers()); - self::assertFalse($result[0]->getPhonenumbers()->isInitialized()); - self::assertInstanceOf(CmsAddress::class, $result[0]->getAddress()); - self::assertEquals($result[0]->getAddress()->getUser(), $result[0]); - self::assertEquals('Brazil', $result[0]->getAddress()->getCountry()); - self::assertEquals(10827, $result[0]->getAddress()->getZipCode()); - self::assertEquals('SΓ£o Paulo', $result[0]->getAddress()->getCity()); - } - - /** @group DDC-1663 */ - public function testJoinedOneToManyNativeNamedQueryWithResultSetMapping(): void - { - $user = new CmsUser(); - $user->name = 'Fabio B. Silva'; - $user->username = 'FabioBatSilva'; - $user->status = 'dev'; - - $phone = new CmsPhonenumber(); - $phone->phonenumber = 424242; - - $user->addPhonenumber($phone); - - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - - $repository = $this->_em->getRepository(CmsUser::class); - - $result = $repository->createNativeNamedQuery('fetchJoinedPhonenumber') - ->setParameter(1, 'FabioBatSilva')->getResult(); - - self::assertCount(1, $result); - self::assertInstanceOf(CmsUser::class, $result[0]); - self::assertEquals('Fabio B. Silva', $result[0]->name); - self::assertInstanceOf(PersistentCollection::class, $result[0]->getPhonenumbers()); - self::assertTrue($result[0]->getPhonenumbers()->isInitialized()); - self::assertCount(1, $result[0]->getPhonenumbers()); - $phones = $result[0]->getPhonenumbers(); - self::assertEquals(424242, $phones[0]->phonenumber); - self::assertSame($phones[0]->getUser(), $result[0]); - } - - /** @group DDC-1663 */ - public function testMixedNativeNamedQueryNormalJoin(): void - { - $user1 = new CmsUser(); - $user1->name = 'Fabio B. Silva'; - $user1->username = 'FabioBatSilva'; - $user1->status = 'dev'; - - $user2 = new CmsUser(); - $user2->name = 'test tester'; - $user2->username = 'test'; - $user2->status = 'tester'; - - $phone1 = new CmsPhonenumber(); - $phone2 = new CmsPhonenumber(); - $phone3 = new CmsPhonenumber(); - $phone1->phonenumber = 11111111; - $phone2->phonenumber = 22222222; - $phone3->phonenumber = 33333333; - - $user1->addPhonenumber($phone1); - $user1->addPhonenumber($phone2); - $user2->addPhonenumber($phone3); - - $this->_em->persist($user1); - $this->_em->persist($user2); - $this->_em->flush(); - - $this->_em->clear(); - - $repository = $this->_em->getRepository(CmsUser::class); - - $result = $repository->createNativeNamedQuery('fetchUserPhonenumberCount') - ->setParameter(1, ['test', 'FabioBatSilva'])->getResult(); - - self::assertCount(2, $result); - self::assertIsArray($result[0]); - self::assertIsArray($result[1]); - - // first user => 2 phonenumbers - self::assertInstanceOf(CmsUser::class, $result[0][0]); - self::assertEquals('Fabio B. Silva', $result[0][0]->name); - self::assertEquals(2, $result[0]['numphones']); - - // second user => 1 phonenumbers - self::assertInstanceOf(CmsUser::class, $result[1][0]); - self::assertEquals('test tester', $result[1][0]->name); - self::assertEquals(1, $result[1]['numphones']); - } - - /** @group DDC-1663 */ - public function testNativeNamedQueryInheritance(): void - { - $person = new CompanyPerson(); - $person->setName('Fabio B. Silva'); - - $employee = new CompanyEmployee(); - $employee->setName('Fabio Silva'); - $employee->setSalary(100000); - $employee->setDepartment('IT'); - - $this->_em->persist($person); - $this->_em->persist($employee); - - $this->_em->flush(); - $this->_em->clear(); - - $repository = $this->_em->getRepository(CompanyPerson::class); - - $result = $repository->createNativeNamedQuery('fetchAllWithSqlResultSetMapping') - ->getResult(); - - self::assertCount(2, $result); - self::assertInstanceOf(CompanyPerson::class, $result[0]); - self::assertInstanceOf(CompanyEmployee::class, $result[1]); - self::assertIsNumeric($result[0]->getId()); - self::assertIsNumeric($result[1]->getId()); - self::assertEquals('Fabio B. Silva', $result[0]->getName()); - self::assertEquals('Fabio Silva', $result[1]->getName()); - - $this->_em->clear(); - - $result = $repository->createNativeNamedQuery('fetchAllWithResultClass') - ->getResult(); - - self::assertCount(2, $result); - self::assertInstanceOf(CompanyPerson::class, $result[0]); - self::assertInstanceOf(CompanyEmployee::class, $result[1]); - self::assertIsNumeric($result[0]->getId()); - self::assertIsNumeric($result[1]->getId()); - self::assertEquals('Fabio B. Silva', $result[0]->getName()); - self::assertEquals('Fabio Silva', $result[1]->getName()); - } - - /** - * @group DDC-1663 - * DQL : SELECT u, a, COUNT(p) AS numphones FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.address a JOIN u.phonenumbers p - */ - public function testMultipleEntityResults(): void - { - $user = new CmsUser(); - $user->name = 'Fabio B. Silva'; - $user->username = 'FabioBatSilva'; - $user->status = 'dev'; - - $addr = new CmsAddress(); - $addr->country = 'Brazil'; - $addr->zip = 10827; - $addr->city = 'SΓ£o Paulo'; - - $phone = new CmsPhonenumber(); - $phone->phonenumber = 424242; - - $user->setAddress($addr); - $user->addPhonenumber($phone); - - $this->_em->clear(); - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - - $repository = $this->_em->getRepository(CmsUser::class); - $query = $repository->createNativeNamedQuery('fetchMultipleJoinsEntityResults'); - $result = $query->getResult(); - - self::assertCount(1, $result); - self::assertIsArray($result[0]); - self::assertInstanceOf(CmsUser::class, $result[0][0]); - self::assertEquals('Fabio B. Silva', $result[0][0]->name); - self::assertInstanceOf(CmsAddress::class, $result[0][0]->getAddress()); - self::assertEquals($result[0][0]->getAddress()->getUser(), $result[0][0]); - self::assertEquals('Brazil', $result[0][0]->getAddress()->getCountry()); - self::assertEquals(10827, $result[0][0]->getAddress()->getZipCode()); - - self::assertEquals(1, $result[0]['numphones']); - } - - /** @group DDC-1663 */ - public function testNamedNativeQueryInheritance(): void - { - $contractMetadata = $this->_em->getClassMetadata(CompanyContract::class); - $flexMetadata = $this->_em->getClassMetadata(CompanyFlexContract::class); - - $contractQueries = $contractMetadata->getNamedNativeQueries(); - $flexQueries = $flexMetadata->getNamedNativeQueries(); - - $contractMappings = $contractMetadata->getSqlResultSetMappings(); - $flexMappings = $flexMetadata->getSqlResultSetMappings(); - - // contract queries - self::assertEquals('all-contracts', $contractQueries['all-contracts']['name']); - self::assertEquals(CompanyContract::class, $contractQueries['all-contracts']['resultClass']); - - self::assertEquals('all', $contractQueries['all']['name']); - self::assertEquals(CompanyContract::class, $contractQueries['all']['resultClass']); - - // flex contract queries - self::assertEquals('all-contracts', $flexQueries['all-contracts']['name']); - self::assertEquals(CompanyFlexContract::class, $flexQueries['all-contracts']['resultClass']); - - self::assertEquals('all-flex', $flexQueries['all-flex']['name']); - self::assertEquals(CompanyFlexContract::class, $flexQueries['all-flex']['resultClass']); - - self::assertEquals('all', $flexQueries['all']['name']); - self::assertEquals(CompanyFlexContract::class, $flexQueries['all']['resultClass']); - - // contract result mapping - self::assertEquals('mapping-all-contracts', $contractMappings['mapping-all-contracts']['name']); - self::assertEquals(CompanyContract::class, $contractMappings['mapping-all-contracts']['entities'][0]['entityClass']); - - self::assertEquals('mapping-all', $contractMappings['mapping-all']['name']); - self::assertEquals(CompanyContract::class, $contractMappings['mapping-all-contracts']['entities'][0]['entityClass']); - - // flex contract result mapping - self::assertEquals('mapping-all-contracts', $flexMappings['mapping-all-contracts']['name']); - self::assertEquals(CompanyFlexContract::class, $flexMappings['mapping-all-contracts']['entities'][0]['entityClass']); - - self::assertEquals('mapping-all', $flexMappings['mapping-all']['name']); - self::assertEquals(CompanyFlexContract::class, $flexMappings['mapping-all']['entities'][0]['entityClass']); - - self::assertEquals('mapping-all-flex', $flexMappings['mapping-all-flex']['name']); - self::assertEquals(CompanyFlexContract::class, $flexMappings['mapping-all-flex']['entities'][0]['entityClass']); - } - - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testGenerateSelectClauseNoRenameSingleEntity(): void { $rsm = new ResultSetMappingBuilder($this->_em); @@ -823,7 +478,7 @@ public function testGenerateSelectClauseNoRenameSingleEntity(): void $this->assertSQLEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause); } - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testGenerateSelectClauseCustomRenames(): void { $rsm = new ResultSetMappingBuilder($this->_em); @@ -837,7 +492,7 @@ public function testGenerateSelectClauseCustomRenames(): void $this->assertSQLEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause); } - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testGenerateSelectClauseRenameTableAlias(): void { $rsm = new ResultSetMappingBuilder($this->_em); @@ -848,7 +503,7 @@ public function testGenerateSelectClauseRenameTableAlias(): void $this->assertSQLEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause); } - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testGenerateSelectClauseIncrement(): void { $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); @@ -859,7 +514,7 @@ public function testGenerateSelectClauseIncrement(): void $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause); } - /** @group DDC-2055 */ + #[Group('DDC-2055')] public function testGenerateSelectClauseToString(): void { $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); @@ -868,7 +523,7 @@ public function testGenerateSelectClauseToString(): void $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string) $rsm); } - /** @group DDC-3899 */ + #[Group('DDC-3899')] public function testGenerateSelectClauseWithDiscriminatorColumn(): void { $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); diff --git a/tests/Tests/ORM/Functional/NewOperatorTest.php b/tests/Tests/ORM/Functional/NewOperatorTest.php index 304c6976967..2394b6fd880 100644 --- a/tests/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Tests/ORM/Functional/NewOperatorTest.php @@ -4,20 +4,27 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\Common\Persistence\PersistentObject; +use Doctrine\ORM\Exception\DuplicateFieldException; +use Doctrine\ORM\Exception\NoMatchingPropertyException; use Doctrine\ORM\Query; +use Doctrine\ORM\Query\QueryException; use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsAddressDTO; +use Doctrine\Tests\Models\CMS\CmsAddressDTONamedArgs; use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsUserDTO; +use Doctrine\Tests\Models\CMS\CmsUserDTONamedArgs; +use Doctrine\Tests\Models\CMS\CmsUserDTOVariadicArg; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; -use function class_exists; use function count; +use function sprintf; -/** @group DDC-1574 */ +#[Group('DDC-1574')] class NewOperatorTest extends OrmFunctionalTestCase { /** @var list */ @@ -102,7 +109,7 @@ private function loadFixtures(): void $this->fixtures = [$u1, $u2, $u3]; } - /** @dataProvider provideDataForHydrationMode */ + #[DataProvider('provideDataForHydrationMode')] public function testShouldSupportsBasicUsage($hydrationMode): void { $dql = ' @@ -143,7 +150,7 @@ public function testShouldSupportsBasicUsage($hydrationMode): void self::assertEquals($this->fixtures[2]->address->city, $result[2]->address); } - /** @dataProvider provideDataForHydrationMode */ + #[DataProvider('provideDataForHydrationMode')] public function testShouldIgnoreAliasesForSingleObject($hydrationMode): void { $dql = ' @@ -208,68 +215,6 @@ public function testShouldAssumeFromEntityNamespaceWhenNotGiven(): void self::assertInstanceOf(CmsUserDTO::class, $result[2]); } - public function testShouldSupportFromEntityNamespaceAlias(): void - { - if (! class_exists(PersistentObject::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2'); - } - - $dql = ' - SELECT - new CmsUserDTO(u.name, e.email, a.city) - FROM - cms:CmsUser u - JOIN - u.email e - JOIN - u.address a - ORDER BY - u.name'; - - $this->_em->getConfiguration() - ->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS'); - - $query = $this->_em->createQuery($dql); - $result = $query->getResult(); - - self::assertCount(3, $result); - - self::assertInstanceOf(CmsUserDTO::class, $result[0]); - self::assertInstanceOf(CmsUserDTO::class, $result[1]); - self::assertInstanceOf(CmsUserDTO::class, $result[2]); - } - - public function testShouldSupportValueObjectNamespaceAlias(): void - { - if (! class_exists(PersistentObject::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2'); - } - - $dql = ' - SELECT - new cms:CmsUserDTO(u.name, e.email, a.city) - FROM - cms:CmsUser u - JOIN - u.email e - JOIN - u.address a - ORDER BY - u.name'; - - $this->_em->getConfiguration() - ->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS'); - - $query = $this->_em->createQuery($dql); - $result = $query->getResult(); - - self::assertCount(3, $result); - - self::assertInstanceOf(CmsUserDTO::class, $result[0]); - self::assertInstanceOf(CmsUserDTO::class, $result[1]); - self::assertInstanceOf(CmsUserDTO::class, $result[2]); - } - public function testShouldSupportLiteralExpression(): void { $dql = " @@ -405,17 +350,17 @@ public function testShouldSupportSimpleArithmeticExpression(): void self::assertEquals( $this->fixtures[0]->address->id + $this->fixtures[0]->id, - $result[0]->phonenumbers + $result[0]->phonenumbers, ); self::assertEquals( $this->fixtures[1]->address->id + $this->fixtures[1]->id, - $result[1]->phonenumbers + $result[1]->phonenumbers, ); self::assertEquals( $this->fixtures[2]->address->id + $this->fixtures[2]->id, - $result[2]->phonenumbers + $result[2]->phonenumbers, ); } @@ -465,17 +410,17 @@ public function testShouldSupportAggregateFunctions(): void self::assertEquals( count($this->fixtures[0]->phonenumbers), - $result[0]->phonenumbers + $result[0]->phonenumbers, ); self::assertEquals( count($this->fixtures[1]->phonenumbers), - $result[1]->phonenumbers + $result[1]->phonenumbers, ); self::assertEquals( count($this->fixtures[2]->phonenumbers), - $result[2]->phonenumbers + $result[2]->phonenumbers, ); } @@ -525,17 +470,17 @@ public function testShouldSupportArithmeticExpression(): void self::assertEquals( count($this->fixtures[0]->phonenumbers) + $this->fixtures[0]->id, - $result[0]->phonenumbers + $result[0]->phonenumbers, ); self::assertEquals( count($this->fixtures[1]->phonenumbers) + $this->fixtures[1]->id, - $result[1]->phonenumbers + $result[1]->phonenumbers, ); self::assertEquals( count($this->fixtures[2]->phonenumbers) + $this->fixtures[2]->id, - $result[2]->phonenumbers + $result[2]->phonenumbers, ); } @@ -1045,7 +990,7 @@ public function testShouldSupportMultipleNewOperatorsAndMultipleScalarsWithAndWi public function testInvalidClassException(): void { - $this->expectException('Doctrine\ORM\Query\QueryException'); + $this->expectException(QueryException::class); $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \'\InvalidClass(u.name)\': Error: Class "\InvalidClass" is not defined.'); $dql = 'SELECT new \InvalidClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'; $this->_em->createQuery($dql)->getResult(); @@ -1053,7 +998,7 @@ public function testInvalidClassException(): void public function testInvalidClassConstructorException(): void { - $this->expectException('Doctrine\ORM\Query\QueryException'); + $this->expectException(QueryException::class); $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \'\stdClass(u.name)\': Error: Class "\stdClass" has not a valid constructor.'); $dql = 'SELECT new \stdClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'; $this->_em->createQuery($dql)->getResult(); @@ -1061,7 +1006,7 @@ public function testInvalidClassConstructorException(): void public function testInvalidClassWithoutConstructorException(): void { - $this->expectException('Doctrine\ORM\Query\QueryException'); + $this->expectException(QueryException::class); $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \'Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name)\': Error: Number of arguments does not match with "Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs" constructor declaration.'); $dql = 'SELECT new Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'; $this->_em->createQuery($dql)->getResult(); @@ -1069,11 +1014,399 @@ public function testInvalidClassWithoutConstructorException(): void public function testClassCantBeInstantiatedException(): void { - $this->expectException('Doctrine\ORM\Query\QueryException'); + $this->expectException(QueryException::class); $this->expectExceptionMessage('[Semantical Error] line 0, col 11 near \'Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name)\': Error: Class "Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor" can not be instantiated.'); $dql = 'SELECT new Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u'; $this->_em->createQuery($dql)->getResult(); } + + public function testShouldSupportNestedNewOperators(): void + { + $dql = ' + SELECT + new CmsUserDTO( + u.name, + e.email, + new CmsAddressDTO( + a.country, + a.city, + a.zip, + new CmsAddressDTO( + a.country, + a.city, + a.zip + ) + ) + ) as user, + u.status, + u.username as cmsUserUsername + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name'; + + $query = $this->getEntityManager()->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(3, $result); + + self::assertInstanceOf(CmsUserDTO::class, $result[0]['user']); + self::assertInstanceOf(CmsUserDTO::class, $result[1]['user']); + self::assertInstanceOf(CmsUserDTO::class, $result[2]['user']); + + self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->address); + self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->address); + self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->address); + + self::assertSame($this->fixtures[0]->name, $result[0]['user']->name); + self::assertSame($this->fixtures[1]->name, $result[1]['user']->name); + self::assertSame($this->fixtures[2]->name, $result[2]['user']->name); + + self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email); + self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email); + self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email); + + self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->address->city); + self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->address->city); + self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->address->city); + + self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->address->country); + self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->address->country); + self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->address->country); + + self::assertSame($this->fixtures[0]->status, $result[0]['status']); + self::assertSame($this->fixtures[1]->status, $result[1]['status']); + self::assertSame($this->fixtures[2]->status, $result[2]['status']); + + self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']); + self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']); + self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']); + } + + public function testNamedArguments(): void + { + $dql = <<<'SQL' + SELECT + new named CmsUserDTONamedArgs( + e.email, + u.name, + CONCAT(a.country, ' ', a.city, ' ', a.zip) AS address + ) as user, + u.status, + u.username as cmsUserUsername + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name + SQL; + + $query = $this->getEntityManager()->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(3, $result); + + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']); + + self::assertSame($this->fixtures[0]->name, $result[0]['user']->name); + self::assertSame($this->fixtures[1]->name, $result[1]['user']->name); + self::assertSame($this->fixtures[2]->name, $result[2]['user']->name); + + self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email); + self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email); + self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email); + + self::assertSame(sprintf( + '%s %s %s', + $this->fixtures[0]->address->country, + $this->fixtures[0]->address->city, + $this->fixtures[0]->address->zip, + ), $result[0]['user']->address); + self::assertSame( + sprintf( + '%s %s %s', + $this->fixtures[1]->address->country, + $this->fixtures[1]->address->city, + $this->fixtures[1]->address->zip, + ), + $result[1]['user']->address, + ); + self::assertSame( + sprintf( + '%s %s %s', + $this->fixtures[2]->address->country, + $this->fixtures[2]->address->city, + $this->fixtures[2]->address->zip, + ), + $result[2]['user']->address, + ); + + self::assertSame($this->fixtures[0]->status, $result[0]['status']); + self::assertSame($this->fixtures[1]->status, $result[1]['status']); + self::assertSame($this->fixtures[2]->status, $result[2]['status']); + + self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']); + self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']); + self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']); + } + + public function testVariadicArgument(): void + { + $dql = <<<'SQL' + SELECT + new named CmsUserDTOVariadicArg( + CONCAT(a.country, ' ', a.city, ' ', a.zip) AS address, + e.email, + u.name + ) as user, + u.status, + u.username as cmsUserUsername + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name + SQL; + + $query = $this->getEntityManager()->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(3, $result); + + self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[0]['user']); + self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[1]['user']); + self::assertInstanceOf(CmsUserDTOVariadicArg::class, $result[2]['user']); + + self::assertSame($this->fixtures[0]->name, $result[0]['user']->name); + self::assertSame($this->fixtures[1]->name, $result[1]['user']->name); + self::assertSame($this->fixtures[2]->name, $result[2]['user']->name); + + self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email); + self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email); + self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email); + + self::assertSame( + sprintf( + '%s %s %s', + $this->fixtures[0]->address->country, + $this->fixtures[0]->address->city, + $this->fixtures[0]->address->zip, + ), + $result[0]['user']->address, + ); + self::assertSame( + sprintf( + '%s %s %s', + $this->fixtures[1]->address->country, + $this->fixtures[1]->address->city, + $this->fixtures[1]->address->zip, + ), + $result[1]['user']->address, + ); + self::assertSame( + sprintf( + '%s %s %s', + $this->fixtures[2]->address->country, + $this->fixtures[2]->address->city, + $this->fixtures[2]->address->zip, + ), + $result[2]['user']->address, + ); + + self::assertSame($this->fixtures[0]->status, $result[0]['status']); + self::assertSame($this->fixtures[1]->status, $result[1]['status']); + self::assertSame($this->fixtures[2]->status, $result[2]['status']); + + self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']); + self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']); + self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']); + } + + public function testShouldSupportNestedNewOperatorsAndNamedArguments(): void + { + $dql = ' + SELECT + new named CmsUserDTONamedArgs( + e.email, + u.name as name, + new CmsAddressDTO( + a.country, + a.city, + a.zip + ) as addressDto + ) as user, + u.status, + u.username as cmsUserUsername + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name'; + + $query = $this->getEntityManager()->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(3, $result); + + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']); + + self::assertNull($result[0]['user']->address); + self::assertNull($result[1]['user']->address); + self::assertNull($result[2]['user']->address); + + self::assertInstanceOf(CmsAddressDTO::class, $result[0]['user']->addressDto); + self::assertInstanceOf(CmsAddressDTO::class, $result[1]['user']->addressDto); + self::assertInstanceOf(CmsAddressDTO::class, $result[2]['user']->addressDto); + + self::assertSame($this->fixtures[0]->name, $result[0]['user']->name); + self::assertSame($this->fixtures[1]->name, $result[1]['user']->name); + self::assertSame($this->fixtures[2]->name, $result[2]['user']->name); + + self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email); + self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email); + self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email); + + self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->addressDto->city); + self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->addressDto->city); + self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->addressDto->city); + + self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->addressDto->country); + self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->addressDto->country); + self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->addressDto->country); + + self::assertSame($this->fixtures[0]->status, $result[0]['status']); + self::assertSame($this->fixtures[1]->status, $result[1]['status']); + self::assertSame($this->fixtures[2]->status, $result[2]['status']); + + self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']); + self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']); + self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']); + } + + public function testShouldSupportNestedNamedArguments(): void + { + $dql = ' + SELECT + new named CmsUserDTONamedArgs( + e.email, + u.name as name, + new named CmsAddressDTONamedArgs( + a.zip, + a.city, + a.country + ) as addressDtoNamedArgs + ) as user, + u.status, + u.username as cmsUserUsername + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER BY + u.name'; + + $query = $this->getEntityManager()->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(3, $result); + + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[0]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[1]['user']); + self::assertInstanceOf(CmsUserDTONamedArgs::class, $result[2]['user']); + + self::assertNull($result[0]['user']->address); + self::assertNull($result[1]['user']->address); + self::assertNull($result[2]['user']->address); + + self::assertNull($result[0]['user']->addressDto); + self::assertNull($result[1]['user']->addressDto); + self::assertNull($result[2]['user']->addressDto); + + self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[0]['user']->addressDtoNamedArgs); + self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[1]['user']->addressDtoNamedArgs); + self::assertInstanceOf(CmsAddressDTONamedArgs::class, $result[2]['user']->addressDtoNamedArgs); + + self::assertSame($this->fixtures[0]->name, $result[0]['user']->name); + self::assertSame($this->fixtures[1]->name, $result[1]['user']->name); + self::assertSame($this->fixtures[2]->name, $result[2]['user']->name); + + self::assertSame($this->fixtures[0]->email->email, $result[0]['user']->email); + self::assertSame($this->fixtures[1]->email->email, $result[1]['user']->email); + self::assertSame($this->fixtures[2]->email->email, $result[2]['user']->email); + + self::assertSame($this->fixtures[0]->address->city, $result[0]['user']->addressDtoNamedArgs->city); + self::assertSame($this->fixtures[1]->address->city, $result[1]['user']->addressDtoNamedArgs->city); + self::assertSame($this->fixtures[2]->address->city, $result[2]['user']->addressDtoNamedArgs->city); + + self::assertSame($this->fixtures[0]->address->country, $result[0]['user']->addressDtoNamedArgs->country); + self::assertSame($this->fixtures[1]->address->country, $result[1]['user']->addressDtoNamedArgs->country); + self::assertSame($this->fixtures[2]->address->country, $result[2]['user']->addressDtoNamedArgs->country); + + self::assertSame($this->fixtures[0]->status, $result[0]['status']); + self::assertSame($this->fixtures[1]->status, $result[1]['status']); + self::assertSame($this->fixtures[2]->status, $result[2]['status']); + + self::assertSame($this->fixtures[0]->username, $result[0]['cmsUserUsername']); + self::assertSame($this->fixtures[1]->username, $result[1]['cmsUserUsername']); + self::assertSame($this->fixtures[2]->username, $result[2]['cmsUserUsername']); + } + + public function testExceptionIfTwoAliases(): void + { + $dql = ' + SELECT + new named Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + u.username AS name + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u'; + + $this->expectException(DuplicateFieldException::class); + $this->expectExceptionMessage('Name "name" for "u.username AS name" already in use.'); + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + } + + public function testExceptionIfFunctionHasNoAlias(): void + { + $dql = " + SELECT + new named Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + CASE WHEN (e.email = 'email@test1.com') THEN 'TEST1' ELSE 'OTHER_TEST' END + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e"; + + $this->expectException(NoMatchingPropertyException::class); + $this->expectExceptionMessage('Column name "CASE WHEN (e.email = \'email@test1.com\') THEN \'TEST1\' ELSE \'OTHER_TEST\' END" does not match any property name. Consider aliasing it to the name of an existing property.'); + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + } } class ClassWithTooMuchArgs diff --git a/tests/Tests/ORM/Functional/NotifyPolicyTest.php b/tests/Tests/ORM/Functional/NotifyPolicyTest.php deleted file mode 100644 index 610696a29eb..00000000000 --- a/tests/Tests/ORM/Functional/NotifyPolicyTest.php +++ /dev/null @@ -1,220 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8383'); - - $this->createSchemaForModels(NotifyUser::class, NotifyGroup::class); - } - - public function testChangeTracking(): void - { - $user = new NotifyUser(); - $group = new NotifyGroup(); - $user->setName('roman'); - $group->setName('dev'); - - $user->getGroups()->add($group); - $group->getUsers()->add($user); - - $this->_em->persist($user); - $this->_em->persist($group); - - self::assertCount(1, $user->listeners); - self::assertCount(1, $group->listeners); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertCount(1, $user->listeners); - self::assertCount(1, $group->listeners); - - $userId = $user->getId(); - $groupId = $group->getId(); - unset($user, $group); - - $user = $this->_em->find(NotifyUser::class, $userId); - self::assertEquals(1, $user->getGroups()->count()); - $group = $this->_em->find(NotifyGroup::class, $groupId); - self::assertEquals(1, $group->getUsers()->count()); - - self::assertCount(1, $user->listeners); - self::assertCount(1, $group->listeners); - - $group2 = new NotifyGroup(); - $group2->setName('nerds'); - $this->_em->persist($group2); - $user->getGroups()->add($group2); - $group2->getUsers()->add($user); - - $group->setName('geeks'); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertCount(1, $user->listeners); - self::assertCount(1, $group->listeners); - - $group2Id = $group2->getId(); - unset($group2, $user); - - $user = $this->_em->find(NotifyUser::class, $userId); - self::assertEquals(2, $user->getGroups()->count()); - $group2 = $this->_em->find(NotifyGroup::class, $group2Id); - self::assertEquals(1, $group2->getUsers()->count()); - $group = $this->_em->find(NotifyGroup::class, $groupId); - self::assertEquals(1, $group->getUsers()->count()); - self::assertEquals('geeks', $group->getName()); - } -} - -class NotifyBaseEntity implements NotifyPropertyChanged -{ - /** @phpstan-var list */ - public $listeners = []; - - public function addPropertyChangedListener(PropertyChangedListener $listener): void - { - $this->listeners[] = $listener; - } - - protected function onPropertyChanged($propName, $oldValue, $newValue): void - { - if ($this->listeners) { - foreach ($this->listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } - } -} - -/** - * @Entity - * @ChangeTrackingPolicy("NOTIFY") - */ -class NotifyUser extends NotifyBaseEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="NotifyGroup") - */ - private $groups; - - public function __construct() - { - $this->groups = new ArrayCollection(); - } - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->onPropertyChanged('name', $this->name, $name); - $this->name = $name; - } - - /** @phpstan-return Collection */ - public function getGroups(): Collection - { - return $this->groups; - } -} - -/** @Entity */ -class NotifyGroup extends NotifyBaseEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="NotifyUser", mappedBy="groups") - */ - private $users; - - public function __construct() - { - $this->users = new ArrayCollection(); - } - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->onPropertyChanged('name', $this->name, $name); - $this->name = $name; - } - - /** @phpstan-return Collection */ - public function getUsers(): Collection - { - return $this->users; - } -} diff --git a/tests/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php index 1f8695cd432..ebd23a43635 100644 --- a/tests/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php @@ -9,20 +9,18 @@ use Doctrine\Tests\Models\ECommerce\ECommerceFeature; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Tests a bidirectional one-to-one association mapping (without inheritance). */ class OneToManyBidirectionalAssociationTest extends OrmFunctionalTestCase { - /** @var ECommerceProduct */ - private $product; + private ECommerceProduct $product; - /** @var ECommerceFeature */ - private $firstFeature; + private ECommerceFeature $firstFeature; - /** @var ECommerceFeature */ - private $secondFeature; + private ECommerceFeature $secondFeature; protected function setUp(): void { @@ -35,7 +33,7 @@ protected function setUp(): void $this->firstFeature = new ECommerceFeature(); $this->firstFeature->setDescription('Model writing tutorial'); $this->secondFeature = new ECommerceFeature(); - $this->secondFeature->setDescription('Annotations examples'); + $this->secondFeature->setDescription('Attributes examples'); } public function testSavesAOneToManyAssociationWithCascadeSaveSet(): void @@ -95,7 +93,7 @@ public function testEagerLoadsOneToManyAssociation(): void self::assertInstanceOf(ECommerceFeature::class, $features[1]); self::assertSame($product, $features[1]->getProduct()); self::assertFalse($this->isUninitializedObject($features[1]->getProduct())); - self::assertEquals('Annotations examples', $features[1]->getDescription()); + self::assertEquals('Attributes examples', $features[1]->getDescription()); } public function testLazyLoadsObjectsOnTheOwningSide(): void @@ -114,7 +112,7 @@ public function testLazyLoadsObjectsOnTheOwningSide(): void self::assertEquals('Model writing tutorial', $features[0]->getDescription()); self::assertInstanceOf(ECommerceFeature::class, $features[1]); self::assertSame($product, $features[1]->getProduct()); - self::assertEquals('Annotations examples', $features[1]->getDescription()); + self::assertEquals('Attributes examples', $features[1]->getDescription()); } public function testLazyLoadsObjectsOnTheInverseSide(): void @@ -158,7 +156,7 @@ public function testJoinFromOwningSide(): void self::assertCount(0, $features); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testMatching(): void { $this->createFixture(); @@ -167,7 +165,7 @@ public function testMatching(): void $features = $product->getFeatures(); $results = $features->matching(new Criteria( - Criteria::expr()->eq('description', 'Model writing tutorial') + Criteria::expr()->eq('description', 'Model writing tutorial'), )); self::assertInstanceOf(Collection::class, $results); @@ -179,7 +177,7 @@ public function testMatching(): void self::assertCount(2, $results); } - /** @group DDC-2340 */ + #[Group('DDC-2340')] public function testMatchingOnDirtyCollection(): void { $this->createFixture(); @@ -193,7 +191,7 @@ public function testMatchingOnDirtyCollection(): void $features->add($thirdFeature); $results = $features->matching(new Criteria( - Criteria::expr()->eq('description', 'Model writing tutorial') + Criteria::expr()->eq('description', 'Model writing tutorial'), )); self::assertCount(2, $results); @@ -211,7 +209,7 @@ public function testMatchingBis(): void $product->addFeature($thirdFeature); $results = $features->matching(new Criteria( - Criteria::expr()->eq('description', 'Third feature') + Criteria::expr()->eq('description', 'Third feature'), )); self::assertInstanceOf(Collection::class, $results); @@ -237,7 +235,7 @@ public function assertFeatureForeignKeyIs($value, ECommerceFeature $feature): vo { $foreignKey = $this->_em->getConnection()->executeQuery( 'SELECT product_id FROM ecommerce_features WHERE id=?', - [$feature->getId()] + [$feature->getId()], )->fetchOne(); self::assertEquals($value, $foreignKey); } diff --git a/tests/Tests/ORM/Functional/OneToManyOrphanRemovalTest.php b/tests/Tests/ORM/Functional/OneToManyOrphanRemovalTest.php index 232946f5e94..807db4c753f 100644 --- a/tests/Tests/ORM/Functional/OneToManyOrphanRemovalTest.php +++ b/tests/Tests/ORM/Functional/OneToManyOrphanRemovalTest.php @@ -7,6 +7,7 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Tests a bidirectional one-to-many association mapping with orphan removal. @@ -62,7 +63,7 @@ public function testOrphanRemoval(): void self::assertCount(0, $result, 'CmsPhonenumber should be removed by orphanRemoval'); } - /** @group DDC-3382 */ + #[Group('DDC-3382')] public function testOrphanRemovalRemoveFromCollection(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -78,7 +79,7 @@ public function testOrphanRemovalRemoveFromCollection(): void self::assertCount(1, $result, 'CmsPhonenumber should be removed by orphanRemoval'); } - /** @group DDC-3382 */ + #[Group('DDC-3382')] public function testOrphanRemovalClearCollectionAndReAdd(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -96,7 +97,7 @@ public function testOrphanRemovalClearCollectionAndReAdd(): void self::assertCount(1, $result, 'CmsPhonenumber should be removed by orphanRemoval'); } - /** @group DDC-2524 */ + #[Group('DDC-2524')] public function testOrphanRemovalClearCollectionAndAddNew(): void { $user = $this->_em->find(CmsUser::class, $this->userId); @@ -115,7 +116,7 @@ public function testOrphanRemovalClearCollectionAndAddNew(): void self::assertCount(1, $result, 'Old CmsPhonenumbers should be removed by orphanRemoval and new one added'); } - /** @group DDC-1496 */ + #[Group('DDC-1496')] public function testOrphanRemovalUnitializedCollection(): void { $user = $this->_em->find(CmsUser::class, $this->userId); diff --git a/tests/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php b/tests/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php index a18cbace1f1..66983656506 100644 --- a/tests/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php @@ -15,14 +15,11 @@ */ class OneToManySelfReferentialAssociationTest extends OrmFunctionalTestCase { - /** @var ECommerceCategory */ - private $parent; + private ECommerceCategory $parent; - /** @var ECommerceCategory */ - private $firstChild; + private ECommerceCategory $firstChild; - /** @var ECommerceCategory */ - private $secondChild; + private ECommerceCategory $secondChild; protected function setUp(): void { @@ -101,8 +98,8 @@ public function testEagerLoadsOneToManyAssociation(): void public function testLazyLoadsOneToManyAssociation(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceCategory::class); - $metadata->associationMappings['children']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceCategory::class); + $metadata->associationMappings['children']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCategory c order by c.id asc'); $result = $query->getResult(); diff --git a/tests/Tests/ORM/Functional/OneToManyUnidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/OneToManyUnidirectionalAssociationTest.php index c1bf4eed724..cefef0e15da 100644 --- a/tests/Tests/ORM/Functional/OneToManyUnidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToManyUnidirectionalAssociationTest.php @@ -54,7 +54,7 @@ public function testPersistOwningInverseCascade(): void $routes = $this->_em->createQuery( 'SELECT r, l, f, t FROM Doctrine\Tests\Models\Routing\RoutingRoute r ' . - 'JOIN r.legs l JOIN l.fromLocation f JOIN l.toLocation t' + 'JOIN r.legs l JOIN l.fromLocation f JOIN l.toLocation t', )->getSingleResult(); self::assertCount(1, $routes->legs); diff --git a/tests/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php index b0896734d53..007d0180d82 100644 --- a/tests/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php @@ -14,11 +14,9 @@ */ class OneToOneBidirectionalAssociationTest extends OrmFunctionalTestCase { - /** @var ECommerceCustomer */ - private $customer; + private ECommerceCustomer $customer; - /** @var ECommerceCart */ - private $cart; + private ECommerceCart $cart; protected function setUp(): void { @@ -76,8 +74,8 @@ public function testEagerLoad(): void public function testLazyLoadsObjectsOnTheOwningSide(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceCart::class); - $metadata->associationMappings['customer']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceCart::class); + $metadata->associationMappings['customer']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCart c'); $result = $query->getResult(); @@ -90,8 +88,8 @@ public function testLazyLoadsObjectsOnTheOwningSide(): void public function testInverseSideIsNeverLazy(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceCustomer::class); - $metadata->associationMappings['mentor']['fetch'] = ClassMetadata::FETCH_EAGER; + $metadata = $this->_em->getClassMetadata(ECommerceCustomer::class); + $metadata->associationMappings['mentor']->fetch = ClassMetadata::FETCH_EAGER; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c'); $result = $query->getResult(); diff --git a/tests/Tests/ORM/Functional/OneToOneEagerLoadingTest.php b/tests/Tests/ORM/Functional/OneToOneEagerLoadingTest.php index 81138141afa..e841b8cf64b 100644 --- a/tests/Tests/ORM/Functional/OneToOneEagerLoadingTest.php +++ b/tests/Tests/ORM/Functional/OneToOneEagerLoadingTest.php @@ -15,10 +15,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-952 */ +#[Group('DDC-952')] class OneToOneEagerLoadingTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -30,11 +29,11 @@ protected function setUp(): void TrainDriver::class, TrainOwner::class, Waggon::class, - TrainOrder::class + TrainOrder::class, ); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadOneToOneOwningSide(): void { $train = new Train(new TrainOwner('Alexander')); @@ -50,14 +49,14 @@ public function testEagerLoadOneToOneOwningSide(): void $this->getQueryLog()->reset()->enable(); - $train = $this->_em->find(get_class($train), $train->id); + $train = $this->_em->find($train::class, $train->id); self::assertFalse($this->isUninitializedObject($train->driver)); self::assertEquals('Benjamin', $train->driver->name); $this->assertQueryCount(1); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadOneToOneNullOwningSide(): void { $train = new Train(new TrainOwner('Alexander')); @@ -68,13 +67,13 @@ public function testEagerLoadOneToOneNullOwningSide(): void $this->getQueryLog()->reset()->enable(); - $train = $this->_em->find(get_class($train), $train->id); + $train = $this->_em->find($train::class, $train->id); self::assertNull($train->driver); $this->assertQueryCount(1); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadOneToOneInverseSide(): void { $owner = new TrainOwner('Alexander'); @@ -86,14 +85,14 @@ public function testEagerLoadOneToOneInverseSide(): void $this->getQueryLog()->reset()->enable(); - $this->_em->find(get_class($owner), $owner->id); + $this->_em->find($owner::class, $owner->id); self::assertFalse($this->isUninitializedObject($owner->train)); self::assertInstanceOf(Train::class, $owner->train); $this->assertQueryCount(1); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadOneToOneNullInverseSide(): void { $driver = new TrainDriver('Dagny Taggert'); @@ -106,7 +105,7 @@ public function testEagerLoadOneToOneNullInverseSide(): void $this->getQueryLog()->reset()->enable(); - $driver = $this->_em->find(get_class($driver), $driver->id); + $driver = $this->_em->find($driver::class, $driver->id); self::assertNull($driver->train); $this->assertQueryCount(1); @@ -122,12 +121,12 @@ public function testEagerLoadManyToOne(): void $this->_em->flush(); $this->_em->clear(); - $waggon = $this->_em->find(get_class($waggon), $waggon->id); + $waggon = $this->_em->find($waggon::class, $waggon->id); self::assertFalse($this->isUninitializedObject($waggon->train)); self::assertInstanceOf(Train::class, $waggon->train); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides(): void { $train = new Train(new TrainOwner('Alexander')); @@ -140,17 +139,17 @@ public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides(): $driverId = $driver->id; $this->_em->clear(); - $train = $this->_em->find(get_class($train), $train->id); + $train = $this->_em->find($train::class, $train->id); self::assertNotNull($train, 'It should be possible to find the train even though it has no driver'); self::assertSame($trainId, $train->id); $this->_em->clear(); - $driver = $this->_em->find(get_class($driver), $driver->id); + $driver = $this->_em->find($driver::class, $driver->id); self::assertNotNull($driver, 'It should be possible to find the driver even though they drive no train'); self::assertSame($driverId, $driver->id); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSide(): void { $waggon = new Waggon(); @@ -163,22 +162,22 @@ public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSid $this->_em->flush(); $this->_em->clear(); - $waggon = $this->_em->find(get_class($waggon), $waggon->id); + $waggon = $this->_em->find($waggon::class, $waggon->id); // The last query is the eager loading of the owner of the train $this->assertSQLEquals( 'SELECT t0.id AS id_1, t0.name AS name_2, t3.id AS id_4, t3.driver_id AS driver_id_5, t3.owner_id AS owner_id_6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)', - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); // The one before is the fetching of the waggon and train $this->assertSQLEquals( 'SELECT t0.id AS id_1, t0.train_id AS train_id_2, t3.id AS id_4, t3.driver_id AS driver_id_5, t3.owner_id AS owner_id_6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?', - $this->getLastLoggedQuery(1)['sql'] + $this->getLastLoggedQuery(1)['sql'], ); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningSide(): void { $owner = new TrainOwner('Alexander'); @@ -186,11 +185,11 @@ public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningS $this->_em->flush(); $this->_em->clear(); - $owner = $this->_em->find(get_class($owner), $owner->id); + $owner = $this->_em->find($owner::class, $owner->id); self::assertNotNull($owner, 'An owner without a train should be able to exist.'); } - /** @group DDC-1946 */ + #[Group('DDC-1946')] public function testEagerLoadingDoesNotBreakRefresh(): void { $train = new Train(new TrainOwner('Johannes')); @@ -207,39 +206,35 @@ public function testEagerLoadingDoesNotBreakRefresh(): void } } -/** @Entity */ +#[Entity] class Train { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; /** * Owning side * * @var TrainDriver - * @OneToOne(targetEntity="TrainDriver", inversedBy="train", fetch="EAGER", cascade={"persist"}) - * @JoinColumn(nullable=true) */ + #[OneToOne(targetEntity: 'TrainDriver', inversedBy: 'train', fetch: 'EAGER', cascade: ['persist'])] + #[JoinColumn(nullable: true)] public $driver; /** * Owning side * * @var TrainOwner - * @OneToOne(targetEntity="TrainOwner", inversedBy="train", fetch="EAGER", cascade={"persist"}) - * @JoinColumn(nullable=false) */ + #[OneToOne(targetEntity: 'TrainOwner', inversedBy: 'train', fetch: 'EAGER', cascade: ['persist'])] + #[JoinColumn(nullable: false)] public $owner; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Waggon", mappedBy="train", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Waggon', mappedBy: 'train', cascade: ['persist'])] public $waggons; public function __construct(TrainOwner $owner) @@ -267,34 +262,27 @@ public function addWaggon(Waggon $w): void } } -/** @Entity */ +#[Entity] class TrainDriver { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - /** * Inverse side * * @var Train - * @OneToOne(targetEntity="Train", mappedBy="driver", fetch="EAGER") */ + #[OneToOne(targetEntity: 'Train', mappedBy: 'driver', fetch: 'EAGER')] public $train; - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + ) { } public function setTrain(Train $t): void @@ -303,34 +291,27 @@ public function setTrain(Train $t): void } } -/** @Entity */ +#[Entity] class TrainOwner { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - /** * Inverse side * * @var Train - * @OneToOne(targetEntity="Train", mappedBy="owner", fetch="EAGER") */ + #[OneToOne(targetEntity: 'Train', mappedBy: 'owner', fetch: 'EAGER')] public $train; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + ) { } public function setTrain(Train $t): void @@ -339,21 +320,17 @@ public function setTrain(Train $t): void } } -/** @Entity */ +#[Entity] class Waggon { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var Train - * @ManyToOne(targetEntity="Train", inversedBy="waggons", fetch="EAGER") - * @JoinColumn(nullable=false) - */ + /** @var Train */ + #[ManyToOne(targetEntity: 'Train', inversedBy: 'waggons', fetch: 'EAGER')] + #[JoinColumn(nullable: false)] public $train; public function setTrain($train): void @@ -362,25 +339,18 @@ public function setTrain($train): void } } -/** @Entity */ +#[Entity] class TrainOrder { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var Train - * @OneToOne(targetEntity="Train", fetch="EAGER") - */ - public $train; - - public function __construct(Train $train) - { - $this->train = $train; + public function __construct( + #[OneToOne(targetEntity: 'Train', fetch: 'EAGER')] + public Train|null $train = null, + ) { } } diff --git a/tests/Tests/ORM/Functional/OneToOneInverseSideLoadAfterDqlQueryTest.php b/tests/Tests/ORM/Functional/OneToOneInverseSideLoadAfterDqlQueryTest.php index cda8d2f150a..9a3ffbd6e9e 100644 --- a/tests/Tests/ORM/Functional/OneToOneInverseSideLoadAfterDqlQueryTest.php +++ b/tests/Tests/ORM/Functional/OneToOneInverseSideLoadAfterDqlQueryTest.php @@ -7,6 +7,7 @@ use Doctrine\Tests\Models\OneToOneInverseSideLoad\InverseSide; use Doctrine\Tests\Models\OneToOneInverseSideLoad\OwningSide; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -19,7 +20,7 @@ protected function setUp(): void $this->createSchemaForModels(OwningSide::class, InverseSide::class); } - /** @group GH-6759 */ + #[Group('GH-6759')] public function testInverseSideOneToOneLoadedAfterDqlQuery(): void { $owner = new OwningSide(); @@ -51,12 +52,12 @@ public function testInverseSideOneToOneLoadedAfterDqlQuery(): void $this->assertSQLEquals( 'select o0_.id as id_0 from one_to_one_inverse_side_load_inverse o0_ where o0_.id = ?', - $this->getLastLoggedQuery(1)['sql'] + $this->getLastLoggedQuery(1)['sql'], ); $this->assertSQLEquals( 'select t0.id as id_1, t0.inverse as inverse_2 from one_to_one_inverse_side_load_owning t0 WHERE t0.inverse = ?', - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); } } diff --git a/tests/Tests/ORM/Functional/OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest.php b/tests/Tests/ORM/Functional/OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest.php index 7e2ca8a120e..14d6422d1cf 100644 --- a/tests/Tests/ORM/Functional/OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest.php +++ b/tests/Tests/ORM/Functional/OneToOneInverseSideWithAssociativeIdLoadAfterDqlQueryTest.php @@ -8,6 +8,7 @@ use Doctrine\Tests\Models\OneToOneInverseSideWithAssociativeIdLoad\InverseSideIdTarget; use Doctrine\Tests\Models\OneToOneInverseSideWithAssociativeIdLoad\OwningSide; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -20,7 +21,7 @@ protected function setUp(): void $this->createSchemaForModels(OwningSide::class, InverseSideIdTarget::class, InverseSide::class); } - /** @group GH-11108 */ + #[Group('GH-11108')] public function testInverseSideWithAssociativeIdOneToOneLoadedAfterDqlQuery(): void { $owner = new OwningSide(); @@ -57,12 +58,12 @@ public function testInverseSideWithAssociativeIdOneToOneLoadedAfterDqlQuery(): v $this->assertSQLEquals( 'select o0_.associativeid as associativeid_0 from one_to_one_inverse_side_assoc_id_load_inverse o0_ where o0_.associativeid = ?', - $this->getLastLoggedQuery(1)['sql'] + $this->getLastLoggedQuery(1)['sql'], ); $this->assertSQLEquals( 'select t0.id as id_1, t0.inverse as inverse_2 from one_to_one_inverse_side_assoc_id_load_owning t0 where t0.inverse = ?', - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); } } diff --git a/tests/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php b/tests/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php index 93b89acd242..4f16cf003cc 100644 --- a/tests/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php @@ -13,8 +13,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * Tests a self referential one-to-one association mapping (without inheritance). @@ -25,11 +24,9 @@ */ class OneToOneSelfReferentialAssociationTest extends OrmFunctionalTestCase { - /** @var ECommerceCustomer */ - private $customer; + private ECommerceCustomer $customer; - /** @var ECommerceCustomer */ - private $mentor; + private ECommerceCustomer $mentor; protected function setUp(): void { @@ -83,13 +80,13 @@ public function testEagerLoadsAssociation(): void $this->assertLoadingOfAssociation($customer); } - /** @group mine */ + #[Group('mine')] public function testLazyLoadsAssociation(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceCustomer::class); - $metadata->associationMappings['mentor']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceCustomer::class); + $metadata->associationMappings['mentor']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery("select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c where c.name='Luke Skywalker'"); $result = $query->getResult(); @@ -109,7 +106,7 @@ public function testMultiSelfReference(): void $this->_em->clear(); - $entity2 = $this->_em->find(get_class($entity1), $entity1->getId()); + $entity2 = $this->_em->find($entity1::class, $entity1->getId()); self::assertInstanceOf(MultiSelfReference::class, $entity2->getOther1()); self::assertInstanceOf(MultiSelfReference::class, $entity2->getOther2()); @@ -148,30 +145,21 @@ private function createFixture(): int } } -/** @Entity */ +#[Entity] class MultiSelfReference { - /** - * @var int - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - */ - private $id; - - /** - * @var MultiSelfReference|null - * @OneToOne(targetEntity="MultiSelfReference", cascade={"persist"}) - * @JoinColumn(name="other1", referencedColumnName="id") - */ - private $other1; - - /** - * @var MultiSelfReference|null - * @OneToOne(targetEntity="MultiSelfReference", cascade={"persist"}) - * @JoinColumn(name="other2", referencedColumnName="id") - */ - private $other2; + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] + private int $id; + + #[OneToOne(targetEntity: 'MultiSelfReference', cascade: ['persist'])] + #[JoinColumn(name: 'other1', referencedColumnName: 'id')] + private MultiSelfReference|null $other1 = null; + + #[OneToOne(targetEntity: 'MultiSelfReference', cascade: ['persist'])] + #[JoinColumn(name: 'other2', referencedColumnName: 'id')] + private MultiSelfReference|null $other2 = null; public function getId(): int { @@ -183,7 +171,7 @@ public function setOther1(MultiSelfReference $other1): void $this->other1 = $other1; } - public function getOther1(): ?MultiSelfReference + public function getOther1(): MultiSelfReference|null { return $this->other1; } @@ -193,7 +181,7 @@ public function setOther2(MultiSelfReference $other2): void $this->other2 = $other2; } - public function getOther2(): ?MultiSelfReference + public function getOther2(): MultiSelfReference|null { return $this->other2; } diff --git a/tests/Tests/ORM/Functional/OneToOneSingleTableInheritanceTest.php b/tests/Tests/ORM/Functional/OneToOneSingleTableInheritanceTest.php index b433be59d65..50af4af656e 100644 --- a/tests/Tests/ORM/Functional/OneToOneSingleTableInheritanceTest.php +++ b/tests/Tests/ORM/Functional/OneToOneSingleTableInheritanceTest.php @@ -8,6 +8,7 @@ use Doctrine\Tests\Models\OneToOneSingleTableInheritance\LitterBox; use Doctrine\Tests\Models\OneToOneSingleTableInheritance\Pet; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -20,16 +21,15 @@ protected function setUp(): void $this->createSchemaForModels( Pet::class, Cat::class, - LitterBox::class + LitterBox::class, ); } /** * Tests a unidirectional one-to-one association mapping from an inheritance child class - * - * @group DDC-3517 - * @group #1265 */ + #[Group('DDC-3517')] + #[Group('#1265')] public function testFindFromOneToOneOwningSideJoinedTableInheritance(): void { $cat = new Cat(); diff --git a/tests/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php b/tests/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php index f314f5a6578..b2baaaa254e 100644 --- a/tests/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php +++ b/tests/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php @@ -9,8 +9,7 @@ use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\Models\ECommerce\ECommerceShipping; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * Tests a unidirectional one-to-one association mapping (without inheritance). @@ -18,11 +17,9 @@ */ class OneToOneUnidirectionalAssociationTest extends OrmFunctionalTestCase { - /** @var ECommerceProduct */ - private $product; + private ECommerceProduct $product; - /** @var ECommerceShipping */ - private $shipping; + private ECommerceShipping $shipping; protected function setUp(): void { @@ -71,8 +68,8 @@ public function testEagerLoad(): void public function testLazyLoadsObjects(): void { $this->createFixture(); - $metadata = $this->_em->getClassMetadata(ECommerceProduct::class); - $metadata->associationMappings['shipping']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->_em->getClassMetadata(ECommerceProduct::class); + $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); $result = $query->getResult(); @@ -113,12 +110,12 @@ public function assertForeignKeyIs($value): void { $foreignKey = $this->_em->getConnection()->executeQuery( 'SELECT shipping_id FROM ecommerce_products WHERE id=?', - [$this->product->getId()] + [$this->product->getId()], )->fetchOne(); self::assertEquals($value, $foreignKey); } - /** @group DDC-762 */ + #[Group('DDC-762')] public function testNullForeignKey(): void { $product = new ECommerceProduct(); @@ -127,7 +124,7 @@ public function testNullForeignKey(): void $this->_em->persist($product); $this->_em->flush(); - $product = $this->_em->find(get_class($product), $product->getId()); + $product = $this->_em->find($product::class, $product->getId()); self::assertNull($product->getShipping()); } diff --git a/tests/Tests/ORM/Functional/OrderedJoinedTableInheritanceCollectionTest.php b/tests/Tests/ORM/Functional/OrderedJoinedTableInheritanceCollectionTest.php index b4324f81563..d93ef14b2c2 100644 --- a/tests/Tests/ORM/Functional/OrderedJoinedTableInheritanceCollectionTest.php +++ b/tests/Tests/ORM/Functional/OrderedJoinedTableInheritanceCollectionTest.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -31,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( OJTICPet::class, OJTICCat::class, - OJTICDog::class + OJTICDog::class, ); $dog = new OJTICDog(); @@ -65,7 +66,7 @@ public function testOrderdOneToManyCollection(): void $this->_em->clear(); $result = $this->_em->createQuery( - "SELECT p, c FROM Doctrine\Tests\ORM\Functional\OJTICPet p JOIN p.children c WHERE p.name = 'Poofy'" + "SELECT p, c FROM Doctrine\Tests\ORM\Functional\OJTICPet p JOIN p.children c WHERE p.name = 'Poofy'", ) ->getResult(); @@ -77,51 +78,37 @@ public function testOrderdOneToManyCollection(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "cat" = "OJTICCat", - * "dog" = "OJTICDog"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['cat' => 'OJTICCat', 'dog' => 'OJTICDog'])] abstract class OJTICPet { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $name; - /** - * @var OJTICPet - * @ManyToOne(targetEntity="OJTICPet") - */ + /** @var OJTICPet */ + #[ManyToOne(targetEntity: 'OJTICPet')] public $mother; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="OJTICPet", mappedBy="mother") - * @OrderBy({"name" = "ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'OJTICPet', mappedBy: 'mother')] + #[OrderBy(['name' => 'ASC'])] public $children; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="OJTICPet") - * @JoinTable(name="OTJIC_Pet_Friends", - * joinColumns={@JoinColumn(name="pet_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="friend_id", referencedColumnName="id")}) - * @OrderBy({"name" = "ASC"}) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'OTJIC_Pet_Friends')] + #[JoinColumn(name: 'pet_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'friend_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'OJTICPet')] + #[OrderBy(['name' => 'ASC'])] public $friends; public function getName(): string @@ -130,12 +117,12 @@ public function getName(): string } } -/** @Entity */ +#[Entity] class OJTICCat extends OJTICPet { } -/** @Entity */ +#[Entity] class OJTICDog extends OJTICPet { } diff --git a/tests/Tests/ORM/Functional/PaginationTest.php b/tests/Tests/ORM/Functional/PaginationTest.php index a856ce81827..b8ea24e7fef 100644 --- a/tests/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Tests/ORM/Functional/PaginationTest.php @@ -6,6 +6,15 @@ use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\Query; +use Doctrine\ORM\Query\AST\ComparisonExpression; +use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\Literal; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\WhereClause; +use Doctrine\ORM\Query\SqlOutputWalker; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\TreeWalkerAdapter; use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Tests\DbalTypes\CustomIdObject; use Doctrine\Tests\DbalTypes\CustomIdObjectType; @@ -20,6 +29,8 @@ use Doctrine\Tests\Models\Pagination\Logo; use Doctrine\Tests\Models\Pagination\User1; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use ReflectionMethod; use RuntimeException; @@ -27,7 +38,7 @@ use function iterator_to_array; use function sprintf; -/** @group DDC-1613 */ +#[Group('DDC-1613')] class PaginationTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -48,7 +59,7 @@ protected function setUp(): void $this->populate(); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testCountSimpleWithoutJoin($useOutputWalkers): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'; @@ -59,7 +70,7 @@ public function testCountSimpleWithoutJoin($useOutputWalkers): void self::assertCount(9, $paginator); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testCountWithFetchJoin($useOutputWalkers): void { $dql = 'SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g'; @@ -94,7 +105,7 @@ public function testCountComplexWithoutOutputWalker(): void self::assertCount(3, $paginator); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testCountWithComplexScalarOrderBy($useOutputWalkers): void { $dql = 'SELECT l FROM Doctrine\Tests\Models\Pagination\Logo l ORDER BY l.imageWidth * l.imageHeight DESC'; @@ -105,7 +116,7 @@ public function testCountWithComplexScalarOrderBy($useOutputWalkers): void self::assertCount(9, $paginator); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateSimpleWithoutJoin($useOutputWalkers, $fetchJoinCollection): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'; @@ -217,7 +228,7 @@ private function iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetc self::assertEquals($checkField . '5', $result[0]->$checkField); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateSimpleWithoutJoinWithOrder($useOutputWalkers, $fetchJoinCollection): void { // Ascending @@ -226,7 +237,7 @@ public function testIterateSimpleWithoutJoinWithOrder($useOutputWalkers, $fetchJ $this->iterateWithOrderDesc($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateSimpleWithoutJoinWithOrderAndLimit($useOutputWalkers, $fetchJoinCollection): void { // Ascending @@ -235,7 +246,7 @@ public function testIterateSimpleWithoutJoinWithOrderAndLimit($useOutputWalkers, $this->iterateWithOrderDescWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateSimpleWithoutJoinWithOrderAndLimitAndOffset($useOutputWalkers, $fetchJoinCollection): void { // Ascending @@ -244,7 +255,7 @@ public function testIterateSimpleWithoutJoinWithOrderAndLimitAndOffset($useOutpu $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrder($fetchJoinCollection): void { // Ascending @@ -253,7 +264,7 @@ public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrder($fe $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'image'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndLimit($fetchJoinCollection): void { // Ascending @@ -262,7 +273,7 @@ public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndL $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'image'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndLimitAndOffset($fetchJoinCollection): void { // Ascending @@ -271,7 +282,7 @@ public function testIterateSimpleWithOutputWalkerWithoutJoinWithComplexOrderAndL $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'image'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoin($useOutputWalkers): void { $dql = 'SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g'; @@ -282,7 +293,7 @@ public function testIterateWithFetchJoin($useOutputWalkers): void self::assertCount(9, $paginator->getIterator()); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrder($useOutputWalkers): void { $dql = 'SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g ORDER BY u.username'; @@ -290,7 +301,7 @@ public function testIterateWithFetchJoinWithOrder($useOutputWalkers): void $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrderAndLimit($useOutputWalkers): void { $dql = 'SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g ORDER BY u.username'; @@ -298,7 +309,7 @@ public function testIterateWithFetchJoinWithOrderAndLimit($useOutputWalkers): vo $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrderAndLimitAndOffset($useOutputWalkers): void { $dql = 'SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g ORDER BY u.username'; @@ -306,7 +317,7 @@ public function testIterateWithFetchJoinWithOrderAndLimitAndOffset($useOutputWal $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateWithRegularJoinWithOrderByColumnFromJoined($useOutputWalkers, $fetchJoinCollection): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e ORDER BY e.email'; @@ -314,7 +325,7 @@ public function testIterateWithRegularJoinWithOrderByColumnFromJoined($useOutput $this->iterateWithOrderDesc($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimit($useOutputWalkers, $fetchJoinCollection): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e ORDER BY e.email'; @@ -322,7 +333,7 @@ public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimit($ $this->iterateWithOrderDescWithLimit($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e ORDER BY e.email'; @@ -330,7 +341,7 @@ public function testIterateWithRegularJoinWithOrderByColumnFromJoinedWithLimitAn $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, $fetchJoinCollection, $dql, 'username'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoined($fetchJoinCollection): void { // long function name is loooooooooooong @@ -340,7 +351,7 @@ public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByRef $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoinedWithLimit($fetchJoinCollection): void { // long function name is loooooooooooong @@ -350,7 +361,7 @@ public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByRef $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByReferencingJoinedWithLimitAndOffset($fetchJoinCollection): void { // long function name is loooooooooooong @@ -360,7 +371,7 @@ public function testIterateWithOutputWalkersWithRegularJoinWithComplexOrderByRef $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrderByColumnFromJoined($useOutputWalkers): void { $dql = 'SELECT u,g,e FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email'; @@ -368,7 +379,7 @@ public function testIterateWithFetchJoinWithOrderByColumnFromJoined($useOutputWa $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimit($useOutputWalkers): void { $dql = 'SELECT u,g,e FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email'; @@ -376,7 +387,7 @@ public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimit($us $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimitAndOffset($useOutputWalkers): void { $dql = 'SELECT u,g,e FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g JOIN u.email e ORDER BY e.email'; @@ -384,7 +395,7 @@ public function testIterateWithFetchJoinWithOrderByColumnFromJoinedWithLimitAndO $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'username'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoined($fetchJoinCollection): void { $dql = 'SELECT c,l FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight'; @@ -392,7 +403,7 @@ public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByRefer $this->iterateWithOrderDesc(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimit($fetchJoinCollection): void { $dql = 'SELECT c,l FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight'; @@ -400,7 +411,7 @@ public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByRefer $this->iterateWithOrderDescWithLimit(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimitAndOffset($fetchJoinCollection): void { $dql = 'SELECT c,l FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.logo l ORDER BY l.imageWidth * l.imageHeight'; @@ -408,7 +419,7 @@ public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByRefer $this->iterateWithOrderDescWithLimitAndOffset(true, $fetchJoinCollection, $dql, 'name'); } - /** @dataProvider fetchJoinCollection */ + #[DataProvider('fetchJoinCollection')] public function testIterateWithOutputWalkersWithFetchJoinWithComplexOrderByReferencingJoinedWithLimitAndOffsetWithInheritanceType($fetchJoinCollection): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\Pagination\User u ORDER BY u.id'; @@ -435,7 +446,7 @@ public function testJoinedClassTableInheritance(): void self::assertCount(1, $paginator->getIterator()); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromBoth($useOutputWalkers): void { $dql = 'SELECT c, d FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.departments d ORDER BY c.name'; @@ -466,7 +477,7 @@ public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromBothWithLi $this->iterateWithOrderDescWithLimit(false, true, $dqlDesc, 'name'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRoot($useOutputWalkers): void { $dql = 'SELECT c, d FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.departments d ORDER BY c.name'; @@ -474,7 +485,7 @@ public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRoot($useO $this->iterateWithOrderDesc($useOutputWalkers, true, $dql, 'name'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLimit($useOutputWalkers): void { $dql = 'SELECT c, d FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.departments d ORDER BY c.name'; @@ -482,7 +493,7 @@ public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLi $this->iterateWithOrderDescWithLimit($useOutputWalkers, true, $dql, 'name'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLimitAndOffset($useOutputWalkers): void { $dql = 'SELECT c, d FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.departments d ORDER BY c.name'; @@ -490,7 +501,7 @@ public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromRootWithLi $this->iterateWithOrderDescWithLimitAndOffset($useOutputWalkers, true, $dql, 'name'); } - /** @dataProvider useOutputWalkers */ + #[DataProvider('useOutputWalkers')] public function testIterateWithFetchJoinOneToManyWithOrderByColumnFromJoined($useOutputWalkers): void { $dql = 'SELECT c, d FROM Doctrine\Tests\Models\Pagination\Company c JOIN c.departments d ORDER BY d.name'; @@ -549,7 +560,7 @@ public function testDetectOutputWalker(): void // If the Paginator detects the custom output walker it should fall back to using the // Tree walkers for pagination, which leads to an exception. If the query works, the output walkers were used - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, Query\SqlWalker::class); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlWalker::class); $paginator = new Paginator($query); $this->expectException(RuntimeException::class); @@ -595,7 +606,7 @@ public function testQueryWalkerIsKept(): void self::assertEquals(1, $paginator->count()); } - /** @group GH-7890 */ + #[Group('GH-7890')] public function testCustomIdTypeWithoutOutputWalker(): void { $this->_em->persist(new CustomIdObjectTypeParent(new CustomIdObject('foo'))); @@ -620,22 +631,20 @@ public function testCountQueryStripsParametersInSelect(): void $query = $this->_em->createQuery( 'SELECT u, (CASE WHEN u.id < :vipMaxId THEN 1 ELSE 0 END) AS hidden promotedFirst FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u - WHERE u.id < :id or 1=1' + WHERE u.id < :id or 1=1', ); $query->setParameter('vipMaxId', 10); $query->setParameter('id', 100); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); $paginator = new Paginator($query); $getCountQuery = new ReflectionMethod($paginator, 'getCountQuery'); - $getCountQuery->setAccessible(true); - self::assertCount(2, $getCountQuery->invoke($paginator)->getParameters()); self::assertCount(9, $paginator); - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, Query\SqlWalker::class); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlOutputWalker::class); $paginator = new Paginator($query); @@ -645,7 +654,7 @@ public function testCountQueryStripsParametersInSelect(): void self::assertCount(9, $paginator); } - /** @dataProvider useOutputWalkersAndFetchJoinCollection */ + #[DataProvider('useOutputWalkersAndFetchJoinCollection')] public function testPaginationWithSubSelectOrderByExpression($useOutputWalker, $fetchJoinCollection): void { $query = $this->_em->createQuery( @@ -658,7 +667,7 @@ public function testPaginationWithSubSelectOrderByExpression($useOutputWalker, $ ) AS HIDDEN max_version FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY max_version DESC -SQL +SQL, ); $paginator = new Paginator($query, $fetchJoinCollection); @@ -788,21 +797,21 @@ public static function useOutputWalkersAndFetchJoinCollection(): array } } -class CustomPaginationTestTreeWalker extends Query\TreeWalkerAdapter +class CustomPaginationTestTreeWalker extends TreeWalkerAdapter { - public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): void + public function walkSelectStatement(SelectStatement $selectStatement): void { - $condition = new Query\AST\ConditionalPrimary(); + $condition = new ConditionalPrimary(); - $path = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, 'u', 'name'); - $path->type = Query\AST\PathExpression::TYPE_STATE_FIELD; + $path = new PathExpression(PathExpression::TYPE_STATE_FIELD, 'u', 'name'); + $path->type = PathExpression::TYPE_STATE_FIELD; - $condition->simpleConditionalExpression = new Query\AST\ComparisonExpression( + $condition->simpleConditionalExpression = new ComparisonExpression( $path, '=', - new Query\AST\Literal(Query\AST\Literal::STRING, 'Name1') + new Literal(Literal::STRING, 'Name1'), ); - $selectStatement->whereClause = new Query\AST\WhereClause($condition); + $selectStatement->whereClause = new WhereClause($condition); } } diff --git a/tests/Tests/ORM/Functional/ParserResultSerializationTest.php b/tests/Tests/ORM/Functional/ParserResultSerializationTest.php index 7b2ae59119a..af247a35b87 100644 --- a/tests/Tests/ORM/Functional/ParserResultSerializationTest.php +++ b/tests/Tests/ORM/Functional/ParserResultSerializationTest.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Tests\OrmFunctionalTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; use ReflectionMethod; -use ReflectionProperty; use Symfony\Component\VarExporter\Instantiator; use Symfony\Component\VarExporter\VarExporter; @@ -32,11 +33,9 @@ protected function setUp(): void parent::setUp(); } - /** - * @param Closure(ParserResult): ParserResult $toSerializedAndBack - * - * @dataProvider provideToSerializedAndBack - */ + /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */ + #[DataProvider('provideToSerializedAndBack')] + #[WithoutErrorHandler] public function testSerializeParserResultForQueryWithSqlWalker(Closure $toSerializedAndBack): void { $query = $this->_em @@ -54,11 +53,8 @@ public function testSerializeParserResultForQueryWithSqlWalker(Closure $toSerial $this->assertNotNull($unserialized->prepareSqlExecutor($query)); } - /** - * @param Closure(ParserResult): ParserResult $toSerializedAndBack - * - * @dataProvider provideToSerializedAndBack - */ + /** @param Closure(ParserResult): ParserResult $toSerializedAndBack */ + #[DataProvider('provideToSerializedAndBack')] public function testSerializeParserResultForQueryWithSqlOutputWalker(Closure $toSerializedAndBack): void { $query = $this->_em @@ -74,7 +70,7 @@ public function testSerializeParserResultForQueryWithSqlOutputWalker(Closure $to } /** @return Generator */ - public function provideToSerializedAndBack(): Generator + public static function provideToSerializedAndBack(): Generator { yield 'native serialization function' => [ static function (ParserResult $parserResult): ParserResult { @@ -84,7 +80,7 @@ static function (ParserResult $parserResult): ParserResult { $instantiatorMethod = new ReflectionMethod(Instantiator::class, 'instantiate'); if ($instantiatorMethod->getReturnType() === null) { - $this->markTestSkipped('symfony/var-exporter 5.4+ is required.'); + self::markTestSkipped('symfony/var-exporter 5.4+ is required.'); } yield 'symfony/var-exporter' => [ @@ -94,36 +90,7 @@ static function (ParserResult $parserResult): ParserResult { ]; } - public function testItSerializesParserResultWithAForwardCompatibleFormat(): void - { - $query = $this->_em - ->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u.name = :name'); - - // Use the (legacy) SqlWalker which directly puts an SqlExecutor instance into the parser result - $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, Query\SqlWalker::class); - - $parserResult = self::parseQuery($query); - $serialized = serialize($parserResult); - $this->assertStringNotContainsString( - '_sqlStatements', - $serialized, - 'ParserResult should not contain any reference to _sqlStatements, which is a legacy property.' - ); - $unserialized = unserialize($serialized); - - $r = new ReflectionProperty($unserialized->getSqlExecutor(), '_sqlStatements'); - $r->setAccessible(true); - - $this->assertSame( - $r->getValue($unserialized->getSqlExecutor()), - $unserialized->getSqlExecutor()->getSqlStatements(), - 'The legacy property should be populated with the same value as the new one.' - ); - } - - /** - * @dataProvider provideSerializedSingleSelectResults - */ + #[DataProvider('provideSerializedSingleSelectResults')] public function testUnserializeSingleSelectResult(string $serialized): void { $unserialized = unserialize($serialized); @@ -138,8 +105,6 @@ public function testUnserializeSingleSelectResult(string $serialized): void /** @return Generator */ public static function provideSerializedSingleSelectResults(): Generator { - yield '2.14.3' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_14_3.txt'), "\n")]; - yield '2.15.0' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_15_0.txt'), "\n")]; yield '2.17.0' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_17_0.txt'), "\n")]; } diff --git a/tests/Tests/ORM/Functional/ParserResults/single_select_2_14_3.txt b/tests/Tests/ORM/Functional/ParserResults/single_select_2_14_3.txt deleted file mode 100644 index db83fe2de48..00000000000 Binary files a/tests/Tests/ORM/Functional/ParserResults/single_select_2_14_3.txt and /dev/null differ diff --git a/tests/Tests/ORM/Functional/ParserResults/single_select_2_15_0.txt b/tests/Tests/ORM/Functional/ParserResults/single_select_2_15_0.txt deleted file mode 100644 index 2c306211b86..00000000000 Binary files a/tests/Tests/ORM/Functional/ParserResults/single_select_2_15_0.txt and /dev/null differ diff --git a/tests/Tests/ORM/Functional/PersistentCollectionCriteriaTest.php b/tests/Tests/ORM/Functional/PersistentCollectionCriteriaTest.php index 44aef3f7c6e..fb4d03bde4b 100644 --- a/tests/Tests/ORM/Functional/PersistentCollectionCriteriaTest.php +++ b/tests/Tests/ORM/Functional/PersistentCollectionCriteriaTest.php @@ -89,7 +89,7 @@ public function testCanCountWithoutLoadingPersistentCollection(): void // Make sure it works with constraints $tweets = $user->tweets->matching(new Criteria( - Criteria::expr()->eq('content', 'Foo') + Criteria::expr()->eq('content', 'Foo'), )); self::assertInstanceOf(LazyCriteriaCollection::class, $tweets); diff --git a/tests/Tests/ORM/Functional/PersistentCollectionTest.php b/tests/Tests/ORM/Functional/PersistentCollectionTest.php index 075017ee07d..5191377524b 100644 --- a/tests/Tests/ORM/Functional/PersistentCollectionTest.php +++ b/tests/Tests/ORM/Functional/PersistentCollectionTest.php @@ -9,6 +9,7 @@ use Doctrine\Tests\Models\PersistentObject\PersistentCollectionContent; use Doctrine\Tests\Models\PersistentObject\PersistentCollectionHolder; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function class_exists; @@ -24,7 +25,7 @@ protected function setUp(): void $this->createSchemaForModels( PersistentCollectionHolder::class, - PersistentCollectionContent::class + PersistentCollectionContent::class, ); PersistentObject::setObjectManager($this->_em); @@ -78,10 +79,8 @@ public function testExtraLazyIsEmptyDoesNotInitializeCollection(): void self::assertFalse($collection->isInitialized()); } - /** - * @group #1206 - * @group DDC-3430 - */ + #[Group('#1206')] + #[Group('DDC-3430')] public function testMatchingDoesNotModifyTheGivenCriteria(): void { $collectionHolder = new PersistentCollectionHolder(); diff --git a/tests/Tests/ORM/Functional/PersistentObjectTest.php b/tests/Tests/ORM/Functional/PersistentObjectTest.php deleted file mode 100644 index 33434839cec..00000000000 --- a/tests/Tests/ORM/Functional/PersistentObjectTest.php +++ /dev/null @@ -1,89 +0,0 @@ -createSchemaForModels(PersistentEntity::class); - - PersistentObject::setObjectManager($this->_em); - } - - public function testPersist(): void - { - $entity = new PersistentEntity(); - $entity->setName('test'); - - $this->_em->persist($entity); - $this->_em->flush(); - - $this->addToAssertionCount(1); - } - - public function testFind(): void - { - $entity = new PersistentEntity(); - $entity->setName('test'); - - $this->_em->persist($entity); - $this->_em->flush(); - $this->_em->clear(); - - $entity = $this->_em->find(PersistentEntity::class, $entity->getId()); - - self::assertEquals('test', $entity->getName()); - $entity->setName('foobar'); - - $this->_em->flush(); - } - - public function testGetReference(): void - { - $entity = new PersistentEntity(); - $entity->setName('test'); - - $this->_em->persist($entity); - $this->_em->flush(); - $this->_em->clear(); - - $entity = $this->_em->getReference(PersistentEntity::class, $entity->getId()); - - self::assertEquals('test', $entity->getName()); - } - - public function testSetAssociation(): void - { - $entity = new PersistentEntity(); - $entity->setName('test'); - $entity->setParent($entity); - - $this->_em->persist($entity); - $this->_em->flush(); - $this->_em->clear(); - - $entity = $this->_em->getReference(PersistentEntity::class, $entity->getId()); - self::assertSame($entity, $entity->getParent()); - } -} diff --git a/tests/Tests/ORM/Functional/PostFlushEventTest.php b/tests/Tests/ORM/Functional/PostFlushEventTest.php index 274a36c2c14..86e36c3e15b 100644 --- a/tests/Tests/ORM/Functional/PostFlushEventTest.php +++ b/tests/Tests/ORM/Functional/PostFlushEventTest.php @@ -15,8 +15,7 @@ */ class PostFlushEventTest extends OrmFunctionalTestCase { - /** @var PostFlushListener */ - private $listener; + private PostFlushListener $listener; protected function setUp(): void { @@ -45,7 +44,7 @@ public function testListenerShouldNotBeNotifiedWhenFlushThrowsException(): void try { $this->_em->flush(); - } catch (Exception $ex) { + } catch (Exception) { $exceptionRaised = true; } diff --git a/tests/Tests/ORM/Functional/PostLoadEventTest.php b/tests/Tests/ORM/Functional/PostLoadEventTest.php index 8d6ee3b9c15..01d634b0380 100644 --- a/tests/Tests/ORM/Functional/PostLoadEventTest.php +++ b/tests/Tests/ORM/Functional/PostLoadEventTest.php @@ -12,12 +12,12 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use RuntimeException; class PostLoadEventTest extends OrmFunctionalTestCase { - /** @var int */ - private $userId; + private int|null $userId = null; protected function setUp(): void { @@ -190,7 +190,7 @@ public function testLoadedProxyAssociationToManyShouldTriggerEvent(): void $phonenumbersCol->first(); } - /** @group DDC-3005 */ + #[Group('DDC-3005')] public function testAssociationsArePopulatedWhenEventIsFired(): void { $checkerListener = new PostLoadListenerCheckAssociationsArePopulated(); @@ -205,7 +205,7 @@ public function testAssociationsArePopulatedWhenEventIsFired(): void self::assertTrue($checkerListener->populated, 'Association of email is not populated in postLoad event'); } - /** @group DDC-3005 */ + #[Group('DDC-3005')] public function testEventRaisedCorrectTimesWhenOtherEntityLoadedInEventHandler(): void { $eventManager = $this->_em->getEventManager(); @@ -288,7 +288,7 @@ public function postLoad(PostLoadEventArgs $event): void class PostLoadListenerLoadEntityInEventHandler { /** @var array */ - private $firedByClasses = []; + private array $firedByClasses = []; public function postLoad(PostLoadEventArgs $event): void { diff --git a/tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php b/tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php index 1cd05c3fc5a..0cc8776ba50 100644 --- a/tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php +++ b/tests/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php @@ -41,7 +41,7 @@ protected function setUp(): void CmsArticle::class, CmsAddress::class, CmsEmail::class, - CmsGroup::class + CmsGroup::class, ); $this->user = new CmsUser(); diff --git a/tests/Tests/ORM/Functional/QueryBuilderParenthesisTest.php b/tests/Tests/ORM/Functional/QueryBuilderParenthesisTest.php index d90c49787b2..2cebe1abc52 100644 --- a/tests/Tests/ORM/Functional/QueryBuilderParenthesisTest.php +++ b/tests/Tests/ORM/Functional/QueryBuilderParenthesisTest.php @@ -37,7 +37,7 @@ public function testParenthesisOnSingleLine(): void self::assertSame( 'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1 OR o.property2 = :value2) AND (o.property1 = :value1 or o.property2 = :value2)', - $dql + $dql, ); } @@ -49,7 +49,7 @@ public function testParenthesisOnMultiLine(): void $queryBuilder->andWhere( 'o.property1 = :value1 -OR o.property2 = :value2' +OR o.property2 = :value2', ); $queryBuilder->setParameter('value1', 'x'); $queryBuilder->setParameter('value2', 'x'); @@ -63,38 +63,30 @@ public function testParenthesisOnMultiLine(): void self::assertSame( 'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1 OR o.property2 = :value2)', - $dql + $dql, ); } } -/** @Entity */ +#[Entity] class QueryBuilderParenthesisEntity { - /** - * @var int|null - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int|null */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var string|null - * @Column() - */ + /** @var string|null */ + #[Column] public $property1; - /** - * @var string|null - * @Column() - */ + /** @var string|null */ + #[Column] public $property2; - /** - * @var string|null - * @Column() - */ + /** @var string|null */ + #[Column] public $property3; } diff --git a/tests/Tests/ORM/Functional/QueryCacheTest.php b/tests/Tests/ORM/Functional/QueryCacheTest.php index 3894ecef697..891a3ba18c7 100644 --- a/tests/Tests/ORM/Functional/QueryCacheTest.php +++ b/tests/Tests/ORM/Functional/QueryCacheTest.php @@ -8,6 +8,7 @@ use Doctrine\ORM\Query\Exec\AbstractSqlExecutor; use Doctrine\ORM\Query\ParserResult; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -42,7 +43,7 @@ public function testQueryCacheDependsOnHints(): array return [$query, $cache]; } - /** @depends testQueryCacheDependsOnHints */ + #[Depends('testQueryCacheDependsOnHints')] public function testQueryCacheDoesNotDependOnFirstResultForDefaultOutputWalker(array $previous): void { [$query, $cache] = $previous; @@ -58,7 +59,7 @@ public function testQueryCacheDoesNotDependOnFirstResultForDefaultOutputWalker(a self::assertCount($cacheCount, $cache->getValues()); } - /** @depends testQueryCacheDependsOnHints */ + #[Depends('testQueryCacheDependsOnHints')] public function testQueryCacheDoesNotDependOnMaxResultsForDefaultOutputWalker(array $previous): void { [$query, $cache] = $previous; @@ -73,7 +74,7 @@ public function testQueryCacheDoesNotDependOnMaxResultsForDefaultOutputWalker(ar self::assertCount($cacheCount, $cache->getValues()); } - /** @depends testQueryCacheDependsOnHints */ + #[Depends('testQueryCacheDependsOnHints')] public function testQueryCacheDependsOnHydrationMode(array $previous): void { [$query, $cache] = $previous; diff --git a/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php b/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php index e0feac0df3d..24d1853335f 100644 --- a/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php +++ b/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php @@ -5,10 +5,13 @@ namespace Doctrine\Tests\ORM\Functional; use DateTimeImmutable; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\ORM\AbstractQuery; +use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\Models\Company\CompanyManager; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function round; use function sprintf; @@ -32,7 +35,7 @@ public function testAggregateSum(): void $salarySum = $this->_em->createQuery('SELECT SUM(m.salary) AS salary FROM Doctrine\Tests\Models\Company\CompanyManager m') ->getSingleResult(); - self::assertEquals(1500000, $salarySum['salary']); + self::assertEquals(1_500_000, $salarySum['salary']); } public function testAggregateAvg(): void @@ -246,7 +249,7 @@ public function testOperatorMultiply(): void self::assertEquals(200000, $result[0]['op']); self::assertEquals(400000, $result[1]['op']); self::assertEquals(800000, $result[2]['op']); - self::assertEquals(1600000, $result[3]['op']); + self::assertEquals(1_600_000, $result[3]['op']); } public function testOperatorDiv(): void @@ -258,7 +261,7 @@ public function testOperatorDiv(): void self::assertEquals(200000, $result[0]['op']); self::assertEquals(400000, $result[1]['op']); self::assertEquals(800000, $result[2]['op']); - self::assertEquals(1600000, $result[3]['op']); + self::assertEquals(1_600_000, $result[3]['op']); } public function testConcatFunction(): void @@ -273,7 +276,7 @@ public function testConcatFunction(): void self::assertEquals('Benjamin E.HR', $arg[3]['namedep']); } - /** @group DDC-1014 */ + #[Group('DDC-1014')] public function testDateDiff(): void { $query = $this->_em->createQuery("SELECT DATE_DIFF(CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(), 10, 'day')) AS diff FROM Doctrine\Tests\Models\Company\CompanyManager m"); @@ -287,18 +290,16 @@ public function testDateDiff(): void self::assertEqualsWithDelta(10, $arg[0]['diff'], 1, 'Should be roughly 10 (or 9)'); } - /** - * @group DDC-1014 - * @group DDC-2938 - * @dataProvider dateAddSubProvider - */ + #[DataProvider('dateAddSubProvider')] + #[Group('DDC-1014')] + #[Group('DDC-2938')] public function testDateAdd(string $unit, int $amount, int $delta = 0): void { $query = sprintf( 'SELECT CURRENT_TIMESTAMP() as now, DATE_ADD(CURRENT_TIMESTAMP(), %d, \'%s\') AS add FROM %s m', $amount, $unit, - CompanyManager::class + CompanyManager::class, ); $result = $this->_em->createQuery($query) @@ -313,7 +314,7 @@ public function testDateAdd(string $unit, int $amount, int $delta = 0): void if ( $unit === 'month' && $inOneUnit->format('m') === $now->modify('+2 month')->format('m') - && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform + && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform ) { $inOneUnit = new DateTimeImmutable('last day of next month'); } @@ -321,22 +322,20 @@ public function testDateAdd(string $unit, int $amount, int $delta = 0): void self::assertEqualsWithDelta( $inOneUnit, new DateTimeImmutable($result['add']), - $delta + $delta, ); } - /** - * @group DDC-1014 - * @group DDC-2938 - * @dataProvider dateAddSubProvider - */ + #[DataProvider('dateAddSubProvider')] + #[Group('DDC-1014')] + #[Group('DDC-2938')] public function testDateSub(string $unit, int $amount, int $delta = 0): void { $query = sprintf( 'SELECT CURRENT_TIMESTAMP() as now, DATE_SUB(CURRENT_TIMESTAMP(), %d, \'%s\') AS sub FROM %s m', $amount, $unit, - CompanyManager::class + CompanyManager::class, ); $result = $this->_em->createQuery($query) @@ -351,7 +350,7 @@ public function testDateSub(string $unit, int $amount, int $delta = 0): void if ( $unit === 'month' && $oneUnitAgo->format('m') === $now->format('m') - && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform + && ! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform ) { $oneUnitAgo = new DateTimeImmutable('last day of previous month'); } @@ -359,7 +358,7 @@ public function testDateSub(string $unit, int $amount, int $delta = 0): void self::assertEqualsWithDelta( $oneUnitAgo, new DateTimeImmutable($result['sub']), - $delta + $delta, ); } @@ -378,7 +377,7 @@ public static function dateAddSubProvider(): array ]; } - /** @group DDC-1213 */ + #[Group('DDC-1213')] public function testBitOrComparison(): void { $dql = 'SELECT m, ' . @@ -400,7 +399,7 @@ public function testBitOrComparison(): void self::assertEquals($result[3][0]['salary'] / 100000 | 2, $result[3]['salary_bit_or']); } - /** @group DDC-1213 */ + #[Group('DDC-1213')] public function testBitAndComparison(): void { $dql = 'SELECT m, ' . @@ -489,4 +488,34 @@ protected function generateFixture(): void $this->_em->flush(); $this->_em->clear(); } + + #[Group('GH-11240')] + public function testDateAddWithColumnInterval(): void + { + $query = sprintf( + 'SELECT DATE_ADD(CURRENT_TIMESTAMP(), m.salary, \'day\') AS add FROM %s m', + CompanyEmployee::class, + ); + + $result = $this->_em->createQuery($query) + ->setMaxResults(1) + ->getSingleResult(AbstractQuery::HYDRATE_ARRAY); + + self::assertArrayHasKey('add', $result); + } + + #[Group('GH-11240')] + public function testDateSubWithColumnInterval(): void + { + $query = sprintf( + 'SELECT DATE_SUB(CURRENT_TIMESTAMP(), m.salary, \'day\') AS add FROM %s m', + CompanyEmployee::class, + ); + + $result = $this->_em->createQuery($query) + ->setMaxResults(1) + ->getSingleResult(AbstractQuery::HYDRATE_ARRAY); + + self::assertArrayHasKey('add', $result); + } } diff --git a/tests/Tests/ORM/Functional/QueryParameterTest.php b/tests/Tests/ORM/Functional/QueryParameterTest.php index db5dffd9df1..e7599cea5ea 100644 --- a/tests/Tests/ORM/Functional/QueryParameterTest.php +++ b/tests/Tests/ORM/Functional/QueryParameterTest.php @@ -5,19 +5,16 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Types\Types; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function class_exists; - -/** @group GH-11278 */ +#[Group('GH-11278')] final class QueryParameterTest extends OrmFunctionalTestCase { - /** @var int */ - private $userId; + private int $userId; protected function setUp(): void { @@ -104,7 +101,7 @@ public function testArrayParameterTypeInBuilder(): void ->select('u.name') ->where('u.username IN (:usernames)') ->orderBy('u.username') - ->setParameter('usernames', ['john', 'jane'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) + ->setParameter('usernames', ['john', 'jane'], ArrayParameterType::STRING) ->getQuery() ->getArrayResult(); @@ -119,7 +116,7 @@ public function testArrayParameterTypeInQuery(): void ->where('u.username IN (:usernames)') ->orderBy('u.username') ->getQuery() - ->setParameter('usernames', ['john', 'jane'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) + ->setParameter('usernames', ['john', 'jane'], ArrayParameterType::STRING) ->getArrayResult(); self::assertSame([['name' => 'Jane Doe'], ['name' => 'John Doe']], $result); diff --git a/tests/Tests/ORM/Functional/QueryTest.php b/tests/Tests/ORM/Functional/QueryTest.php index 791c5c31719..10e26df023f 100644 --- a/tests/Tests/ORM/Functional/QueryTest.php +++ b/tests/Tests/ORM/Functional/QueryTest.php @@ -5,10 +5,9 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Persistence\PersistentObject; -use Doctrine\DBAL\Logging\Middleware as LoggingMiddleware; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\QueryException; @@ -20,9 +19,8 @@ use Doctrine\Tests\Models\Enums\AccessLevel; use Doctrine\Tests\Models\Enums\UserStatus; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function array_values; -use function class_exists; use function count; use function iterator_to_array; @@ -111,6 +109,52 @@ public function testJoinQueries(): void self::assertEquals('Symfony 2', $users[0]->articles[1]->topic); } + public function testJoinPartialArrayHydration(): void + { + $user = new CmsUser(); + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $article1 = new CmsArticle(); + $article1->topic = 'Doctrine 2'; + $article1->text = 'This is an introduction to Doctrine 2.'; + $user->addArticle($article1); + + $article2 = new CmsArticle(); + $article2->topic = 'Symfony 2'; + $article2->text = 'This is an introduction to Symfony 2.'; + $user->addArticle($article2); + + $this->_em->persist($user); + $this->_em->persist($article1); + $this->_em->persist($article2); + + $this->_em->flush(); + $this->_em->clear(); + + $query = $this->_em->createQuery('select partial u.{id, username}, partial a.{id, topic} from ' . CmsUser::class . ' u join u.articles a ORDER BY a.topic'); + $users = $query->getArrayResult(); + + $this->assertEquals([ + [ + 'id' => $user->id, + 'username' => 'gblanco', + 'articles' => + [ + [ + 'id' => $article1->id, + 'topic' => 'Doctrine 2', + ], + [ + 'id' => $article2->id, + 'topic' => 'Symfony 2', + ], + ], + ], + ], $users); + } + public function testUsingZeroBasedQueryParameterShouldWork(): void { $user = new CmsUser(); @@ -168,9 +212,6 @@ public function testInvalidInputParameterThrowsException(): void ->getSingleResult(); } - /** - * @requires PHP 8.1 - */ public function testUseStringEnumCaseAsParameter(): void { $user = new CmsUser(); @@ -198,9 +239,6 @@ public function testUseStringEnumCaseAsParameter(): void self::assertSame('jane', $result[0]->username); } - /** - * @requires PHP 8.1 - */ public function testUseIntegerEnumCaseAsParameter(): void { $user = new CmsUser(); @@ -238,18 +276,10 @@ public function testSetParameters(): void ->setParameters($parameters) ->getResult(); - if (! class_exists(LoggingMiddleware::class)) { - // DBAL 2 logs queries before resolving parameter positions - self::assertSame( - ['jwage', 'active'], - $this->getLastLoggedQuery()['params'] - ); - } else { - self::assertSame( - [1 => 'jwage', 2 => 'active'], - $this->getLastLoggedQuery()['params'] - ); - } + self::assertSame( + [1 => 'jwage', 2 => 'active'], + $this->getLastLoggedQuery()['params'], + ); } public function testSetParametersBackwardsCompatible(): void @@ -260,13 +290,10 @@ public function testSetParametersBackwardsCompatible(): void ->setParameters($parameters) ->getResult(); - self::assertSame( - class_exists(LoggingMiddleware::class) ? $parameters : array_values($parameters), - $this->getLastLoggedQuery()['params'] - ); + self::assertSame($parameters, $this->getLastLoggedQuery()['params']); } - /** @group DDC-1070 */ + #[Group('DDC-1070')] public function testIterateResultAsArrayAndParams(): void { $article1 = new CmsArticle(); @@ -284,8 +311,7 @@ public function testIterateResultAsArrayAndParams(): void $this->_em->clear(); $articleId = $article1->id; - $query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a WHERE a.topic = ?1'); - $articles = $query->iterate(new ArrayCollection([new Parameter(1, 'Doctrine 2')]), Query::HYDRATE_ARRAY); + $query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a WHERE a.topic = ?1'); $expectedArticle = [ 'id' => $articleId, @@ -294,14 +320,9 @@ public function testIterateResultAsArrayAndParams(): void 'version' => 1, ]; - $articles = iterator_to_array($articles, false); - - self::assertCount(1, $articles); - self::assertEquals([[$expectedArticle]], $articles); - $articles = $query->toIterable( new ArrayCollection([new Parameter(1, 'Doctrine 2')]), - Query::HYDRATE_ARRAY + Query::HYDRATE_ARRAY, ); $articles = IterableTester::iterableToArray($articles); @@ -327,25 +348,6 @@ public function testIterateResultIterativelyBuildUpUnitOfWork(): void $this->_em->clear(); $query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a'); - $articles = $query->iterate(); - - $iteratedCount = 0; - $topics = []; - - foreach ($articles as $row) { - $article = $row[0]; - $topics[] = $article->topic; - - $identityMap = $this->_em->getUnitOfWork()->getIdentityMap(); - $identityMapCount = count($identityMap[CmsArticle::class]); - self::assertGreaterThan($iteratedCount, $identityMapCount); - - $iteratedCount++; - } - - self::assertSame(['Doctrine 2', 'Symfony 2'], $topics); - self::assertSame(2, $iteratedCount); - $articles = $query->toIterable(); $iteratedCount = 0; @@ -428,21 +430,6 @@ public function testIterateResultClearEveryCycle(): void $query = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a'); - $articles = $query->iterate(); - $iteratedCount = 0; - $topics = []; - foreach ($articles as $row) { - $article = $row[0]; - $topics[] = $article->topic; - - $this->_em->clear(); - - $iteratedCount++; - } - - self::assertSame(['Doctrine 2', 'Symfony 2'], $topics); - self::assertSame(2, $iteratedCount); - $articles = $query->toIterable(); $iteratedCount = 0; $topics = []; @@ -460,13 +447,6 @@ public function testIterateResultClearEveryCycle(): void $this->_em->flush(); } - public function testIterateResultFetchJoinedCollectionThrowsException(): void - { - $this->expectException(QueryException::class); - $query = $this->_em->createQuery("SELECT u, a FROM ' . CmsUser::class . ' u JOIN u.articles a"); - $query->iterate(); - } - public function testToIterableResultFetchJoinedCollectionThrowsException(): void { $this->expectException(QueryException::class); @@ -477,14 +457,14 @@ public function testToIterableResultFetchJoinedCollectionThrowsException(): void public function testGetSingleResultThrowsExceptionOnNoResult(): void { - $this->expectException('Doctrine\ORM\NoResultException'); + $this->expectException(NoResultException::class); $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a') ->getSingleResult(); } public function testGetSingleScalarResultThrowsExceptionOnNoResult(): void { - $this->expectException('Doctrine\ORM\NoResultException'); + $this->expectException(NoResultException::class); $this->_em->createQuery('select a.id from Doctrine\Tests\Models\CMS\CmsArticle a') ->getSingleScalarResult(); } @@ -504,7 +484,7 @@ public function testGetSingleScalarResultThrowsExceptionOnSingleRowWithMultipleC $this->expectException(NonUniqueResultException::class); $this->expectExceptionMessage( 'The query returned a row containing multiple columns. Change the query or use a different result function' - . ' like getScalarResult().' + . ' like getScalarResult().', ); $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u') @@ -538,7 +518,7 @@ public function testGetSingleScalarResultThrowsExceptionOnNonUniqueResult(): voi $this->expectException(NonUniqueResultException::class); $this->expectExceptionMessage( - 'The query returned multiple rows. Change the query or use a different result function like getScalarResult().' + 'The query returned multiple rows. Change the query or use a different result function like getScalarResult().', ); $this->_em->createQuery('select a.id from Doctrine\Tests\Models\CMS\CmsArticle a') @@ -582,24 +562,7 @@ public function testModifiedLimitQuery(): void ->getScalarResult(); } - public function testSupportsQueriesWithEntityNamespaces(): void - { - if (! class_exists(PersistentObject::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2'); - } - - $this->_em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS'); - - try { - $query = $this->_em->createQuery('UPDATE CMS:CmsUser u SET u.name = ?1'); - self::assertEquals('UPDATE cms_users SET name = ?', $query->getSQL()); - $query->free(); - } finally { - $this->_em->getConfiguration()->setEntityNamespaces([]); - } - } - - /** @group DDC-604 */ + #[Group('DDC-604')] public function testEntityParameters(): void { $article = new CmsArticle(); @@ -626,7 +589,7 @@ public function testEntityParameters(): void self::assertTrue($this->isUninitializedObject($result[0]->user)); } - /** @group DDC-952 */ + #[Group('DDC-952')] public function testEnableFetchEagerMode(): void { for ($i = 0; $i < 10; $i++) { @@ -655,7 +618,7 @@ public function testEnableFetchEagerMode(): void } } - /** @group DDC-991 */ + #[Group('DDC-991')] public function testgetOneOrNullResult(): void { $user = new CmsUser(); @@ -677,7 +640,7 @@ public function testgetOneOrNullResult(): void self::assertEquals('gblanco', $fetchedUsername); } - /** @group DDC-991 */ + #[Group('DDC-991')] public function testgetOneOrNullResultSeveralRows(): void { $user = new CmsUser(); @@ -700,7 +663,7 @@ public function testgetOneOrNullResultSeveralRows(): void $fetchedUser = $query->getOneOrNullResult(); } - /** @group DDC-991 */ + #[Group('DDC-991')] public function testgetOneOrNullResultNoRows(): void { $query = $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u'); @@ -710,7 +673,7 @@ public function testgetOneOrNullResultNoRows(): void self::assertNull($query->getOneOrNullResult(Query::HYDRATE_SCALAR)); } - /** @group DBAL-171 */ + #[Group('DBAL-171')] public function testParameterOrder(): void { $user1 = new CmsUser(); @@ -739,7 +702,7 @@ public function testParameterOrder(): void [ new Parameter('b', [$user1->id, $user2->id, $user3->id]), new Parameter('a', 'developer'), - ] + ], )); $result = $query->getResult(); @@ -853,7 +816,7 @@ public function testQueryWithHiddenAsSelectExpression(): void self::assertInstanceOf(CmsUser::class, $users[0]); } - /** @group DDC-1651 */ + #[Group('DDC-1651')] public function testSetParameterBindingSingleIdentifierObject(): void { $userC = new CmsUser(); @@ -874,7 +837,7 @@ public function testSetParameterBindingSingleIdentifierObject(): void $q->getResult(); } - /** @group DDC-2319 */ + #[Group('DDC-2319')] public function testSetCollectionParameterBindingSingleIdentifierObject(): void { $u1 = new CmsUser(); @@ -922,7 +885,7 @@ public function testSetCollectionParameterBindingSingleIdentifierObject(): void self::assertEquals($u3->username, $resultUser3->username); } - /** @group DDC-1822 */ + #[Group('DDC-1822')] public function testUnexpectedResultException(): void { $dql = 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'; @@ -939,7 +902,7 @@ public function testUnexpectedResultException(): void $this->_em->createQuery($dql)->getSingleResult(); self::fail('Expected exception "\Doctrine\ORM\NoResultException".'); } catch (UnexpectedResultException $exc) { - self::assertInstanceOf('\Doctrine\ORM\NoResultException', $exc); + self::assertInstanceOf(NoResultException::class, $exc); } $this->_em->persist($u1); @@ -951,7 +914,7 @@ public function testUnexpectedResultException(): void $this->_em->createQuery($dql)->getSingleResult(); self::fail('Expected exception "\Doctrine\ORM\NonUniqueResultException".'); } catch (UnexpectedResultException $exc) { - self::assertInstanceOf('\Doctrine\ORM\NonUniqueResultException', $exc); + self::assertInstanceOf(NonUniqueResultException::class, $exc); } } diff --git a/tests/Tests/ORM/Functional/ReadOnlyTest.php b/tests/Tests/ORM/Functional/ReadOnlyTest.php index 297c2d5a3e4..7a27a8b2605 100644 --- a/tests/Tests/ORM/Functional/ReadOnlyTest.php +++ b/tests/Tests/ORM/Functional/ReadOnlyTest.php @@ -10,14 +10,12 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * Functional Query tests. - * - * @group DDC-692 */ +#[Group('DDC-692')] class ReadOnlyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -44,7 +42,7 @@ public function testReadOnlyEntityNeverChangeTracked(): void self::assertEquals(1234, $dbReadOnly->numericValue); } - /** @group DDC-1659 */ + #[Group('DDC-1659')] public function testClearReadOnly(): void { $readOnly = new ReadOnlyEntity('Test1', 1234); @@ -57,7 +55,7 @@ public function testClearReadOnly(): void self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly)); } - /** @group DDC-1659 */ + #[Group('DDC-1659')] public function testClearEntitiesReadOnly(): void { $readOnly = new ReadOnlyEntity('Test1', 1234); @@ -65,7 +63,7 @@ public function testClearEntitiesReadOnly(): void $this->_em->flush(); $this->_em->getUnitOfWork()->markReadOnly($readOnly); - $this->_em->clear(get_class($readOnly)); + $this->_em->clear(); self::assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly)); } @@ -153,30 +151,20 @@ public function testNotReadOnlyIfObjectWasKnownBefore(): void } } -/** @Entity(readOnly=true) */ +#[Entity(readOnly: true)] class ReadOnlyEntity { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - /** - * @var int - * @Column(type="integer") - */ - public $numericValue; - - public function __construct($name, $number) - { - $this->name = $name; - $this->numericValue = $number; + + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + #[Column(type: 'integer')] + public int $numericValue, + ) { } } diff --git a/tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php b/tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php index 2aa53a00212..67fa9ac1c10 100644 --- a/tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php +++ b/tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php @@ -14,9 +14,6 @@ use function dirname; -/** - * @requires PHP 8.1 - */ class ReadonlyPropertiesTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +24,7 @@ protected function setUp(): void $this->_em = $this->getEntityManager(null, new AttributeDriver( [dirname(__DIR__, 2) . '/Models/ReadonlyProperties'], - true + true, )); $this->_schemaTool = new SchemaTool($this->_em); @@ -79,7 +76,7 @@ public function testEntityWithEagerManyToOne(): void [$book] = $this->_em->createQueryBuilder() ->from(SimpleBook::class, 'b') ->join('b.author', 'a') - ->select(['b', 'a']) + ->select('b', 'a') ->where('b.id = :id') ->setParameter('id', $bookId) ->getQuery() diff --git a/tests/Tests/ORM/Functional/ReferenceProxyTest.php b/tests/Tests/ORM/Functional/ReferenceProxyTest.php index bb4a2cfb36c..55f65956757 100644 --- a/tests/Tests/ORM/Functional/ReferenceProxyTest.php +++ b/tests/Tests/ORM/Functional/ReferenceProxyTest.php @@ -12,10 +12,10 @@ use Doctrine\Tests\Models\ECommerce\ECommerceProduct2; use Doctrine\Tests\Models\ECommerce\ECommerceShipping; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; use function file_exists; -use function get_class; use function str_replace; use function strlen; use function substr; @@ -67,18 +67,18 @@ public function testLazyLoadsFieldValuesFromDatabase(): void self::assertEquals('Doctrine Cookbook', $productProxy->getName()); } - /** @group DDC-727 */ + #[Group('DDC-727')] public function testAccessMetatadaForProxy(): void { $id = $this->createProduct(); $entity = $this->_em->getReference(ECommerceProduct::class, $id); - $class = $this->_em->getClassMetadata(get_class($entity)); + $class = $this->_em->getClassMetadata($entity::class); self::assertEquals(ECommerceProduct::class, $class->name); } - /** @group DDC-1033 */ + #[Group('DDC-1033')] public function testReferenceFind(): void { $id = $this->createProduct(); @@ -90,7 +90,7 @@ public function testReferenceFind(): void self::assertEquals('Doctrine Cookbook', $entity2->getName()); } - /** @group DDC-1033 */ + #[Group('DDC-1033')] public function testCloneProxy(): void { $id = $this->createProduct(); @@ -131,7 +131,7 @@ public function testCloneProxyWithResetId(): void self::assertEquals('Clone of Doctrine Cookbook', $clone->getName()); } - /** @group DDC-733 */ + #[Group('DDC-733')] public function testInitializeProxy(): void { $id = $this->createProduct(); @@ -144,7 +144,7 @@ public function testInitializeProxy(): void self::assertFalse($this->isUninitializedObject($entity), 'Should be initialized after called UnitOfWork::initializeObject()'); } - /** @group DDC-1163 */ + #[Group('DDC-1163')] public function testInitializeChangeAndFlushProxy(): void { $id = $this->createProduct(); @@ -160,7 +160,7 @@ public function testInitializeChangeAndFlushProxy(): void self::assertEquals('Doctrine 2 Cookbook', $entity->getName()); } - /** @group DDC-1022 */ + #[Group('DDC-1022')] public function testWakeupOnProxy(): void { $id = $this->createProduct(); @@ -191,7 +191,7 @@ public function testDoNotInitializeProxyOnGettingTheIdentifier(): void self::assertTrue($this->isUninitializedObject($entity), "Getting the identifier doesn't initialize the proxy."); } - /** @group DDC-1625 */ + #[Group('DDC-1625')] public function testDoNotInitializeProxyOnGettingTheIdentifierDDC1625(): void { $id = $this->createAuction(); @@ -239,7 +239,7 @@ public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier(): v self::assertFalse($this->isUninitializedObject($entity), 'Getting something other than the identifier initializes the proxy.'); } - /** @group DDC-1604 */ + #[Group('DDC-1604')] public function testCommonPersistenceProxy(): void { $id = $this->createProduct(); @@ -252,8 +252,8 @@ public function testCommonPersistenceProxy(): void self::assertTrue($this->isUninitializedObject($entity)); self::assertEquals(ECommerceProduct::class, $className); - $restName = str_replace($this->_em->getConfiguration()->getProxyNamespace(), '', get_class($entity)); - $restName = substr(get_class($entity), strlen($this->_em->getConfiguration()->getProxyNamespace()) + 1); + $restName = str_replace($this->_em->getConfiguration()->getProxyNamespace(), '', $entity::class); + $restName = substr($entity::class, strlen($this->_em->getConfiguration()->getProxyNamespace()) + 1); $proxyFileName = $this->_em->getConfiguration()->getProxyDir() . DIRECTORY_SEPARATOR . str_replace('\\', '', $restName) . '.php'; self::assertTrue(file_exists($proxyFileName), 'Proxy file name cannot be found generically.'); diff --git a/tests/Tests/ORM/Functional/ResultCacheTest.php b/tests/Tests/ORM/Functional/ResultCacheTest.php index 96c321331df..ce44c60a86f 100644 --- a/tests/Tests/ORM/Functional/ResultCacheTest.php +++ b/tests/Tests/ORM/Functional/ResultCacheTest.php @@ -4,7 +4,6 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\NativeQuery; @@ -13,13 +12,14 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use function assert; use function count; use function iterator_to_array; -use function method_exists; use function sprintf; class ResultCacheTest extends OrmFunctionalTestCase @@ -85,37 +85,7 @@ public function testSetResultCacheId(): void self::assertCacheHasItem('testing_result_cache_id', $cache); } - public function testUseResultCacheTrue(): void - { - $cache = new ArrayAdapter(); - $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); - - $query->useResultCache(true); - $this->setResultCache($query, $cache); - $query->setResultCacheId('testing_result_cache_id'); - $query->getResult(); - - self::assertCacheHasItem('testing_result_cache_id', $cache); - - $this->resetCache(); - } - - public function testUseResultCacheFalse(): void - { - $cache = new ArrayAdapter(); - $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); - - $this->setResultCache($query, $cache); - $query->setResultCacheId('testing_result_cache_id'); - $query->useResultCache(false); - $query->getResult(); - - self::assertFalse($cache->hasItem('testing_result_cache_id')); - - $this->resetCache(); - } - - /** @group DDC-1026 */ + #[Group('DDC-1026')] public function testUseResultCacheParams(): void { $cache = new ArrayAdapter(); @@ -123,7 +93,6 @@ public function testUseResultCacheParams(): void $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1'); $this->setResultCache($query, $cache); - $query->enableResultCache(); // these queries should result in cache miss: $query->setParameter(1, 1); @@ -184,7 +153,7 @@ public function testEnableResultCacheWithIterable(): void $this->resetCache(); } - /** @group DDC-1026 */ + #[Group('DDC-1026')] public function testEnableResultCacheParams(): void { $cache = new ArrayAdapter(); @@ -248,7 +217,7 @@ public function testNativeQueryResultCaching(): array return [$query, $cache]; } - /** @depends testNativeQueryResultCaching */ + #[Depends('testNativeQueryResultCaching')] public function testResultCacheNotDependsOnQueryHints(array $previous): void { [$query, $cache] = $previous; @@ -263,7 +232,7 @@ public function testResultCacheNotDependsOnQueryHints(array $previous): void self::assertCount($cacheCount, $cache->getValues()); } - /** @depends testNativeQueryResultCaching */ + #[Depends('testNativeQueryResultCaching')] public function testResultCacheDependsOnParameters(array $previous): void { [$query, $cache] = $previous; @@ -278,7 +247,7 @@ public function testResultCacheDependsOnParameters(array $previous): void self::assertCount($cacheCount + 1, $cache->getValues()); } - /** @depends testNativeQueryResultCaching */ + #[Depends('testNativeQueryResultCaching')] public function testResultCacheNotDependsOnHydrationMode(array $previous): void { [$query, $cache] = $previous; @@ -293,7 +262,7 @@ public function testResultCacheNotDependsOnHydrationMode(array $previous): void self::assertCount($cacheCount, $cache->getValues()); } - /** @group DDC-909 */ + #[Group('DDC-909')] public function testResultCacheWithObjectParameter(): void { $user1 = new CmsUser(); @@ -355,22 +324,17 @@ public function testResultCacheWithObjectParameter(): void private function setResultCache(AbstractQuery $query, CacheItemPoolInterface $cache): void { - $profile = new QueryCacheProfile(); - - if (method_exists($profile, 'setResultCache')) { - $profile = $profile->setResultCache($cache); - } else { - $profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($cache)); - } - - $query->setResultCacheProfile($profile); + $query->setResultCacheProfile( + (new QueryCacheProfile()) + ->setResultCache($cache), + ); } private static function assertCacheHasItem(string $key, CacheItemPoolInterface $cache): void { self::assertTrue( - $cache->hasItem($key) || DoctrineProvider::wrap($cache)->contains($key), - sprintf('Failed asserting that a given cache contains the key "%s".', $key) + $cache->hasItem($key), + sprintf('Failed asserting that a given cache contains the key "%s".', $key), ); } @@ -382,8 +346,8 @@ private function resetCache(): void private static function assertCacheDoesNotHaveItem(string $key, CacheItemPoolInterface $cache): void { self::assertFalse( - $cache->hasItem($key) || DoctrineProvider::wrap($cache)->contains($key), - sprintf('Failed asserting that a given cache does not contain the key "%s".', $key) + $cache->hasItem($key), + sprintf('Failed asserting that a given cache does not contain the key "%s".', $key), ); } } diff --git a/tests/Tests/ORM/Functional/SQLFilterTest.php b/tests/Tests/ORM/Functional/SQLFilterTest.php index f6b966cdd88..9012895e595 100644 --- a/tests/Tests/ORM/Functional/SQLFilterTest.php +++ b/tests/Tests/ORM/Functional/SQLFilterTest.php @@ -27,59 +27,35 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use ReflectionMethod; use Symfony\Component\Cache\Adapter\ArrayAdapter; use function count; use function in_array; +use function method_exists; use function serialize; /** * Tests SQLFilter functionality. - * - * @group non-cacheable */ +#[Group('non-cacheable')] class SQLFilterTest extends OrmFunctionalTestCase { - /** @var int */ - private $userId; - - /** @var int */ - private $userId2; - - /** @var int */ - private $articleId; - - /** @var int */ - private $articleId2; - - /** @var int */ - private $groupId; - - /** @var int */ - private $groupId2; - - /** @var int */ - private $managerId; - - /** @var int */ - private $managerId2; - - /** @var int */ - private $contractId1; - - /** @var int */ - private $contractId2; - - /** @var int */ - private $organizationId; - - /** @var int */ - private $eventId1; - - /** @var int */ - private $eventId2; + private int $userId; + private int $userId2; + private int $articleId; + private int $articleId2; + private int $groupId; + private int $groupId2; + private int $managerId; + private int $managerId2; + private int $contractId1; + private int $contractId2; + private int $organizationId; + private int $eventId1; + private int $eventId2; protected function setUp(): void { @@ -93,9 +69,9 @@ public function tearDown(): void { parent::tearDown(); - $class = $this->_em->getClassMetadata(CmsUser::class); - $class->associationMappings['groups']['fetch'] = ClassMetadata::FETCH_LAZY; - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_LAZY; + $class = $this->_em->getClassMetadata(CmsUser::class); + $class->associationMappings['groups']->fetch = ClassMetadata::FETCH_LAZY; + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_LAZY; } public function testConfigureFilter(): void @@ -125,7 +101,7 @@ public function testEntityManagerEnableFilter(): void $exceptionThrown = false; try { $filter = $em->getFilters()->enable('foo'); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { $exceptionThrown = true; } @@ -163,7 +139,7 @@ public function testEntityManagerDisableFilter(): void $exceptionThrown = false; try { $filter = $em->getFilters()->disable('foo'); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { $exceptionThrown = true; } @@ -173,7 +149,7 @@ public function testEntityManagerDisableFilter(): void $exceptionThrown = false; try { $filter = $em->getFilters()->disable('locale'); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { $exceptionThrown = true; } @@ -195,14 +171,14 @@ public function testEntityManagerGetFilter(): void $exceptionThrown = false; try { $filter = $em->getFilters()->getFilter('soft_delete'); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { $exceptionThrown = true; } self::assertTrue($exceptionThrown); } - /** @group DDC-2203 */ + #[Group('DDC-2203')] public function testEntityManagerIsFilterEnabled(): void { $em = $this->getEntityManager(); @@ -228,28 +204,23 @@ private function configureFilters(EntityManagerInterface $em): void $config->addFilter('soft_delete', '\Doctrine\Tests\ORM\Functional\MySoftDeleteFilter'); } - /** @return Connection&MockObject */ - private function getMockConnection(): Connection + private function getMockConnection(): Connection&MockObject { $connection = $this->createMock(Connection::class); - $connection->method('getEventManager') - ->willReturn(new EventManager()); + if (method_exists($connection, 'getEventManager')) { + $connection->method('getEventManager') + ->willReturn(new EventManager()); + } return $connection; } - /** @return EntityManagerInterface&MockObject */ - private function getMockEntityManager(): EntityManagerInterface + private function getMockEntityManager(): EntityManagerInterface&MockObject { return $this->createMock(EntityManagerInterface::class); } - /** - * @phpstan-param EntityManagerInterface&MockObject $em - * - * @return FilterCollection&MockObject - */ - private function addMockFilterCollection(EntityManagerInterface $em): FilterCollection + private function addMockFilterCollection(EntityManagerInterface&MockObject $em): FilterCollection&MockObject { $filterCollection = $this->createMock(FilterCollection::class); @@ -286,10 +257,8 @@ public function testSQLFilterGetSetParameter(): void self::assertEquals("'en'", $filter->getParameter('locale')); } - /** - * @group DDC-3161 - * @group 1054 - */ + #[Group('DDC-3161')] + #[Group('1054')] public function testSQLFilterGetConnection(): void { // Setup mock connection @@ -303,7 +272,6 @@ public function testSQLFilterGetConnection(): void $filter = new MyLocaleFilter($em); $reflMethod = new ReflectionMethod(SQLFilter::class, 'getConnection'); - $reflMethod->setAccessible(true); self::assertSame($conn, $reflMethod->invoke($filter)); } @@ -339,9 +307,7 @@ public function testSQLFilterSetArrayParameterInfersType(): void // Setup mock connection $conn = $this->getMockConnection(); $conn->method('quote') - ->willReturnCallback(static function ($value) { - return "'" . $value . "'"; - }); + ->willReturnCallback(static fn ($value) => "'" . $value . "'"); $em = $this->getMockEntityManager(); $em->method('getConnection') @@ -465,14 +431,14 @@ public function testRepositoryFindBy(): void $this->loadFixtureData(); self::assertCount(1, $this->_em->getRepository(CmsGroup::class)->findBy( - ['id' => $this->groupId2] + ['id' => $this->groupId2], )); $this->useCMSGroupPrefixFilter(); $this->_em->clear(); self::assertCount(0, $this->_em->getRepository(CmsGroup::class)->findBy( - ['id' => $this->groupId2] + ['id' => $this->groupId2], )); } @@ -493,14 +459,14 @@ public function testRepositoryFindOneBy(): void $this->loadFixtureData(); self::assertNotNull($this->_em->getRepository(CmsGroup::class)->findOneBy( - ['id' => $this->groupId2] + ['id' => $this->groupId2], )); $this->useCMSGroupPrefixFilter(); $this->_em->clear(); self::assertNull($this->_em->getRepository(CmsGroup::class)->findOneBy( - ['id' => $this->groupId2] + ['id' => $this->groupId2], )); } @@ -537,6 +503,21 @@ public function testToOneFilter(): void self::assertEquals(2, count($query->getResult())); } + public function testOneToOneInverseSideWithFilter(): void + { + $this->loadFixtureData(); + + $conf = $this->_em->getConfiguration(); + $conf->addFilter('country', '\Doctrine\Tests\ORM\Functional\CMSCountryFilter'); + $this->_em->getFilters()->enable('country')->setParameterList('country', ['Germany'], Types::STRING); + + $user = $this->_em->find(CmsUser::class, $this->userId); + self::assertNotEmpty($user->address); + + $user2 = $this->_em->find(CmsUser::class, $this->userId2); + self::assertEmpty($user2->address); + } + public function testManyToManyFilter(): void { $this->loadFixtureData(); @@ -587,9 +568,9 @@ public function testWhereOrFilter(): void private function loadLazyFixtureData(): void { - $class = $this->_em->getClassMetadata(CmsUser::class); - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['groups']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + $class = $this->_em->getClassMetadata(CmsUser::class); + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['groups']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; $this->loadFixtureData(); } @@ -1184,8 +1165,7 @@ public function testRetrieveListAsSingleThrowsException(): void class MySoftDeleteFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== 'MyEntity\SoftDeleteNewsItem') { return ''; @@ -1197,8 +1177,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class MyLocaleFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if (! in_array('LocaleAware', $targetEntity->reflClass->getInterfaceNames(), true)) { return ''; @@ -1210,8 +1189,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CMSCountryFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CmsAddress::class) { return ''; @@ -1223,8 +1201,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CMSGroupPrefixFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CmsGroup::class) { return ''; @@ -1236,8 +1213,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CMSArticleTopicFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CmsArticle::class) { return ''; @@ -1249,8 +1225,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CompanyPersonNameFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias, $targetTable = '') + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CompanyPerson::class) { return ''; @@ -1262,8 +1237,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CompletedContractFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias, $targetTable = '') + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CompanyContract::class) { return ''; @@ -1275,8 +1249,7 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli class CompanyEventFilter extends SQLFilter { - /** {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias, $targetTable = '') + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { if ($targetEntity->name !== CompanyEvent::class) { return ''; diff --git a/tests/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php b/tests/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php index a4188578b84..bcdcfddc03a 100644 --- a/tests/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php +++ b/tests/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php @@ -4,11 +4,10 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool; -use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Schema; use Doctrine\Tests\OrmFunctionalTestCase; - -use function method_exists; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * Functional tests for the Class Table Inheritance mapping strategy. @@ -22,23 +21,18 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-966 */ + #[Group('DDC-966')] public function testGeneratedSchema(): Schema { - $method = method_exists(AbstractSchemaManager::class, 'introspectSchema') ? - 'introspectSchema' : - 'createSchema'; - $schema = $this->createSchemaManager()->$method(); + $schema = $this->createSchemaManager()->introspectSchema(); self::assertTrue($schema->hasTable('company_contracts')); return $schema; } - /** - * @group DDC-966 - * @depends testGeneratedSchema - */ + #[Depends('testGeneratedSchema')] + #[Group('DDC-966')] public function testSingleTableInheritance(Schema $schema): void { $table = $schema->getTable('company_contracts'); diff --git a/tests/Tests/ORM/Functional/SchemaTool/DBAL483Test.php b/tests/Tests/ORM/Functional/SchemaTool/DBAL483Test.php index 619b86d3dbd..5abd59eb5a8 100644 --- a/tests/Tests/ORM/Functional/SchemaTool/DBAL483Test.php +++ b/tests/Tests/ORM/Functional/SchemaTool/DBAL483Test.php @@ -9,13 +9,14 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_filter; use function str_contains; class DBAL483Test extends OrmFunctionalTestCase { - /** @group DBAL-483 */ + #[Group('DBAL-483')] public function testDefaultValueIsComparedCorrectly(): void { $class = DBAL483Default::class; @@ -24,34 +25,26 @@ public function testDefaultValueIsComparedCorrectly(): void $updateSql = $this->getUpdateSchemaSqlForModels($class); - $updateSql = array_filter($updateSql, static function ($sql) { - return str_contains($sql, 'DBAL483'); - }); + $updateSql = array_filter($updateSql, static fn ($sql) => str_contains($sql, 'DBAL483')); self::assertCount(0, $updateSql); } } -/** @Entity */ +#[Entity] class DBAL483Default { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var int - * @Column(type="integer", options={"default": 0}) - */ + /** @var int */ + #[Column(type: 'integer', options: ['default' => 0])] public $num; - /** - * @var string - * @Column(type="string", options={"default": "foo"}) - */ + /** @var string */ + #[Column(type: 'string', options: ['default' => 'foo'])] public $str = 'foo'; } diff --git a/tests/Tests/ORM/Functional/SchemaTool/DDC214Test.php b/tests/Tests/ORM/Functional/SchemaTool/DDC214Test.php index 2e674d38d63..8e9df91ca93 100644 --- a/tests/Tests/ORM/Functional/SchemaTool/DDC214Test.php +++ b/tests/Tests/ORM/Functional/SchemaTool/DDC214Test.php @@ -4,16 +4,13 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Schema\AbstractSchemaManager; -use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\Tests\Models; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_filter; use function implode; -use function method_exists; use function str_contains; use const PHP_EOL; @@ -29,12 +26,12 @@ protected function setUp(): void $conn = $this->_em->getConnection(); - if ($conn->getDatabasePlatform() instanceof SqlitePlatform) { + if ($conn->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('SQLite does not support ALTER TABLE statements.'); } } - /** @group DDC-214 */ + #[Group('DDC-214')] public function testCmsAddressModel(): void { $this->assertCreatedSchemaNeedsNoUpdates( @@ -43,11 +40,11 @@ public function testCmsAddressModel(): void Models\CMS\CmsAddress::class, Models\CMS\CmsGroup::class, Models\CMS\CmsArticle::class, - Models\CMS\CmsEmail::class + Models\CMS\CmsEmail::class, ); } - /** @group DDC-214 */ + #[Group('DDC-214')] public function testCompanyModel(): void { $this->assertCreatedSchemaNeedsNoUpdates( @@ -58,7 +55,7 @@ public function testCompanyModel(): void Models\Company\CompanyEvent::class, Models\Company\CompanyAuction::class, Models\Company\CompanyRaffle::class, - Models\Company\CompanyCar::class + Models\Company\CompanyCar::class, ); } @@ -69,27 +66,14 @@ public function assertCreatedSchemaNeedsNoUpdates(string ...$classes): void $sm = $this->createSchemaManager(); - $method = method_exists(AbstractSchemaManager::class, 'introspectSchema') ? - 'introspectSchema' : - 'createSchema'; - $fromSchema = $sm->$method(); + $fromSchema = $sm->introspectSchema(); $toSchema = $this->getSchemaForModels(...$classes); - - if (method_exists($sm, 'createComparator')) { - $comparator = $sm->createComparator(); - } else { - $comparator = new Comparator(); - } - + $comparator = $sm->createComparator(); $schemaDiff = $comparator->compareSchemas($fromSchema, $toSchema); - $sql = method_exists(AbstractPlatform::class, 'getAlterSchemaSQL') ? - $this->_em->getConnection()->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff) : - $schemaDiff->toSql($this->_em->getConnection()->getDatabasePlatform()); + $sql = $this->_em->getConnection()->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff); - $sql = array_filter($sql, static function ($sql) { - return ! str_contains($sql, 'DROP'); - }); + $sql = array_filter($sql, static fn ($sql) => ! str_contains($sql, 'DROP')); self::assertCount(0, $sql, 'SQL: ' . implode(PHP_EOL, $sql)); } diff --git a/tests/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php b/tests/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php index 9ec1f42b9af..cbfc491d654 100644 --- a/tests/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php +++ b/tests/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php @@ -5,17 +5,25 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool; use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Schema\Visitor\Visitor; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Tools\SchemaTool; -use Doctrine\Tests\Models; +use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsGroup; +use Doctrine\Tests\Models\CMS\CmsPhonenumber; +use Doctrine\Tests\Models\CMS\CmsTag; +use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\Generic\BooleanModel; +use Doctrine\Tests\Models\Generic\DecimalModel; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function method_exists; -use function sprintf; +use function class_exists; class MySqlSchemaToolTest extends OrmFunctionalTestCase { @@ -31,26 +39,35 @@ protected function setUp(): void public function testGetCreateSchemaSql(): void { $classes = [ - $this->_em->getClassMetadata(Models\CMS\CmsGroup::class), - $this->_em->getClassMetadata(Models\CMS\CmsUser::class), - $this->_em->getClassMetadata(Models\CMS\CmsTag::class), - $this->_em->getClassMetadata(Models\CMS\CmsAddress::class), - $this->_em->getClassMetadata(Models\CMS\CmsEmail::class), - $this->_em->getClassMetadata(Models\CMS\CmsPhonenumber::class), + $this->_em->getClassMetadata(CmsGroup::class), + $this->_em->getClassMetadata(CmsUser::class), + $this->_em->getClassMetadata(CmsTag::class), + $this->_em->getClassMetadata(CmsAddress::class), + $this->_em->getClassMetadata(CmsEmail::class), + $this->_em->getClassMetadata(CmsPhonenumber::class), ]; - $tool = new SchemaTool($this->_em); - $sql = $tool->getCreateSchemaSql($classes); - $collation = $this->getColumnCollationDeclarationSQL('utf8_unicode_ci'); - - self::assertEquals('CREATE TABLE cms_groups (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[0]); - self::assertEquals('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[1]); - self::assertEquals('CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[2]); - self::assertEquals('CREATE TABLE cms_users_tags (user_id INT NOT NULL, tag_id INT NOT NULL, INDEX IDX_93F5A1ADA76ED395 (user_id), INDEX IDX_93F5A1ADBAD26311 (tag_id), PRIMARY KEY(user_id, tag_id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[3]); - self::assertEquals('CREATE TABLE cms_tags (id INT AUTO_INCREMENT NOT NULL, tag_name VARCHAR(50) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[4]); - self::assertEquals('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[5]); - self::assertEquals('CREATE TABLE cms_emails (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(250) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[6]); - self::assertEquals('CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[7]); + $tool = new SchemaTool($this->_em); + $sql = $tool->getCreateSchemaSql($classes); + + self::assertEquals('CREATE TABLE cms_groups (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[0]); + self::assertThat($sql[1], self::logicalOr( + // DBAL 3 + self::equalTo('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'), + // DBAL 4 (see https://github.com/doctrine/dbal/pull/4777) + self::equalTo('CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, email_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'), + )); + self::assertEquals('CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[2]); + self::assertEquals('CREATE TABLE cms_users_tags (user_id INT NOT NULL, tag_id INT NOT NULL, INDEX IDX_93F5A1ADA76ED395 (user_id), INDEX IDX_93F5A1ADBAD26311 (tag_id), PRIMARY KEY(user_id, tag_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[3]); + self::assertEquals('CREATE TABLE cms_tags (id INT AUTO_INCREMENT NOT NULL, tag_name VARCHAR(50) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[4]); + self::assertThat($sql[5], self::logicalOr( + // DBAL 3 + self::equalTo('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'), + // DBAL 4 (see https://github.com/doctrine/dbal/pull/4777) + self::equalTo('CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'), + )); + self::assertEquals('CREATE TABLE cms_emails (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(250) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[6]); + self::assertEquals('CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[7]); self::assertEquals('ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id)', $sql[8]); self::assertEquals('ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)', $sql[9]); self::assertEquals('ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id)', $sql[10]); @@ -62,42 +79,35 @@ public function testGetCreateSchemaSql(): void self::assertCount(15, $sql); } - private function getColumnCollationDeclarationSQL(string $collation): string - { - if (method_exists($this->_em->getConnection()->getDatabasePlatform(), 'getColumnCollationDeclarationSQL')) { - return $this->_em->getConnection()->getDatabasePlatform()->getColumnCollationDeclarationSQL($collation); - } - - return sprintf('COLLATE %s', $collation); - } - public function testGetCreateSchemaSql2(): void { - $classes = [$this->_em->getClassMetadata(Models\Generic\DecimalModel::class)]; + $classes = [$this->_em->getClassMetadata(DecimalModel::class)]; - $tool = new SchemaTool($this->_em); - $sql = $tool->getCreateSchemaSql($classes); - $collation = $this->getColumnCollationDeclarationSQL('utf8_unicode_ci'); + $tool = new SchemaTool($this->_em); + $sql = $tool->getCreateSchemaSql($classes); self::assertCount(1, $sql); - self::assertEquals('CREATE TABLE decimal_model (id INT AUTO_INCREMENT NOT NULL, `decimal` NUMERIC(5, 2) NOT NULL, `high_scale` NUMERIC(14, 4) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[0]); + self::assertEquals('CREATE TABLE decimal_model (id INT AUTO_INCREMENT NOT NULL, `decimal` NUMERIC(5, 2) NOT NULL, `high_scale` NUMERIC(14, 4) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[0]); } public function testGetCreateSchemaSql3(): void { - $classes = [$this->_em->getClassMetadata(Models\Generic\BooleanModel::class)]; + $classes = [$this->_em->getClassMetadata(BooleanModel::class)]; - $tool = new SchemaTool($this->_em); - $sql = $tool->getCreateSchemaSql($classes); - $collation = $this->getColumnCollationDeclarationSQL('utf8_unicode_ci'); + $tool = new SchemaTool($this->_em); + $sql = $tool->getCreateSchemaSql($classes); self::assertCount(1, $sql); - self::assertEquals('CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[0]); + self::assertEquals('CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[0]); } - /** @group DBAL-204 */ + #[Group('DBAL-204')] public function testGetCreateSchemaSql4(): void { + if (! class_exists(Visitor::class)) { + self::markTestSkipped('Test valid for doctrine/dbal:3.x only.'); + } + $classes = [$this->_em->getClassMetadata(MysqlSchemaNamespacedEntity::class)]; $tool = new SchemaTool($this->_em); @@ -107,17 +117,13 @@ public function testGetCreateSchemaSql4(): void } } -/** - * @Entity - * @Table("namespace.entity") - */ +#[Table('namespace.entity')] +#[Entity] class MysqlSchemaNamespacedEntity { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index 2ccd2c20a11..61aef5d4040 100644 --- a/tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -12,8 +12,8 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; -use Doctrine\Tests\Models; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_filter; use function implode; @@ -31,91 +31,70 @@ protected function setUp(): void } } - public function testPostgresMetadataSequenceIncrementedBy10(): void - { - $address = $this->_em->getClassMetadata(Models\CMS\CmsAddress::class); - - self::assertEquals(1, $address->sequenceGeneratorDefinition['allocationSize']); - } - - /** @group DDC-1657 */ + #[Group('DDC-1657')] public function testUpdateSchemaWithPostgreSQLSchema(): void { $sql = $this->getUpdateSchemaSqlForModels( DDC1657Screen::class, - DDC1657Avatar::class + DDC1657Avatar::class, ); - $sql = array_filter($sql, static function ($sql) { - return str_starts_with($sql, 'DROP SEQUENCE stonewood.'); - }); + $sql = array_filter($sql, static fn ($sql) => str_starts_with($sql, 'DROP SEQUENCE stonewood.')); self::assertCount(0, $sql, implode("\n", $sql)); } } -/** - * @Entity - * @Table(name="stonewood.screen") - */ +#[Table(name: 'stonewood.screen')] +#[Entity] class DDC1657Screen { /** * Identifier - * - * @var int - * @Id - * @GeneratedValue(strategy="IDENTITY") - * @Column(name="pk", type="integer", nullable=false) */ - private $pk; + #[Id] + #[GeneratedValue(strategy: 'IDENTITY')] + #[Column(name: 'pk', type: 'integer', nullable: false)] + private int $pk; /** * Title - * - * @var string - * @Column(name="title", type="string", length=255, nullable=false) */ - private $title; + #[Column(name: 'title', type: 'string', length: 255, nullable: false)] + private string $title; /** * Path - * - * @var string - * @Column(name="path", type="string", length=255, nullable=false) */ - private $path; + #[Column(name: 'path', type: 'string', length: 255, nullable: false)] + private string $path; /** * Register date * * @var Date - * @Column(name="ddate", type="date", nullable=false) */ + #[Column(name: 'ddate', type: 'date', nullable: false)] private $ddate; /** * Avatar * * @var Stonewood\Model\Entity\Avatar - * @ManyToOne(targetEntity="DDC1657Avatar") - * @JoinColumn(name="pk_avatar", referencedColumnName="pk", nullable=true, onDelete="CASCADE") */ + #[ManyToOne(targetEntity: 'DDC1657Avatar')] + #[JoinColumn(name: 'pk_avatar', referencedColumnName: 'pk', nullable: true, onDelete: 'CASCADE')] private $avatar; } -/** - * @Entity - * @Table(name="stonewood.avatar") - */ +#[Table(name: 'stonewood.avatar')] +#[Entity] class DDC1657Avatar { /** * Identifier - * - * @var int - * @Id - * @GeneratedValue(strategy="IDENTITY") - * @Column(name="pk", type="integer", nullable=false) */ - private $pk; + #[Id] + #[GeneratedValue(strategy: 'IDENTITY')] + #[Column(name: 'pk', type: 'integer', nullable: false)] + private int $pk; } diff --git a/tests/Tests/ORM/Functional/SchemaValidatorTest.php b/tests/Tests/ORM/Functional/SchemaValidatorTest.php index 2e8ccae416a..b8888b6fefc 100644 --- a/tests/Tests/ORM/Functional/SchemaValidatorTest.php +++ b/tests/Tests/ORM/Functional/SchemaValidatorTest.php @@ -11,6 +11,8 @@ use Doctrine\Tests\DbalTypes\NegativeToPositiveType; use Doctrine\Tests\DbalTypes\UpperCaseStringType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function array_keys; use function constant; @@ -18,9 +20,8 @@ /** * Test the validity of all modelsets - * - * @group DDC-1601 */ +#[Group('DDC-1601')] class SchemaValidatorTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -57,7 +58,7 @@ public static function dataValidateModelSets(): array return $modelSets; } - /** @dataProvider dataValidateModelSets */ + #[DataProvider('dataValidateModelSets')] public function testValidateModelSets(string $modelSet): void { $validator = new SchemaValidator($this->_em); diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyTest.php index af27215ce97..874cc79f231 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheCompositePrimaryKeyTest.php @@ -7,8 +7,9 @@ use DateTime; use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\Flight; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheCompositePrimaryKeyTest extends SecondLevelCacheFunctionalTestCase { public function testPutAndLoadCompositPrimaryKeyEntities(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheConcurrentTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheConcurrentTest.php index 3b315ece59a..b8da952105d 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheConcurrentTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheConcurrentTest.php @@ -14,18 +14,16 @@ use Doctrine\Tests\Mocks\TimestampRegionMock; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; use Psr\Cache\CacheItemPoolInterface; use function assert; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheConcurrentTest extends SecondLevelCacheFunctionalTestCase { - /** @var CacheFactorySecondLevelCacheConcurrentTest */ - private $cacheFactory; - - /** @var ClassMetadata */ - private $countryMetadata; + private CacheFactorySecondLevelCacheConcurrentTest $cacheFactory; + private ClassMetadata $countryMetadata; protected function setUp(): void { @@ -132,12 +130,8 @@ public function testBasicConcurrentCollectionReadLock(): void class CacheFactorySecondLevelCacheConcurrentTest extends DefaultCacheFactory { - /** @var CacheItemPoolInterface */ - private $cache; - - public function __construct(CacheItemPoolInterface $cache) + public function __construct(private CacheItemPoolInterface $cache) { - $this->cache = $cache; } public function getRegion(array $cache): ConcurrentRegionMock diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php index c63ccfeb625..0fe074dfd9f 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php @@ -8,8 +8,9 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheCriteriaTest extends SecondLevelCacheFunctionalTestCase { public function testMatchingPut(): void @@ -26,7 +27,7 @@ public function testMatchingPut(): void $this->getQueryLog()->reset()->enable(); $name = $this->countries[0]->getName(); $result1 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $name) + Criteria::expr()->eq('name', $name), )); // Because matching returns lazy collection, we force initialization @@ -41,7 +42,7 @@ public function testMatchingPut(): void $this->_em->clear(); $result2 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $name) + Criteria::expr()->eq('name', $name), )); $this->assertQueryCount(1); @@ -65,7 +66,7 @@ public function testRepositoryMatching(): void $repository = $this->_em->getRepository(Country::class); $this->getQueryLog()->reset()->enable(); $result1 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $this->countries[0]->getName()) + Criteria::expr()->eq('name', $this->countries[0]->getName()), )); // Because matching returns lazy collection, we force initialization @@ -79,7 +80,7 @@ public function testRepositoryMatching(): void $this->_em->clear(); $result2 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $this->countries[0]->getName()) + Criteria::expr()->eq('name', $this->countries[0]->getName()), )); // Because matching returns lazy collection, we force initialization @@ -94,7 +95,7 @@ public function testRepositoryMatching(): void self::assertEquals($this->countries[0]->getName(), $result2[0]->getName()); $result3 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $this->countries[1]->getName()) + Criteria::expr()->eq('name', $this->countries[1]->getName()), )); // Because matching returns lazy collection, we force initialization @@ -109,7 +110,7 @@ public function testRepositoryMatching(): void self::assertEquals($this->countries[1]->getName(), $result3[0]->getName()); $result4 = $repository->matching(new Criteria( - Criteria::expr()->eq('name', $this->countries[1]->getName()) + Criteria::expr()->eq('name', $this->countries[1]->getName()), )); $this->assertQueryCount(2); @@ -134,7 +135,7 @@ public function testCollectionMatching(): void $this->getQueryLog()->reset()->enable(); $collection = $entity->getCities(); $matching = $collection->matching(new Criteria( - Criteria::expr()->eq('name', $itemName) + Criteria::expr()->eq('name', $itemName), )); $this->assertQueryCount(1); @@ -147,7 +148,7 @@ public function testCollectionMatching(): void $this->getQueryLog()->reset()->enable(); $collection = $entity->getCities(); $matching = $collection->matching(new Criteria( - Criteria::expr()->eq('name', $itemName) + Criteria::expr()->eq('name', $itemName), )); $this->assertQueryCount(0); diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheExtraLazyCollectionTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheExtraLazyCollectionTest.php index d8c14c33058..97fa3640614 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheExtraLazyCollectionTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheExtraLazyCollectionTest.php @@ -8,8 +8,9 @@ use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\Models\Cache\Travel; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheExtraLazyCollectionTest extends SecondLevelCacheFunctionalTestCase { protected function setUp(): void @@ -19,8 +20,8 @@ protected function setUp(): void $sourceEntity = $this->_em->getClassMetadata(Travel::class); $targetEntity = $this->_em->getClassMetadata(City::class); - $sourceEntity->associationMappings['visitedCities']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $targetEntity->associationMappings['travels']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + $sourceEntity->associationMappings['visitedCities']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $targetEntity->associationMappings['travels']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; } public function tearDown(): void @@ -30,8 +31,8 @@ public function tearDown(): void $sourceEntity = $this->_em->getClassMetadata(Travel::class); $targetEntity = $this->_em->getClassMetadata(City::class); - $sourceEntity->associationMappings['visitedCities']['fetch'] = ClassMetadata::FETCH_LAZY; - $targetEntity->associationMappings['travels']['fetch'] = ClassMetadata::FETCH_LAZY; + $sourceEntity->associationMappings['visitedCities']->fetch = ClassMetadata::FETCH_LAZY; + $targetEntity->associationMappings['travels']->fetch = ClassMetadata::FETCH_LAZY; } public function testCacheCountAfterAddThenFlush(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheFunctionalTestCase.php b/tests/Tests/ORM/Functional/SecondLevelCacheFunctionalTestCase.php index 6d664c5796b..290f9654291 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheFunctionalTestCase.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheFunctionalTestCase.php @@ -22,42 +22,42 @@ use Doctrine\Tests\Models\Cache\TravelerProfile; use Doctrine\Tests\Models\Cache\TravelerProfileInfo; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] abstract class SecondLevelCacheFunctionalTestCase extends OrmFunctionalTestCase { /** @phpstan-var list */ - protected $people = []; + protected array $people = []; /** @phpstan-var list
*/ - protected $addresses = []; + protected array $addresses = []; /** @phpstan-var list */ - protected $countries = []; + protected array $countries = []; /** @phpstan-var list */ - protected $states = []; + protected array $states = []; /** @phpstan-var list */ - protected $cities = []; + protected array $cities = []; /** @phpstan-var list */ - protected $travels = []; + protected array $travels = []; /** @phpstan-var list */ - protected $travelers = []; + protected array $travelers = []; /** @phpstan-var list */ - protected $attractions = []; + protected array $attractions = []; /** @phpstan-var list */ - protected $attractionsInfo = []; + protected array $attractionsInfo = []; /** @phpstan-var list */ - protected $travelersWithProfile = []; + protected array $travelersWithProfile = []; - /** @var Cache */ - protected $cache; + protected Cache $cache; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheJoinTableInheritanceTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheJoinTableInheritanceTest.php index eab7b416514..5ddfc1e3379 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheJoinTableInheritanceTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheJoinTableInheritanceTest.php @@ -9,11 +9,11 @@ use Doctrine\Tests\Models\Cache\AttractionContactInfo; use Doctrine\Tests\Models\Cache\AttractionInfo; use Doctrine\Tests\Models\Cache\AttractionLocationInfo; +use PHPUnit\Framework\Attributes\Group; use function count; -use function get_class; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheJoinTableInheritanceTest extends SecondLevelCacheFunctionalTestCase { public function testUseSameRegion(): void @@ -54,7 +54,7 @@ public function testJoinTableCountaisRootClass(): void foreach ($this->attractionsInfo as $info) { self::assertTrue($this->cache->containsEntity(AttractionInfo::class, $info->getId())); - self::assertTrue($this->cache->containsEntity(get_class($info), $info->getId())); + self::assertTrue($this->cache->containsEntity($info::class, $info->getId())); } } @@ -214,7 +214,7 @@ public function testQueryCacheShouldBeEvictedOnTimestampUpdate(): void $contact = new AttractionContactInfo( '1234-1234', - $this->_em->find(Attraction::class, $this->attractions[5]->getId()) + $this->_em->find(Attraction::class, $this->attractions[5]->getId()), ); $this->_em->persist($contact); diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheManyToManyTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheManyToManyTest.php index abd821c0ba4..40ece2a011a 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheManyToManyTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheManyToManyTest.php @@ -9,8 +9,9 @@ use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\Models\Cache\Travel; use Doctrine\Tests\Models\Cache\Traveler; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheManyToManyTest extends SecondLevelCacheFunctionalTestCase { public function testShouldPutManyToManyCollectionOwningSideOnPersist(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php index e06f1a5f4d3..0329a2b8151 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheManyToOneTest.php @@ -12,8 +12,9 @@ use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\Models\Cache\Token; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheManyToOneTest extends SecondLevelCacheFunctionalTestCase { public function testPutOnPersist(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php index 9e38d79a0b4..53a5dbccb86 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheOneToManyTest.php @@ -11,10 +11,11 @@ use Doctrine\Tests\Models\Cache\Token; use Doctrine\Tests\Models\Cache\Travel; use Doctrine\Tests\Models\Cache\Traveler; +use PHPUnit\Framework\Attributes\Group; use function sprintf; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheOneToManyTest extends SecondLevelCacheFunctionalTestCase { public function testShouldPutCollectionInverseSideOnPersist(): void @@ -383,7 +384,7 @@ public function testCacheInitializeCollectionWithNewObjects(): void $query = sprintf( 'SELECT t, tt FROM Doctrine\Tests\Models\Cache\Traveler t JOIN t.travels tt WHERE t.id = %s', - $travelerId + $travelerId, ); $result = $this->_em->createQuery($query)->getSingleResult(); diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php index ac04b843891..bf667e6a197 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheOneToOneTest.php @@ -12,8 +12,9 @@ use Doctrine\Tests\Models\Cache\Traveler; use Doctrine\Tests\Models\Cache\TravelerProfile; use Doctrine\Tests\Models\Cache\TravelerProfileInfo; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheOneToOneTest extends SecondLevelCacheFunctionalTestCase { public function testPutOneToOneOnUnidirectionalPersist(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php index 984cb3e3929..e2dec649342 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php @@ -9,6 +9,7 @@ use Doctrine\ORM\Cache\EntityCacheEntry; use Doctrine\ORM\Cache\EntityCacheKey; use Doctrine\ORM\Cache\Exception\CacheException; +use Doctrine\ORM\Cache\QueryCacheEntry; use Doctrine\ORM\Cache\QueryCacheKey; use Doctrine\ORM\Proxy\InternalProxy; use Doctrine\ORM\Query; @@ -17,9 +18,10 @@ use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; use ReflectionMethod; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheQueryCacheTest extends SecondLevelCacheFunctionalTestCase { public function testBasicQueryCache(): void @@ -287,7 +289,7 @@ public function testBasicQueryCachePutEntityCache(): void self::assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount($this->getDefaultQueryRegionName())); } - /** @group 5854 */ + #[Group('5854')] public function testMultipleNestedDQLAliases(): void { $this->loadFixturesCountries(); @@ -804,7 +806,6 @@ public function testQueryCacheLifetime(): void $getHash = static function (AbstractQuery $query) { $method = new ReflectionMethod($query, 'getHash'); - $method->setAccessible(true); return $method->invoke($query); }; @@ -828,8 +829,8 @@ public function testQueryCacheLifetime(): void ->getRegion() ->get($key); - self::assertInstanceOf(Cache\QueryCacheEntry::class, $entry); - $entry->time /= 2; + self::assertInstanceOf(QueryCacheEntry::class, $entry); + $entry = new QueryCacheEntry($entry->result, $entry->time / 2); $this->cache->getQueryCache() ->getRegion() diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheRepositoryTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheRepositoryTest.php index 5ac7ab8242f..7ed7732526c 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheRepositoryTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheRepositoryTest.php @@ -7,8 +7,9 @@ use Doctrine\ORM\Proxy\InternalProxy; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheRepositoryTest extends SecondLevelCacheFunctionalTestCase { public function testRepositoryCacheFind(): void diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheSingleTableInheritanceTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheSingleTableInheritanceTest.php index 5612e92bfd1..43d130bd026 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheSingleTableInheritanceTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheSingleTableInheritanceTest.php @@ -10,11 +10,11 @@ use Doctrine\Tests\Models\Cache\Beach; use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\Models\Cache\Restaurant; +use PHPUnit\Framework\Attributes\Group; use function count; -use function get_class; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheSingleTableInheritanceTest extends SecondLevelCacheFunctionalTestCase { public function testUseSameRegion(): void @@ -53,7 +53,7 @@ public function testCountaisRootClass(): void foreach ($this->attractions as $attraction) { self::assertTrue($this->cache->containsEntity(Attraction::class, $attraction->getId())); - self::assertTrue($this->cache->containsEntity(get_class($attraction), $attraction->getId())); + self::assertTrue($this->cache->containsEntity($attraction::class, $attraction->getId())); } } @@ -235,7 +235,7 @@ public function testQueryCacheShouldBeEvictedOnTimestampUpdate(): void $contact = new Beach( 'Botafogo', - $this->_em->find(City::class, $this->cities[1]->getId()) + $this->_em->find(City::class, $this->cities[1]->getId()), ); $this->_em->persist($contact); diff --git a/tests/Tests/ORM/Functional/SecondLevelCacheTest.php b/tests/Tests/ORM/Functional/SecondLevelCacheTest.php index c54841e20d3..145a7286165 100644 --- a/tests/Tests/ORM/Functional/SecondLevelCacheTest.php +++ b/tests/Tests/ORM/Functional/SecondLevelCacheTest.php @@ -12,11 +12,12 @@ use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Exception; +use PHPUnit\Framework\Attributes\Group; use RuntimeException; use function uniqid; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class SecondLevelCacheTest extends SecondLevelCacheFunctionalTestCase { public function testPutOnPersist(): void @@ -208,7 +209,7 @@ public function testPostFlushFailure(): void Events::postFlush => static function (): void { throw new RuntimeException('post flush failure'); }, - ] + ], ); $this->_em->getEventManager() @@ -240,7 +241,7 @@ public function testPostUpdateFailure(): void Events::postUpdate => static function (): void { throw new RuntimeException('post update failure'); }, - ] + ], ); $this->_em->getEventManager() @@ -287,7 +288,7 @@ public function testPostRemoveFailure(): void Events::postRemove => static function (): void { throw new RuntimeException('post remove failure'); }, - ] + ], ); $this->_em->getEventManager() @@ -314,7 +315,7 @@ public function testPostRemoveFailure(): void self::assertFalse( $this->cache->containsEntity(Country::class, $countryId), - 'Removal attempts should clear the cache entry corresponding to the entity' + 'Removal attempts should clear the cache entry corresponding to the entity', ); self::assertInstanceOf(Country::class, $this->_em->find(Country::class, $countryId)); @@ -338,13 +339,12 @@ public function testCachedNewEntityExists(): void class ListenerSecondLevelCacheTest { - /** @var array */ - public $callbacks; - - /** @phpstan-param array $callbacks */ - public function __construct(array $callbacks = []) + /** + * @param array $callbacks + * @phpstan-param array $callbacks + */ + public function __construct(public array $callbacks = []) { - $this->callbacks = $callbacks; } private function dispatch(string $eventName, EventArgs $args): void diff --git a/tests/Tests/ORM/Functional/SequenceEmulatedIdentityStrategyTest.php b/tests/Tests/ORM/Functional/SequenceEmulatedIdentityStrategyTest.php deleted file mode 100644 index 1ed55483f50..00000000000 --- a/tests/Tests/ORM/Functional/SequenceEmulatedIdentityStrategyTest.php +++ /dev/null @@ -1,90 +0,0 @@ -_em->getConnection()->getDatabasePlatform()->usesSequenceEmulatedIdentityColumns()) { - self::markTestSkipped( - 'This test is special to platforms emulating IDENTITY key generation strategy through sequences.' - ); - } else { - $this->createSchemaForModels(SequenceEmulatedIdentityEntity::class); - } - } - - protected function tearDown(): void - { - parent::tearDown(); - - $connection = $this->_em->getConnection(); - $platform = $connection->getDatabasePlatform(); - - // drop sequence manually due to dependency - $connection->executeStatement( - $platform->getDropSequenceSQL( - $platform->getIdentitySequenceName('seq_identity', 'id') - ) - ); - } - - public function testPreSavePostSaveCallbacksAreInvoked(): void - { - $entity = new SequenceEmulatedIdentityEntity(); - $entity->setValue('hello'); - $this->_em->persist($entity); - $this->_em->flush(); - self::assertIsNumeric($entity->getId()); - self::assertGreaterThan(0, $entity->getId()); - self::assertTrue($this->_em->contains($entity)); - } -} - -/** - * @Entity - * @Table(name="seq_identity") - */ -class SequenceEmulatedIdentityEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="IDENTITY") - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $value; - - public function getId(): int - { - return $this->id; - } - - public function getValue(): string - { - return $this->value; - } - - public function setValue(string $value): void - { - $this->value = $value; - } -} diff --git a/tests/Tests/ORM/Functional/SequenceGeneratorTest.php b/tests/Tests/ORM/Functional/SequenceGeneratorTest.php index 6f2664bbf0e..371afa9579d 100644 --- a/tests/Tests/ORM/Functional/SequenceGeneratorTest.php +++ b/tests/Tests/ORM/Functional/SequenceGeneratorTest.php @@ -39,15 +39,13 @@ public function testHighAllocationSizeSequence(): void } } -/** @Entity */ +#[Entity] class SequenceEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="SEQUENCE") - * @SequenceGenerator(allocationSize=5, sequenceName="person_id_seq") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'SEQUENCE')] + #[SequenceGenerator(allocationSize: 5, sequenceName: 'person_id_seq')] public $id; } diff --git a/tests/Tests/ORM/Functional/SingleTableCompositeKeyTest.php b/tests/Tests/ORM/Functional/SingleTableCompositeKeyTest.php index e9ce797eb91..74ee6f2454e 100644 --- a/tests/Tests/ORM/Functional/SingleTableCompositeKeyTest.php +++ b/tests/Tests/ORM/Functional/SingleTableCompositeKeyTest.php @@ -7,6 +7,7 @@ use Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass; use Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class SingleTableCompositeKeyTest extends OrmFunctionalTestCase { @@ -29,7 +30,7 @@ public function testInsertWithCompositeKey(): void self::assertEquals($childEntity, $entity); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testUpdateWithCompositeKey(): void { $childEntity = new SingleChildClass(); diff --git a/tests/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Tests/ORM/Functional/SingleTableInheritanceTest.php index 935b4dd6e43..f3fa00560d2 100644 --- a/tests/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -13,27 +13,23 @@ use Doctrine\Tests\Models\Company\CompanyFlexContract; use Doctrine\Tests\Models\Company\CompanyFlexUltraContract; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_map; -use function get_class; use function sort; class SingleTableInheritanceTest extends OrmFunctionalTestCase { - /** @var CompanyEmployee */ - private $salesPerson; + private CompanyEmployee|null $salesPerson = null; /** @var list */ - private $engineers = []; + private array $engineers = []; - /** @var CompanyFixContract */ - private $fix; + private CompanyFixContract|null $fix = null; - /** @var CompanyFlexContract */ - private $flex; + private CompanyFlexContract|null $flex = null; - /** @var CompanyFlexUltraContract */ - private $ultra; + private CompanyFlexUltraContract|null $ultra = null; protected function setUp(): void { @@ -243,9 +239,7 @@ public function testQueryScalarWithDiscriminatorValue(): void $contracts = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c ORDER BY c.id')->getScalarResult(); - $discrValues = array_map(static function ($a) { - return $a['c_discr']; - }, $contracts); + $discrValues = array_map(static fn ($a) => $a['c_discr'], $contracts); sort($discrValues); @@ -263,7 +257,7 @@ public function testQueryChildClassWithCondition(): void self::assertEquals(1000, $contract->getFixPrice()); } - /** @group non-cacheable */ + #[Group('non-cacheable')] public function testUpdateChildClassWithCondition(): void { $this->loadFullFixture(); @@ -320,19 +314,19 @@ public function testDeleteByBaseClassCondition(): void self::assertFalse($contracts[0]->isCompleted(), 'Only non completed contracts should be left.'); } - /** @group DDC-130 */ + #[Group('DDC-130')] public function testDeleteJoinTableRecords(): void { $this->loadFullFixture(); // remove managed copy of the fix contract - $this->_em->remove($this->_em->find(get_class($this->fix), $this->fix->getId())); + $this->_em->remove($this->_em->find($this->fix::class, $this->fix->getId())); $this->_em->flush(); - self::assertNull($this->_em->find(get_class($this->fix), $this->fix->getId()), 'Contract should not be present in the database anymore.'); + self::assertNull($this->_em->find($this->fix::class, $this->fix->getId()), 'Contract should not be present in the database anymore.'); } - /** @group DDC-817 */ + #[Group('DDC-817')] public function testFindByAssociation(): void { $this->loadFullFixture(); @@ -354,25 +348,25 @@ public function testFindByAssociation(): void self::assertCount(1, $contracts, 'There should be 1 entities related to ' . $this->salesPerson->getId() . " for 'Doctrine\Tests\Models\Company\CompanyFlexUltraContract'"); } - /** @group DDC-1637 */ + #[Group('DDC-1637')] public function testInheritanceMatching(): void { $this->loadFullFixture(); $repository = $this->_em->getRepository(CompanyContract::class); $contracts = $repository->matching(new Criteria( - Criteria::expr()->eq('salesPerson', $this->salesPerson) + Criteria::expr()->eq('salesPerson', $this->salesPerson), )); self::assertCount(3, $contracts); $repository = $this->_em->getRepository(CompanyFixContract::class); $contracts = $repository->matching(new Criteria( - Criteria::expr()->eq('salesPerson', $this->salesPerson) + Criteria::expr()->eq('salesPerson', $this->salesPerson), )); self::assertCount(1, $contracts); } - /** @group DDC-2430 */ + #[Group('DDC-2430')] public function testMatchingNonObjectOnAssocationThrowsException(): void { $this->loadFullFixture(); @@ -383,14 +377,14 @@ public function testMatchingNonObjectOnAssocationThrowsException(): void $this->expectExceptionMessage('annot match on Doctrine\Tests\Models\Company\CompanyContract::salesPerson with a non-object value.'); $contracts = $repository->matching(new Criteria( - Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()) + Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()), )); // Load the association because it's wrapped in a lazy collection $contracts->toArray(); } - /** @group DDC-834 */ + #[Group('DDC-834')] public function testGetReferenceEntityWithSubclasses(): void { $this->loadFullFixture(); @@ -406,7 +400,7 @@ public function testGetReferenceEntityWithSubclasses(): void self::assertTrue($this->isUninitializedObject($ref), 'A proxy can be generated only if no subclasses exists for the requested reference.'); } - /** @group DDC-952 */ + #[Group('DDC-952')] public function testEagerLoadInheritanceHierarchy(): void { $this->loadFullFixture(); diff --git a/tests/Tests/ORM/Functional/StandardEntityPersisterTest.php b/tests/Tests/ORM/Functional/StandardEntityPersisterTest.php index 247c5e1d708..163208fd85a 100644 --- a/tests/Tests/ORM/Functional/StandardEntityPersisterTest.php +++ b/tests/Tests/ORM/Functional/StandardEntityPersisterTest.php @@ -69,7 +69,7 @@ public function testAddPersistRetrieve(): void $q = $this->_em->createQuery( 'SELECT p, f FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p - JOIN p.features f' + JOIN p.features f', ); $res = $q->getResult(); @@ -99,7 +99,7 @@ public function testAddPersistRetrieve(): void $q = $this->_em->createQuery( 'SELECT p, f FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p - JOIN p.features f' + JOIN p.features f', ); $res = $q->getResult(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1040Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1040Test.php index f59eb394bd8..277adf03405 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1040Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1040Test.php @@ -7,8 +7,9 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1040 */ +#[Group('DDC-1040')] class DDC1040Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1041Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1041Test.php index e998df352d6..69e1fac0dc7 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1041Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1041Test.php @@ -4,10 +4,12 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use Doctrine\Tests\Models; +use Doctrine\Tests\Models\Company\CompanyFixContract; +use Doctrine\Tests\Models\Company\CompanyFlexContract; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1041 */ +#[Group('DDC-1041')] class DDC1041Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -19,7 +21,7 @@ protected function setUp(): void public function testGrabWrongSubtypeReturnsNull(): void { - $fix = new Models\Company\CompanyFixContract(); + $fix = new CompanyFixContract(); $fix->setFixPrice(2000); $this->_em->persist($fix); @@ -27,8 +29,7 @@ public function testGrabWrongSubtypeReturnsNull(): void $id = $fix->getId(); - self::assertNull($this->_em->find(Models\Company\CompanyFlexContract::class, $id)); - self::assertNull($this->_em->getReference(Models\Company\CompanyFlexContract::class, $id)); - self::assertNull($this->_em->getPartialReference(Models\Company\CompanyFlexContract::class, $id)); + self::assertNull($this->_em->find(CompanyFlexContract::class, $id)); + self::assertNull($this->_em->getReference(CompanyFlexContract::class, $id)); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1043Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1043Test.php index 2bde56bc163..e391187da93 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1043Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1043Test.php @@ -6,8 +6,9 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1043 */ +#[Group('DDC-1043')] class DDC1043Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1080Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1080Test.php index c1e3a4b22da..1e8f337e58b 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1080Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1080Test.php @@ -16,8 +16,9 @@ use Doctrine\ORM\Mapping\OrderBy; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1080 */ +#[Group('DDC-1080')] class DDC1080Test extends OrmFunctionalTestCase { public function testHydration(): void @@ -25,7 +26,7 @@ public function testHydration(): void $this->createSchemaForModels( DDC1080Foo::class, DDC1080Bar::class, - DDC1080FooBar::class + DDC1080FooBar::class, ); $foo1 = new DDC1080Foo(); @@ -76,32 +77,23 @@ public function testHydration(): void } -/** - * @Entity - * @Table(name="foo") - */ +#[Table(name: 'foo')] +#[Entity] class DDC1080Foo { - /** - * @var int - * @Id - * @Column(name="fooID", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'fooID', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $fooID; - /** - * @var string - * @Column(name="fooTitle", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'fooTitle', type: 'string', length: 255)] protected $fooTitle; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1080FooBar", mappedBy="foo", - * cascade={"persist"}) - * @OrderBy({"orderNr"="ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1080FooBar', mappedBy: 'foo', cascade: ['persist'])] + #[OrderBy(['orderNr' => 'ASC'])] protected $fooBars; public function __construct() @@ -140,32 +132,23 @@ public function setFooBars(array $fooBars): void $this->fooBars = $fooBars; } } -/** - * @Entity - * @Table(name="bar") - */ +#[Table(name: 'bar')] +#[Entity] class DDC1080Bar { - /** - * @var int - * @Id - * @Column(name="barID", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'barID', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $barID; - /** - * @var string - * @Column(name="barTitle", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'barTitle', type: 'string', length: 255)] protected $barTitle; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1080FooBar", mappedBy="bar", - * cascade={"persist"}) - * @OrderBy({"orderNr"="ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1080FooBar', mappedBy: 'bar', cascade: ['persist'])] + #[OrderBy(['orderNr' => 'ASC'])] protected $fooBars; public function __construct() @@ -205,32 +188,24 @@ public function setFooBars(array $fooBars): void } } -/** - * @Table(name="fooBar") - * @Entity - */ +#[Table(name: 'fooBar')] +#[Entity] class DDC1080FooBar { - /** - * @var DDC1080Foo - * @ManyToOne(targetEntity="DDC1080Foo") - * @JoinColumn(name="fooID", referencedColumnName="fooID") - * @Id - */ + /** @var DDC1080Foo */ + #[ManyToOne(targetEntity: 'DDC1080Foo')] + #[JoinColumn(name: 'fooID', referencedColumnName: 'fooID')] + #[Id] protected $foo = null; - /** - * @var DDC1080Bar - * @ManyToOne(targetEntity="DDC1080Bar") - * @JoinColumn(name="barID", referencedColumnName="barID") - * @Id - */ + /** @var DDC1080Bar */ + #[ManyToOne(targetEntity: 'DDC1080Bar')] + #[JoinColumn(name: 'barID', referencedColumnName: 'barID')] + #[Id] protected $bar = null; - /** - * @var int orderNr - * @Column(name="orderNr", type="integer", nullable=false) - */ + /** @var int orderNr */ + #[Column(name: 'orderNr', type: 'integer', nullable: false)] protected $orderNr = null; public function getFoo(): DDC1080Foo @@ -257,12 +232,12 @@ public function setBar(DDC1080Bar $bar): DDC1080FooBar return $this; } - public function getOrderNr(): ?int + public function getOrderNr(): int|null { return $this->orderNr; } - public function setOrderNr(?int $orderNr): DDC1080FooBar + public function setOrderNr(int|null $orderNr): DDC1080FooBar { $this->orderNr = $orderNr; diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1113Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1113Test.php index d02a855c56a..8409aed66cf 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1113Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1113Test.php @@ -13,11 +13,10 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-1113 - * @group DDC-1306 - */ +#[Group('DDC-1113')] +#[Group('DDC-1306')] class DDC1113Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -28,7 +27,7 @@ protected function setUp(): void DDC1113Engine::class, DDC1113Vehicle::class, DDC1113Car::class, - DDC1113Bus::class + DDC1113Bus::class, ); } @@ -56,52 +55,42 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"vehicle" = "DDC1113Vehicle", "car" = "DDC1113Car", "bus" = "DDC1113Bus"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['vehicle' => 'DDC1113Vehicle', 'car' => 'DDC1113Car', 'bus' => 'DDC1113Bus'])] class DDC1113Vehicle { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC1113Vehicle - * @ManyToOne(targetEntity="DDC1113Vehicle") - */ + /** @var DDC1113Vehicle */ + #[ManyToOne(targetEntity: 'DDC1113Vehicle')] public $parent; - /** - * @var DDC1113Engine - * @OneToOne(targetEntity="DDC1113Engine", cascade={"persist", "remove"}) - */ + /** @var DDC1113Engine */ + #[OneToOne(targetEntity: 'DDC1113Engine', cascade: ['persist', 'remove'])] public $engine; } -/** @Entity */ +#[Entity] class DDC1113Car extends DDC1113Vehicle { } -/** @Entity */ +#[Entity] class DDC1113Bus extends DDC1113Vehicle { } -/** @Entity */ +#[Entity] class DDC1113Engine { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1129Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1129Test.php index 4719bc12462..202b70d6563 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1129Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1129Test.php @@ -6,8 +6,9 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1129 */ +#[Group('DDC-1129')] class DDC1129Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1163Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1163Test.php index dbff2863d4e..c038908e696 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1163Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1163Test.php @@ -12,22 +12,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -use function get_class; -/** @group DDC-1163 */ +#[Group('DDC-1163')] class DDC1163Test extends OrmFunctionalTestCase { - /** @var int|null */ - private $productId; + private int|null $productId = null; - /** @var int|null */ - private $proxyHolderId; + private int|null $proxyHolderId = null; protected function setUp(): void { @@ -37,7 +34,7 @@ protected function setUp(): void DDC1163Product::class, DDC1163SpecialProduct::class, DDC1163ProxyHolder::class, - DDC1163Tag::class + DDC1163Tag::class, ); } @@ -98,7 +95,7 @@ private function setPropertyAndAssignTagToSpecialProduct(): void // this screams violation of law of demeter ;) self::assertEquals( DDC1163SpecialProduct::class, - $this->_em->getUnitOfWork()->getEntityPersister(get_class($specialProduct))->getClassMetadata()->name + $this->_em->getUnitOfWork()->getEntityPersister($specialProduct::class)->getClassMetadata()->name, ); $tag = new DDC1163Tag('Foo'); @@ -107,22 +104,16 @@ private function setPropertyAndAssignTagToSpecialProduct(): void } } -/** @Entity */ +#[Entity] class DDC1163ProxyHolder { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var DDC1163SpecialProduct - * @OneToOne(targetEntity="DDC1163SpecialProduct") - */ - private $specialProduct; + #[OneToOne(targetEntity: 'DDC1163SpecialProduct')] + private DDC1163SpecialProduct|null $specialProduct = null; public function getId(): int { @@ -140,20 +131,16 @@ public function getSpecialProduct(): DDC1163SpecialProduct } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"special" = "DDC1163SpecialProduct"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['special' => 'DDC1163SpecialProduct'])] abstract class DDC1163Product { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] protected $id; public function getId(): int @@ -162,14 +149,11 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class DDC1163SpecialProduct extends DDC1163Product { - /** - * @var string - * @Column(name="subclass_property", type="string", nullable=true) - */ - private $subclassProperty; + #[Column(name: 'subclass_property', type: 'string', nullable: true)] + private string|null $subclassProperty = null; public function setSubclassProperty(string $value): void { @@ -177,33 +161,22 @@ public function setSubclassProperty(string $value): void } } -/** @Entity */ +#[Entity] class DDC1163Tag { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ - private $name; - /** - * @var Product - * @ManyToOne(targetEntity="DDC1163Product", inversedBy="tags") - * @JoinColumns({ - * @JoinColumn(name="product_id", referencedColumnName="id") - * }) - */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + /** @var Product */ + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[ManyToOne(targetEntity: 'DDC1163Product', inversedBy: 'tags')] private $product; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(name: 'name', type: 'string', length: 255)] + private string $name, + ) { } public function setProduct(DDC1163Product $product): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC117Test.php b/tests/Tests/ORM/Functional/Ticket/DDC117Test.php index c18c683b042..dba78b9afd7 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC117Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC117Test.php @@ -4,7 +4,6 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\Models\DDC117\DDC117ApproveChanges; use Doctrine\Tests\Models\DDC117\DDC117Article; @@ -15,30 +14,23 @@ use Doctrine\Tests\Models\DDC117\DDC117Translation; use Doctrine\Tests\OrmFunctionalTestCase; use Exception; +use PHPUnit\Framework\Attributes\Group; use function assert; use function count; -use function get_class; -/** @group DDC-117 */ +#[Group('DDC-117')] class DDC117Test extends OrmFunctionalTestCase { - use VerifyDeprecations; + private DDC117Article|null $article1; - /** @var DDC117Article */ - private $article1; + private DDC117Article|null $article2; - /** @var DDC117Article */ - private $article2; + private DDC117Reference $reference; - /** @var DDC117Reference */ - private $reference; + private DDC117Translation|null $translation; - /** @var DDC117Translation */ - private $translation; - - /** @var DDC117ArticleDetails */ - private $articleDetails; + private DDC117ArticleDetails $articleDetails; protected function setUp(): void { @@ -69,7 +61,7 @@ protected function setUp(): void $this->_em->clear(); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testAssociationOnlyCompositeKey(): void { $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; @@ -106,7 +98,7 @@ public function testAssociationOnlyCompositeKey(): void $this->_em->contains($dqlRef); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testUpdateAssociationEntity(): void { $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; @@ -122,7 +114,7 @@ public function testUpdateAssociationEntity(): void self::assertEquals('New Description!!', $mapRef->getDescription()); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testFetchDql(): void { $dql = 'SELECT r, s FROM Doctrine\Tests\Models\DDC117\DDC117Reference r JOIN r.source s WHERE s.title = ?1'; @@ -136,7 +128,7 @@ public function testFetchDql(): void } } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testRemoveCompositeElement(): void { $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; @@ -150,10 +142,8 @@ public function testRemoveCompositeElement(): void self::assertNull($this->_em->find(DDC117Reference::class, $idCriteria)); } - /** - * @group DDC-117 - * @group non-cacheable - */ + #[Group('DDC-117')] + #[Group('non-cacheable')] public function testDqlRemoveCompositeElement(): void { $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; @@ -167,7 +157,7 @@ public function testDqlRemoveCompositeElement(): void self::assertNull($this->_em->find(DDC117Reference::class, $idCriteria)); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testInverseSideAccess(): void { $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id()); @@ -194,7 +184,7 @@ public function testInverseSideAccess(): void } } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testMixedCompositeKey(): void { $idCriteria = ['article' => $this->article1->id(), 'language' => 'en']; @@ -215,7 +205,7 @@ public function testMixedCompositeKey(): void self::assertInstanceOf(DDC117Translation::class, $this->translation); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testMixedCompositeKeyViolateUniqueness(): void { $this->article1 = $this->_em->find(DDC117Article::class, $this->article1->id()); @@ -227,7 +217,7 @@ public function testMixedCompositeKeyViolateUniqueness(): void $this->_em->flush(); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testOneToOneForeignObjectId(): void { $this->article1 = new DDC117Article('Foo'); @@ -242,22 +232,22 @@ public function testOneToOneForeignObjectId(): void $this->_em->flush(); $this->_em->clear(); - $article = $this->_em->find(get_class($this->article1), $this->article1->id()); + $article = $this->_em->find($this->article1::class, $this->article1->id()); assert($article instanceof DDC117Article); self::assertEquals('not so very long text!', $article->getText()); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testOneToOneCascadeRemove(): void { - $article = $this->_em->find(get_class($this->article1), $this->article1->id()); + $article = $this->_em->find($this->article1::class, $this->article1->id()); $this->_em->remove($article); $this->_em->flush(); self::assertFalse($this->_em->contains($article->getDetails())); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testOneToOneCascadePersist(): void { $this->article1 = new DDC117Article('Foo'); @@ -269,7 +259,7 @@ public function testOneToOneCascadePersist(): void self::assertSame($this->articleDetails, $this->_em->find(DDC117ArticleDetails::class, $this->article1)); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testReferencesToForeignKeyEntities(): void { $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; @@ -290,10 +280,10 @@ public function testReferencesToForeignKeyEntities(): void self::assertInstanceOf(DDC117Translation::class, $approveChanges->getTranslation()); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testLoadOneToManyCollectionOfForeignKeyEntities(): void { - $article = $this->_em->find(get_class($this->article1), $this->article1->id()); + $article = $this->_em->find($this->article1::class, $this->article1->id()); assert($article instanceof DDC117Article); $translations = $article->getTranslations(); @@ -302,7 +292,7 @@ public function testLoadOneToManyCollectionOfForeignKeyEntities(): void self::assertTrue($translations->isInitialized()); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testLoadManyToManyCollectionOfForeignKeyEntities(): void { $editor = $this->loadEditorFixture(); @@ -319,7 +309,7 @@ public function testLoadManyToManyCollectionOfForeignKeyEntities(): void self::assertContainsOnly(DDC117Translation::class, $editor->reviewingTranslations); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testClearManyToManyCollectionOfForeignKeyEntities(): void { $editor = $this->loadEditorFixture(); @@ -329,11 +319,11 @@ public function testClearManyToManyCollectionOfForeignKeyEntities(): void $this->_em->flush(); $this->_em->clear(); - $editor = $this->_em->find(get_class($editor), $editor->id); + $editor = $this->_em->find($editor::class, $editor->id); self::assertCount(0, $editor->reviewingTranslations); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testLoadInverseManyToManyCollection(): void { $editor = $this->loadEditorFixture(); @@ -358,7 +348,7 @@ public function testLoadInverseManyToManyCollection(): void self::assertCount(1, $trans->reviewedByEditors); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testLoadOneToManyOfSourceEntityWithAssociationIdentifier(): void { $editor = $this->loadEditorFixture(); @@ -367,7 +357,7 @@ public function testLoadOneToManyOfSourceEntityWithAssociationIdentifier(): void $this->_em->flush(); $this->_em->clear(); - $editor = $this->_em->find(get_class($editor), $editor->id); + $editor = $this->_em->find($editor::class, $editor->id); $lastTranslatedBy = $editor->reviewingTranslations[0]->getLastTranslatedBy(); $lastTranslatedBy->count(); @@ -378,13 +368,13 @@ private function loadEditorFixture(): DDC117Editor { $editor = new DDC117Editor('beberlei'); - $article1 = $this->_em->find(get_class($this->article1), $this->article1->id()); + $article1 = $this->_em->find($this->article1::class, $this->article1->id()); assert($article1 instanceof DDC117Article); foreach ($article1->getTranslations() as $translation) { $editor->reviewingTranslations[] = $translation; } - $article2 = $this->_em->find(get_class($this->article2), $this->article2->id()); + $article2 = $this->_em->find($this->article2::class, $this->article2->id()); assert($article2 instanceof DDC117Article); $article2->addTranslation('de', 'Vanille-Krapferl'); // omnomnom $article2->addTranslation('fr', "Sorry can't speak french!"); @@ -398,24 +388,10 @@ private function loadEditorFixture(): DDC117Editor $this->_em->flush(); $this->_em->clear(); - return $this->_em->find(get_class($editor), $editor->id); - } - - /** @group DDC-1519 */ - public function testMergeForeignKeyIdentifierEntity(): void - { - $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; - - $refRep = $this->_em->find(DDC117Reference::class, $idCriteria); - - $this->_em->clear(DDC117Reference::class); - $refRep = $this->_em->merge($refRep); - - self::assertEquals($this->article1->id(), $refRep->source()->id()); - self::assertEquals($this->article2->id(), $refRep->target()->id()); + return $this->_em->find($editor::class, $editor->id); } - /** @group DDC-1652 */ + #[Group('DDC-1652')] public function testArrayHydrationWithCompositeKey(): void { $dql = 'SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t'; @@ -441,7 +417,7 @@ public function testArrayHydrationWithCompositeKey(): void self::assertCount($before + 3, $data); } - /** @group DDC-2246 */ + #[Group('DDC-2246')] public function testGetEntityState(): void { if ($this->isSecondLevelCacheEnabled) { @@ -461,7 +437,7 @@ public function testGetEntityState(): void self::assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($reference)); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testIndexByOnCompositeKeyField(): void { $article = $this->_em->find(DDC117Article::class, $this->article1->id()); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1181Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1181Test.php index c4e2c87c240..77d3fe52ad7 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1181Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1181Test.php @@ -9,10 +9,10 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC1181Test extends OrmFunctionalTestCase { @@ -23,11 +23,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC1181Hotel::class, DDC1181Booking::class, - DDC1181Room::class + DDC1181Room::class, ); } - /** @group DDC-1181 */ + #[Group('DDC-1181')] public function testIssue(): void { $hotel = new DDC1181Hotel(); @@ -59,55 +59,41 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1181Hotel { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @OneToMany(targetEntity="DDC1181Booking", mappedBy="hotel", cascade={"remove"}) - * @var Booking[] - */ + /** @var Booking[] */ + #[OneToMany(targetEntity: 'DDC1181Booking', mappedBy: 'hotel', cascade: ['remove'])] public $bookings; } -/** @Entity */ +#[Entity] class DDC1181Booking { - /** - * @var Hotel - * @Id - * @ManyToOne(targetEntity="DDC1181Hotel", inversedBy="bookings") - * @JoinColumns({ - * @JoinColumn(name="hotel_id", referencedColumnName="id") - * }) - */ + /** @var Hotel */ + #[JoinColumn(name: 'hotel_id', referencedColumnName: 'id')] + #[Id] + #[ManyToOne(targetEntity: 'DDC1181Hotel', inversedBy: 'bookings')] public $hotel; - /** - * @var Room - * @Id - * @ManyToOne(targetEntity="DDC1181Room") - * @JoinColumns({ - * @JoinColumn(name="room_id", referencedColumnName="id") - * }) - */ + /** @var Room */ + #[JoinColumn(name: 'room_id', referencedColumnName: 'id')] + #[Id] + #[ManyToOne(targetEntity: 'DDC1181Room')] public $room; } -/** @Entity */ +#[Entity] class DDC1181Room { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1193Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1193Test.php index cc7967710c4..28f6073614e 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1193Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1193Test.php @@ -10,8 +10,7 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; class DDC1193Test extends OrmFunctionalTestCase { @@ -22,11 +21,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC1193Company::class, DDC1193Person::class, - DDC1193Account::class + DDC1193Account::class, ); } - /** @group DDC-1193 */ + #[Group('DDC-1193')] public function testIssue(): void { $company = new DDC1193Company(); @@ -42,7 +41,7 @@ public function testIssue(): void $companyId = $company->id; $this->_em->clear(); - $company = $this->_em->find(get_class($company), $companyId); + $company = $this->_em->find($company::class, $companyId); self::assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($company), 'Company is in identity map.'); self::assertTrue($this->isUninitializedObject($company->member), 'Pre-Condition'); @@ -51,54 +50,44 @@ public function testIssue(): void $this->_em->remove($company); $this->_em->flush(); - self::assertCount(0, $this->_em->getRepository(get_class($account))->findAll()); + self::assertCount(0, $this->_em->getRepository($account::class)->findAll()); } } -/** @Entity */ +#[Entity] class DDC1193Company { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC1193Person - * @OneToOne(targetEntity="DDC1193Person", cascade={"persist", "remove"}) - */ + /** @var DDC1193Person */ + #[OneToOne(targetEntity: 'DDC1193Person', cascade: ['persist', 'remove'])] public $member; } -/** @Entity */ +#[Entity] class DDC1193Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC1193Account - * @OneToOne(targetEntity="DDC1193Account", cascade={"persist", "remove"}) - */ + /** @var DDC1193Account */ + #[OneToOne(targetEntity: 'DDC1193Account', cascade: ['persist', 'remove'])] public $account; } -/** @Entity */ +#[Entity] class DDC1193Account { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1209Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1209Test.php index 9ec052b0026..c8ed1302f32 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1209Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1209Test.php @@ -12,6 +12,8 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; class DDC1209Test extends OrmFunctionalTestCase { @@ -22,11 +24,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC1209One::class, DDC1209Two::class, - DDC1209Three::class + DDC1209Three::class, ); } - /** @group DDC-1209 */ + #[Group('DDC-1209')] public function testIdentifierCanHaveCustomType(): void { $entity = new DDC1209Three(); @@ -37,7 +39,7 @@ public function testIdentifierCanHaveCustomType(): void self::assertSame($entity, $this->_em->find(DDC1209Three::class, $entity->date)); } - /** @group DDC-1209 */ + #[Group('DDC-1209')] public function testCompositeIdentifierCanHaveCustomType(): void { $future1 = new DDC1209One(); @@ -59,22 +61,19 @@ public function testCompositeIdentifierCanHaveCustomType(): void 'startingDatetime' => $future2->startingDatetime, 'duringDatetime' => $future2->duringDatetime, 'endingDatetime' => $future2->endingDatetime, - ] - ) + ], + ), ); } } -/** @Entity */ +#[Entity] class DDC1209One { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; public function getId(): int { @@ -82,55 +81,42 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class DDC1209Two { - /** - * @var DDC1209One - * @Id - * @ManyToOne(targetEntity="DDC1209One") - * @JoinColumn(referencedColumnName="id", nullable=false) - */ - private $future1; - - /** - * @var DateTime2 - * @Id - * @Column(type="datetime", nullable=false) - */ + /** @var DateTime2 */ + #[Id] + #[Column(type: 'datetime', nullable: false)] public $startingDatetime; - /** - * @var DateTime2 - * @Id - * @Column(type="datetime", nullable=false) - */ + /** @var DateTime2 */ + #[Id] + #[Column(type: 'datetime', nullable: false)] public $duringDatetime; - /** - * @var DateTime2 - * @Id - * @Column(type="datetime", nullable=false) - */ + /** @var DateTime2 */ + #[Id] + #[Column(type: 'datetime', nullable: false)] public $endingDatetime; - public function __construct(DDC1209One $future1) - { - $this->future1 = $future1; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'DDC1209One')] + #[JoinColumn(referencedColumnName: 'id', nullable: false)] + private DDC1209One $future1, + ) { $this->startingDatetime = new DateTime2(); $this->duringDatetime = new DateTime2(); $this->endingDatetime = new DateTime2(); } } -/** @Entity */ +#[Entity] class DDC1209Three { - /** - * @var DateTime2 - * @Id - * @Column(type="datetime", name="somedate") - */ + /** @var DateTime2 */ + #[Id] + #[Column(type: 'datetime', name: 'somedate')] public $date; public function __construct() @@ -139,7 +125,7 @@ public function __construct() } } -class DateTime2 extends DateTime +class DateTime2 extends DateTime implements Stringable { public function __toString(): string { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1225Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1225Test.php index 9649d82a5c4..a4e6efb69b0 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1225Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1225Test.php @@ -12,10 +12,11 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function strtolower; -/** @group DDC-1225 */ +#[Group('DDC-1225')] class DDC1225Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +25,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1225TestEntity1::class, - DDC1225TestEntity2::class + DDC1225TestEntity2::class, ); } @@ -38,24 +39,19 @@ public function testIssue(): void self::assertEquals( strtolower('SELECT t0_.test_entity2_id AS test_entity2_id_0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?'), - strtolower($qb->getQuery()->getSQL()) + strtolower($qb->getQuery()->getSQL()), ); } } -/** - * @Entity - * @Table(name="te1") - */ +#[Table(name: 'te1')] +#[Entity] class DDC1225TestEntity1 { - /** - * @var DDC1225TestEntity2 - * @Id - * @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC1225TestEntity2") - * @JoinColumn(name="test_entity2_id", referencedColumnName="id", nullable=false) - */ - private $testEntity2; + #[Id] + #[ManyToOne(targetEntity: 'Doctrine\Tests\ORM\Functional\Ticket\DDC1225TestEntity2')] + #[JoinColumn(name: 'test_entity2_id', referencedColumnName: 'id', nullable: false)] + private DDC1225TestEntity2|null $testEntity2 = null; public function setTestEntity2(DDC1225TestEntity2 $testEntity2): void { @@ -68,17 +64,12 @@ public function getTestEntity2(): DDC1225TestEntity2 } } -/** - * @Entity - * @Table(name="te2") - */ +#[Table(name: 'te2')] +#[Entity] class DDC1225TestEntity2 { - /** - * @var int - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] + private int $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1228Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1228Test.php index d5a95261c12..3f77bace8eb 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1228Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1228Test.php @@ -10,11 +10,10 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-1228 - * @group DDC-1226 - */ +#[Group('DDC-1228')] +#[Group('DDC-1226')] class DDC1228Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -76,27 +75,21 @@ public function testRefresh(): void } } -/** @Entity */ +#[Entity] class DDC1228User { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @Column(type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name = 'Bar'; - /** - * @var DDC1228Profile - * @OneToOne(targetEntity="DDC1228Profile") - */ + /** @var DDC1228Profile */ + #[OneToOne(targetEntity: 'DDC1228Profile')] public $profile; public function getProfile(): DDC1228Profile @@ -105,21 +98,17 @@ public function getProfile(): DDC1228Profile } } -/** @Entity */ +#[Entity] class DDC1228Profile { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @Column(type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; public function getName(): string diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1238Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1238Test.php index bdb1d70231a..7a3cce370fd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1238Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1238Test.php @@ -9,8 +9,9 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1238 */ +#[Group('DDC-1238')] class DDC1238Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -66,29 +67,23 @@ public function testIssueProxyClear(): void } } -/** @Entity */ +#[Entity] class DDC1238User { - /** - * @var int|null - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - private $id; - - /** - * @Column - * @var string|null - */ - private $name; - - public function getId(): ?int + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int|null $id = null; + + #[Column] + private string|null $name = null; + + public function getId(): int|null { return $this->id; } - public function getName(): ?string + public function getName(): string|null { return $this->name; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1250Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1250Test.php index 549ef7ddccc..38b972654d8 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1250Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1250Test.php @@ -11,8 +11,9 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1250 */ +#[Group('DDC-1250')] class DDC1250Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -43,28 +44,22 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1250ClientHistory { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC1250ClientHistory - * @OneToOne(targetEntity="DDC1250ClientHistory", inversedBy="declinedBy") - * @JoinColumn(name="declined_clients_history_id", referencedColumnName="id") - */ + /** @var DDC1250ClientHistory */ + #[OneToOne(targetEntity: 'DDC1250ClientHistory', inversedBy: 'declinedBy')] + #[JoinColumn(name: 'declined_clients_history_id', referencedColumnName: 'id')] public $declinedClientsHistory; - /** - * @var DDC1250ClientHistory - * @OneToOne(targetEntity="DDC1250ClientHistory", mappedBy="declinedClientsHistory") - */ + /** @var DDC1250ClientHistory */ + #[OneToOne(targetEntity: 'DDC1250ClientHistory', mappedBy: 'declinedClientsHistory')] public $declinedBy; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1276Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1276Test.php deleted file mode 100644 index de6f1116674..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1276Test.php +++ /dev/null @@ -1,52 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testIssue(): void - { - $user = new CmsUser(); - $user->name = 'Benjamin'; - $user->username = 'beberlei'; - $user->status = 'active'; - $this->_em->persist($user); - - for ($i = 0; $i < 2; $i++) { - $group = new CmsGroup(); - $group->name = 'group' . $i; - $user->groups[] = $group; - $this->_em->persist($group); - } - - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(CmsUser::class, $user->id); - $cloned = clone $user; - - self::assertSame($user->groups, $cloned->groups); - self::assertEquals(2, count($user->groups)); - $this->_em->merge($cloned); - - self::assertEquals(2, count($user->groups)); - - $this->_em->flush(); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1300Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1300Test.php index c58d77f7913..1bb898dc835 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1300Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1300Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1300 */ +#[Group('DDC-1300')] class DDC1300Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -23,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1300Foo::class, - DDC1300FooLocale::class + DDC1300FooLocale::class, ); } @@ -50,58 +51,45 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1300Foo { - /** - * @var int fooID - * @Column(name="fooID", type="integer", nullable=false) - * @GeneratedValue(strategy="AUTO") - * @Id - */ + /** @var int fooID */ + #[Column(name: 'fooID', type: 'integer', nullable: false)] + #[GeneratedValue(strategy: 'AUTO')] + #[Id] public $fooID = null; - /** - * @var string fooReference - * @Column(name="fooReference", type="string", nullable=true, length=45) - */ + /** @var string fooReference */ + #[Column(name: 'fooReference', type: 'string', nullable: true, length: 45)] public $fooReference = null; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1300FooLocale", mappedBy="foo", - * cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1300FooLocale', mappedBy: 'foo', cascade: ['persist'])] public $fooLocaleRefFoo = null; /** @param mixed[]|null $options */ - public function __construct(?array $options = null) + public function __construct(array|null $options = null) { $this->fooLocaleRefFoo = new ArrayCollection(); } } -/** @Entity */ +#[Entity] class DDC1300FooLocale { - /** - * @var DDC1300Foo - * @ManyToOne(targetEntity="DDC1300Foo") - * @JoinColumn(name="fooID", referencedColumnName="fooID") - * @Id - */ + /** @var DDC1300Foo */ + #[ManyToOne(targetEntity: 'DDC1300Foo')] + #[JoinColumn(name: 'fooID', referencedColumnName: 'fooID')] + #[Id] public $foo = null; - /** - * @var string locale - * @Column(name="locale", type="string", nullable=false, length=5) - * @Id - */ + /** @var string locale */ + #[Column(name: 'locale', type: 'string', nullable: false, length: 5)] + #[Id] public $locale = null; - /** - * @var string title - * @Column(name="title", type="string", nullable=true, length=150) - */ + /** @var string title */ + #[Column(name: 'title', type: 'string', nullable: true, length: 150)] public $title = null; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1301Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1301Test.php index 8880af1a2f7..746250e2acd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1301Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1301Test.php @@ -5,17 +5,18 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\Tests\Models; +use Doctrine\Tests\Models\Legacy\LegacyArticle; +use Doctrine\Tests\Models\Legacy\LegacyCar; +use Doctrine\Tests\Models\Legacy\LegacyUser; +use Doctrine\Tests\Models\Legacy\LegacyUserReference; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group non-cacheable - * @group DDC-1301 - */ +#[Group('non-cacheable')] +#[Group('DDC-1301')] class DDC1301Test extends OrmFunctionalTestCase { - /** @var int */ - private $userId; + private int|null $userId = null; protected function setUp(): void { @@ -23,10 +24,10 @@ protected function setUp(): void parent::setUp(); - $class = $this->_em->getClassMetadata(Models\Legacy\LegacyUser::class); - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['references']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; - $class->associationMappings['cars']['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + $class = $this->_em->getClassMetadata(LegacyUser::class); + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['references']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; + $class->associationMappings['cars']->fetch = ClassMetadata::FETCH_EXTRA_LAZY; $this->loadFixture(); } @@ -35,15 +36,15 @@ public function tearDown(): void { parent::tearDown(); - $class = $this->_em->getClassMetadata(Models\Legacy\LegacyUser::class); - $class->associationMappings['articles']['fetch'] = ClassMetadata::FETCH_LAZY; - $class->associationMappings['references']['fetch'] = ClassMetadata::FETCH_LAZY; - $class->associationMappings['cars']['fetch'] = ClassMetadata::FETCH_LAZY; + $class = $this->_em->getClassMetadata(LegacyUser::class); + $class->associationMappings['articles']->fetch = ClassMetadata::FETCH_LAZY; + $class->associationMappings['references']->fetch = ClassMetadata::FETCH_LAZY; + $class->associationMappings['cars']->fetch = ClassMetadata::FETCH_LAZY; } public function testCountNotInitializesLegacyCollection(): void { - $user = $this->_em->find(Models\Legacy\LegacyUser::class, $this->userId); + $user = $this->_em->find(LegacyUser::class, $this->userId); $this->getQueryLog()->reset()->enable(); self::assertFalse($user->articles->isInitialized()); @@ -58,7 +59,7 @@ public function testCountNotInitializesLegacyCollection(): void public function testCountNotInitializesLegacyCollectionWithForeignIdentifier(): void { - $user = $this->_em->find(Models\Legacy\LegacyUser::class, $this->userId); + $user = $this->_em->find(LegacyUser::class, $this->userId); $this->getQueryLog()->reset()->enable(); self::assertFalse($user->references->isInitialized()); @@ -73,7 +74,7 @@ public function testCountNotInitializesLegacyCollectionWithForeignIdentifier(): public function testCountNotInitializesLegacyManyToManyCollection(): void { - $user = $this->_em->find(Models\Legacy\LegacyUser::class, $this->userId); + $user = $this->_em->find(LegacyUser::class, $this->userId); $this->getQueryLog()->reset()->enable(); self::assertFalse($user->cars->isInitialized()); @@ -88,15 +89,15 @@ public function testCountNotInitializesLegacyManyToManyCollection(): void public function loadFixture(): void { - $user1 = new Models\Legacy\LegacyUser(); + $user1 = new LegacyUser(); $user1->username = 'beberlei'; $user1->name = 'Benjamin'; - $user2 = new Models\Legacy\LegacyUser(); + $user2 = new LegacyUser(); $user2->username = 'jwage'; $user2->name = 'Jonathan'; - $user3 = new Models\Legacy\LegacyUser(); + $user3 = new LegacyUser(); $user3->username = 'romanb'; $user3->name = 'Roman'; @@ -104,12 +105,12 @@ public function loadFixture(): void $this->_em->persist($user2); $this->_em->persist($user3); - $article1 = new Models\Legacy\LegacyArticle(); + $article1 = new LegacyArticle(); $article1->topic = 'Test'; $article1->text = 'Test'; $article1->setAuthor($user1); - $article2 = new Models\Legacy\LegacyArticle(); + $article2 = new LegacyArticle(); $article2->topic = 'Test'; $article2->text = 'Test'; $article2->setAuthor($user1); @@ -117,13 +118,13 @@ public function loadFixture(): void $this->_em->persist($article1); $this->_em->persist($article2); - $car1 = new Models\Legacy\LegacyCar(); + $car1 = new LegacyCar(); $car1->description = 'Test1'; - $car2 = new Models\Legacy\LegacyCar(); + $car2 = new LegacyCar(); $car2->description = 'Test2'; - $car3 = new Models\Legacy\LegacyCar(); + $car3 = new LegacyCar(); $car3->description = 'Test3'; $user1->addCar($car1); @@ -139,8 +140,8 @@ public function loadFixture(): void $this->_em->flush(); - $detail1 = new Models\Legacy\LegacyUserReference($user1, $user2, 'foo'); - $detail2 = new Models\Legacy\LegacyUserReference($user1, $user3, 'bar'); + $detail1 = new LegacyUserReference($user1, $user2, 'foo'); + $detail2 = new LegacyUserReference($user1, $user3, 'bar'); $this->_em->persist($detail1); $this->_em->persist($detail2); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1306Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1306Test.php index 6af2cbd4e6c..0affa231990 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1306Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1306Test.php @@ -8,8 +8,9 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1306 */ +#[Group('DDC-1306')] class DDC1306Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1335Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1335Test.php index 083283901ea..69156476c44 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1335Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1335Test.php @@ -15,8 +15,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1335 */ +#[Group('DDC-1335')] class DDC1335Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -26,7 +27,7 @@ protected function setUp(): void $this->createSchemaForModels(DDC1335User::class, DDC1335Phone::class); try { $this->loadFixture(); - } catch (UniqueConstraintViolationException $e) { + } catch (UniqueConstraintViolationException) { } } @@ -153,39 +154,25 @@ private function loadFixture(): void } } -/** @Entity */ +#[Entity] class DDC1335User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255, unique=true) - */ - public $email; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1335Phone", mappedBy="user", cascade={"persist", "remove"}) - */ - public $phones; - - public function __construct($email, $name, array $numbers = []) - { - $this->name = $name; - $this->email = $email; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + public int|null $id = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1335Phone', mappedBy: 'user', cascade: ['persist', 'remove'])] + public Collection $phones; + + public function __construct( + #[Column(type: 'string', length: 255, unique: true)] + public string $email, + #[Column(type: 'string', length: 255)] + public string $name, + array $numbers = [], + ) { $this->phones = new ArrayCollection(); foreach ($numbers as $number) { @@ -194,33 +181,20 @@ public function __construct($email, $name, array $numbers = []) } } -/** @Entity */ +#[Entity] class DDC1335Phone { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue - */ - public $id; - - /** - * @var string - * @Column(name="numericalValue", type="string", nullable = false) - */ - public $numericalValue; - - /** - * @var DDC1335User - * @ManyToOne(targetEntity="DDC1335User", inversedBy="phones") - * @JoinColumn(name="user_id", referencedColumnName="id", nullable = false) - */ - public $user; - - public function __construct($user, $number) - { - $this->user = $user; - $this->numericalValue = $number; + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue] + public int|null $id = null; + + public function __construct( + #[ManyToOne(targetEntity: 'DDC1335User', inversedBy: 'phones')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)] + public DDC1335User $user, + #[Column(name: 'numericalValue', type: 'string', nullable: false)] + public string $numericalValue, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1383Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1383Test.php deleted file mode 100644 index bdfae404c1d..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1383Test.php +++ /dev/null @@ -1,110 +0,0 @@ -createSchemaForModels( - DDC1383AbstractEntity::class, - DDC1383Entity::class - ); - } - - public function testFailingCase(): void - { - $parent = new DDC1383Entity(); - $child = new DDC1383Entity(); - - $child->setReference($parent); - - $this->_em->persist($parent); - $this->_em->persist($child); - - $id = $child->getId(); - - $this->_em->flush(); - $this->_em->clear(); - - // Try merging the parent entity - $child = $this->_em->merge($child); - $parent = $child->getReference(); - - // Parent is not instance of the abstract class - self::assertTrue( - $parent instanceof DDC1383AbstractEntity, - 'Entity class is ' . get_debug_type($parent) . ', "DDC1383AbstractEntity" was expected' - ); - - // Parent is NOT instance of entity - self::assertTrue( - $parent instanceof DDC1383Entity, - 'Entity class is ' . get_debug_type($parent) . ', "DDC1383Entity" was expected' - ); - } -} - -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="integer") - * @DiscriminatorMap({1 = "DDC1383Entity"}) - */ -abstract class DDC1383AbstractEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - protected $id; - - public function getId(): ?int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; - } -} - -/** @Entity */ -class DDC1383Entity extends DDC1383AbstractEntity -{ - /** - * @var DDC1383AbstractEntity - * @ManyToOne(targetEntity="DDC1383AbstractEntity") - */ - protected $reference; - - public function getReference(): DDC1383AbstractEntity - { - return $this->reference; - } - - public function setReference(DDC1383AbstractEntity $reference): void - { - $this->reference = $reference; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1392Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1392Test.php deleted file mode 100644 index 0bf3cf27107..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1392Test.php +++ /dev/null @@ -1,115 +0,0 @@ -createSchemaForModels(DDC1392File::class, DDC1392Picture::class); - } - - public function testFailingCase(): void - { - $file = new DDC1392File(); - - $picture = new DDC1392Picture(); - $picture->setFile($file); - - $em = $this->_em; - $em->persist($picture); - $em->flush(); - $em->clear(); - - $fileId = $file->getFileId(); - $pictureId = $picture->getPictureId(); - - self::assertTrue($fileId > 0); - - $picture = $em->find(DDC1392Picture::class, $pictureId); - self::assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), 'Lazy Proxy should be marked MANAGED.'); - - $file = $picture->getFile(); - - // With this activated there will be no problem - //$file->__load(); - - $picture->setFile(null); - - $em->clear(); - - $em->merge($file); - - $em->flush(); - - $q = $this->_em->createQuery('SELECT COUNT(e) FROM ' . __NAMESPACE__ . '\DDC1392File e'); - $result = $q->getSingleScalarResult(); - - self::assertEquals(1, $result); - } -} - -/** @Entity */ -class DDC1392Picture -{ - /** - * @var int - * @Column(name="picture_id", type="integer") - * @Id - * @GeneratedValue - */ - private $pictureId; - - /** - * @var DDC1392File - * @ManyToOne(targetEntity="DDC1392File", cascade={"persist", "remove"}) - * @JoinColumn(name="file_id", referencedColumnName="file_id") - */ - private $file; - - public function getPictureId(): int - { - return $this->pictureId; - } - - public function setFile(?DDC1392File $value = null): void - { - $this->file = $value; - } - - public function getFile(): ?DDC1392File - { - return $this->file; - } -} - -/** @Entity */ -class DDC1392File -{ - /** - * @var int - * @Column(name="file_id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $fileId; - - public function getFileId(): int - { - return $this->fileId; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1400Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1400Test.php index 1f756d3aa94..6b5d8002d5a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1400Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1400Test.php @@ -12,19 +12,20 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1400 */ +#[Group('DDC-1400')] class DDC1400Test extends OrmFunctionalTestCase { protected function setUp(): void { parent::setUp(); - $this->createSchemaForModels( - DDC1400Article::class, - DDC1400User::class, - DDC1400UserState::class - ); + $this->createSchemaForModels( + DDC1400Article::class, + DDC1400User::class, + DDC1400UserState::class, + ); } public function testFailingCase(): void @@ -69,68 +70,52 @@ public function testFailingCase(): void } } -/** @Entity */ +#[Entity] class DDC1400Article { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1400UserState", mappedBy="article", indexBy="userId", fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1400UserState', mappedBy: 'article', indexBy: 'userId', fetch: 'EXTRA_LAZY')] public $userStates; } -/** @Entity */ +#[Entity] class DDC1400User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1400UserState", mappedBy="user", indexBy="articleId", fetch="EXTRA_LAZY") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1400UserState', mappedBy: 'user', indexBy: 'articleId', fetch: 'EXTRA_LAZY')] public $userStates; } -/** @Entity */ +#[Entity] class DDC1400UserState { - /** - * @var DDC1400Article - * @Id - * @ManyToOne(targetEntity="DDC1400Article", inversedBy="userStates") - */ + /** @var DDC1400Article */ + #[Id] + #[ManyToOne(targetEntity: 'DDC1400Article', inversedBy: 'userStates')] public $article; - /** - * @var DDC1400User - * @Id - * @ManyToOne(targetEntity="DDC1400User", inversedBy="userStates") - */ + /** @var DDC1400User */ + #[Id] + #[ManyToOne(targetEntity: 'DDC1400User', inversedBy: 'userStates')] public $user; - /** - * @var int - * @Column(name="user_id", type="integer") - */ + /** @var int */ + #[Column(name: 'user_id', type: 'integer')] public $userId; - /** - * @var int - * @Column(name="article_id", type="integer") - */ + /** @var int */ + #[Column(name: 'article_id', type: 'integer')] public $articleId; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1404Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1404Test.php deleted file mode 100644 index 8c032bd5722..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1404Test.php +++ /dev/null @@ -1,116 +0,0 @@ -createSchemaForModels( - DDC1404ParentEntity::class, - DDC1404ChildEntity::class - ); - - $this->loadFixtures(); - } - - public function testTicket(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8592'); - - $repository = $this->_em->getRepository(DDC1404ChildEntity::class); - $queryAll = $repository->createNamedQuery('all'); - $queryFirst = $repository->createNamedQuery('first'); - $querySecond = $repository->createNamedQuery('second'); - - self::assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p', $queryAll->getDQL()); - self::assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p WHERE p.id = 1', $queryFirst->getDQL()); - self::assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p WHERE p.id = 2', $querySecond->getDQL()); - - self::assertCount(2, $queryAll->getResult()); - self::assertCount(1, $queryFirst->getResult()); - self::assertCount(1, $querySecond->getResult()); - } - - public function loadFixtures(): void - { - $c1 = new DDC1404ChildEntity('ChildEntity 1'); - $c2 = new DDC1404ChildEntity('ChildEntity 2'); - - $this->_em->persist($c1); - $this->_em->persist($c2); - - $this->_em->flush(); - } -} - -/** - * @MappedSuperclass - * @NamedQueries({ - * @NamedQuery(name="all", query="SELECT p FROM __CLASS__ p"), - * @NamedQuery(name="first", query="SELECT p FROM __CLASS__ p WHERE p.id = 1"), - * }) - */ -class DDC1404ParentEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue() - */ - protected $id; - - public function getId(): int - { - return $this->id; - } -} - -/** - * @Entity - * @NamedQueries({ - * @NamedQuery(name="first", query="SELECT p FROM __CLASS__ p WHERE p.id = 1"), - * @NamedQuery(name="second", query="SELECT p FROM __CLASS__ p WHERE p.id = 2") - * }) - */ -class DDC1404ChildEntity extends DDC1404ParentEntity -{ - /** - * @var string - * @Column(type="string") - */ - private $name; - - public function __construct(string $name) - { - $this->name = $name; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->name = $name; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC142Test.php b/tests/Tests/ORM/Functional/Ticket/DDC142Test.php index 16806987ace..1f542f36039 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC142Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC142Test.php @@ -7,11 +7,10 @@ use Doctrine\Tests\Models\Quote\Address; use Doctrine\Tests\Models\Quote\User; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-1845 - * @group DDC-142 - */ +#[Group('DDC-1845')] +#[Group('DDC-142')] class DDC142Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1430Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1430Test.php index b2f3202263a..8a1bf5b646a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1430Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1430Test.php @@ -16,8 +16,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; use Exception; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1430 */ +#[Group('DDC-1430')] class DDC1430Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -29,10 +30,10 @@ protected function setUp(): void [ $this->_em->getClassMetadata(DDC1430Order::class), $this->_em->getClassMetadata(DDC1430OrderProduct::class), - ] + ], ); $this->loadFixtures(); - } catch (Exception $exc) { + } catch (Exception) { } } @@ -139,43 +140,30 @@ public function loadFixtures(): void } } -/** @Entity */ +#[Entity] class DDC1430Order { - /** - * @var int - * @Id - * @Column(name="order_id", type="integer") - * @GeneratedValue() - */ + /** @var int */ + #[Id] + #[Column(name: 'order_id', type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var DateTime - * @Column(name="created_at", type="datetime") - */ - private $date; + #[Column(name: 'created_at', type: 'datetime')] + private DateTime $date; - /** - * @var string - * @Column(name="order_status", type="string", length=255) - */ - private $status; - - /** - * @OneToMany(targetEntity="DDC1430OrderProduct", mappedBy="order", cascade={"persist", "remove"}) - * @var Collection $products - */ - private $products; + #[OneToMany(targetEntity: 'DDC1430OrderProduct', mappedBy: 'order', cascade: ['persist', 'remove'])] + private Collection $products; public function getId(): int { return $this->id; } - public function __construct(string $status) - { - $this->status = $status; + public function __construct( + #[Column(name: 'order_status', type: 'string', length: 255)] + private string $status, + ) { $this->date = new DateTime(); $this->products = new ArrayCollection(); } @@ -207,33 +195,23 @@ public function addProduct(DDC1430OrderProduct $product): void } } -/** @Entity */ +#[Entity] class DDC1430OrderProduct { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue() - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var DDC1430Order $order - * @ManyToOne(targetEntity="DDC1430Order", inversedBy="products") - * @JoinColumn(name="order_id", referencedColumnName="order_id", nullable = false) - */ - private $order; - - /** - * @var float - * @Column(type="float") - */ - private $value; + #[ManyToOne(targetEntity: 'DDC1430Order', inversedBy: 'products')] + #[JoinColumn(name: 'order_id', referencedColumnName: 'order_id', nullable: false)] + private DDC1430Order|null $order = null; - public function __construct(float $value) - { - $this->value = $value; + public function __construct( + #[Column(type: 'float')] + private float $value, + ) { } public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1436Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1436Test.php index 8ffa2a499d0..b3cd965f413 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1436Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1436Test.php @@ -11,8 +11,9 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1436 */ +#[Group('DDC-1436')] class DDC1436Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -54,22 +55,18 @@ public function testIdentityMap(): void } } -/** @Entity */ +#[Entity] class DDC1436Page { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="id") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: 'id')] protected $id; - /** - * @var DDC1436Page - * @ManyToOne(targetEntity="DDC1436Page") - * @JoinColumn(name="pid", referencedColumnName="id") - */ + /** @var DDC1436Page */ + #[ManyToOne(targetEntity: 'DDC1436Page')] + #[JoinColumn(name: 'pid', referencedColumnName: 'id')] protected $parent; public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC144Test.php b/tests/Tests/ORM/Functional/Ticket/DDC144Test.php index fb905ae22ed..593add03fbc 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC144Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC144Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC144Test extends OrmFunctionalTestCase { @@ -22,11 +23,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC144FlowElement::class, - DDC144Operand::class + DDC144Operand::class, ); } - /** @group DDC-144 */ + #[Group('DDC-144')] public function testIssue(): void { $operand = new DDC144Operand(); @@ -40,27 +41,21 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="ddc144_flowelements") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(type="string", name="discr") - * @DiscriminatorMap({"flowelement" = "DDC144FlowElement", "operand" = "DDC144Operand"}) - */ +#[Table(name: 'ddc144_flowelements')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(type: 'string', name: 'discr')] +#[DiscriminatorMap(['flowelement' => 'DDC144FlowElement', 'operand' => 'DDC144Operand'])] class DDC144FlowElement { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $property; } @@ -69,16 +64,12 @@ abstract class DDC144Expression extends DDC144FlowElement abstract public function method(): void; } -/** - * @Entity - * @Table(name="ddc144_operands") - */ +#[Table(name: 'ddc144_operands')] +#[Entity] class DDC144Operand extends DDC144Expression { - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $operandProperty; public function method(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1452Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1452Test.php index 5e4151aec6d..f3095c2b924 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1452Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1452Test.php @@ -15,8 +15,9 @@ use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1452 */ +#[Group('DDC-1452')] class DDC1452Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +28,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1452EntityA::class, - DDC1452EntityB::class + DDC1452EntityB::class, ); } @@ -90,27 +91,21 @@ public function testFetchJoinOneToOneFromInverse(): void } } -/** @Entity */ +#[Entity] class DDC1452EntityA { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $title; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1452EntityB", mappedBy="entityAFrom") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1452EntityB', mappedBy: 'entityAFrom')] public $entitiesB; public function __construct() @@ -125,25 +120,19 @@ public function getEntitiesB(): Collection } } -/** @Entity */ +#[Entity] class DDC1452EntityB { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC1452EntityA - * @ManyToOne(targetEntity="DDC1452EntityA", inversedBy="entitiesB") - */ + /** @var DDC1452EntityA */ + #[ManyToOne(targetEntity: 'DDC1452EntityA', inversedBy: 'entitiesB')] public $entityAFrom; - /** - * @var DDC1452EntityA - * @ManyToOne(targetEntity="DDC1452EntityA") - */ + /** @var DDC1452EntityA */ + #[ManyToOne(targetEntity: 'DDC1452EntityA')] public $entityATo; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1454Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1454Test.php index 28632ee4bf3..51629664dd7 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1454Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1454Test.php @@ -12,11 +12,12 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function getrandmax; +use function mt_getrandmax; use function random_int; -/** @group DDC-1454 */ +#[Group('DDC-1454')] class DDC1454Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -34,29 +35,25 @@ public function testFailingCase(): void } } -/** @Entity */ +#[Entity] class DDC1454Picture extends DDC1454File { } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"file" = "DDC1454File", "picture" = "DDC1454Picture"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['file' => 'DDC1454File', 'picture' => 'DDC1454Picture'])] class DDC1454File { - /** - * @var int - * @Column(name="file_id", type="integer") - * @Id - */ + /** @var int */ + #[Column(name: 'file_id', type: 'integer')] + #[Id] public $fileId; public function __construct() { - $this->fileId = random_int(0, getrandmax()); + $this->fileId = random_int(0, mt_getrandmax()); } public function getFileId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1458Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1458Test.php index b45ed39b984..5ea50bca219 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1458Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1458Test.php @@ -19,7 +19,7 @@ protected function setUp(): void $this->createSchemaForModels( TestEntity::class, - TestAdditionalEntity::class + TestAdditionalEntity::class, ); } @@ -58,27 +58,21 @@ public function testIssue(): void } -/** @Entity */ +#[Entity] class TestEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] protected $value; - /** - * @var TestAdditionalEntity - * @OneToOne(targetEntity="TestAdditionalEntity", inversedBy="entity", orphanRemoval=true, cascade={"persist", "remove"}) - */ + /** @var TestAdditionalEntity */ + #[OneToOne(targetEntity: 'TestAdditionalEntity', inversedBy: 'entity', orphanRemoval: true, cascade: ['persist', 'remove'])] protected $additional; public function getValue(): int @@ -101,26 +95,20 @@ public function setAdditional(TestAdditionalEntity $additional): void $this->additional = $additional; } } -/** @Entity */ +#[Entity] class TestAdditionalEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var TestEntity - * @OneToOne(targetEntity="TestEntity", mappedBy="additional") - */ + /** @var TestEntity */ + #[OneToOne(targetEntity: 'TestEntity', mappedBy: 'additional')] protected $entity; - /** - * @var bool - * @Column(type="boolean") - */ + /** @var bool */ + #[Column(type: 'boolean')] protected $bool; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1461Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1461Test.php index 50bedb26e0b..47ad88b9eb5 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1461Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1461Test.php @@ -12,10 +12,9 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-1461 */ +#[Group('DDC-1461')] class DDC1461Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +23,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1461TwitterAccount::class, - DDC1461User::class + DDC1461User::class, ); } @@ -43,49 +42,37 @@ public function testChangeDetectionDeferredExplicit(): void $this->_em->persist($user); $this->_em->flush(); - $user = $this->_em->find(get_class($user), $user->id); + $user = $this->_em->find($user::class, $user->id); self::assertNotNull($user->twitterAccount); } } -/** - * @Entity - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class DDC1461User { - /** - * @var int - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC1461TwitterAccount - * @OneToOne(targetEntity="DDC1461TwitterAccount", orphanRemoval=true, fetch="EAGER", cascade = {"persist"}, inversedBy="user") - */ + /** @var DDC1461TwitterAccount */ + #[OneToOne(targetEntity: 'DDC1461TwitterAccount', orphanRemoval: true, fetch: 'EAGER', cascade: ['persist'], inversedBy: 'user')] public $twitterAccount; } -/** - * @Entity - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class DDC1461TwitterAccount { - /** - * @var int - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC1461User - * @OneToOne(targetEntity="DDC1461User", fetch="EAGER") - */ + /** @var DDC1461User */ + #[OneToOne(targetEntity: 'DDC1461User', fetch: 'EAGER')] public $user; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1509Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1509Test.php deleted file mode 100644 index 4dd6363e742..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1509Test.php +++ /dev/null @@ -1,133 +0,0 @@ -createSchemaForModels( - DDC1509AbstractFile::class, - DDC1509File::class, - DDC1509Picture::class - ); - } - - public function testFailingCase(): void - { - $file = new DDC1509File(); - $thumbnail = new DDC1509File(); - - $picture = new DDC1509Picture(); - $picture->setFile($file); - $picture->setThumbnail($thumbnail); - - $em = $this->_em; - assert($em instanceof EntityManager); - $em->persist($picture); - $em->flush(); - $em->clear(); - - $id = $picture->getPictureId(); - - $pic = $em->merge($picture); - assert($pic instanceof DDC1509Picture); - - self::assertNotNull($pic->getThumbnail()); - self::assertNotNull($pic->getFile()); - } -} - -/** @Entity */ -class DDC1509Picture -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var DDC1509AbstractFile - * @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"}) - */ - private $thumbnail; - - /** - * @var DDC1509AbstractFile|null - * @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"}) - */ - private $file; - - public function getPictureId(): int - { - return $this->id; - } - - public function setFile(?DDC1509AbstractFile $value = null): void - { - $this->file = $value; - } - - public function getFile(): ?DDC1509AbstractFile - { - return $this->file; - } - - public function getThumbnail(): DDC1509AbstractFile - { - return $this->thumbnail; - } - - public function setThumbnail(DDC1509AbstractFile $thumbnail): void - { - $this->thumbnail = $thumbnail; - } -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"abstractFile" = "DDC1509AbstractFile", "file" = "DDC1509File"}) - */ -class DDC1509AbstractFile -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - public function getFileId(): int - { - return $this->id; - } -} - -/** @Entity */ -class DDC1509File extends DDC1509AbstractFile -{ -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1514Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1514Test.php index 0672dc5f933..a8dac0915eb 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1514Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1514Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1514 */ +#[Group('DDC-1514')] class DDC1514Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +25,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1514EntityA::class, DDC1514EntityB::class, - DDC1514EntityC::class + DDC1514EntityC::class, ); } @@ -67,33 +68,25 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1514EntityA { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $title; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC1514EntityB", mappedBy="entityAFrom") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC1514EntityB', mappedBy: 'entityAFrom')] public $entitiesB; - /** - * @var DDC1514EntityC - * @ManyToOne(targetEntity="DDC1514EntityC") - */ + /** @var DDC1514EntityC */ + #[ManyToOne(targetEntity: 'DDC1514EntityC')] public $entityC; public function __construct() @@ -102,43 +95,33 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC1514EntityB { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC1514EntityA - * @ManyToOne(targetEntity="DDC1514EntityA", inversedBy="entitiesB") - */ + /** @var DDC1514EntityA */ + #[ManyToOne(targetEntity: 'DDC1514EntityA', inversedBy: 'entitiesB')] public $entityAFrom; - /** - * @var DDC1514EntityA - * @ManyToOne(targetEntity="DDC1514EntityA") - */ + /** @var DDC1514EntityA */ + #[ManyToOne(targetEntity: 'DDC1514EntityA')] public $entityATo; } -/** @Entity */ +#[Entity] class DDC1514EntityC { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $title; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1515Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1515Test.php index d22e88a1384..7b3aa08974e 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1515Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1515Test.php @@ -10,8 +10,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1515 */ +#[Group('DDC-1515')] class DDC1515Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -20,7 +21,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1515Foo::class, - DDC1515Bar::class + DDC1515Bar::class, ); } @@ -41,31 +42,25 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1515Foo { - /** - * @var DDC1515Bar - * @OneToOne(targetEntity="DDC1515Bar", inversedBy="foo") - * @Id - */ + /** @var DDC1515Bar */ + #[OneToOne(targetEntity: 'DDC1515Bar', inversedBy: 'foo')] + #[Id] public $bar; } -/** @Entity */ +#[Entity] class DDC1515Bar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC1515Foo - * @OneToOne(targetEntity="DDC1515Foo", mappedBy="bar") - */ + /** @var DDC1515Foo */ + #[OneToOne(targetEntity: 'DDC1515Foo', mappedBy: 'bar')] public $foo; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1526Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1526Test.php index 27fdbea3d37..cad27a4f057 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1526Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1526Test.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1526 */ +#[Group('DDC-1526')] class DDC1526Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -52,25 +53,19 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1526Menu { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var DDC1526Menu - * @ManyToOne(targetEntity="DDC1526Menu", inversedBy="children") - */ + /** @var DDC1526Menu */ + #[ManyToOne(targetEntity: 'DDC1526Menu', inversedBy: 'children')] public $parent; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1526Menu", mappedBy="parent") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1526Menu', mappedBy: 'parent')] public $children; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1545Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1545Test.php index 95fff41fff5..fdfaf740345 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1545Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1545Test.php @@ -7,18 +7,16 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1545 */ +#[Group('DDC-1545')] class DDC1545Test extends OrmFunctionalTestCase { - /** @var int */ - private $articleId; + private int|null $articleId = null; - /** @var int */ - private $userId; + private int|null $userId = null; - /** @var int */ - private $user2Id; + private int|null $user2Id = null; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1548Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1548Test.php index 872c508a989..ae07efd4791 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1548Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1548Test.php @@ -10,8 +10,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1548 */ +#[Group('DDC-1548')] class DDC1548Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -21,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1548E1::class, DDC1548E2::class, - DDC1548Rel::class + DDC1548Rel::class, ); } @@ -43,47 +44,37 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC1548E1 { - /** - * @var DDC1548Rel - * @Id - * @OneToOne(targetEntity="DDC1548Rel", inversedBy="e1") - */ + /** @var DDC1548Rel */ + #[Id] + #[OneToOne(targetEntity: 'DDC1548Rel', inversedBy: 'e1')] public $rel; } -/** @Entity */ +#[Entity] class DDC1548E2 { - /** - * @var DDC1548Rel - * @Id - * @OneToOne(targetEntity="DDC1548Rel", inversedBy="e2") - */ + /** @var DDC1548Rel */ + #[Id] + #[OneToOne(targetEntity: 'DDC1548Rel', inversedBy: 'e2')] public $rel; } -/** @Entity */ +#[Entity] class DDC1548Rel { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC1548E1 - * @OneToOne(targetEntity="DDC1548E1", mappedBy="rel") - */ + /** @var DDC1548E1 */ + #[OneToOne(targetEntity: 'DDC1548E1', mappedBy: 'rel')] public $e1; - /** - * @var DDC1548E2 - * @OneToOne(targetEntity="DDC1548E2", mappedBy="rel") - */ + /** @var DDC1548E2 */ + #[OneToOne(targetEntity: 'DDC1548E2', mappedBy: 'rel')] public $e2; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1594Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1594Test.php deleted file mode 100644 index cf60a0303a9..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1594Test.php +++ /dev/null @@ -1,45 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testIssue(): void - { - $user = new CmsUser(); - $user->status = 'foo'; - $user->username = 'foo'; - $user->name = 'foo'; - - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - $detachedUser = clone $user; - $detachedUser->name = 'bar'; - $detachedUser->status = 'bar'; - - $newUser = $this->_em->getReference(get_class($user), $user->id); - - $mergedUser = $this->_em->merge($detachedUser); - - self::assertNotSame($mergedUser, $detachedUser); - self::assertEquals('bar', $detachedUser->getName()); - self::assertEquals('bar', $mergedUser->getName()); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1595Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1595Test.php index 34a91678cf5..e6035ee4218 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1595Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1595Test.php @@ -12,17 +12,17 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-1595 - * @group DDC-1596 - * @group non-cacheable - */ +#[Group('DDC-1595')] +#[Group('DDC-1596')] +#[Group('non-cacheable')] class DDC1595Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -32,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1595BaseInheritance::class, DDC1595InheritedEntity1::class, - DDC1595InheritedEntity2::class + DDC1595InheritedEntity2::class, ); } @@ -51,14 +51,14 @@ public function testIssue(): void // DDC-1596 $this->assertSQLEquals( "SELECT t0.id AS id_1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')", - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); $entities = $entity1->getEntities()->getValues(); self::assertEquals( "SELECT t0.id AS id_1, t0.type FROM base t0 INNER JOIN entity1_entity2 ON t0.id = entity1_entity2.item WHERE entity1_entity2.parent = ? AND t0.type IN ('Entity2')", - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); $this->_em->clear(); @@ -68,46 +68,34 @@ public function testIssue(): void $this->assertSQLEquals( 'SELECT COUNT(*) FROM entity1_entity2 t WHERE t.parent = ?', - $this->getLastLoggedQuery()['sql'] + $this->getLastLoggedQuery()['sql'], ); } } -/** - * @Entity - * @Table(name="base") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({ - * "Entity1" = "DDC1595InheritedEntity1", - * "Entity2" = "DDC1595InheritedEntity2" - * }) - */ +#[Table(name: 'base')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['Entity1' => 'DDC1595InheritedEntity1', 'Entity2' => 'DDC1595InheritedEntity2'])] abstract class DDC1595BaseInheritance { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; } -/** - * @Entity - * @Table(name="entity1") - */ +#[Table(name: 'entity1')] +#[Entity] class DDC1595InheritedEntity1 extends DDC1595BaseInheritance { - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC1595InheritedEntity2", fetch="EXTRA_LAZY") - * @JoinTable(name="entity1_entity2", - * joinColumns={@JoinColumn(name="parent", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="item", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'entity1_entity2')] + #[JoinColumn(name: 'parent', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'item', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC1595InheritedEntity2', fetch: 'EXTRA_LAZY')] protected $entities; /** @phpstan-return Collection */ @@ -117,10 +105,8 @@ public function getEntities(): Collection } } -/** - * @Entity - * @Table(name="entity2") - */ +#[Table(name: 'entity2')] +#[Entity] class DDC1595InheritedEntity2 extends DDC1595BaseInheritance { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC163Test.php b/tests/Tests/ORM/Functional/Ticket/DDC163Test.php index 5cb354bebc0..6c0c7c94fe3 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC163Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC163Test.php @@ -6,6 +6,7 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC163Test extends OrmFunctionalTestCase { @@ -16,7 +17,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-163 */ + #[Group('DDC-163')] public function testQueryWithOrConditionUsingTwoRelationOnSameEntity(): void { $p1 = new CompanyPerson(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1643Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1643Test.php index 59957a59eea..56bb9630de3 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1643Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1643Test.php @@ -7,17 +7,14 @@ use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-1643 */ +#[Group('DDC-1643')] class DDC1643Test extends OrmFunctionalTestCase { - /** @var CmsUser */ - private $user1; + private CmsUser|null $user1; - /** @var CmsUser */ - private $user2; + private CmsUser|null $user2; protected function setUp(): void { @@ -47,8 +44,8 @@ protected function setUp(): void $this->_em->flush(); $this->_em->clear(); - $this->user1 = $this->_em->find(get_class($user1), $user1->id); - $this->user2 = $this->_em->find(get_class($user1), $user2->id); + $this->user1 = $this->_em->find($user1::class, $user1->id); + $this->user2 = $this->_em->find($user1::class, $user2->id); } public function testClonePersistentCollectionAndReuse(): void @@ -60,7 +57,7 @@ public function testClonePersistentCollectionAndReuse(): void $this->_em->flush(); $this->_em->clear(); - $user1 = $this->_em->find(get_class($user1), $user1->id); + $user1 = $this->_em->find($user1::class, $user1->id); self::assertCount(2, $user1->groups); } @@ -75,8 +72,8 @@ public function testClonePersistentCollectionAndShare(): void $this->_em->flush(); $this->_em->clear(); - $user1 = $this->_em->find(get_class($user1), $user1->id); - $user2 = $this->_em->find(get_class($user1), $user2->id); + $user1 = $this->_em->find($user1::class, $user1->id); + $user2 = $this->_em->find($user1::class, $user2->id); self::assertCount(2, $user1->groups); self::assertCount(2, $user2->groups); @@ -96,8 +93,8 @@ public function testCloneThenDirtyPersistentCollection(): void $this->_em->flush(); $this->_em->clear(); - $user1 = $this->_em->find(get_class($user1), $user1->id); - $user2 = $this->_em->find(get_class($user1), $user2->id); + $user1 = $this->_em->find($user1::class, $user1->id); + $user2 = $this->_em->find($user1::class, $user2->id); self::assertCount(3, $user2->groups); self::assertCount(2, $user1->groups); @@ -119,8 +116,8 @@ public function testNotCloneAndPassAroundFlush(): void $this->_em->flush(); $this->_em->clear(); - $user1 = $this->_em->find(get_class($user1), $user1->id); - $user2 = $this->_em->find(get_class($user1), $user2->id); + $user1 = $this->_em->find($user1::class, $user1->id); + $user2 = $this->_em->find($user1::class, $user2->id); self::assertCount(3, $user2->groups); self::assertCount(3, $user1->groups); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1654Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1654Test.php index e2e06a24bd7..fdd3b52cb90 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1654Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1654Test.php @@ -11,8 +11,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1654 */ +#[Group('DDC-1654')] class DDC1654Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -23,7 +24,7 @@ protected function setUp(): void [ DDC1654Post::class, DDC1654Comment::class, - ] + ], ); } @@ -73,7 +74,7 @@ public function testManyToManyRemoveElementFromCollectionOrphanRemoval(): void self::assertCount(0, $comments); } - /** @group DDC-3382 */ + #[Group('DDC-3382')] public function testManyToManyRemoveElementFromReAddToCollectionOrphanRemoval(): void { $post = new DDC1654Post(); @@ -112,7 +113,7 @@ public function testManyToManyClearCollectionOrphanRemoval(): void self::assertCount(0, $comments); } - /** @group DDC-3382 */ + #[Group('DDC-3382')] public function testManyToManyClearCollectionReAddOrphanRemoval(): void { $post = new DDC1654Post(); @@ -134,33 +135,26 @@ public function testManyToManyClearCollectionReAddOrphanRemoval(): void } } -/** @Entity */ +#[Entity] class DDC1654Post { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC1654Comment", orphanRemoval=true, - * cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC1654Comment', orphanRemoval: true, cascade: ['persist'])] public $comments = []; } -/** @Entity */ +#[Entity] class DDC1654Comment { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1655Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1655Test.php index ef6ed4f01d3..d6e721d9247 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1655Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1655Test.php @@ -16,15 +16,13 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\PostLoad; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; use function get_debug_type; -/** - * @group DDC-1655 - * @group DDC-1640 - * @group DDC-1556 - */ +#[Group('DDC-1655')] +#[Group('DDC-1640')] +#[Group('DDC-1556')] class DDC1655Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -34,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1655Foo::class, DDC1655Bar::class, - DDC1655Baz::class + DDC1655Baz::class, ); } @@ -58,7 +56,7 @@ public function testPostLoadOneToManyInheritance(): void $this->_em->flush(); $this->_em->clear(); - $baz = $this->_em->find(get_class($baz), $baz->id); + $baz = $this->_em->find($baz::class, $baz->id); foreach ($baz->foos as $foo) { self::assertEquals(1, $foo->loaded, 'should have loaded callback counter incremented for ' . get_debug_type($foo)); } @@ -76,11 +74,11 @@ public function testPostLoadInheritanceChild(): void $this->_em->flush(); $this->_em->clear(); - $bar = $this->_em->find(get_class($bar), $bar->id); + $bar = $this->_em->find($bar::class, $bar->id); self::assertEquals(1, $bar->loaded); self::assertEquals(1, $bar->subLoaded); - $bar = $this->_em->find(get_class($bar), $bar->id); + $bar = $this->_em->find($bar::class, $bar->id); self::assertEquals(1, $bar->loaded); self::assertEquals(1, $bar->subLoaded); @@ -97,71 +95,56 @@ public function testPostLoadInheritanceChild(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({ - * "foo" = "DDC1655Foo", - * "bar" = "DDC1655Bar" - * }) - * @HasLifecycleCallbacks - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['foo' => 'DDC1655Foo', 'bar' => 'DDC1655Bar'])] +#[HasLifecycleCallbacks] class DDC1655Foo { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; /** @var int */ public $loaded = 0; - /** - * @var DDC1655Baz - * @ManyToOne(targetEntity="DDC1655Baz", inversedBy="foos") - */ + /** @var DDC1655Baz */ + #[ManyToOne(targetEntity: 'DDC1655Baz', inversedBy: 'foos')] public $baz; - /** @PostLoad */ + #[PostLoad] public function postLoad(): void { $this->loaded++; } } -/** - * @Entity - * @HasLifecycleCallbacks - */ +#[Entity] +#[HasLifecycleCallbacks] class DDC1655Bar extends DDC1655Foo { /** @var int */ public $subLoaded; - /** @PostLoad */ + #[PostLoad] public function postSubLoaded(): void { $this->subLoaded++; } } -/** @Entity */ +#[Entity] class DDC1655Baz { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC1655Foo", mappedBy="baz") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC1655Foo', mappedBy: 'baz')] public $foos = []; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1666Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1666Test.php index 054831e01ce..3ff41210c23 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1666Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1666Test.php @@ -7,8 +7,9 @@ use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1666 */ +#[Group('DDC-1666')] class DDC1666Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1685Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1685Test.php index ba7fd8e582c..9173f5e7865 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1685Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1685Test.php @@ -8,13 +8,13 @@ use Doctrine\Tests\Models\DDC117\DDC117Article; use Doctrine\Tests\Models\DDC117\DDC117ArticleDetails; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use RuntimeException; -/** @group DDC-1685 */ +#[Group('DDC-1685')] class DDC1685Test extends OrmFunctionalTestCase { - /** @var Paginator */ - private $paginator; + private Paginator $paginator; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC168Test.php b/tests/Tests/ORM/Functional/Ticket/DDC168Test.php index e480e6bcc0c..54147ba39c4 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC168Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC168Test.php @@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function ksort; @@ -35,7 +36,7 @@ public function tearDown(): void parent::tearDown(); } - /** @group DDC-168 */ + #[Group('DDC-168')] public function testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray(): void { $spouse = new CompanyEmployee(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1690Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1690Test.php deleted file mode 100644 index 9dd806334d1..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1690Test.php +++ /dev/null @@ -1,204 +0,0 @@ -createSchemaForModels(DDC1690Parent::class, DDC1690Child::class); - } - - public function testChangeTracking(): void - { - $parent = new DDC1690Parent(); - $child = new DDC1690Child(); - $parent->setName('parent'); - $child->setName('child'); - - $parent->setChild($child); - $child->setParent($parent); - - $this->_em->persist($parent); - $this->_em->persist($child); - - self::assertEquals(1, count($parent->listeners)); - self::assertEquals(1, count($child->listeners)); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertEquals(1, count($parent->listeners)); - self::assertEquals(1, count($child->listeners)); - - $parentId = $parent->getId(); - $childId = $child->getId(); - unset($parent, $child); - - $parent = $this->_em->find(DDC1690Parent::class, $parentId); - $child = $this->_em->find(DDC1690Child::class, $childId); - - self::assertEquals(1, count($parent->listeners)); - self::assertCount(0, $child->listeners); - - $this->_em->getUnitOfWork()->initializeObject($child); - - self::assertCount(1, $child->listeners); - unset($parent, $child); - - $parent = $this->_em->find(DDC1690Parent::class, $parentId); - $child = $parent->getChild(); - - self::assertEquals(1, count($parent->listeners)); - self::assertEquals(1, count($child->listeners)); - unset($parent, $child); - - $child = $this->_em->find(DDC1690Child::class, $childId); - $parent = $child->getParent(); - - self::assertEquals(1, count($parent->listeners)); - self::assertEquals(1, count($child->listeners)); - } -} - -class NotifyBaseEntity implements NotifyPropertyChanged -{ - /** @phpstan-var list */ - public $listeners = []; - - public function addPropertyChangedListener(PropertyChangedListener $listener): void - { - if (! in_array($listener, $this->listeners, true)) { - $this->listeners[] = $listener; - } - } - - protected function onPropertyChanged($propName, $oldValue, $newValue): void - { - if ($this->listeners) { - foreach ($this->listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } - } -} - -/** - * @Entity - * @ChangeTrackingPolicy("NOTIFY") - */ -class DDC1690Parent extends NotifyBaseEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @var DDC1690Child - * @OneToOne(targetEntity="DDC1690Child") - */ - private $child; - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->onPropertyChanged('name', $this->name, $name); - $this->name = $name; - } - - public function setChild(DDC1690Child $child): void - { - $this->child = $child; - } - - public function getChild(): DDC1690Child - { - return $this->child; - } -} - -/** @Entity */ -class DDC1690Child extends NotifyBaseEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @var DDC1690Parent - * @OneToOne(targetEntity="DDC1690Parent", mappedBy="child") - */ - private $parent; - - public function getId(): int - { - return $this->id; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->onPropertyChanged('name', $this->name, $name); - $this->name = $name; - } - - public function setParent(DDC1690Parent $parent): void - { - $this->parent = $parent; - } - - public function getParent(): DDC1690Parent - { - return $this->parent; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1695Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1695Test.php index 14c0bc572fd..e5693313316 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1695Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1695Test.php @@ -4,20 +4,22 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use DateTimeZone; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1695 */ +#[Group('DDC-1695')] class DDC1695Test extends OrmFunctionalTestCase { public function testIssue(): void { - if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform) { + if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('Only with sqlite'); } @@ -26,124 +28,68 @@ public function testIssue(): void self::assertEquals( 'SELECT d0_."SmallText" AS SmallText_0, d0_."PublishDate" AS PublishDate_1 FROM "DDC1695News" d0_', - $sql + $sql, ); } } -/** - * @Table(name="`DDC1695News`") - * @Entity - */ +#[Table(name: '`DDC1695News`')] +#[Entity] class DDC1695News { - /** - * @var int - * @Column(name="`IdNews`", type="integer", nullable=false) - * @Id - * @GeneratedValue - */ - private $idNews; - - /** - * @var int - * @Column(name="`IdUser`", type="bigint", nullable=false) - */ - private $idUser; - - /** - * @var int - * @Column(name="`IdLanguage`", type="integer", nullable=false) - */ - private $idLanguage; - - /** - * @var int - * @Column(name="`IdCondition`", type="integer", nullable=true) - */ - private $idCondition; - - /** - * @var int - * @Column(name="`IdHealthProvider`", type="integer", nullable=true) - */ - private $idHealthProvider; - - /** - * @var int - * @Column(name="`IdSpeciality`", type="integer", nullable=true) - */ - private $idSpeciality; - - /** - * @var int - * @Column(name="`IdMedicineType`", type="integer", nullable=true) - */ - private $idMedicineType; - - /** - * @var int - * @Column(name="`IdTreatment`", type="integer", nullable=true) - */ - private $idTreatment; - - /** - * @var string - * @Column(name="`Title`", type="string", nullable=true) - */ - private $title; - - /** - * @var string - * @Column(name="`SmallText`", type="string", nullable=true) - */ - private $smallText; - - /** - * @var string - * @Column(name="`LongText`", type="string", nullable=true) - */ - private $longText; - - /** - * @var DateTimeZone - * @Column(name="`PublishDate`", type="datetimetz", nullable=true) - */ - private $publishDate; - - /** - * @var array - * @Column(name="`IdxNews`", type="json_array", nullable=true) - */ - private $idxNews; - - /** - * @var bool - * @Column(name="`Highlight`", type="boolean", nullable=false) - */ - private $highlight; - - /** - * @var int - * @Column(name="`Order`", type="integer", nullable=false) - */ - private $order; - - /** - * @var bool - * @Column(name="`Deleted`", type="boolean", nullable=false) - */ - private $deleted; - - /** - * @var bool - * @Column(name="`Active`", type="boolean", nullable=false) - */ - private $active; - - /** - * @var bool - * @Column(name="`UpdateToHighlighted`", type="boolean", nullable=true) - */ - private $updateToHighlighted; + #[Column(name: '`IdNews`', type: 'integer', nullable: false)] + #[Id] + #[GeneratedValue] + private int $idNews; + + #[Column(name: '`IdUser`', type: 'bigint', nullable: false)] + private int $idUser; + + #[Column(name: '`IdLanguage`', type: 'integer', nullable: false)] + private int $idLanguage; + + #[Column(name: '`IdCondition`', type: 'integer', nullable: true)] + private int $idCondition; + + #[Column(name: '`IdHealthProvider`', type: 'integer', nullable: true)] + private int $idHealthProvider; + + #[Column(name: '`IdSpeciality`', type: 'integer', nullable: true)] + private int $idSpeciality; + + #[Column(name: '`IdMedicineType`', type: 'integer', nullable: true)] + private int $idMedicineType; + + #[Column(name: '`IdTreatment`', type: 'integer', nullable: true)] + private int $idTreatment; + + #[Column(name: '`Title`', type: 'string', nullable: true)] + private string $title; + + #[Column(name: '`SmallText`', type: 'string', nullable: true)] + private string $smallText; + + #[Column(name: '`LongText`', type: 'string', nullable: true)] + private string $longText; + + #[Column(name: '`PublishDate`', type: 'datetimetz', nullable: true)] + private DateTimeZone $publishDate; + + #[Column(name: '`IdxNews`', type: 'json_array', nullable: true)] + private array $idxNews; + + #[Column(name: '`Highlight`', type: 'boolean', nullable: false)] + private bool $highlight; + + #[Column(name: '`Order`', type: 'integer', nullable: false)] + private int $order; + + #[Column(name: '`Deleted`', type: 'boolean', nullable: false)] + private bool $deleted; + + #[Column(name: '`Active`', type: 'boolean', nullable: false)] + private bool $active; + + #[Column(name: '`UpdateToHighlighted`', type: 'boolean', nullable: true)] + private bool $updateToHighlighted; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1707Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1707Test.php index cc6b2428a71..8d57644a752 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1707Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1707Test.php @@ -14,8 +14,9 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\PostLoad; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1707 */ +#[Group('DDC-1707')] class DDC1707Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +25,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1707Base::class, - DDC1707Child::class + DDC1707Child::class, ); } @@ -39,32 +40,28 @@ public function testPostLoadOnChild(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"c": "DDC1707Child"}) - * @HasLifecycleCallbacks - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['c' => 'DDC1707Child'])] +#[HasLifecycleCallbacks] abstract class DDC1707Base { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; /** @var bool */ public $postLoad = false; - /** @PostLoad */ + #[PostLoad] public function onPostLoad(): void { $this->postLoad = true; } } -/** @Entity */ +#[Entity] class DDC1707Child extends DDC1707Base { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1719Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1719Test.php index 54560c4d2d1..212dac335ee 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1719Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1719Test.php @@ -10,8 +10,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1719 */ +#[Group('DDC-1719')] class DDC1719Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -81,28 +82,19 @@ public function testCreateRetrieveUpdateDelete(): void } } -/** - * @Entity - * @Table(name="`ddc-1719-simple-entity`") - */ +#[Table(name: '`ddc-1719-simple-entity`')] +#[Entity] class DDC1719SimpleEntity { - /** - * @var int - * @Id - * @Column(type="integer", name="`simple-entity-id`") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer', name: '`simple-entity-id`')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", name="`simple-entity-value`") - */ - public $value; - - public function __construct(string $value) - { - $this->value = $value; + public function __construct( + #[Column(type: 'string', name: '`simple-entity-value`')] + public string $value, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1734Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1734Test.php deleted file mode 100644 index 35fbffdea88..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC1734Test.php +++ /dev/null @@ -1,88 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - /** - * This test is DDC-1734 minus the serialization, i.e. it works - * - * @group DDC-1734 - */ - public function testMergeWorksOnNonSerializedProxies(): void - { - $group = new CmsGroup(); - - $group->setName('Foo'); - $this->_em->persist($group); - $this->_em->flush(); - $this->_em->clear(); - - $proxy = $this->getProxy($group); - - self::assertTrue($this->isUninitializedObject($proxy)); - - $this->_em->detach($proxy); - $this->_em->clear(); - - $proxy = $this->_em->merge($proxy); - - self::assertEquals('Foo', $proxy->getName(), 'The entity is broken'); - } - - /** - * This test reproduces DDC-1734 which is: - * - A non-initialized proxy is detached and serialized (the identifier of the proxy is *not* serialized) - * - the object is deserialized and merged (to turn into an entity) - * - the entity is broken because it has no identifier and no field defined - * - * @group DDC-1734 - */ - public function testMergeWorksOnSerializedProxies(): void - { - $group = new CmsGroup(); - - $group->setName('Foo'); - $this->_em->persist($group); - $this->_em->flush(); - $this->_em->clear(); - - $proxy = $this->getProxy($group); - - self::assertTrue($this->isUninitializedObject($proxy)); - - $this->_em->detach($proxy); - $serializedProxy = serialize($proxy); - $this->_em->clear(); - - $unserializedProxy = $this->_em->merge(unserialize($serializedProxy)); - self::assertEquals('Foo', $unserializedProxy->getName(), 'The entity is broken'); - } - - /** @param object $object */ - private function getProxy($object): InternalProxy - { - $metadataFactory = $this->_em->getMetadataFactory(); - $className = get_class($object); - $identifier = $metadataFactory->getMetadataFor($className)->getIdentifierValues($object); - - return $this->_em->getProxyFactory()->getProxy($className, $identifier); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1757Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1757Test.php index d8229f335b1..8378dd34056 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1757Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1757Test.php @@ -34,67 +34,51 @@ public function testFailingCase(): void self::assertEquals( 'SELECT _a FROM ' . __NAMESPACE__ . '\DDC1757A _a, ' . __NAMESPACE__ . '\DDC1757B _b INNER JOIN _b.c _c INNER JOIN _c.d _d', $dql, - 'Wrong DQL query' + 'Wrong DQL query', ); } } -/** @Entity */ +#[Entity] class DDC1757A { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; } -/** @Entity */ +#[Entity] class DDC1757B { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var DDC1757C - * @OneToOne(targetEntity="DDC1757C") - */ - private $c; + #[OneToOne(targetEntity: 'DDC1757C')] + private DDC1757C $c; } -/** @Entity */ +#[Entity] class DDC1757C { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC1757D - * @OneToOne(targetEntity="DDC1757D") - */ - private $d; + #[OneToOne(targetEntity: 'DDC1757D')] + private DDC1757D $d; } -/** @Entity */ +#[Entity] class DDC1757D { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1778Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1778Test.php index b87b5f9cbba..b3bc4277193 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1778Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1778Test.php @@ -7,15 +7,14 @@ use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1778 */ +#[Group('DDC-1778')] class DDC1778Test extends OrmFunctionalTestCase { - /** @var CmsUser */ - private $user; + private CmsUser|null $user; - /** @var CmsPhonenumber */ - private $phone; + private CmsPhonenumber|null $phone; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1787Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1787Test.php index 1f470502c95..4cd7a886d73 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1787Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1787Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1787 */ +#[Group('DDC-1787')] class DDC1787Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -23,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC1787Foo::class, - DDC1787Bar::class + DDC1787Bar::class, ); } @@ -40,28 +41,20 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"bar" = "DDC1787Bar", "foo" = "DDC1787Foo"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['bar' => 'DDC1787Bar', 'foo' => 'DDC1787Foo'])] class DDC1787Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var int - * @Version - * @Column(type="integer") - */ - private $version; + #[Version] + #[Column(type: 'integer')] + private int $version; public function getVersion(): int { @@ -69,7 +62,7 @@ public function getVersion(): int } } -/** @Entity */ +#[Entity] class DDC1787Bar extends DDC1787Foo { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1843Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1843Test.php index cd037335bf2..b92d6408dbd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1843Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1843Test.php @@ -6,11 +6,10 @@ use Doctrine\Tests\Models\Quote\Group; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group as TestGroup; -/** - * @group DDC-1845 - * @group DDC-1843 - */ +#[TestGroup('DDC-1845')] +#[TestGroup('DDC-1843')] class DDC1843Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1884Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1884Test.php index 20378d20bdd..b5b76a5b125 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1884Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1884Test.php @@ -9,8 +9,9 @@ use Doctrine\Tests\Models\Taxi\PaidRide; use Doctrine\Tests\Models\Taxi\Ride; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1884 */ +#[Group('DDC-1884')] class DDC1884Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1885Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1885Test.php index 678783188de..9290345ee51 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1885Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1885Test.php @@ -7,15 +7,13 @@ use Doctrine\Tests\Models\Quote\Group; use Doctrine\Tests\Models\Quote\User; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group as TestGroup; -/** - * @group DDC-1845 - * @group DDC-1885 - */ +#[TestGroup('DDC-1845')] +#[TestGroup('DDC-1885')] class DDC1885Test extends OrmFunctionalTestCase { - /** @var User */ - private $user; + private User $user; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1918Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1918Test.php index 295ebb94a91..2fcc7546c27 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1918Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1918Test.php @@ -8,10 +8,11 @@ use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function iterator_to_array; -/** @group DDC-1918 */ +#[Group('DDC-1918')] class DDC1918Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1925Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1925Test.php index 2aa46ae8032..ec3ded3b068 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1925Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1925Test.php @@ -10,18 +10,18 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -/** - * @group DDC-1925 - * @group DDC-1210 - */ +#[Group('DDC-1925')] +#[Group('DDC-1210')] class DDC1925Test extends OrmFunctionalTestCase { public function testIssue(): void @@ -54,35 +54,23 @@ public function testIssue(): void } } -/** - * @Table - * @Entity - */ +#[Table] +#[Entity] class DDC1925Product { - /** - * @var int $id - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string $title - * @Column(name="title", type="string", length=255) - */ - private $title; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC1925User") - * @JoinTable( - * name="user_purchases", - * joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")} - * ) - */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(name: 'title', type: 'string', length: 255)] + private string|null $title = null; + + /** @phpstan-var Collection */ + #[JoinTable(name: 'user_purchases')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC1925User')] private $buyers; /** @@ -122,25 +110,17 @@ public function addBuyer(DDC1925User $buyer): void } } -/** - * @Table - * @Entity - */ +#[Table] +#[Entity] class DDC1925User { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(name="title", type="string", length=255) - */ - private $title; + #[Column(name: 'title', type: 'string', length: 255)] + private string|null $title = null; /** * Get id diff --git a/tests/Tests/ORM/Functional/Ticket/DDC192Test.php b/tests/Tests/ORM/Functional/Ticket/DDC192Test.php index 108b410665c..6ddff6a387d 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC192Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC192Test.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-192 */ +#[Group('DDC-192')] class DDC192Test extends OrmFunctionalTestCase { public function testSchemaCreation(): void @@ -30,53 +31,41 @@ public function testSchemaCreation(): void foreach ($classes as $class) { self::assertContains( $this->_em->getClassMetadata($class)->getTableName(), - $tables + $tables, ); } } } -/** - * @Entity - * @Table(name="ddc192_users") - */ +#[Table(name: 'ddc192_users')] +#[Entity] class DDC192User { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255)] public $name; } -/** - * @Entity - * @Table(name="ddc192_phonenumbers") - */ +#[Table(name: 'ddc192_phonenumbers')] +#[Entity] class DDC192Phonenumber { - /** - * @var string - * @Id - * @Column(name="phone", type="string", length=40) - */ + /** @var string */ + #[Id] + #[Column(name: 'phone', type: 'string', length: 40)] protected $phone; - /** - * @var DDC192User - * @Id - * @ManyToOne(targetEntity="DDC192User") - * @JoinColumn(name="userId", referencedColumnName="id") - */ + /** @var DDC192User */ + #[Id] + #[ManyToOne(targetEntity: 'DDC192User')] + #[JoinColumn(name: 'userId', referencedColumnName: 'id')] protected $user; public function setPhone(string $value): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1995Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1995Test.php index 4a2639d82ce..205f592a3b9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1995Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1995Test.php @@ -7,8 +7,9 @@ use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1995 */ +#[Group('DDC-1995')] class DDC1995Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC1998Test.php b/tests/Tests/ORM/Functional/Ticket/DDC1998Test.php index 039f8b95c7a..4f754a9baf0 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC1998Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC1998Test.php @@ -11,8 +11,10 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; -/** @group DDC-1998 */ +#[Group('DDC-1998')] class DDC1998Test extends OrmFunctionalTestCase { public function testSqlConversionAsIdentifier(): void @@ -45,20 +47,16 @@ public function testSqlConversionAsIdentifier(): void } } -/** @Entity */ +#[Entity] class DDC1998Entity { - /** - * @var string - * @Id - * @Column(type="ddc1998", length=255) - */ + /** @var string */ + #[Id] + #[Column(type: 'ddc1998', length: 255)] public $id; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $num = 0; } @@ -69,7 +67,7 @@ class DDC1998Type extends StringType /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { return (string) $value; } @@ -77,28 +75,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): DDC1998Id { return new DDC1998Id($value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } } -class DDC1998Id +class DDC1998Id implements Stringable { - /** @var string */ - private $val; - - public function __construct(string $val) + public function __construct(private string $val) { - $this->val = $val; } public function __toString(): string diff --git a/tests/Tests/ORM/Functional/Ticket/DDC199Test.php b/tests/Tests/ORM/Functional/Ticket/DDC199Test.php index 9e9d8a34779..9cfd9149c9b 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC199Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC199Test.php @@ -27,7 +27,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC199ParentClass::class, DDC199ChildClass::class, - DDC199RelatedClass::class + DDC199RelatedClass::class, ); } @@ -64,71 +64,53 @@ public function testPolymorphicLoading(): void } -/** - * @Entity - * @Table(name="ddc199_entities") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"parent" = "DDC199ParentClass", "child" = "DDC199ChildClass"}) - */ +#[Table(name: 'ddc199_entities')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['parent' => 'DDC199ParentClass', 'child' => 'DDC199ChildClass'])] class DDC199ParentClass { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $parentData; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC199RelatedClass", mappedBy="parent") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC199RelatedClass', mappedBy: 'parent')] public $relatedEntities; } -/** @Entity */ +#[Entity] class DDC199ChildClass extends DDC199ParentClass { - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $childData; } -/** - * @Entity - * @Table(name="ddc199_relatedclass") - */ +#[Table(name: 'ddc199_relatedclass')] +#[Entity] class DDC199RelatedClass { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $relatedData; - /** - * @var DDC199ParentClass - * @ManyToOne(targetEntity="DDC199ParentClass", inversedBy="relatedEntities") - * @JoinColumn(name="parent_id", referencedColumnName="id") - */ + /** @var DDC199ParentClass */ + #[ManyToOne(targetEntity: 'DDC199ParentClass', inversedBy: 'relatedEntities')] + #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')] public $parent; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2012Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2012Test.php index 27208610dd8..fe9ec893ed6 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2012Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2012Test.php @@ -15,19 +15,16 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function explode; -use function get_class; use function implode; use function is_array; -use function method_exists; use function sprintf; use function strtolower; -/** - * @group DDC-2012 - * @group non-cacheable - */ +#[Group('DDC-2012')] +#[Group('non-cacheable')] class DDC2012Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -40,7 +37,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2012Item::class, - DDC2012ItemPerson::class + DDC2012ItemPerson::class, ); } @@ -53,7 +50,7 @@ public function testIssue(): void $this->_em->flush(); $this->_em->clear(); - $item = $this->_em->find(get_class($item), $item->id); + $item = $this->_em->find($item::class, $item->id); self::assertArrayHasKey('convertToDatabaseValueSQL', DDC2012TsVectorType::$calls); self::assertArrayHasKey('convertToDatabaseValue', DDC2012TsVectorType::$calls); @@ -72,7 +69,7 @@ public function testIssue(): void $this->_em->flush(); $this->_em->clear(); - $item = $this->_em->find(get_class($item), $item->id); + $item = $this->_em->find($item::class, $item->id); self::assertCount(2, DDC2012TsVectorType::$calls['convertToDatabaseValueSQL']); self::assertCount(2, DDC2012TsVectorType::$calls['convertToDatabaseValue']); @@ -83,37 +80,26 @@ public function testIssue(): void } } -/** - * @Table(name="ddc2010_item") - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type_id", type="smallint") - * @DiscriminatorMap({ - * 1 = "DDC2012ItemPerson", - * 2 = "DDC2012Item" - * }) - */ +#[Table(name: 'ddc2010_item')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type_id', type: 'smallint')] +#[DiscriminatorMap([1 => 'DDC2012ItemPerson', 2 => 'DDC2012Item'])] class DDC2012Item { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @phpstan-var list - * @Column(name="tsv", type="tsvector", length=255, nullable=true) - */ + /** @phpstan-var list */ + #[Column(name: 'tsv', type: 'tsvector', length: 255, nullable: true)] public $tsv; } -/** - * @Table(name="ddc2010_item_person") - * @Entity - */ +#[Table(name: 'ddc2010_item_person')] +#[Entity] class DDC2012ItemPerson extends DDC2012Item { } @@ -128,19 +114,15 @@ class DDC2012TsVectorType extends Type /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { if (is_array($value)) { $value = implode(' ', $value); @@ -156,8 +138,10 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} + * + * @return list */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): array { self::$calls[__FUNCTION__][] = [ 'value' => $value, @@ -170,7 +154,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string { self::$calls[__FUNCTION__][] = [ 'sqlExpr' => $sqlExpr, @@ -183,18 +167,7 @@ public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) return sprintf('UPPER(%s)', $sqlExpr); } - /** - * {@inheritDoc} - */ - public function canRequireSQLConversion() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::MYTYPE; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2074Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2074Test.php index 37b51cb049b..5e47b5c6ed3 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2074Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2074Test.php @@ -9,8 +9,9 @@ use Doctrine\Tests\Models\ECommerce\ECommerceCategory; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2074 */ +#[Group('DDC-2074')] class DDC2074Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2084Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2084Test.php index ae05d733b70..dfa44bcd9bd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2084Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2084Test.php @@ -4,9 +4,13 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; +use Doctrine\Tests\ORM\Functional\Ticket\DDC2084\MyEntity1; +use Doctrine\Tests\ORM\Functional\Ticket\DDC2084\MyEntity2; +use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2084 */ +#[Group('DDC-2084')] class DDC2084Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -15,14 +19,14 @@ protected function setUp(): void $this->createSchemaForModels( __NAMESPACE__ . '\DDC2084\MyEntity1', - __NAMESPACE__ . '\DDC2084\MyEntity2' + __NAMESPACE__ . '\DDC2084\MyEntity2', ); } - public function loadFixture(): DDC2084\MyEntity1 + public function loadFixture(): MyEntity1 { - $e2 = new DDC2084\MyEntity2('Foo'); - $e1 = new DDC2084\MyEntity1($e2); + $e2 = new MyEntity2('Foo'); + $e1 = new MyEntity1($e2); $this->_em->persist($e2); $this->_em->flush(); @@ -48,14 +52,14 @@ public function testIssue(): void public function testInvalidIdentifierBindingEntityException(): void { - $this->expectException('Doctrine\ORM\ORMInvalidArgumentException'); + $this->expectException(ORMInvalidArgumentException::class); $this->expectExceptionMessage( <<<'EXCEPTION' Binding entities to query parameters only allowed for entities that have an identifier. Class "Doctrine\Tests\ORM\Functional\Ticket\DDC2084\MyEntity2" does not have an identifier. -EXCEPTION +EXCEPTION, ); - $this->_em->find(__NAMESPACE__ . '\DDC2084\MyEntity1', new DDC2084\MyEntity2('Foo')); + $this->_em->find(__NAMESPACE__ . '\DDC2084\MyEntity1', new MyEntity2('Foo')); } } @@ -69,23 +73,16 @@ public function testInvalidIdentifierBindingEntityException(): void use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="DDC2084_ENTITY1") - */ +#[Table(name: 'DDC2084_ENTITY1')] +#[Entity] class MyEntity1 { - /** - * @var MyEntity2 - * @Id - * @OneToOne(targetEntity="MyEntity2") - * @JoinColumn(name="entity2_id", referencedColumnName="id", nullable=false) - */ - private $entity2; - - public function __construct(MyEntity2 $myEntity2) - { - $this->entity2 = $myEntity2; + public function __construct( + #[Id] + #[OneToOne(targetEntity: 'MyEntity2')] + #[JoinColumn(name: 'entity2_id', referencedColumnName: 'id', nullable: false)] + private MyEntity2 $entity2, + ) { } public function setMyEntity2(MyEntity2 $myEntity2): void @@ -99,29 +96,19 @@ public function getMyEntity2(): MyEntity2 } } -/** - * @Entity - * @Table(name="DDC2084_ENTITY2") - */ +#[Table(name: 'DDC2084_ENTITY2')] +#[Entity] class MyEntity2 { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column - */ - private $value; - - public function __construct(string $value) - { - $this->value = $value; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + public function __construct( + #[Column] + private string $value, + ) { } public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2090Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2090Test.php index a736313d7a5..3a76b05ca48 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2090Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2090Test.php @@ -9,11 +9,10 @@ use Doctrine\ORM\Query\Parameter; use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-2090 - * @group non-cacheable - */ +#[Group('DDC-2090')] +#[Group('non-cacheable')] class DDC2090Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2106Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2106Test.php index ff3a8cd28dd..10043467bb1 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2106Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2106Test.php @@ -14,8 +14,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2106 */ +#[Group('DDC-2106')] class DDC2106Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -31,7 +32,7 @@ public function testDetachedEntityAsId(): void $entity = new DDC2106Entity(); $this->_em->persist($entity); $this->_em->flush(); - $this->_em->clear(DDC2106Entity::class); + $this->_em->clear(); $entity = $this->_em->getRepository(DDC2106Entity::class)->findOneBy([]); // ... and a managed entity without id @@ -44,27 +45,21 @@ public function testDetachedEntityAsId(): void } } -/** @Entity */ +#[Entity] class DDC2106Entity { - /** - * @var int - * @Id - * @GeneratedValue(strategy="IDENTITY") - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'IDENTITY')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC2106Entity - * @ManyToOne(targetEntity="DDC2106Entity", inversedBy="children") - */ + /** @var DDC2106Entity */ + #[ManyToOne(targetEntity: 'DDC2106Entity', inversedBy: 'children')] public $parent; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2106Entity", mappedBy="parent", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2106Entity', mappedBy: 'parent', cascade: ['persist'])] public $children; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/DDC211Test.php b/tests/Tests/ORM/Functional/Ticket/DDC211Test.php index 0cd826cba9a..7a04f5ee621 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC211Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC211Test.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -52,34 +53,25 @@ public function testIssue(): void } -/** - * @Entity - * @Table(name="ddc211_users") - */ +#[Table(name: 'ddc211_users')] +#[Entity] class DDC211User { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255)] protected $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC211Group", inversedBy="users") - * @JoinTable(name="user_groups", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'user_groups')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC211Group', inversedBy: 'users')] protected $groups; public function __construct() @@ -99,30 +91,22 @@ public function getGroups(): Collection } } -/** - * @Entity - * @Table(name="ddc211_groups") - */ +#[Table(name: 'ddc211_groups')] +#[Entity] class DDC211Group { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255)] protected $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC211User", mappedBy="groups") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC211User', mappedBy: 'groups')] protected $users; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php index 3041765bd50..58d029b0100 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2138Test.php @@ -18,14 +18,16 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; +use Doctrine\Tests\ORM\Functional\Ticket\Doctrine\Common\Collections\Collection; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; use function reset; class DDC2138Test extends OrmFunctionalTestCase { - /** @group DDC-2138 */ + #[Group('DDC-2138')] public function testForeignKeyOnSTIWithMultipleMapping(): void { $em = $this->_em; @@ -34,7 +36,7 @@ public function testForeignKeyOnSTIWithMultipleMapping(): void DDC2138Structure::class, DDC2138UserFollowedObject::class, DDC2138UserFollowedStructure::class, - DDC2138UserFollowedUser::class + DDC2138UserFollowedUser::class, ); self::assertTrue($schema->hasTable('users_followed_objects'), 'Table users_followed_objects should exist.'); @@ -57,42 +59,32 @@ public function testForeignKeyOnSTIWithMultipleMapping(): void -/** - * @Table(name="structures") - * @Entity - */ +#[Table(name: 'structures')] +#[Entity] class DDC2138Structure { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=32, nullable=true) - */ + /** @var string */ + #[Column(type: 'string', length: 32, nullable: true)] protected $name; } -/** - * @Entity - * @Table(name="users_followed_objects") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="object_type", type="smallint") - * @DiscriminatorMap({4 = "DDC2138UserFollowedUser", 3 = "DDC2138UserFollowedStructure"}) - */ +#[Table(name: 'users_followed_objects')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'object_type', type: 'smallint')] +#[DiscriminatorMap([4 => 'DDC2138UserFollowedUser', 3 => 'DDC2138UserFollowedStructure'])] abstract class DDC2138UserFollowedObject { - /** - * @var int $id - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int $id */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] protected $id; /** @@ -104,30 +96,20 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class DDC2138UserFollowedStructure extends DDC2138UserFollowedObject { - /** - * @ManyToOne(targetEntity="DDC2138User", inversedBy="followedStructures") - * @JoinColumn(name="user_id", referencedColumnName="id", nullable=false) - * @var User $user - */ - protected $user; - - /** - * @ManyToOne(targetEntity="DDC2138Structure") - * @JoinColumn(name="object_id", referencedColumnName="id", nullable=false) - * @var Structure $followedStructure - */ - private $followedStructure; - /** * Construct a UserFollowedStructure entity */ - public function __construct(User $user, Structure $followedStructure) - { - $this->user = $user; - $this->followedStructure = $followedStructure; + public function __construct( + #[ManyToOne(targetEntity: 'DDC2138User', inversedBy: 'followedStructures')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)] + protected User $user, + #[ManyToOne(targetEntity: 'DDC2138Structure')] + #[JoinColumn(name: 'object_id', referencedColumnName: 'id', nullable: false)] + private Structure $followedStructure, + ) { } public function getUser(): User @@ -144,30 +126,20 @@ public function getFollowedStructure(): Structure } } -/** @Entity */ +#[Entity] class DDC2138UserFollowedUser extends DDC2138UserFollowedObject { - /** - * @ManyToOne(targetEntity="DDC2138User", inversedBy="followedUsers") - * @JoinColumn(name="user_id", referencedColumnName="id", nullable=false) - * @var User $user - */ - protected $user; - - /** - * @ManyToOne(targetEntity="DDC2138User") - * @JoinColumn(name="object_id", referencedColumnName="id", nullable=false) - * @var User $user - */ - private $followedUser; - /** * Construct a UserFollowedUser entity */ - public function __construct(User $user, User $followedUser) - { - $this->user = $user; - $this->followedUser = $followedUser; + public function __construct( + #[ManyToOne(targetEntity: 'DDC2138User', inversedBy: 'followedUsers')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)] + protected User $user, + #[ManyToOne(targetEntity: 'DDC2138User')] + #[JoinColumn(name: 'object_id', referencedColumnName: 'id', nullable: false)] + private User $followedUser, + ) { } public function getUser(): User @@ -184,36 +156,26 @@ public function getFollowedUser(): User } } -/** - * @Table(name="users") - * @Entity - */ +#[Table(name: 'users')] +#[Entity] class DDC2138User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var string - * @Column(type="string", length=32, nullable=true) - */ + /** @var string */ + #[Column(type: 'string', length: 32, nullable: true)] protected $name; - /** - * @var ArrayCollection $followedUsers - * @OneToMany(targetEntity="DDC2138UserFollowedUser", mappedBy="user", cascade={"persist"}, orphanRemoval=true) - */ + /** @var ArrayCollection $followedUsers */ + #[OneToMany(targetEntity: 'DDC2138UserFollowedUser', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] protected $followedUsers; - /** - * @var ArrayCollection $followedStructures - * @OneToMany(targetEntity="DDC2138UserFollowedStructure", mappedBy="user", cascade={"persist"}, orphanRemoval=true) - */ + /** @var ArrayCollection $followedStructures */ + #[OneToMany(targetEntity: 'DDC2138UserFollowedStructure', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] protected $followedStructures; public function __construct() @@ -236,7 +198,7 @@ public function removeFollowedUser(UserFollowedUser $followedUsers): User return $this; } - public function getFollowedUsers(): Doctrine\Common\Collections\Collection + public function getFollowedUsers(): Collection { return $this->followedUsers; } @@ -255,7 +217,7 @@ public function removeFollowedStructure(UserFollowedStructure $followedStructure return $this; } - public function getFollowedStructures(): Doctrine\Common\Collections\Collection + public function getFollowedStructures(): Collection { return $this->followedStructures; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2175Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2175Test.php index 51c31b76e96..cde8fbb20bc 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2175Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2175Test.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2175 */ +#[Group('DDC-2175')] class DDC2175Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -45,31 +46,23 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorMap({"entity": "DDC2175Entity"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap(['entity' => 'DDC2175Entity'])] class DDC2175Entity { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $field; - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2182Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2182Test.php index b3e9cafc707..d804be0caef 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2182Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2182Test.php @@ -13,9 +13,6 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; -use function method_exists; -use function sprintf; - class DDC2182Test extends OrmFunctionalTestCase { public function testPassColumnOptionsToJoinColumns(): void @@ -24,60 +21,36 @@ public function testPassColumnOptionsToJoinColumns(): void self::markTestSkipped('This test is useful for all databases, but designed only for mysql.'); } - $sql = $this->_schemaTool->getCreateSchemaSql( + $sql = $this->_schemaTool->getCreateSchemaSql( [ $this->_em->getClassMetadata(DDC2182OptionParent::class), $this->_em->getClassMetadata(DDC2182OptionChild::class), - ] + ], ); - $collation = $this->getColumnCollationDeclarationSQL('utf8_unicode_ci'); - - self::assertEquals('CREATE TABLE DDC2182OptionParent (id INT UNSIGNED NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[0]); - self::assertEquals('CREATE TABLE DDC2182OptionChild (id VARCHAR(255) NOT NULL, parent_id INT UNSIGNED DEFAULT NULL, INDEX IDX_B314D4AD727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 ' . $collation . ' ENGINE = InnoDB', $sql[1]); + self::assertEquals('CREATE TABLE DDC2182OptionParent (id INT UNSIGNED NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[0]); + self::assertEquals('CREATE TABLE DDC2182OptionChild (id VARCHAR(255) NOT NULL, parent_id INT UNSIGNED DEFAULT NULL, INDEX IDX_B314D4AD727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB', $sql[1]); self::assertEquals('ALTER TABLE DDC2182OptionChild ADD CONSTRAINT FK_B314D4AD727ACA70 FOREIGN KEY (parent_id) REFERENCES DDC2182OptionParent (id)', $sql[2]); } - - private function getColumnCollationDeclarationSQL(string $collation): string - { - if (method_exists($this->_em->getConnection()->getDatabasePlatform(), 'getColumnCollationDeclarationSQL')) { - return $this->_em->getConnection()->getDatabasePlatform()->getColumnCollationDeclarationSQL($collation); - } - - return sprintf('COLLATE %s', $collation); - } } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class DDC2182OptionParent { - /** - * @var int - * @Id - * @Column(type="integer", options={"unsigned": true}) - */ - private $id; + #[Id] + #[Column(type: 'integer', options: ['unsigned' => true])] + private int $id; } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class DDC2182OptionChild { - /** - * @var string - * @Id - * @Column - */ - private $id; + #[Id] + #[Column] + private string $id; - /** - * @var DDC2182OptionParent - * @ManyToOne(targetEntity="DDC2182OptionParent") - * @JoinColumn(referencedColumnName="id") - */ - private $parent; + #[ManyToOne(targetEntity: 'DDC2182OptionParent')] + #[JoinColumn(referencedColumnName: 'id')] + private DDC2182OptionParent $parent; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2214Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2214Test.php index ce246fd1aff..316cb7669c4 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2214Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2214Test.php @@ -4,8 +4,6 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Logging\Middleware as LoggingMiddleware; use Doctrine\DBAL\ParameterType; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; @@ -13,17 +11,16 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -use function class_exists; /** * Verifies that the type of parameters being bound to an SQL query is the same * of the identifier of the entities used as parameters in the DQL query, even * if the bound objects are proxies. - * - * @group DDC-2214 */ +#[Group('DDC-2214')] class DDC2214Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -55,41 +52,30 @@ public function testIssue(): void ->setParameter('ids', [$bar]) ->getResult(); - if (! class_exists(LoggingMiddleware::class)) { - // DBAL 2 logs queries before resolving parameter positions - self::assertEquals([Connection::PARAM_INT_ARRAY], $this->getLastLoggedQuery()['types']); - } else { - self::assertEquals([1 => ParameterType::INTEGER], $this->getLastLoggedQuery()['types']); - } + self::assertEquals([1 => ParameterType::INTEGER], $this->getLastLoggedQuery()['types']); } } -/** @Entity */ +#[Entity] class DDC2214Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC2214Bar - * @ManyToOne(targetEntity="DDC2214Bar") - */ + /** @var DDC2214Bar */ + #[ManyToOne(targetEntity: 'DDC2214Bar')] public $bar; } -/** @Entity */ +#[Entity] class DDC2214Bar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2224Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2224Test.php index a0b4019f60f..322743511ad 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2224Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2224Test.php @@ -12,12 +12,13 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use function method_exists; use function sprintf; -/** @group DDC-2224 */ +#[Group('DDC-2224')] class DDC2224Test extends OrmFunctionalTestCase { public static function setUpBeforeClass(): void @@ -37,7 +38,7 @@ public function testIssue(): Query return $query; } - /** @depends testIssue */ + #[Depends('testIssue')] public function testCacheMissWhenTypeChanges(Query $query): void { $query->setParameter('field', 'test', 'string'); @@ -50,13 +51,9 @@ class DDC2224Type extends Type /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } public function getName(): string @@ -67,34 +64,22 @@ public function getName(): string /** * {@inheritDoc} */ - public function canRequireSQLConversion() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform): string { return sprintf('FUNCTION(%s)', $sqlExpr); } } -/** @Entity */ +#[Entity] class DDC2224Entity { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var mixed - * @Column(type="DDC2224Type", length=255) - */ + /** @var mixed */ + #[Column(type: 'DDC2224Type', length: 255)] public $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2230Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2230Test.php deleted file mode 100644 index 8557858ac92..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC2230Test.php +++ /dev/null @@ -1,113 +0,0 @@ -createSchemaForModels( - DDC2230User::class, - DDC2230Address::class - ); - } - - public function testNotifyTrackingNotCalledOnUninitializedProxies(): void - { - $insertedUser = new DDC2230User(); - $insertedUser->address = new DDC2230Address(); - - $this->_em->persist($insertedUser); - $this->_em->persist($insertedUser->address); - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(DDC2230User::class, $insertedUser->id); - - $this->_em->clear(); - - $mergedUser = $this->_em->merge($user); - - $address = $mergedUser->address; - - self::assertTrue($this->isUninitializedObject($address)); - } - - public function testNotifyTrackingCalledOnProxyInitialization(): void - { - $insertedAddress = new DDC2230Address(); - - $this->_em->persist($insertedAddress); - $this->_em->flush(); - $this->_em->clear(); - - $addressProxy = $this->_em->getReference(DDC2230Address::class, $insertedAddress->id); - assert($addressProxy instanceof DDC2230Address); - - self::assertTrue($this->isUninitializedObject($addressProxy)); - self::assertNull($addressProxy->listener); - - $this->_em->getUnitOfWork()->initializeObject($addressProxy); - - self::assertSame($this->_em->getUnitOfWork(), $addressProxy->listener); - } -} - -/** @Entity */ -class DDC2230User -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var DDC2230Address - * @OneToOne(targetEntity="DDC2230Address") - */ - public $address; -} - -/** - * @Entity - * @ChangeTrackingPolicy("NOTIFY") - */ -class DDC2230Address implements NotifyPropertyChanged -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** @var \Doctrine\Common\PropertyChangedListener */ - public $listener; - - /** {@inheritDoc} */ - public function addPropertyChangedListener(PropertyChangedListener $listener) - { - $this->listener = $listener; - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2231Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2231Test.php deleted file mode 100644 index 1f8a3c604e7..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC2231Test.php +++ /dev/null @@ -1,86 +0,0 @@ -createSchemaForModels(DDC2231EntityY::class); - } - - public function testInjectObjectManagerInProxyIfInitializedInUow(): void - { - $y1 = new DDC2231EntityY(); - - $this->_em->persist($y1); - - $this->_em->flush(); - $this->_em->clear(); - - $y1ref = $this->_em->getReference(get_class($y1), $y1->id); - - self::assertTrue($this->isUninitializedObject($y1ref)); - - $id = $y1ref->doSomething(); - - self::assertFalse($this->isUninitializedObject($y1ref)); - self::assertEquals($this->_em, $y1ref->om); - } -} - -if (interface_exists(ObjectManagerAware::class)) { - /** - * @Entity - * @Table(name="ddc2231_y") - */ - class DDC2231EntityY implements ObjectManagerAware - { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - public $id; - - /** @var ObjectManager */ - public $om; - - public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata): void - { - $this->om = $objectManager; - } - - public function getId(): int - { - return $this->id; - } - - public function doSomething(): void - { - } - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2252Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2252Test.php index dd24f685966..4eed0a25ce9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2252Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2252Test.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\HasLifecycleCallbacks; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -18,8 +19,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2252 */ +#[Group('DDC-2252')] class DDC2252Test extends OrmFunctionalTestCase { /** @phpstan-var DDC2252User */ @@ -32,7 +34,7 @@ class DDC2252Test extends OrmFunctionalTestCase private $membership; /** @phpstan-var list */ - private $privileges = []; + private array $privileges = []; protected function setUp(): void { @@ -42,7 +44,7 @@ protected function setUp(): void DDC2252User::class, DDC2252Privilege::class, DDC2252Membership::class, - DDC2252MerchantAccount::class + DDC2252MerchantAccount::class, ); $this->loadFixtures(); @@ -119,18 +121,14 @@ public function testIssue(): void } } -/** - * @Entity() - * @Table(name="ddc2252_acl_privilege") - */ +#[Table(name: 'ddc2252_acl_privilege')] +#[Entity] class DDC2252Privilege { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $privilegeid; public function getPrivilegeid(): int @@ -139,17 +137,13 @@ public function getPrivilegeid(): int } } -/** - * @Entity - * @Table(name="ddc2252_mch_account") - */ +#[Table(name: 'ddc2252_mch_account')] +#[Entity] class DDC2252MerchantAccount { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] protected $accountid = 111; public function getAccountid(): int @@ -158,24 +152,18 @@ public function getAccountid(): int } } -/** - * @Entity - * @Table(name="ddc2252_user_account") - */ +#[Table(name: 'ddc2252_user_account')] +#[Entity] class DDC2252User { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] protected $uid = 222; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2252Membership", mappedBy="userAccount", cascade={"persist"}) - * @JoinColumn(name="uid", referencedColumnName="uid") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2252Membership', mappedBy: 'userAccount', cascade: ['persist'])] + #[JoinColumn(name: 'uid', referencedColumnName: 'uid')] protected $memberships; public function __construct() @@ -200,49 +188,30 @@ public function addMembership(DDC2252Membership $membership): void } } -/** - * @Entity - * @Table(name="ddc2252_mch_account_member") - * @HasLifecycleCallbacks - */ +#[Table(name: 'ddc2252_mch_account_member')] +#[Entity] +#[HasLifecycleCallbacks] class DDC2252Membership { - /** - * @var DDC2252User - * @Id - * @ManyToOne(targetEntity="DDC2252User", inversedBy="memberships") - * @JoinColumn(name="uid", referencedColumnName="uid") - */ - protected $userAccount; - - /** - * @var DDC2252MerchantAccount - * @Id - * @ManyToOne(targetEntity="DDC2252MerchantAccount") - * @JoinColumn(name="mch_accountid", referencedColumnName="accountid") - */ - protected $merchantAccount; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC2252Privilege", indexBy="privilegeid") - * @JoinTable(name="ddc2252_user_mch_account_privilege", - * joinColumns={ - * @JoinColumn(name="mch_accountid", referencedColumnName="mch_accountid"), - * @JoinColumn(name="uid", referencedColumnName="uid") - * }, - * inverseJoinColumns={ - * @JoinColumn(name="privilegeid", referencedColumnName="privilegeid") - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'ddc2252_user_mch_account_privilege')] + #[JoinColumn(name: 'mch_accountid', referencedColumnName: 'mch_accountid')] + #[JoinColumn(name: 'uid', referencedColumnName: 'uid')] + #[InverseJoinColumn(name: 'privilegeid', referencedColumnName: 'privilegeid')] + #[ManyToMany(targetEntity: 'DDC2252Privilege', indexBy: 'privilegeid')] protected $privileges; - public function __construct(DDC2252User $user, DDC2252MerchantAccount $merchantAccount) - { - $this->userAccount = $user; - $this->merchantAccount = $merchantAccount; - $this->privileges = new ArrayCollection(); + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'DDC2252User', inversedBy: 'memberships')] + #[JoinColumn(name: 'uid', referencedColumnName: 'uid')] + protected DDC2252User $userAccount, + #[Id] + #[ManyToOne(targetEntity: 'DDC2252MerchantAccount')] + #[JoinColumn(name: 'mch_accountid', referencedColumnName: 'accountid')] + protected DDC2252MerchantAccount $merchantAccount, + ) { + $this->privileges = new ArrayCollection(); } public function addPrivilege(DDC2252Privilege $privilege): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2256Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2256Test.php deleted file mode 100644 index 7672b5f446d..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC2256Test.php +++ /dev/null @@ -1,143 +0,0 @@ -createSchemaForModels( - DDC2256User::class, - DDC2256Group::class - ); - } - - public function testIssue(): void - { - if (! class_exists(PersistentObject::class)) { - $this->markTestSkipped('This test requires doctrine/persistence 2'); - } - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8818'); - $config = $this->_em->getConfiguration(); - $config->addEntityNamespace('MyNamespace', __NAMESPACE__); - - $user = new DDC2256User(); - $user->name = 'user'; - $group = new DDC2256Group(); - $group->name = 'group'; - $user->group = $group; - - $this->_em->persist($user); - $this->_em->persist($group); - $this->_em->flush(); - $this->_em->clear(); - - $sql = 'SELECT u.id, u.name, g.id as group_id, g.name as group_name FROM ddc2256_users u LEFT JOIN ddc2256_groups g ON u.group_id = g.id'; - - // Test ResultSetMapping. - $rsm = new ResultSetMapping(); - - $rsm->addEntityResult('MyNamespace:DDC2256User', 'u'); - $rsm->addFieldResult('u', 'id', 'id'); - $rsm->addFieldResult('u', 'name', 'name'); - - $rsm->addJoinedEntityResult('MyNamespace:DDC2256Group', 'g', 'u', 'group'); - $rsm->addFieldResult('g', 'group_id', 'id'); - $rsm->addFieldResult('g', 'group_name', 'name'); - - self::assertCount(1, $this->_em->createNativeQuery($sql, $rsm)->getResult()); - - // Test ResultSetMappingBuilder. - $rsm = new ResultSetMappingBuilder($this->_em); - $rsm->addRootEntityFromClassMetadata('MyNamespace:DDC2256User', 'u'); - $rsm->addJoinedEntityFromClassMetadata('MyNamespace:DDC2256Group', 'g', 'u', 'group', ['id' => 'group_id', 'name' => 'group_name']); - - self::assertCount(1, $this->_em->createNativeQuery($sql, $rsm)->getResult()); - } -} - -/** - * @Entity - * @Table(name="ddc2256_users") - */ -class DDC2256User -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string") - */ - public $name; - - /** - * @var DDC2256Group - * @ManyToOne(targetEntity="DDC2256Group", inversedBy="users")A - * @JoinColumn(name="group_id") - */ - public $group; -} - -/** - * @Entity - * @Table(name="ddc2256_groups") - */ -class DDC2256Group -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string") - */ - public $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2256User", mappedBy="group") - */ - public $users; - - public function __construct() - { - $this->users = new ArrayCollection(); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php index 0d251fe6ffb..1f96942df10 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php @@ -13,10 +13,11 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -/** @group DDC-2306 */ +#[Group('DDC-2306')] class DDC2306Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +28,7 @@ protected function setUp(): void DDC2306Zone::class, DDC2306User::class, DDC2306Address::class, - DDC2306UserAddress::class + DDC2306UserAddress::class, ); } @@ -75,44 +76,36 @@ public function testIssue(): void self::assertEquals( $userId, $user->id, - 'As of DDC-1734, the identifier is NULL for un-managed proxies. The identifier should be an integer here' + 'As of DDC-1734, the identifier is NULL for un-managed proxies. The identifier should be an integer here', ); } } -/** @Entity */ +#[Entity] class DDC2306Zone { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC2306User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2306UserAddress[]|Collection - * @OneToMany(targetEntity="DDC2306UserAddress", mappedBy="user") - */ + /** @var DDC2306UserAddress[]|Collection */ + #[OneToMany(targetEntity: 'DDC2306UserAddress', mappedBy: 'user')] public $addresses; - /** - * @var DDC2306Zone - * @ManyToOne(targetEntity="DDC2306Zone", fetch="EAGER") - */ + /** @var DDC2306Zone */ + #[ManyToOne(targetEntity: 'DDC2306Zone', fetch: 'EAGER')] public $zone; /** Constructor */ @@ -122,27 +115,21 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC2306Address { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2306UserAddress[]|Collection - * @OneToMany(targetEntity="DDC2306UserAddress", mappedBy="address", orphanRemoval=true) - */ + /** @var DDC2306UserAddress[]|Collection */ + #[OneToMany(targetEntity: 'DDC2306UserAddress', mappedBy: 'address', orphanRemoval: true)] public $users; - /** - * @var DDC2306Zone - * @ManyToOne(targetEntity="DDC2306Zone", fetch="EAGER") - */ + /** @var DDC2306Zone */ + #[ManyToOne(targetEntity: 'DDC2306Zone', fetch: 'EAGER')] public $zone; /** Constructor */ @@ -152,35 +139,22 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC2306UserAddress { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2306User - * @ManyToOne(targetEntity="DDC2306User") - */ - public $user; - - /** - * @var DDC2306Address - * @ManyToOne(targetEntity="DDC2306Address", fetch="LAZY") - */ - public $address; - /** Constructor */ - public function __construct(DDC2306User $user, DDC2306Address $address) - { - $this->user = $user; - $this->address = $address; - + public function __construct( + #[ManyToOne(targetEntity: 'DDC2306User')] + public DDC2306User $user, + #[ManyToOne(targetEntity: 'DDC2306Address', fetch: 'LAZY')] + public DDC2306Address $address, + ) { $user->addresses->add($this); $address->users->add($this); } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2346Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2346Test.php index 94c6b649db8..97d331e63c4 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2346Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2346Test.php @@ -16,8 +16,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2346 */ +#[Group('DDC-2346')] class DDC2346Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +28,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2346Foo::class, DDC2346Bar::class, - DDC2346Baz::class + DDC2346Baz::class, ); } @@ -65,21 +66,17 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC2346Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2346Bar[]|Collection - * @OneToMany(targetEntity="DDC2346Bar", mappedBy="foo") - */ + /** @var DDC2346Bar[]|Collection */ + #[OneToMany(targetEntity: 'DDC2346Bar', mappedBy: 'foo')] public $bars; /** Constructor */ @@ -89,31 +86,25 @@ public function __construct() } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"bar" = "DDC2346Bar", "baz" = "DDC2346Baz"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['bar' => 'DDC2346Bar', 'baz' => 'DDC2346Baz'])] class DDC2346Bar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2346Foo - * @ManyToOne(targetEntity="DDC2346Foo", inversedBy="bars", fetch="EAGER") - */ + /** @var DDC2346Foo */ + #[ManyToOne(targetEntity: 'DDC2346Foo', inversedBy: 'bars', fetch: 'EAGER')] public $foo; } -/** @Entity */ +#[Entity] class DDC2346Baz extends DDC2346Bar { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2350Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2350Test.php index 7e934dd1f2a..49d08d8b80a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2350Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2350Test.php @@ -12,11 +12,10 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-2350 - * @group non-cacheable - */ +#[Group('DDC-2350')] +#[Group('non-cacheable')] class DDC2350Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -52,37 +51,29 @@ public function testEagerCollectionsAreOnlyRetrievedOnce(): void } } -/** @Entity */ +#[Entity] class DDC2350User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2350Bug", mappedBy="user", fetch="EAGER") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2350Bug', mappedBy: 'user', fetch: 'EAGER')] public $reportedBugs; } -/** @Entity */ +#[Entity] class DDC2350Bug { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2350User - * @ManyToOne(targetEntity="DDC2350User", inversedBy="reportedBugs") - */ + /** @var DDC2350User */ + #[ManyToOne(targetEntity: 'DDC2350User', inversedBy: 'reportedBugs')] public $user; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2359Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2359Test.php index 882f6bcee60..7e8a753d53d 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2359Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2359Test.php @@ -15,14 +15,12 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; -/** @group DDC-2359 */ +#[Group('DDC-2359')] class DDC2359Test extends TestCase { - use MockBuilderCompatibilityTools; - /** * Verifies that {@see \Doctrine\ORM\Mapping\ClassMetadataFactory::wakeupReflection} is * not called twice when loading metadata from a driver @@ -33,12 +31,12 @@ public function testIssue(): void $mockMetadata = $this->createMock(ClassMetadata::class); $entityManager = $this->createMock(EntityManager::class); - $metadataFactory = $this - ->getMockBuilderWithOnlyMethods(ClassMetadataFactory::class, ['newClassMetadataInstance', 'wakeupReflection']) + $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class) + ->onlyMethods(['newClassMetadataInstance', 'wakeupReflection']) ->getMock(); - $configuration = $this - ->getMockBuilderWithOnlyMethods(Configuration::class, ['getMetadataDriverImpl']) + $configuration = $this->getMockBuilder(Configuration::class) + ->onlyMethods(['getMetadataDriverImpl']) ->getMock(); $connection = $this->createMock(Connection::class); @@ -64,14 +62,12 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC2359Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC237Test.php b/tests/Tests/ORM/Functional/Ticket/DDC237Test.php index ed5e7bff1c0..085d4f4d4df 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC237Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC237Test.php @@ -13,8 +13,6 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; -use function get_class; - class DDC237Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC237EntityX::class, DDC237EntityY::class, - DDC237EntityZ::class + DDC237EntityZ::class, ); } @@ -48,12 +46,12 @@ public function testUninitializedProxyIsInitializedOnFetchJoin(): void $this->_em->flush(); $this->_em->clear(); - $x2 = $this->_em->find(get_class($x), $x->id); // proxy injected for Y + $x2 = $this->_em->find($x::class, $x->id); // proxy injected for Y self::assertTrue($this->isUninitializedObject($x2->y)); // proxy for Y is in identity map - $z2 = $this->_em->createQuery('select z,y from ' . get_class($z) . ' z join z.y y where z.id = ?1') + $z2 = $this->_em->createQuery('select z,y from ' . $z::class . ' z join z.y y where z.id = ?1') ->setParameter(1, $z->id) ->getSingleResult(); self::assertFalse($this->isUninitializedObject($z2->y)); @@ -70,76 +68,54 @@ public function testUninitializedProxyIsInitializedOnFetchJoin(): void } -/** - * @Entity - * @Table(name="ddc237_x") - */ +#[Table(name: 'ddc237_x')] +#[Entity] class DDC237EntityX { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; - /** - * @var DDC237EntityY - * @OneToOne(targetEntity="DDC237EntityY") - * @JoinColumn(name="y_id", referencedColumnName="id") - */ + /** @var DDC237EntityY */ + #[OneToOne(targetEntity: 'DDC237EntityY')] + #[JoinColumn(name: 'y_id', referencedColumnName: 'id')] public $y; } -/** - * @Entity - * @Table(name="ddc237_y") - */ +#[Table(name: 'ddc237_y')] +#[Entity] class DDC237EntityY { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; } -/** - * @Entity - * @Table(name="ddc237_z") - */ +#[Table(name: 'ddc237_z')] +#[Entity] class DDC237EntityZ { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; - /** - * @var DDC237EntityY - * @OneToOne(targetEntity="DDC237EntityY") - * @JoinColumn(name="y_id", referencedColumnName="id") - */ + /** @var DDC237EntityY */ + #[OneToOne(targetEntity: 'DDC237EntityY')] + #[JoinColumn(name: 'y_id', referencedColumnName: 'id')] public $y; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2387Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2387Test.php index 0086cd3765e..997f7e5e49b 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2387Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2387Test.php @@ -7,10 +7,11 @@ use Doctrine\DBAL\Schema\Table; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Tests\ORM\Functional\DatabaseDriverTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC2387Test extends DatabaseDriverTestCase { - /** @group DDC-2387 */ + #[Group('DDC-2387')] public function testCompositeAssociationKeyDetection(): void { $product = new Table('ddc2387_product'); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2409Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2409Test.php deleted file mode 100644 index 9ac4f243a5e..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC2409Test.php +++ /dev/null @@ -1,74 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testIssue(): void - { - $em = $this->_em; - $uow = $em->getUnitOfWork(); - - $originalArticle = new CmsArticle(); - $originalUser = new CmsUser(); - - $originalArticle->topic = 'Unit Test'; - $originalArticle->text = 'How to write a test'; - - $originalUser->name = 'Doctrine Bot'; - $originalUser->username = 'DoctrineBot'; - $originalUser->status = 'active'; - - $originalUser->addArticle($originalArticle); - - $em->persist($originalUser); - $em->persist($originalArticle); - $em->flush(); - $em->clear(); - - $article = $em->find(CmsArticle::class, $originalArticle->id); - $user = new CmsUser(); - - $user->name = 'Doctrine Bot 2.0'; - $user->username = 'BotDoctrine2'; - $user->status = 'new'; - - $article->setAuthor($user); - - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalArticle)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalUser)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($article)); - self::assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); - - $em->clear(CmsUser::class); - $em->clear(CmsArticle::class); - - $userMerged = $em->merge($user); - $articleMerged = $em->merge($article); - - self::assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($article)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($userMerged)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($articleMerged)); - - self::assertNotSame($user, $userMerged); - self::assertNotSame($article, $articleMerged); - self::assertNotSame($userMerged, $articleMerged->user); - self::assertSame($user, $articleMerged->user); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2415Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2415Test.php index 60a0c09d51f..86897f81e47 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2415Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2415Test.php @@ -7,12 +7,13 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Id\AbstractIdGenerator; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\Driver\StaticPHPDriver; +use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function md5; -/** @group DDC-2415 */ +#[Group('DDC-2415')] class DDC2415Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -23,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2415ParentEntity::class, - DDC2415ChildEntity::class + DDC2415ChildEntity::class, ); } @@ -66,7 +67,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'string', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM); @@ -78,12 +79,8 @@ public static function loadMetadata(ClassMetadata $metadata): void class DDC2415ChildEntity extends DDC2415ParentEntity { - /** @var string */ - protected $name; - - public function __construct(string $name) + public function __construct(protected string $name) { - $this->name = $name; } public function getName(): string @@ -97,14 +94,14 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'fieldName' => 'name', 'type' => 'string', - ] + ], ); } } class DDC2415Generator extends AbstractIdGenerator { - public function generateId(EntityManagerInterface $em, $entity): string + public function generateId(EntityManagerInterface $em, object|null $entity): string { return md5($entity->getName()); } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2494Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2494Test.php index daf0f216970..4b9be908390 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2494Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2494Test.php @@ -16,11 +16,10 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-2494 - * @group non-cacheable - */ +#[Group('DDC-2494')] +#[Group('non-cacheable')] class DDC2494Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -33,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2494Currency::class, - DDC2494Campaign::class + DDC2494Campaign::class, ); } @@ -79,35 +78,21 @@ public function testIssue(): void } } -/** - * @Table(name="ddc2494_currency") - * @Entity - */ +#[Table(name: 'ddc2494_currency')] +#[Entity] class DDC2494Currency { - /** - * @var int - * @Id - * @Column(type="ddc2494_tinyint") - */ - protected $id; - - /** - * @var int - * @Column(name="temp", type="ddc2494_tinyint", nullable=false) - */ - protected $temp; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2494Campaign", mappedBy="currency") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2494Campaign', mappedBy: 'currency')] protected $campaigns; - public function __construct(int $id, int $temp) - { - $this->id = $id; - $this->temp = $temp; + public function __construct( + #[Id] + #[Column(type: 'ddc2494_tinyint')] + protected int $id, + #[Column(name: 'temp', type: 'ddc2494_tinyint', nullable: false)] + protected int $temp, + ) { } public function getId(): int @@ -127,30 +112,21 @@ public function getCampaigns(): Collection } } -/** - * @Table(name="ddc2494_campaign") - * @Entity - */ +#[Table(name: 'ddc2494_campaign')] +#[Entity] class DDC2494Campaign { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @var DDC2494Currency - * @ManyToOne(targetEntity="DDC2494Currency", inversedBy="campaigns") - * @JoinColumn(name="currency_id", referencedColumnName="id", nullable=false) - */ - protected $currency; - - public function __construct(DDC2494Currency $currency) - { - $this->currency = $currency; + public function __construct( + #[ManyToOne(targetEntity: 'DDC2494Currency', inversedBy: 'campaigns')] + #[JoinColumn(name: 'currency_id', referencedColumnName: 'id', nullable: false)] + protected DDC2494Currency $currency, + ) { } public function getId(): int @@ -172,15 +148,15 @@ class DDC2494TinyIntType extends Type /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + return $platform->getSmallIntTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { $return = (string) $value; @@ -196,7 +172,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): int { $return = (int) $value; @@ -209,10 +185,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $return; } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return 'ddc2494_tinyint'; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2519Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2519Test.php index 0fa9401c728..b8dfba13b9c 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2519Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2519Test.php @@ -7,8 +7,9 @@ use Doctrine\Tests\Models\Legacy\LegacyUser; use Doctrine\Tests\Models\Legacy\LegacyUserReference; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2519 */ +#[Group('DDC-2519')] class DDC2519Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -20,7 +21,7 @@ protected function setUp(): void $this->loadFixture(); } - /** @group DDC-2519 */ + #[Group('DDC-2519')] public function testIssue(): void { $dql = 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l'; diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2575Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2575Test.php index da76b8dc925..ca7f3d8c2f3 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2575Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2575Test.php @@ -11,18 +11,19 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2575 */ +#[Group('DDC-2575')] class DDC2575Test extends OrmFunctionalTestCase { /** @phpstan-var list */ - private $rootsEntities = []; + private array $rootsEntities = []; /** @phpstan-var list */ - private $aEntities = []; + private array $aEntities = []; /** @phpstan-var list */ - private $bEntities = []; + private array $bEntities = []; protected function setUp(): void { @@ -31,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2575Root::class, DDC2575A::class, - DDC2575B::class + DDC2575B::class, ); $entityRoot1 = new DDC2575Root(1); @@ -95,79 +96,47 @@ public function testHydrationIssue(): void } } -/** @Entity */ +#[Entity] class DDC2575Root { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; - - /** - * @var int - * @Column(type="integer") - */ - public $sampleField; - - /** - * @var DDC2575A - * @OneToOne(targetEntity="DDC2575A", mappedBy="rootRelation") - */ + /** @var DDC2575A */ + #[OneToOne(targetEntity: 'DDC2575A', mappedBy: 'rootRelation')] public $aRelation; - public function __construct(int $id, int $value = 0) - { - $this->id = $id; - $this->sampleField = $value; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + #[Column(type: 'integer')] + public int $sampleField = 0, + ) { } } -/** @Entity */ +#[Entity] class DDC2575A { - /** - * @var DDC2575Root - * @Id - * @OneToOne(targetEntity="DDC2575Root", inversedBy="aRelation") - * @JoinColumn(name="root_id", referencedColumnName="id", nullable=FALSE, onDelete="CASCADE") - */ - public $rootRelation; - - /** - * @var DDC2575B - * @ManyToOne(targetEntity="DDC2575B") - * @JoinColumn(name="b_id", referencedColumnName="id", nullable=FALSE, onDelete="CASCADE") - */ - public $bRelation; - - public function __construct(DDC2575Root $rootRelation, DDC2575B $bRelation) - { - $this->rootRelation = $rootRelation; - $this->bRelation = $bRelation; + public function __construct( + #[Id] + #[OneToOne(targetEntity: 'DDC2575Root', inversedBy: 'aRelation')] + #[JoinColumn(name: 'root_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')] + public DDC2575Root $rootRelation, + #[ManyToOne(targetEntity: 'DDC2575B')] + #[JoinColumn(name: 'b_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')] + public DDC2575B $bRelation, + ) { } } -/** @Entity */ +#[Entity] class DDC2575B { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; - - /** - * @var int - * @Column(type="integer") - */ - public $sampleField; - - public function __construct(int $id, int $value = 0) - { - $this->id = $id; - $this->sampleField = $value; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + #[Column(type: 'integer')] + public int $sampleField = 0, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2579Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2579Test.php index 79a898522ce..fb853ecafea 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2579Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2579Test.php @@ -13,8 +13,10 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; -/** @group DDC-2579 */ +#[Group('DDC-2579')] class DDC2579Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -26,7 +28,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC2579Entity::class, DDC2579EntityAssoc::class, - DDC2579AssocAssoc::class + DDC2579AssocAssoc::class, ); } @@ -66,68 +68,51 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC2579Entity { - /** - * @var DDC2579Id - * @Id - * @Column(type="ddc2579", length=255) - */ + /** @var DDC2579Id */ + #[Id] + #[Column(type: 'ddc2579', length: 255)] public $id; - /** - * @var DDC2579EntityAssoc - * @Id - * @ManyToOne(targetEntity="DDC2579EntityAssoc") - * @JoinColumn(name="relation_id", referencedColumnName="association_id") - */ + /** @var DDC2579EntityAssoc */ + #[Id] + #[ManyToOne(targetEntity: 'DDC2579EntityAssoc')] + #[JoinColumn(name: 'relation_id', referencedColumnName: 'association_id')] public $assoc; - /** - * @var int - * @Column(type="integer") - */ - public $value; - - public function __construct(DDC2579EntityAssoc $assoc, int $value = 0) - { + public function __construct( + DDC2579EntityAssoc $assoc, + #[Column(type: 'integer')] + public int $value = 0, + ) { $this->id = $assoc->assocAssoc->associationId; $this->assoc = $assoc; - $this->value = $value; } } -/** @Entity */ +#[Entity] class DDC2579EntityAssoc { - /** - * @var DDC2579AssocAssoc - * @Id - * @ManyToOne(targetEntity="DDC2579AssocAssoc") - * @JoinColumn(name="association_id", referencedColumnName="associationId") - */ - public $assocAssoc; - - public function __construct(DDC2579AssocAssoc $assocAssoc) - { - $this->assocAssoc = $assocAssoc; + public function __construct( + /** @var DDC2579AssocAssoc */ + #[Id] + #[ManyToOne(targetEntity: 'DDC2579AssocAssoc')] + #[JoinColumn(name: 'association_id', referencedColumnName: 'associationId')] + public $assocAssoc, + ) { } } -/** @Entity */ +#[Entity] class DDC2579AssocAssoc { - /** - * @var DDC2579Id - * @Id - * @Column(type="ddc2579", length=255) - */ - public $associationId; - - public function __construct(DDC2579Id $id) - { - $this->associationId = $id; + public function __construct( + #[Id] + #[Column(type: 'ddc2579', length: 255)] + public DDC2579Id $associationId, + ) { } } @@ -139,7 +124,7 @@ class DDC2579Type extends StringType /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { return (string) $value; } @@ -147,28 +132,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): DDC2579Id { return new DDC2579Id($value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } } -class DDC2579Id +class DDC2579Id implements Stringable { - /** @var string */ - private $val; - - public function __construct(string $val) + public function __construct(private string $val) { - $this->val = $val; } public function __toString(): string diff --git a/tests/Tests/ORM/Functional/Ticket/DDC258Test.php b/tests/Tests/ORM/Functional/Ticket/DDC258Test.php index 7b75607a102..1174cb27e27 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC258Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC258Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC258Test extends OrmFunctionalTestCase { @@ -24,11 +25,11 @@ protected function setUp(): void DDC258Super::class, DDC258Class1::class, DDC258Class2::class, - DDC258Class3::class + DDC258Class3::class, ); } - /** @group DDC-258 */ + #[Group('DDC-258')] public function testIssue(): void { $c1 = new DDC258Class1(); @@ -78,78 +79,59 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="DDC258Super") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"class1" = "DDC258Class1", "class2" = "DDC258Class2", "class3"="DDC258Class3"}) - */ +#[Table(name: 'DDC258Super')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['class1' => 'DDC258Class1', 'class2' => 'DDC258Class2', 'class3' => 'DDC258Class3'])] abstract class DDC258Super { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** @Entity */ +#[Entity] class DDC258Class1 extends DDC258Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $title; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $description; } -/** @Entity */ +#[Entity] class DDC258Class2 extends DDC258Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $title; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $description; - /** - * @var string - * @Column(name="text", type="text") - */ + /** @var string */ + #[Column(name: 'text', type: 'text')] public $text; } /** * An extra class to demonstrate why title and description aren't in Super - * - * @Entity */ +#[Entity] class DDC258Class3 extends DDC258Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $apples; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $bananas; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2602Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2602Test.php index e2255536a03..fc1d74c5a57 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2602Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2602Test.php @@ -16,11 +16,14 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function in_array; use function json_decode; -/** @group DDC-2602 */ +use const JSON_THROW_ON_ERROR; + +#[Group('DDC-2602')] class DDC2602Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -31,7 +34,7 @@ protected function setUp(): void DDC2602User::class, DDC2602Biography::class, DDC2602BiographyField::class, - DDC2602BiographyFieldChoice::class + DDC2602BiographyFieldChoice::class, ); $this->loadFixture(); @@ -137,7 +140,7 @@ public function postLoad(PostLoadEventArgs $event): void '); $result = $query->getResult(); - $content = json_decode($entity->content); + $content = json_decode($entity->content, null, 512, JSON_THROW_ON_ERROR); $fieldList = new ArrayCollection(); foreach ($content as $selection) { @@ -146,9 +149,7 @@ public function postLoad(PostLoadEventArgs $event): void $fieldSelection = new DDC2602FieldSelection(); $fieldSelection->field = $field; - $fieldSelection->choiceList = $field->choiceList->filter(static function ($choice) use ($choiceList) { - return in_array($choice->id, $choiceList, true); - }); + $fieldSelection->choiceList = $field->choiceList->filter(static fn ($choice) => in_array($choice->id, $choiceList, true)); $fieldList->add($fieldSelection); } @@ -158,97 +159,65 @@ public function postLoad(PostLoadEventArgs $event): void } -/** @Entity */ +#[Entity] class DDC2602User { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @Column(type="string", length=15) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 15)] public $name; - /** - * @var DDC2602Biography - * @OneToOne( - * targetEntity="DDC2602Biography", - * inversedBy="user", - * cascade={"persist", "merge", "refresh", "remove"} - * ) - * @JoinColumn(nullable=false) - */ + /** @var DDC2602Biography */ + #[OneToOne(targetEntity: 'DDC2602Biography', inversedBy: 'user', cascade: ['persist', 'refresh', 'remove'])] + #[JoinColumn(nullable: false)] public $biography; } -/** @Entity */ +#[Entity] class DDC2602Biography { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC2602User - * @OneToOne( - * targetEntity="DDC2602User", - * mappedBy="biography", - * cascade={"persist", "merge", "refresh"} - * ) - */ + /** @var DDC2602User */ + #[OneToOne(targetEntity: 'DDC2602User', mappedBy: 'biography', cascade: ['persist', 'refresh'])] public $user; - /** - * @Column(type="text", nullable=true) - * @var string - */ + /** @var string */ + #[Column(type: 'text', nullable: true)] public $content; /** @var array */ public $fieldList = []; } -/** @Entity */ +#[Entity] class DDC2602BiographyField { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", unique=true, length=100) - */ + /** @var string */ + #[Column(type: 'string', unique: true, length: 100)] public $alias; - /** - * @var string - * @Column(type="string", length=100) - */ + /** @var string */ + #[Column(type: 'string', length: 100)] public $label; - /** - * @OneToMany( - * targetEntity="DDC2602BiographyFieldChoice", - * mappedBy="field", - * cascade={"persist", "merge", "refresh"} - * ) - * @var ArrayCollection - */ + /** @var ArrayCollection */ + #[OneToMany(targetEntity: 'DDC2602BiographyFieldChoice', mappedBy: 'field', cascade: ['persist', 'refresh'])] public $choiceList; public function __construct() @@ -257,31 +226,22 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC2602BiographyFieldChoice { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", unique=true, length=100) - */ + /** @var string */ + #[Column(type: 'string', unique: true, length: 100)] public $label; - /** - * @ManyToOne( - * targetEntity="DDC2602BiographyField", - * inversedBy="choiceList" - * ) - * @JoinColumn(onDelete="CASCADE") - * @var DDC2602BiographyField - */ + /** @var DDC2602BiographyField */ + #[ManyToOne(targetEntity: 'DDC2602BiographyField', inversedBy: 'choiceList')] + #[JoinColumn(onDelete: 'CASCADE')] public $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2645Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2645Test.php deleted file mode 100644 index 5e04a376f6e..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC2645Test.php +++ /dev/null @@ -1,76 +0,0 @@ -id = 123; - - $foo = new DDC2645Foo(1, $bar, 'Foo'); - $foo2 = new DDC2645Foo(1, $bar, 'Bar'); - - $this->_em->persist($bar); - $this->_em->persist($foo); - - $foo3 = $this->_em->merge($foo2); - - self::assertSame($foo, $foo3); - self::assertEquals('Bar', $foo->name); - } -} - -/** @Entity */ -class DDC2645Foo -{ - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var DDC2645Bar - * @Id - * @ManyToOne(targetEntity="DDC2645Bar") - */ - private $bar; - - /** - * @var string - * @Column - */ - public $name; - - public function __construct(int $id, DDC2645Bar $bar, string $name) - { - $this->id = $id; - $this->bar = $bar; - $this->name = $name; - } -} - -/** @Entity */ -class DDC2645Bar -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ - public $id; -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2655Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2655Test.php index ae70e7f8c18..e8732151209 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2655Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2655Test.php @@ -6,8 +6,9 @@ use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2655 */ +#[Group('DDC-2655')] class DDC2655Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2660Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2660Test.php index 02b713352cc..8a7f48e36f9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2660Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2660Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Tests\OrmFunctionalTestCase; use Exception; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2660 */ +#[Group('DDC-2660')] class DDC2660Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,9 +28,9 @@ protected function setUp(): void $this->_em->getClassMetadata(DDC2660Product::class), $this->_em->getClassMetadata(DDC2660Customer::class), $this->_em->getClassMetadata(DDC2660CustomerOrder::class), - ] + ], ); - } catch (Exception $e) { + } catch (Exception) { return; } @@ -85,66 +86,41 @@ public function testIssueWithoutExtraColumn(): void } } } -/** - * @Entity - * @Table(name="ddc_2660_product") - */ +#[Table(name: 'ddc_2660_product')] +#[Entity] class DDC2660Product { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** - * @Entity - * @Table(name="ddc_2660_customer") - */ +#[Table(name: 'ddc_2660_customer')] +#[Entity] class DDC2660Customer { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** - * @Entity - * @Table(name="ddc_2660_customer_order") - */ +#[Table(name: 'ddc_2660_customer_order')] +#[Entity] class DDC2660CustomerOrder { - /** - * @var DDC2660Product - * @Id - * @ManyToOne(targetEntity="DDC2660Product") - */ - public $product; - - /** - * @var DDC2660Customer - * @Id - * @ManyToOne(targetEntity="DDC2660Customer") - */ - public $customer; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name) - { - $this->product = $product; - $this->customer = $customer; - $this->name = $name; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'DDC2660Product')] + public DDC2660Product $product, + #[Id] + #[ManyToOne(targetEntity: 'DDC2660Customer')] + public DDC2660Customer $customer, + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2692Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2692Test.php index c7c3ecc348b..f9164aa54c0 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2692Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2692Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2692 */ +#[Group('DDC-2692')] class DDC2692Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -58,17 +59,13 @@ public function preFlush(PreFlushEventArgs $args): void self::assertSame(1, $listener->registeredCalls); } } -/** - * @Entity - * @Table(name="ddc_2692_foo") - */ +#[Table(name: 'ddc_2692_foo')] +#[Entity] class DDC2692Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2759Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2759Test.php index f798938198c..968b3361615 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2759Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2759Test.php @@ -14,8 +14,9 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2759 */ +#[Group('DDC-2759')] class DDC2759Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -26,7 +27,7 @@ protected function setUp(): void DDC2759Qualification::class, DDC2759Category::class, DDC2759QualificationMetadata::class, - DDC2759MetadataCategory::class + DDC2759MetadataCategory::class, ); $qualification = new DDC2759Qualification(); @@ -67,109 +68,72 @@ public function testCorrectNumberOfAssociationsIsReturned(): void } } -/** - * @Entity - * @Table(name="ddc_2759_qualification") - */ +#[Table(name: 'ddc_2759_qualification')] +#[Entity] class DDC2759Qualification { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2759QualificationMetadata - * @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") - */ + /** @var DDC2759QualificationMetadata */ + #[OneToOne(targetEntity: 'DDC2759QualificationMetadata', mappedBy: 'content')] public $metadata; } -/** - * @Entity - * @Table(name="ddc_2759_category") - */ +#[Table(name: 'ddc_2759_category')] +#[Entity] class DDC2759Category { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2759MetadataCategory', mappedBy: 'category')] public $metadataCategories; } -/** - * @Entity - * @Table(name="ddc_2759_qualification_metadata") - */ +#[Table(name: 'ddc_2759_qualification_metadata')] +#[Entity] class DDC2759QualificationMetadata { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2759Qualification - * @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") - */ - public $content; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC2759MetadataCategory', mappedBy: 'metadata')] protected $metadataCategories; - public function __construct(DDC2759Qualification $content) - { - $this->content = $content; + public function __construct( + #[OneToOne(targetEntity: 'DDC2759Qualification', inversedBy: 'metadata')] + public DDC2759Qualification $content, + ) { } } -/** - * @Entity - * @Table(name="ddc_2759_metadata_category") - */ +#[Table(name: 'ddc_2759_metadata_category')] +#[Entity] class DDC2759MetadataCategory { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC2759QualificationMetadata - * @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") - */ - public $metadata; - - /** - * @var DDC2759Category - * @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") - */ - public $category; - - public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category) - { - $this->metadata = $metadata; - $this->category = $category; + public function __construct( + #[ManyToOne(targetEntity: 'DDC2759QualificationMetadata', inversedBy: 'metadataCategories')] + public DDC2759QualificationMetadata $metadata, + #[ManyToOne(targetEntity: 'DDC2759Category', inversedBy: 'metadataCategories')] + public DDC2759Category $category, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2775Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2775Test.php index 2947172a735..4a2d76646c4 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2775Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2775Test.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Functional tests for cascade remove with class table inheritance. @@ -32,11 +33,11 @@ protected function setUp(): void Role::class, AdminRole::class, Authorization::class, - ] + ], ); } - /** @group DDC-2775 */ + #[Group('DDC-2775')] public function testIssueCascadeRemove(): void { $role = new AdminRole(); @@ -65,33 +66,25 @@ public function testIssueCascadeRemove(): void } } -/** - * @Entity - * @Table(name="ddc2775_role") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="role_type", type="string") - * @DiscriminatorMap({"admin"="AdminRole"}) - */ +#[Table(name: 'ddc2775_role')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'role_type', type: 'string')] +#[DiscriminatorMap(['admin' => 'AdminRole'])] abstract class Role { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var User - * @ManyToOne(targetEntity="User", inversedBy="roles") - */ + /** @var User */ + #[ManyToOne(targetEntity: 'User', inversedBy: 'roles')] public $user; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Authorization", mappedBy="role", cascade={"all"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Authorization', mappedBy: 'role', cascade: ['all'], orphanRemoval: true)] public $authorizations; public function addAuthorization(Authorization $authorization): void @@ -101,65 +94,47 @@ public function addAuthorization(Authorization $authorization): void } } -/** - * @Entity - * @Table(name="ddc2775_admin_role") - */ +#[Table(name: 'ddc2775_admin_role')] +#[Entity] class AdminRole extends Role { } -/** - * @Entity - * @Table(name="ddc2775_authorizations") - */ +#[Table(name: 'ddc2775_authorizations')] +#[Entity] class Authorization { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var User - * @ManyToOne(targetEntity="User", inversedBy="authorizations") - */ + /** @var User */ + #[ManyToOne(targetEntity: 'User', inversedBy: 'authorizations')] public $user; - /** - * @var Role - * @ManyToOne(targetEntity="Role", inversedBy="authorizations") - */ + /** @var Role */ + #[ManyToOne(targetEntity: 'Role', inversedBy: 'authorizations')] public $role; } -/** - * @Entity - * @Table(name="ddc2775_users") - */ +#[Table(name: 'ddc2775_users')] +#[Entity] class User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Role", mappedBy="user", cascade={"all"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Role', mappedBy: 'user', cascade: ['all'], orphanRemoval: true)] public $roles; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Authorization", mappedBy="user", cascade={"all"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Authorization', mappedBy: 'user', cascade: ['all'], orphanRemoval: true)] public $authorizations; public function addRole(Role $role): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2780Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2780Test.php index 2e52037edd6..b409a8bba98 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2780Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2780Test.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2780 */ +#[Group('DDC-2780')] class DDC2780Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -50,39 +51,31 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC2780User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @ManyToOne(targetEntity="DDC2780Project") - * @var DDC2780Project - */ + /** @var DDC2780Project */ + #[ManyToOne(targetEntity: 'DDC2780Project')] public $project; } -/** @Entity */ +#[Entity] class DDC2780Project { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @OneToMany(targetEntity="DDC2780User", mappedBy="project") - * @var DDC2780User[] - */ + /** @var DDC2780User[] */ + #[OneToMany(targetEntity: 'DDC2780User', mappedBy: 'project')] public $users; /** Constructor */ diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2790Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2790Test.php index 7984dfd1c41..a979ffd7903 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2790Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2790Test.php @@ -8,12 +8,12 @@ use Doctrine\ORM\Events; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_intersect_key; -use function get_class; use function intval; -/** @group DDC-2790 */ +#[Group('DDC-2790')] class DDC2790Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -36,7 +36,7 @@ public function testIssue(): void $entity->name = 'Roman'; $qb = $this->_em->createQueryBuilder(); - $qb->from(get_class($entity), 'c'); + $qb->from($entity::class, 'c'); $qb->select('count(c)'); $initial = intval($qb->getQuery()->getSingleScalarResult()); @@ -51,7 +51,7 @@ public function testIssue(): void $this->_em->flush(); $qb = $this->_em->createQueryBuilder(); - $qb->from(get_class($entity), 'c'); + $qb->from($entity::class, 'c'); $qb->select('count(c)'); $count = intval($qb->getQuery()->getSingleScalarResult()); self::assertEquals($initial, $count); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC279Test.php b/tests/Tests/ORM/Functional/Ticket/DDC279Test.php index 57345a019ca..340733c103c 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC279Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC279Test.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC279Test extends OrmFunctionalTestCase { @@ -25,11 +26,11 @@ protected function setUp(): void DDC279EntityXAbstract::class, DDC279EntityX::class, DDC279EntityY::class, - DDC279EntityZ::class + DDC279EntityZ::class, ); } - /** @group DDC-279 */ + #[Group('DDC-279')] public function testDDC279(): void { $x = new DDC279EntityX(); @@ -52,7 +53,7 @@ public function testDDC279(): void $query = $this->_em->createQuery( 'SELECT x, y, z FROM Doctrine\Tests\ORM\Functional\Ticket\DDC279EntityX x ' . - 'INNER JOIN x.y y INNER JOIN y.z z WHERE x.id = ?1' + 'INNER JOIN x.y y INNER JOIN y.z z WHERE x.id = ?1', )->setParameter(1, $x->id); $result = $query->getResult(); @@ -68,79 +69,61 @@ public function testDDC279(): void } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"DDC279EntityX" = "DDC279EntityX"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['DDC279EntityX' => 'DDC279EntityX'])] abstract class DDC279EntityXAbstract { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="id", type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(name: 'id', type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; } -/** @Entity */ +#[Entity] class DDC279EntityX extends DDC279EntityXAbstract { - /** - * @var DDC279EntityY - * @OneToOne(targetEntity="DDC279EntityY") - * @JoinColumn(name="y_id", referencedColumnName="id") - */ + /** @var DDC279EntityY */ + #[OneToOne(targetEntity: 'DDC279EntityY')] + #[JoinColumn(name: 'y_id', referencedColumnName: 'id')] public $y; } -/** @Entity */ +#[Entity] class DDC279EntityY { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="id", type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(name: 'id', type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; - /** - * @var DDC279EntityZ - * @OneToOne(targetEntity="DDC279EntityZ") - * @JoinColumn(name="z_id", referencedColumnName="id") - */ + /** @var DDC279EntityZ */ + #[OneToOne(targetEntity: 'DDC279EntityZ')] + #[JoinColumn(name: 'z_id', referencedColumnName: 'id')] public $z; } -/** @Entity */ +#[Entity] class DDC279EntityZ { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="id", type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(name: 'id', type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2825Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2825Test.php index 96b3ca629cd..657a066c78f 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2825Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2825Test.php @@ -5,22 +5,18 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\Models\DDC2825\ExplicitSchemaAndTable; use Doctrine\Tests\Models\DDC2825\SchemaAndTableInTableName; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function sprintf; /** * This class makes tests on the correct use of a database schema when entities are stored - * - * @group DDC-2825 */ +#[Group('DDC-2825')] class DDC2825Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -29,12 +25,12 @@ protected function setUp(): void $platform = $this->_em->getConnection()->getDatabasePlatform(); - if (! $platform->supportsSchemas() && ! $platform->canEmulateSchemas()) { + if (! $platform->supportsSchemas()) { self::markTestSkipped('This test is only useful for databases that support schemas or can emulate them.'); } } - /** @dataProvider getTestedClasses */ + #[DataProvider('getTestedClasses')] public function testClassSchemaMappingsValidity(string $className, string $expectedSchemaName, string $expectedTableName): void { $classMetadata = $this->_em->getClassMetadata($className); @@ -56,11 +52,11 @@ public function testClassSchemaMappingsValidity(string $className, string $expec // Checks sequence name validity self::assertEquals( $fullTableName . '_' . $classMetadata->getSingleIdentifierColumnName() . '_seq', - $classMetadata->getSequenceName($platform) + $classMetadata->getSequenceName($platform), ); } - /** @dataProvider getTestedClasses */ + #[DataProvider('getTestedClasses')] public function testPersistenceOfEntityWithSchemaMapping(string $className): void { $this->createSchemaForModels($className); @@ -87,20 +83,11 @@ public static function getTestedClasses(): array } } -/** - * @Entity - * @Table(name="myschema.order") - */ #[ORM\Entity] #[ORM\Table(name: 'myschema.order')] class DDC2825ClassWithImplicitlyDefinedSchemaAndQuotedTableName { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2862Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2862Test.php index 2c9aa56d4e6..ae7328f0b6c 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2862Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2862Test.php @@ -12,11 +12,10 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-2862 - * @group DDC-2183 - */ +#[Group('DDC-2862')] +#[Group('DDC-2183')] class DDC2862Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -114,38 +113,24 @@ public function testIssueReopened(): void } } -/** - * @Entity - * @Table(name="ddc2862_drivers") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table(name: 'ddc2862_drivers')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class DDC2862Driver { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @Column(type="string", length=255) - * @var string - */ - protected $name; - - /** - * @Cache() - * @OneToOne(targetEntity="DDC2862User") - * @var DDC2862User - */ - protected $userProfile; - - public function __construct($name, $userProfile = null) - { - $this->name = $name; - $this->userProfile = $userProfile; + public function __construct( + #[Column(type: 'string', length: 255)] + protected string $name, + #[Cache] + #[OneToOne(targetEntity: 'DDC2862User')] + protected DDC2862User|null $userProfile = null, + ) { } public function getId(): int @@ -174,30 +159,21 @@ public function getUserProfile(): DDC2862User } } -/** - * @Entity - * @Table(name="ddc2862_users") - * @Cache("NONSTRICT_READ_WRITE") - */ +#[Table(name: 'ddc2862_users')] +#[Entity] +#[Cache('NONSTRICT_READ_WRITE')] class DDC2862User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; - /** - * @Column(type="string", length=255) - * @var string - */ - protected $name; - - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + protected string $name, + ) { } public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2895Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2895Test.php index ad13eb81a19..ca9e6a83437 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2895Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2895Test.php @@ -16,7 +16,6 @@ use Doctrine\Tests\OrmFunctionalTestCase; use function assert; -use function get_class; class DDC2895Test extends OrmFunctionalTestCase { @@ -36,7 +35,7 @@ public function testPostLoadOneToManyInheritance(): void 'prePersist' => ['setLastModifiedPreUpdate'], 'preUpdate' => ['setLastModifiedPreUpdate'], ], - $cm->lifecycleCallbacks + $cm->lifecycleCallbacks, ); $ddc2895 = new DDC2895(); @@ -45,29 +44,23 @@ public function testPostLoadOneToManyInheritance(): void $this->_em->flush(); $this->_em->clear(); - $ddc2895 = $this->_em->find(get_class($ddc2895), $ddc2895->id); + $ddc2895 = $this->_em->find($ddc2895::class, $ddc2895->id); assert($ddc2895 instanceof DDC2895); self::assertNotNull($ddc2895->getLastModified()); } } -/** - * @MappedSuperclass - * @HasLifecycleCallbacks - */ +#[MappedSuperclass] +#[HasLifecycleCallbacks] abstract class AbstractDDC2895 { - /** - * @Column(name="last_modified", type="datetimetz", nullable=false) - * @var DateTime - */ + /** @var DateTime */ + #[Column(name: 'last_modified', type: 'datetimetz', nullable: false)] protected $lastModified; - /** - * @PrePersist - * @PreUpdate - */ + #[PrePersist] + #[PreUpdate] public function setLastModifiedPreUpdate(): void { $this->setLastModified(new DateTime()); @@ -84,28 +77,22 @@ public function getLastModified(): DateTime } } -/** - * @Entity - * @HasLifecycleCallbacks - */ +#[Entity] +#[HasLifecycleCallbacks] class DDC2895 extends AbstractDDC2895 { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** @param mixed $id */ - public function setId($id): void + public function setId(mixed $id): void { $this->id = $id; } - /** @return mixed */ - public function getId() + public function getId(): mixed { return $this->id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2931Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2931Test.php index b2821c945cb..313caa45a05 100755 --- a/tests/Tests/ORM/Functional/Ticket/DDC2931Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2931Test.php @@ -11,8 +11,9 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2931 */ +#[Group('DDC-2931')] class DDC2931Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -70,7 +71,7 @@ public function testFetchJoinedEntitiesCanBeRefreshed(): void ->_em ->createQuery( 'SELECT e, p, c FROM ' - . __NAMESPACE__ . '\\DDC2931User e LEFT JOIN e.parent p LEFT JOIN e.child c WHERE e = :id' + . __NAMESPACE__ . '\\DDC2931User e LEFT JOIN e.parent p LEFT JOIN e.child c WHERE e = :id', ) ->setParameter('id', $second) ->setHint(Query::HINT_REFRESH, true) @@ -84,33 +85,25 @@ public function testFetchJoinedEntitiesCanBeRefreshed(): void } -/** @Entity */ +#[Entity] class DDC2931User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC2931User - * @OneToOne(targetEntity="DDC2931User", inversedBy="child") - */ + /** @var DDC2931User */ + #[OneToOne(targetEntity: 'DDC2931User', inversedBy: 'child')] public $parent; - /** - * @var DDC2931User - * @OneToOne(targetEntity="DDC2931User", mappedBy="parent") - */ + /** @var DDC2931User */ + #[OneToOne(targetEntity: 'DDC2931User', mappedBy: 'parent')] public $child; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $value = 0; /** diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2943Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2943Test.php index 264a56edf2d..f73d7748d71 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2943Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2943Test.php @@ -8,8 +8,9 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2943 */ +#[Group('DDC-2943')] class DDC2943Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2984Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2984Test.php index 88d36881ccb..485c9a755ea 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2984Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2984Test.php @@ -14,10 +14,12 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; use function is_string; -/** @group DDC-2984 */ +#[Group('DDC-2984')] class DDC2984Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +29,7 @@ protected function setUp(): void if (! Type::hasType('ddc2984_domain_user_id')) { Type::addType( 'ddc2984_domain_user_id', - DDC2984UserIdCustomDbalType::class + DDC2984UserIdCustomDbalType::class, ); } @@ -59,29 +61,19 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="users") - */ +#[Table(name: 'users')] +#[Entity] class DDC2984User { - /** - * @Id - * @Column(type="ddc2984_domain_user_id", length=255) - * @GeneratedValue(strategy="NONE") - * @var DDC2984DomainUserId - */ - private $userId; - - /** - * @var string - * @Column(type="string", length=50) - */ - private $name; - - public function __construct(DDC2984DomainUserId $aUserId) - { - $this->userId = $aUserId; + #[Column(type: 'string', length: 50)] + private string|null $name = null; + + public function __construct( + #[Id] + #[Column(type: 'ddc2984_domain_user_id', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + private DDC2984DomainUserId $userId, + ) { } public function userId(): DDC2984DomainUserId @@ -108,14 +100,10 @@ public function sameIdentityAs(DDC2984User $other): bool /** * DDC2984DomainUserId ValueObject */ -class DDC2984DomainUserId +class DDC2984DomainUserId implements Stringable { - /** @var string */ - private $userIdString; - - public function __construct(string $aUserIdString) + public function __construct(private string $userIdString) { - $this->userIdString = $aUserIdString; } public function toString(): string @@ -146,7 +134,7 @@ public function getName(): string /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): DDC2984DomainUserId|null { return ! empty($value) ? new DDC2984DomainUserId($value) @@ -156,7 +144,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { if (empty($value)) { return null; diff --git a/tests/Tests/ORM/Functional/Ticket/DDC2996Test.php b/tests/Tests/ORM/Functional/Ticket/DDC2996Test.php index 133135240ea..a3c66e74d4d 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC2996Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC2996Test.php @@ -12,17 +12,16 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\PreFlush; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-2996 */ +#[Group('DDC-2996')] class DDC2996Test extends OrmFunctionalTestCase { public function testIssue(): void { $this->createSchemaForModels( DDC2996User::class, - DDC2996UserPreference::class + DDC2996UserPreference::class, ); $pref = new DDC2996UserPreference(); @@ -45,59 +44,47 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC2996User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $counter = 0; } -/** - * @Entity - * @HasLifecycleCallbacks - */ +#[Entity] +#[HasLifecycleCallbacks] class DDC2996UserPreference { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $value; - /** - * @var DDC2996User - * @ManyToOne(targetEntity="DDC2996User") - */ + /** @var DDC2996User */ + #[ManyToOne(targetEntity: 'DDC2996User')] public $user; - /** @PreFlush */ + #[PreFlush] public function preFlush($event): void { - $em = $event->getEntityManager(); + $em = $event->getObjectManager(); $uow = $em->getUnitOfWork(); if ($uow->getOriginalEntityData($this->user)) { $this->user->counter++; $uow->recomputeSingleEntityChangeSet( - $em->getClassMetadata(get_class($this->user)), - $this->user + $em->getClassMetadata($this->user::class), + $this->user, ); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3033Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3033Test.php index 7968dc34424..f1224536c69 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3033Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3033Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\HasLifecycleCallbacks; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -20,17 +21,16 @@ use Doctrine\ORM\Mapping\PreUpdate; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-3033 */ +#[Group('DDC-3033')] class DDC3033Test extends OrmFunctionalTestCase { public function testIssue(): void { $this->createSchemaForModels( DDC3033User::class, - DDC3033Product::class + DDC3033Product::class, ); $user = new DDC3033User(); @@ -65,39 +65,29 @@ public function testIssue(): void } } -/** - * @Table - * @Entity - * @HasLifecycleCallbacks - */ +#[Table] +#[Entity] +#[HasLifecycleCallbacks] class DDC3033Product { /** @phpstan-var array */ public $changeSet = []; - /** - * @var int $id - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int $id */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string $title - * @Column(name="title", type="string", length=255) - */ + /** @var string $title */ + #[Column(name: 'title', type: 'string', length: 255)] public $title; - /** - * @var Collection - * @ManyToMany(targetEntity="DDC3033User") - * @JoinTable( - * name="user_purchases_3033", - * joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'user_purchases_3033')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC3033User')] public $buyers; /** @@ -108,42 +98,36 @@ public function __construct() $this->buyers = new ArrayCollection(); } - /** @PreUpdate */ + #[PreUpdate] public function preUpdate(PreUpdateEventArgs $eventArgs): void { } - /** @PostUpdate */ + #[PostUpdate] public function postUpdate(PostUpdateEventArgs $eventArgs): void { $em = $eventArgs->getObjectManager(); $uow = $em->getUnitOfWork(); $entity = $eventArgs->getObject(); - $classMetadata = $em->getClassMetadata(get_class($entity)); + $classMetadata = $em->getClassMetadata($entity::class); $uow->computeChangeSet($classMetadata, $entity); $this->changeSet = $uow->getEntityChangeSet($entity); } } -/** - * @Table - * @Entity - * @HasLifecycleCallbacks - */ +#[Table] +#[Entity] +#[HasLifecycleCallbacks] class DDC3033User { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="title", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 255)] public $name; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3042Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3042Test.php index 486f11f4a18..c20799d56b6 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3042Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3042Test.php @@ -9,8 +9,9 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-3042 */ +#[Group('DDC-3042')] class DDC3042Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,83 +28,59 @@ public function testSQLGenerationDoesNotProvokeAliasCollisions(): void $this ->_em ->createQuery( - 'SELECT f, b FROM ' . __NAMESPACE__ . '\DDC3042Foo f JOIN ' . __NAMESPACE__ . '\DDC3042Bar b WITH 1 = 1' + 'SELECT f, b FROM ' . __NAMESPACE__ . '\DDC3042Foo f JOIN ' . __NAMESPACE__ . '\DDC3042Bar b WITH 1 = 1', ) - ->getSQL() + ->getSQL(), ); } } -/** @Entity */ +#[Entity] class DDC3042Foo { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $field; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field1; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field2; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field3; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field4; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field5; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field6; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field7; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field8; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field9; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $field10; } -/** @Entity */ +#[Entity] class DDC3042Bar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3068Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3068Test.php index c062ec51055..b9397f82aef 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3068Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3068Test.php @@ -8,15 +8,14 @@ use Doctrine\Tests\Models\Taxi\Driver; use Doctrine\Tests\Models\Taxi\Ride; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-3068 */ +#[Group('DDC-3068')] class DDC3068Test extends OrmFunctionalTestCase { - /** @var Driver */ - private $foo; + private Driver $foo; - /** @var Car */ - private $merc; + private Car $merc; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC309Test.php b/tests/Tests/ORM/Functional/Ticket/DDC309Test.php index c3e4878fb32..bc0dde9db39 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC309Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC309Test.php @@ -20,45 +20,6 @@ protected function setUp(): void $this->createSchemaForModels(DDC309Country::class, DDC309User::class); } - public function testTwoIterateHydrations(): void - { - $c1 = new DDC309Country(); - $c2 = new DDC309Country(); - $u1 = new DDC309User(); - $u2 = new DDC309User(); - - $this->_em->persist($c1); - $this->_em->persist($c2); - $this->_em->persist($u1); - $this->_em->persist($u2); - $this->_em->flush(); - $this->_em->clear(); - - $q = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC309Country c')->iterate(); - $c = $q->next(); - - self::assertEquals(1, $c[0]->id); - - $r = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\ORM\Functional\Ticket\DDC309User u')->iterate(); - $u = $r->next(); // This line breaks - - self::assertEquals(1, $u[0]->id); - - $c = $q->next(); - $u = $r->next(); - - self::assertEquals(2, $c[0]->id); - self::assertEquals(2, $u[0]->id); - - do { - $q->next(); - } while ($q->valid()); - - do { - $r->next(); - } while ($r->valid()); - } - public function testTwoToIterableHydrations(): void { $c1 = new DDC309Country(); @@ -103,26 +64,22 @@ public function testTwoToIterableHydrations(): void } } -/** @Entity */ +#[Entity] class DDC309Country { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC309User { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php index bbb606519e6..5ead8d58698 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php @@ -7,39 +7,49 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Embeddable; +use Doctrine\Persistence\Mapping\StaticReflectionService; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use function class_exists; use function serialize; use function unserialize; -/** @group DDC-3103 */ +#[CoversClass(ClassMetadata::class)] +#[Group('DDC-3103')] class DDC3103Test extends OrmFunctionalTestCase { - /** @covers \Doctrine\ORM\Mapping\ClassMetadata::__sleep */ + protected function setUp(): void + { + if (! class_exists(StaticReflectionService::class)) { + self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version'); + } + + parent::setUp(); + } + public function testIssue(): void { $classMetadata = new ClassMetadata(DDC3103ArticleId::class); - $this->createAnnotationDriver()->loadMetadataForClass(DDC3103ArticleId::class, $classMetadata); + $this->createAttributeDriver()->loadMetadataForClass(DDC3103ArticleId::class, $classMetadata); self::assertTrue( $classMetadata->isEmbeddedClass, - 'The isEmbeddedClass property should be true from the mapping data.' + 'The isEmbeddedClass property should be true from the mapping data.', ); self::assertTrue( unserialize(serialize($classMetadata))->isEmbeddedClass, - 'The isEmbeddedClass property should still be true after serialization and unserialization.' + 'The isEmbeddedClass property should still be true after serialization and unserialization.', ); } } -/** @Embeddable */ +#[Embeddable] class DDC3103ArticleId { - /** - * @var string - * @Column(name="name", type="string", length=255) - */ - protected $nameValue; + #[Column(name: 'name', type: 'string', length: 255)] + protected string $nameValue; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3123Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3123Test.php index 13538b3cf43..a11fae45108 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3123Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3123Test.php @@ -9,9 +9,10 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Attributes\Group; use ReflectionProperty; -/** @group DDC-3123 */ +#[Group('DDC-3123')] class DDC3123Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -44,11 +45,10 @@ public function __construct(UnitOfWork $uow) public function postFlush(): void { $property = new ReflectionProperty(UnitOfWork::class, 'extraUpdates'); - $property->setAccessible(true); Assert::assertEmpty( $property->getValue($this->uow), - 'ExtraUpdates are reset before postFlush' + 'ExtraUpdates are reset before postFlush', ); } }); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3160Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3160Test.php index c603616958f..d010e792662 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3160Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3160Test.php @@ -8,8 +8,7 @@ use Doctrine\ORM\Events; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * FlushEventTest @@ -23,7 +22,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-3160 */ + #[Group('DDC-3160')] public function testNoUpdateOnInsert(): void { $listener = new DDC3160OnFlushListener(); @@ -62,7 +61,7 @@ public function onFlush(OnFlushEventArgs $args): void $this->inserts++; if ($entity instanceof CmsUser) { $entity->username = 'romanc'; - $cm = $em->getClassMetadata(get_class($entity)); + $cm = $em->getClassMetadata($entity::class); $uow->recomputeSingleEntityChangeSet($cm, $entity); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3170Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3170Test.php index b24691fd569..d3248f25488 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3170Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3170Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-2306 */ +#[Group('DDC-2306')] class DDC3170Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -25,7 +26,7 @@ protected function setUp(): void DDC3170AbstractEntityJoined::class, DDC3170ProductJoined::class, DDC3170AbstractEntitySingleTable::class, - DDC3170ProductSingleTable::class + DDC3170ProductSingleTable::class, ); } @@ -68,46 +69,38 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"product" = "DDC3170ProductJoined"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['product' => 'DDC3170ProductJoined'])] abstract class DDC3170AbstractEntityJoined { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC3170ProductJoined extends DDC3170AbstractEntityJoined { } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"product" = "DDC3170ProductSingleTable"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['product' => 'DDC3170ProductSingleTable'])] abstract class DDC3170AbstractEntitySingleTable { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC3170ProductSingleTable extends DDC3170AbstractEntitySingleTable { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3192Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3192Test.php index de6ff3a9f7e..6b1f4a74855 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3192Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3192Test.php @@ -16,13 +16,12 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_search; -/** - * @group DDC-2494 - * @group non-cacheable - */ +#[Group('DDC-2494')] +#[Group('non-cacheable')] class DDC3192Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -32,7 +31,7 @@ protected function setUp(): void if (Type::hasType('ddc3192_currency_code')) { self::fail( 'Type ddc3192_currency_code exists for testing DDC-3192 only, ' . - 'but it has already been registered for some reason' + 'but it has already been registered for some reason', ); } @@ -40,7 +39,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC3192Currency::class, - DDC3192Transaction::class + DDC3192Transaction::class, ); } @@ -77,82 +76,59 @@ public function testIssue(): void } } -/** - * @Table(name="ddc3192_currency") - * @Entity - */ +#[Table(name: 'ddc3192_currency')] +#[Entity] class DDC3192Currency { - /** - * @var string - * @Id - * @Column(type="ddc3192_currency_code") - */ - public $code; - - /** - * @var Collection - * @OneToMany(targetEntity="DDC3192Transaction", mappedBy="currency") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'DDC3192Transaction', mappedBy: 'currency')] public $transactions; - public function __construct(string $code) - { - $this->code = $code; + public function __construct( + #[Id] + #[Column(type: 'ddc3192_currency_code')] + public string $code, + ) { } } -/** - * @Table(name="ddc3192_transaction") - * @Entity - */ +#[Table(name: 'ddc3192_transaction')] +#[Entity] class DDC3192Transaction { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var int - * @Column(type="integer") - */ - public $amount; - - /** - * @var DDC3192Currency - * @ManyToOne(targetEntity="DDC3192Currency", inversedBy="transactions") - * @JoinColumn(name="currency_id", referencedColumnName="code", nullable=false) - */ - public $currency; - - public function __construct(int $amount, DDC3192Currency $currency) - { - $this->amount = $amount; - $this->currency = $currency; + public function __construct( + #[Column(type: 'integer')] + public int $amount, + #[ManyToOne(targetEntity: 'DDC3192Currency', inversedBy: 'transactions')] + #[JoinColumn(name: 'currency_id', referencedColumnName: 'code', nullable: false)] + public DDC3192Currency $currency, + ) { } } class DDC3192CurrencyCode extends Type { /** @phpstan-var array */ - private static $map = ['BYR' => 974]; + private static array $map = ['BYR' => 974]; /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + return $platform->getSmallIntTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): int { return self::$map[$value]; } @@ -160,15 +136,12 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): mixed { return array_search((int) $value, self::$map, true); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return 'ddc3192_currency_code'; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3223Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3223Test.php index c4081dfc2ee..509a99d254a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3223Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3223Test.php @@ -30,7 +30,7 @@ protected function setUp(): void Participant::class, Status::class, ProfileStatus::class, - ] + ], ); } @@ -54,60 +54,41 @@ public function testIssueGetId(): void } } -/** - * @Entity - * @Table(name="ddc3223_journalist") - */ +#[Table(name: 'ddc3223_journalist')] +#[Entity] class Journalist extends Participant { } -/** - * @Entity - * @Table(name="ddc3223_participant") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "journalist" = "Journalist", - * "participant" = "Participant", - * }) - */ +#[Table(name: 'ddc3223_participant')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['journalist' => 'Journalist', 'participant' => 'Participant'])] class Participant { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var ProfileStatus - * @ManyToOne(targetEntity="ProfileStatus") - */ + /** @var ProfileStatus */ + #[ManyToOne(targetEntity: 'ProfileStatus')] public $profileStatus; } -/** - * @Entity - * @Table(name="ddc3223_status") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "profile" = "ProfileStatus", - * "status" = "Status", - * }) - */ +#[Table(name: 'ddc3223_status')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['profile' => 'ProfileStatus', 'status' => 'Status'])] class Status { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; public function getId(): int { @@ -115,7 +96,7 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class ProfileStatus extends Status { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3300Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3300Test.php index d71464a754b..8cbc54cf081 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3300Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3300Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Tools\ResolveTargetEntityListener; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-3300 */ +#[Group('DDC-3300')] class DDC3300Test extends OrmFunctionalTestCase { public function testResolveTargetEntitiesChangesDiscriminatorMapValues(): void @@ -24,13 +25,13 @@ public function testResolveTargetEntitiesChangesDiscriminatorMapValues(): void $resolveTargetEntity->addResolveTargetEntity( DDC3300Boss::class, DDC3300HumanBoss::class, - [] + [], ); $resolveTargetEntity->addResolveTargetEntity( DDC3300Employee::class, DDC3300HumanEmployee::class, - [] + [], ); $this->_em->getEventManager()->addEventSubscriber($resolveTargetEntity); @@ -51,23 +52,16 @@ public function testResolveTargetEntitiesChangesDiscriminatorMapValues(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "boss" = "Doctrine\Tests\ORM\Functional\Ticket\DDC3300Boss", - * "employee" = "Doctrine\Tests\ORM\Functional\Ticket\DDC3300Employee" - * }) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['boss' => 'Doctrine\Tests\ORM\Functional\Ticket\DDC3300Boss', 'employee' => 'Doctrine\Tests\ORM\Functional\Ticket\DDC3300Employee'])] abstract class DDC3300Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } @@ -75,18 +69,13 @@ interface DDC3300Boss { } -/** @Entity */ +#[Entity] class DDC3300HumanBoss extends DDC3300Person implements DDC3300Boss { - /** - * @var string - * @Column(type="string", length=255) - */ - public $bossCol; - - public function __construct($bossCol) - { - $this->bossCol = $bossCol; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $bossCol, + ) { } } @@ -94,17 +83,12 @@ interface DDC3300Employee { } -/** @Entity */ +#[Entity] class DDC3300HumanEmployee extends DDC3300Person implements DDC3300Employee { - /** - * @var string - * @Column(type="string", length=255) - */ - public $employeeCol; - - public function __construct($employeeCol) - { - $this->employeeCol = $employeeCol; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $employeeCol, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3303Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3303Test.php index 2534ccb0b4d..c67872160bf 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3303Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3303Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\MappedSuperclass; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC3303Test extends OrmFunctionalTestCase { @@ -24,18 +25,17 @@ protected function setUp(): void } /** - * @group GH-4097 - * @group GH-4277 - * @group GH-5867 - * * When using an embedded field in an inheritance, private properties should also be inherited. */ + #[Group('GH-4097')] + #[Group('GH-4277')] + #[Group('GH-5867')] public function testEmbeddedObjectsAreAlsoInherited(): void { $employee = new DDC3303Employee( 'John Doe', new DDC3303Address('Somewhere', 123, 'Over the rainbow'), - 'Doctrine Inc' + 'Doctrine Inc', ); $this->_em->persist($employee); @@ -46,75 +46,44 @@ public function testEmbeddedObjectsAreAlsoInherited(): void } } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class DDC3303Person { - /** - * @var string - * @Id - * @GeneratedValue(strategy="NONE") - * @Column(type="string", length=255) - */ - private $name; - - /** - * @var DDC3303Address - * @Embedded(class="DDC3303Address") - */ - private $address; - - public function __construct(string $name, DDC3303Address $address) - { - $this->name = $name; - $this->address = $address; + public function __construct( + #[Id] + #[GeneratedValue(strategy: 'NONE')] + #[Column(type: 'string', length: 255)] + private string $name, + #[Embedded(class: 'DDC3303Address')] + private DDC3303Address $address, + ) { } } -/** @Embeddable */ +#[Embeddable] class DDC3303Address { - /** - * @var string - * @Column(type="string", length=255) - */ - private $street; - - /** - * @var int - * @Column(type="integer") - */ - private $number; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $city; - - public function __construct(string $street, int $number, string $city) - { - $this->street = $street; - $this->number = $number; - $this->city = $city; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $street, + #[Column(type: 'integer')] + private int $number, + #[Column(type: 'string', length: 255)] + private string $city, + ) { } } -/** - * @Entity - * @Table(name="ddc3303_employee") - */ +#[Table(name: 'ddc3303_employee')] +#[Entity] class DDC3303Employee extends DDC3303Person { - /** - * @var string - * @Column(type="string", length=255) - */ - private $company; - - public function __construct(string $name, DDC3303Address $address, $company) - { + public function __construct( + string $name, + DDC3303Address $address, + #[Column(type: 'string', length: 255)] + private string $company, + ) { parent::__construct($name, $address); - - $this->company = $company; } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC331Test.php b/tests/Tests/ORM/Functional/Ticket/DDC331Test.php index d71ec398871..48207b61208 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC331Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC331Test.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function strtolower; @@ -20,13 +21,13 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-331 */ + #[Group('DDC-331')] public function testSelectFieldOnRootEntity(): void { $q = $this->_em->createQuery('SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e'); self::assertEquals( strtolower('SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id'), - strtolower($q->getSQL()) + strtolower($q->getSQL()), ); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3330Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3330Test.php index 93301bb6b11..20806b04136 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3330Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3330Test.php @@ -30,7 +30,7 @@ protected function setUp(): void [ DDC3330Building::class, DDC3330Hall::class, - ] + ], ); } @@ -46,7 +46,7 @@ public function testIssueCollectionOrderWithPaginator(): void 'SELECT b, h' . ' FROM Doctrine\Tests\ORM\Functional\Ticket\DDC3330Building b' . ' LEFT JOIN b.halls h' . - ' ORDER BY b.id ASC, h.name DESC' + ' ORDER BY b.id ASC, h.name DESC', ) ->setMaxResults(3); @@ -73,24 +73,18 @@ private function createBuildingAndHalls(): void } } -/** - * @Entity - * @Table(name="ddc3330_building") - */ +#[Table(name: 'ddc3330_building')] +#[Entity] class DDC3330Building { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3330Hall", mappedBy="building", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3330Hall', mappedBy: 'building', cascade: ['persist'])] public $halls; public function addHall(DDC3330Hall $hall): void @@ -100,29 +94,21 @@ public function addHall(DDC3330Hall $hall): void } } -/** - * @Entity - * @Table(name="ddc3330_hall") - */ +#[Table(name: 'ddc3330_hall')] +#[Entity] class DDC3330Hall { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC3330Building - * @ManyToOne(targetEntity="DDC3330Building", inversedBy="halls") - */ + /** @var DDC3330Building */ + #[ManyToOne(targetEntity: 'DDC3330Building', inversedBy: 'halls')] public $building; - /** - * @var string - * @Column(type="string", length=100) - */ + /** @var string */ + #[Column(type: 'string', length: 100)] public $name; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3346Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3346Test.php index da008705b9c..f9d4143f2d6 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3346Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3346Test.php @@ -7,10 +7,11 @@ use Doctrine\Tests\Models\DDC3346\DDC3346Article; use Doctrine\Tests\Models\DDC3346\DDC3346Author; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -/** @group DDC-3346 */ +#[Group('DDC-3346')] class DDC3346Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -25,7 +26,7 @@ protected function setUp(): void public function testFindOneWithEagerFetchWillNotHydrateLimitedCollection(): void { $author = $this->_em->getRepository(DDC3346Author::class)->findOneBy( - ['username' => 'bwoogy'] + ['username' => 'bwoogy'], ); assert($author instanceof DDC3346Author); @@ -38,7 +39,7 @@ public function testFindLimitedWithEagerFetchWillNotHydrateLimitedCollection(): $authors = $this->_em->getRepository(DDC3346Author::class)->findBy( ['username' => 'bwoogy'], null, - 1 + 1, ); self::assertCount(1, $authors); @@ -52,7 +53,7 @@ public function testFindWithEagerFetchAndOffsetWillNotHydrateLimitedCollection() ['username' => 'bwoogy'], null, null, - 0 // using an explicitly defined offset + 0, // using an explicitly defined offset ); self::assertCount(1, $authors); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC345Test.php b/tests/Tests/ORM/Functional/Ticket/DDC345Test.php index e460daa1102..914b0f722c3 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC345Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC345Test.php @@ -30,7 +30,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC345User::class, DDC345Group::class, - DDC345Membership::class + DDC345Membership::class, ); } @@ -65,31 +65,25 @@ public function testTwoIterateHydrations(): void self::assertEquals(1, $membership->prePersistCallCount); self::assertEquals(0, $membership->preUpdateCallCount); - self::assertInstanceOf('DateTime', $membership->updated); + self::assertInstanceOf(DateTime::class, $membership->updated); } } -/** @Entity */ +#[Entity] class DDC345User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC345Membership", mappedBy="user", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC345Membership', mappedBy: 'user', cascade: ['persist'])] public $memberships; public function __construct() @@ -98,27 +92,21 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC345Group { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC345Membership", mappedBy="group", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC345Membership', mappedBy: 'group', cascade: ['persist'])] public $memberships; public function __construct() @@ -127,47 +115,34 @@ public function __construct() } } -/** - * @Entity - * @HasLifecycleCallbacks - * @Table(name="ddc345_memberships", uniqueConstraints={ - * @UniqueConstraint(name="ddc345_memship_fks", columns={"user_id","group_id"}) - * }) - */ +#[Table(name: 'ddc345_memberships')] +#[UniqueConstraint(name: 'ddc345_memship_fks', columns: ['user_id', 'group_id'])] +#[Entity] +#[HasLifecycleCallbacks] class DDC345Membership { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC345User - * @OneToOne(targetEntity="DDC345User", inversedBy="memberships") - * @JoinColumn(name="user_id", referencedColumnName="id", nullable=false) - */ + /** @var DDC345User */ + #[OneToOne(targetEntity: 'DDC345User', inversedBy: 'memberships')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false)] public $user; - /** - * @var DDC345Group - * @OneToOne(targetEntity="DDC345Group", inversedBy="memberships") - * @JoinColumn(name="group_id", referencedColumnName="id", nullable=false) - */ + /** @var DDC345Group */ + #[OneToOne(targetEntity: 'DDC345Group', inversedBy: 'memberships')] + #[JoinColumn(name: 'group_id', referencedColumnName: 'id', nullable: false)] public $group; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $state; - /** - * @var DateTime - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Column(type: 'datetime')] public $updated; /** @var int */ @@ -176,7 +151,7 @@ class DDC345Membership /** @var int */ public $preUpdateCallCount = 0; - /** @PrePersist */ + #[PrePersist] public function doStuffOnPrePersist(): void { //echo "***** PrePersist\n"; @@ -184,7 +159,7 @@ public function doStuffOnPrePersist(): void $this->updated = new DateTime(); } - /** @PreUpdate */ + #[PreUpdate] public function doStuffOnPreUpdate(): void { //echo "***** PreUpdate\n"; diff --git a/tests/Tests/ORM/Functional/Ticket/DDC353Test.php b/tests/Tests/ORM/Functional/Ticket/DDC353Test.php index ae183860ac1..a21583a9cd0 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC353Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC353Test.php @@ -9,7 +9,6 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\OrmFunctionalTestCase; @@ -73,25 +72,17 @@ public function testFailingCase(): void } } -/** @Entity */ +#[Entity] class DDC353Picture { - /** - * @var int - * @Column(name="picture_id", type="integer") - * @Id - * @GeneratedValue - */ - private $pictureId; + #[Column(name: 'picture_id', type: 'integer')] + #[Id] + #[GeneratedValue] + private int $pictureId; - /** - * @var DDC353File - * @ManyToOne(targetEntity="DDC353File", cascade={"persist", "remove"}) - * @JoinColumns({ - * @JoinColumn(name="file_id", referencedColumnName="file_id") - * }) - */ - private $file; + #[JoinColumn(name: 'file_id', referencedColumnName: 'file_id')] + #[ManyToOne(targetEntity: 'DDC353File', cascade: ['persist', 'remove'])] + private DDC353File|null $file = null; public function getPictureId(): int { @@ -109,15 +100,13 @@ public function getFile(): DDC353File } } -/** @Entity */ +#[Entity] class DDC353File { - /** - * @var int - * @Column(name="file_id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(name: 'file_id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $fileId; /** diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3582Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3582Test.php index d4a0fa95922..f4dd2a2eff0 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3582Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3582Test.php @@ -31,36 +31,27 @@ public function testNestedEmbeddablesAreHydratedWithProperClass(): void } } -/** @Entity */ +#[Entity] class DDC3582Entity { - /** - * @var string - * @Column - * @Id - */ - private $id; - - /** - * @var DDC3582Embeddable1 - * @Embedded(class="DDC3582Embeddable1") - */ + /** @var DDC3582Embeddable1 */ + #[Embedded(class: 'DDC3582Embeddable1')] public $embeddable1; - public function __construct(string $id) - { - $this->id = $id; + public function __construct( + #[Column] + #[Id] + private string $id, + ) { $this->embeddable1 = new DDC3582Embeddable1(); } } -/** @Embeddable */ +#[Embeddable] class DDC3582Embeddable1 { - /** - * @var DDC3582Embeddable2 - * @Embedded(class="DDC3582Embeddable2") - */ + /** @var DDC3582Embeddable2 */ + #[Embedded(class: 'DDC3582Embeddable2')] public $embeddable2; public function __construct() @@ -69,13 +60,11 @@ public function __construct() } } -/** @Embeddable */ +#[Embeddable] class DDC3582Embeddable2 { - /** - * @var DDC3582Embeddable3 - * @Embedded(class="DDC3582Embeddable3") - */ + /** @var DDC3582Embeddable3 */ + #[Embedded(class: 'DDC3582Embeddable3')] public $embeddable3; public function __construct() @@ -84,12 +73,10 @@ public function __construct() } } -/** @Embeddable */ +#[Embeddable] class DDC3582Embeddable3 { - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $embeddedValue = 'foo'; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3597Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3597Test.php index ca1c8317b02..0503634f6e9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3597Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3597Test.php @@ -8,8 +8,9 @@ use Doctrine\Tests\Models\DDC3597\DDC3597Media; use Doctrine\Tests\Models\DDC3597\DDC3597Root; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-117 */ +#[Group('DDC-117')] class DDC3597Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -19,11 +20,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC3597Root::class, DDC3597Media::class, - DDC3597Image::class + DDC3597Image::class, ); } - /** @group DDC-3597 */ + #[Group('DDC-3597')] public function testSaveImageEntity(): void { $imageEntity = new DDC3597Image('foobar'); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3634Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3634Test.php index 076d83c9c89..35941fce446 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3634Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3634Test.php @@ -4,31 +4,39 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use BadMethodCallException; -use Closure; -use Doctrine\DBAL\Cache\QueryCacheProfile; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Result; -use Doctrine\DBAL\Statement; -use Doctrine\ORM\EntityManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\DBAL\Driver\Middleware; +use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware; +use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\DiscriminatorMap; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Tests\OrmFunctionalTestCase; - -use function debug_backtrace; -use function sprintf; +use Doctrine\Tests\TestUtil; +use PHPUnit\Framework\Attributes\Group; use const PHP_INT_MAX; -/** @group DDC-3634 */ +#[Group('DDC-3634')] class DDC3634Test extends OrmFunctionalTestCase { + private LastInsertIdMocker $idMocker; + protected function setUp(): void { + $this->idMocker = new LastInsertIdMocker(); + $config = new Configuration(); + $config->setMiddlewares([new LastInsertIdMockMiddleware($this->idMocker)]); + + $this->_em = $this->getEntityManager(TestUtil::getConnection($config)); + $this->_schemaTool = new SchemaTool($this->_em); + parent::setUp(); $metadata = $this->_em->getClassMetadata(DDC3634Entity::class); @@ -40,23 +48,19 @@ protected function setUp(): void $this->createSchemaForModels( DDC3634Entity::class, DDC3634JTIBaseEntity::class, - DDC3634JTIChildEntity::class + DDC3634JTIChildEntity::class, ); } public function testSavesVeryLargeIntegerAutoGeneratedValue(): void { - $veryLargeId = PHP_INT_MAX . PHP_INT_MAX; - - $entityManager = new EntityManager( - new DDC3634LastInsertIdMockingConnection($veryLargeId, $this->_em->getConnection()), - $this->_em->getConfiguration() - ); + $veryLargeId = PHP_INT_MAX . PHP_INT_MAX; + $this->idMocker->mockedId = $veryLargeId; $entity = new DDC3634Entity(); - $entityManager->persist($entity); - $entityManager->flush(); + $this->_em->persist($entity); + $this->_em->flush(); self::assertSame($veryLargeId, $entity->id); } @@ -82,425 +86,78 @@ public function testSavesIntegerAutoGeneratedValueWithJoinedInheritance(): void } } -/** @Entity */ +#[Entity] class DDC3634Entity { - /** - * @var int - * @Id - * @Column(type="bigint") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'bigint')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorMap({ - * DDC3634JTIBaseEntity::class = DDC3634JTIBaseEntity::class, - * DDC3634JTIChildEntity::class = DDC3634JTIChildEntity::class, - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap([DDC3634JTIBaseEntity::class => DDC3634JTIBaseEntity::class, DDC3634JTIChildEntity::class => DDC3634JTIChildEntity::class])] class DDC3634JTIBaseEntity { - /** - * @var int - * @Id - * @Column(type="bigint") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'bigint')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** @Entity */ +#[Entity] class DDC3634JTIChildEntity extends DDC3634JTIBaseEntity { } -class DDC3634LastInsertIdMockingConnection extends Connection +class LastInsertIdMocker { - /** @var Connection */ - private $realConnection; - - /** @var int|string */ - private $identifier; - - /** @param int|string $identifier */ - public function __construct($identifier, Connection $realConnection) - { - $this->realConnection = $realConnection; - $this->identifier = $identifier; - } - - /** @return mixed */ - private function forwardCall() - { - $trace = debug_backtrace(0, 2)[1]; - - return $this->realConnection->{$trace['function']}(...$trace['args']); - } - - /** {@inheritDoc} */ - public function getParams() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getDatabase() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getHost() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getPort() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getUsername() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getPassword() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getDriver() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getConfiguration() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getEventManager() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getDatabasePlatform() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getExpressionBuilder() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function connect() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function isAutoCommit() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function setAutoCommit($autoCommit) - { - $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function setFetchMode($fetchMode) - { - $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function fetchAssoc($statement, array $params = [], array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function fetchArray($statement, array $params = [], array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function fetchColumn($statement, array $params = [], $column = 0, array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function isConnected() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function isTransactionActive() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function delete($tableExpression, array $identifier, array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function close() - { - $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function setTransactionIsolation($level) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getTransactionIsolation() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function update($tableExpression, array $data, array $identifier, array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function insert($tableExpression, array $data, array $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function quoteIdentifier($str) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function quote($input, $type = null) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function fetchAll($sql, array $params = [], $types = []) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function prepare($statement): Statement - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function executeQuery($query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): Result - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp): Result - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function project($query, array $params, Closure $function) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function query($sql = null): Result - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function executeUpdate($query, array $params = [], array $types = []): int - { - throw new BadMethodCallException(sprintf( - 'Call to deprecated method %s().', - __METHOD__ - )); - } - - /** {@inheritDoc} */ - public function executeStatement($query, array $params = [], array $types = []): int - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function exec($statement): int - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getTransactionNestingLevel() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function errorCode() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function errorInfo() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function lastInsertId($seqName = null) - { - return $this->identifier; - } - - /** {@inheritDoc} */ - public function transactional(Closure $func) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getNestTransactionsWithSavepoints() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - protected function _getNestedTransactionSavePointName() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function beginTransaction() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function commit() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function rollBack() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function createSavepoint($savepoint) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function releaseSavepoint($savepoint) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function rollbackSavepoint($savepoint) - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getWrappedConnection() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function getSchemaManager() - { - return $this->forwardCall(); - } - - /** {@inheritDoc} */ - public function setRollbackOnly() - { - $this->forwardCall(); - } + public string|null $mockedId = null; +} - /** {@inheritDoc} */ - public function isRollbackOnly() +final class LastInsertIdMockConnection extends AbstractConnectionMiddleware +{ + public function __construct(DriverConnection $wrappedConnection, private LastInsertIdMocker $idMocker) { - return $this->forwardCall(); + parent::__construct($wrappedConnection); } - /** {@inheritDoc} */ - public function convertToDatabaseValue($value, $type) + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null): int|string { - return $this->forwardCall(); + return $this->idMocker->mockedId ?? parent::lastInsertId($name); } +} - /** {@inheritDoc} */ - public function convertToPHPValue($value, $type) +final class LastInsertIdMockDriver extends AbstractDriverMiddleware +{ + public function __construct(Driver $wrappedDriver, private LastInsertIdMocker $idMocker) { - return $this->forwardCall(); + parent::__construct($wrappedDriver); } - /** {@inheritDoc} */ - public function resolveParams(array $params, array $types) + public function connect(array $params): LastInsertIdMockConnection { - return $this->forwardCall(); + return new LastInsertIdMockConnection( + parent::connect($params), + $this->idMocker, + ); } +} - /** {@inheritDoc} */ - public function createQueryBuilder() +final class LastInsertIdMockMiddleware implements Middleware +{ + public function __construct(private LastInsertIdMocker $idMocker) { - return $this->forwardCall(); } - /** {@inheritDoc} */ - public function ping() + public function wrap(Driver $driver): LastInsertIdMockDriver { - return $this->forwardCall(); + return new LastInsertIdMockDriver($driver, $this->idMocker); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3644Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3644Test.php index 8ada90a500e..249b21e46bd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3644Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3644Test.php @@ -17,6 +17,7 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Functional tests for orphan removal with one to many association. @@ -33,11 +34,11 @@ protected function setUp(): void DDC3644Address::class, DDC3644Animal::class, DDC3644Pet::class, - ] + ], ); } - /** @group DDC-3644 */ + #[Group('DDC-3644')] public function testIssueWithRegularEntity(): void { // Define initial dataset @@ -87,7 +88,7 @@ public function testIssueWithRegularEntity(): void self::assertCount(1, $addresses); } - /** @group DDC-3644 */ + #[Group('DDC-3644')] public function testIssueWithJoinedEntity(): void { // Define initial dataset @@ -136,33 +137,25 @@ public function testIssueWithJoinedEntity(): void } } -/** @Entity */ +#[Entity] class DDC3644User { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer", name="hash_id") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer', name: 'hash_id')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3644Address", mappedBy="user", orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3644Address', mappedBy: 'user', orphanRemoval: true)] public $addresses = []; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3644Pet", mappedBy="owner", orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3644Pet', mappedBy: 'owner', orphanRemoval: true)] public $pets = []; public function setAddresses(Collection $addresses): void @@ -184,71 +177,51 @@ public function setPets(Collection $pets): void } } -/** @Entity */ +#[Entity] class DDC3644Address { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC3644User - * @ManyToOne(targetEntity="DDC3644User", inversedBy="addresses") - * @JoinColumn(referencedColumnName="hash_id") - */ + /** @var DDC3644User */ + #[ManyToOne(targetEntity: 'DDC3644User', inversedBy: 'addresses')] + #[JoinColumn(referencedColumnName: 'hash_id')] public $user; - /** - * @var string - * @Column(type="string", length=255) - */ - public $address; - - public function __construct($address) - { - $this->address = $address; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $address, + ) { } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discriminator", type="string") - * @DiscriminatorMap({"pet" = "DDC3644Pet"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discriminator', type: 'string')] +#[DiscriminatorMap(['pet' => 'DDC3644Pet'])] abstract class DDC3644Animal { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class DDC3644Pet extends DDC3644Animal { - /** - * @var DDC3644User - * @ManyToOne(targetEntity="DDC3644User", inversedBy="pets") - * @JoinColumn(referencedColumnName="hash_id") - */ + /** @var DDC3644User */ + #[ManyToOne(targetEntity: 'DDC3644User', inversedBy: 'pets')] + #[JoinColumn(referencedColumnName: 'hash_id')] public $owner; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3699Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3699Test.php deleted file mode 100644 index da07e63ae61..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC3699Test.php +++ /dev/null @@ -1,102 +0,0 @@ -useModelSet('ddc3699'); - - parent::setUp(); - } - - /** @group DDC-3699 */ - public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToOneUninitializedAssociations(): void - { - $id = 1; - - $child = new DDC3699Child(); - - $child->id = $id; - $child->childField = 'childValue'; - $child->parentField = 'parentValue'; - - $relation = new DDC3699RelationOne(); - - $relation->id = $id; - $relation->child = $child; - $child->oneRelation = $relation; - - $this->_em->persist($relation); - $this->_em->persist($child); - $this->_em->flush(); - $this->_em->clear(); - - $unManagedChild = $this->_em->find(DDC3699Child::class, $id); - assert($unManagedChild instanceof DDC3699Child); - - $this->_em->detach($unManagedChild); - - // make it managed again - $this->_em->find(DDC3699Child::class, $id); - - $unManagedChild->childField = 'modifiedChildValue'; - $unManagedChild->parentField = 'modifiedParentValue'; - - $mergedChild = $this->_em->merge($unManagedChild); - assert($mergedChild instanceof DDC3699Child); - - self::assertSame($mergedChild->childField, 'modifiedChildValue'); - self::assertSame($mergedChild->parentField, 'modifiedParentValue'); - } - - /** @group DDC-3699 */ - public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToManyUninitializedAssociations(): void - { - $id = 2; - - $child = new DDC3699Child(); - - $child->id = $id; - $child->childField = 'childValue'; - $child->parentField = 'parentValue'; - - $relation = new DDC3699RelationMany(); - - $relation->id = $id; - $relation->child = $child; - $child->relations[] = $relation; - - $this->_em->persist($relation); - $this->_em->persist($child); - $this->_em->flush(); - $this->_em->clear(); - - $unmanagedChild = $this->_em->find(DDC3699Child::class, $id); - assert($unmanagedChild instanceof DDC3699Child); - $this->_em->detach($unmanagedChild); - - // make it managed again - $this->_em->find(DDC3699Child::class, $id); - - $unmanagedChild->childField = 'modifiedChildValue'; - $unmanagedChild->parentField = 'modifiedParentValue'; - - $mergedChild = $this->_em->merge($unmanagedChild); - assert($mergedChild instanceof DDC3699Child); - - self::assertSame($mergedChild->childField, 'modifiedChildValue'); - self::assertSame($mergedChild->parentField, 'modifiedParentValue'); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3719Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3719Test.php index 899c74b4d83..bdfd2078167 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3719Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3719Test.php @@ -8,6 +8,7 @@ use Doctrine\Tests\Models\Company\CompanyFlexContract; use Doctrine\Tests\Models\Company\CompanyManager; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC3719Test extends OrmFunctionalTestCase { @@ -18,7 +19,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-3719 */ + #[Group('DDC-3719')] public function testCriteriaOnNotOwningSide(): void { $manager = new CompanyManager(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC371Test.php b/tests/Tests/ORM/Functional/Ticket/DDC371Test.php index 606a591873f..0148501f578 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC371Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC371Test.php @@ -15,8 +15,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-371 */ +#[Group('DDC-371')] class DDC371Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -56,48 +57,34 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC371Child { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - /** - * @var string - * @Column(type="string", length=255) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; - /** - * @var DDC371Parent - * @ManyToOne(targetEntity="DDC371Parent", inversedBy="children") - * @JoinColumn(name="parentId") - */ + /** @var DDC371Parent */ + #[ManyToOne(targetEntity: 'DDC371Parent', inversedBy: 'children')] + #[JoinColumn(name: 'parentId')] public $parent; } -/** @Entity */ +#[Entity] class DDC371Parent { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - /** - * @var string - * @Column(type="string", length=255) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC371Child", mappedBy="parent") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC371Child', mappedBy: 'parent')] public $children; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3785Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3785Test.php index 0ede92c30f1..7fff73d6f81 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3785Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3785Test.php @@ -13,11 +13,14 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; class DDC3785Test extends OrmFunctionalTestCase { @@ -30,11 +33,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC3785Asset::class, DDC3785AssetId::class, - DDC3785Attribute::class + DDC3785Attribute::class, ); } - /** @group DDC-3785 */ + #[Group('DDC-3785')] public function testOwningValueObjectIdIsCorrectlyTransformedWhenRemovingOrphanedChildEntities(): void { $id = new DDC3785AssetId('919609ba-57d9-4a13-be1d-d202521e858a'); @@ -60,34 +63,25 @@ public function testOwningValueObjectIdIsCorrectlyTransformedWhenRemovingOrphane } } -/** - * @Entity - * @Table(name="asset") - */ +#[Table(name: 'asset')] +#[Entity] class DDC3785Asset { - /** - * @var DDC3785AssetId - * @Id - * @GeneratedValue(strategy="NONE") - * @Column(type="ddc3785_asset_id") - */ - private $id; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC3785Attribute", cascade={"persist"}, orphanRemoval=true) - * @JoinTable(name="asset_attributes", - * joinColumns={@JoinColumn(name="asset_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="attribute_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'asset_attributes')] + #[JoinColumn(name: 'asset_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'attribute_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC3785Attribute', cascade: ['persist'], orphanRemoval: true)] private $attributes; /** @phpstan-param list $attributes */ - public function __construct(DDC3785AssetId $id, $attributes = []) - { - $this->id = $id; + public function __construct( + #[Id] + #[GeneratedValue(strategy: 'NONE')] + #[Column(type: 'ddc3785_asset_id')] + private DDC3785AssetId $id, + $attributes = [], + ) { $this->attributes = new ArrayCollection(); foreach ($attributes as $attribute) { @@ -107,51 +101,32 @@ public function getAttributes() } } -/** - * @Entity - * @Table(name="attribute") - */ +#[Table(name: 'attribute')] +#[Entity] class DDC3785Attribute { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $value; - - public function __construct(string $name, string $value) - { - $this->name = $name; - $this->value = $value; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + #[Column(type: 'string', length: 255)] + private string $value, + ) { } } -/** @Embeddable */ -class DDC3785AssetId +#[Embeddable] +class DDC3785AssetId implements Stringable { - /** - * @var string - * @Column(type = "guid") - */ - private $id; - - public function __construct(string $id) - { - $this->id = $id; + public function __construct( + #[Column(type: 'guid')] + private string $id, + ) { } public function __toString(): string @@ -165,15 +140,15 @@ class DDC3785AssetIdType extends Type /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + return $platform->getGuidTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { return (string) $value; } @@ -181,15 +156,12 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): DDC3785AssetId { return new DDC3785AssetId($value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return 'ddc3785_asset_id'; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC381Test.php b/tests/Tests/ORM/Functional/Ticket/DDC381Test.php index 1025f1e0e22..11a53b60981 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC381Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC381Test.php @@ -43,15 +43,13 @@ public function testCallUnserializedProxyMethods(): void } } -/** @Entity */ +#[Entity] class DDC381Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/DDC4024Test.php b/tests/Tests/ORM/Functional/Ticket/DDC4024Test.php index 4cc73f0085c..418254e1d07 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC4024Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC4024Test.php @@ -5,10 +5,11 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\ORM\NonUniqueResultException; -use Doctrine\Tests\DoctrineTestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; -/** @group DDC4024 */ -final class DDC4024Test extends DoctrineTestCase +#[Group('DDC4024')] +final class DDC4024Test extends TestCase { public function testConstructorShouldUseProvidedMessage(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC422Test.php b/tests/Tests/ORM/Functional/Ticket/DDC422Test.php index 2bf3b1c8345..4910356d428 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC422Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC422Test.php @@ -13,13 +13,13 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\PersistentCollection; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; class DDC422Test extends OrmFunctionalTestCase { @@ -30,11 +30,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC422Guest::class, DDC422Customer::class, - DDC422Contact::class + DDC422Contact::class, ); } - /** @group DDC-422 */ + #[Group('DDC-422')] public function testIssue(): void { $customer = new DDC422Customer(); @@ -42,7 +42,7 @@ public function testIssue(): void $this->_em->flush(); $this->_em->clear(); - $customer = $this->_em->find(get_class($customer), $customer->id); + $customer = $this->_em->find($customer::class, $customer->id); self::assertInstanceOf(PersistentCollection::class, $customer->contacts); self::assertFalse($customer->contacts->isInitialized()); @@ -56,34 +56,27 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"guest" = "DDC422Guest", "customer" = "DDC422Customer"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['guest' => 'DDC422Guest', 'customer' => 'DDC422Customer'])] class DDC422Guest { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC422Customer extends DDC422Guest { - /** - * @var Collection - * @ManyToMany(targetEntity="DDC422Contact", cascade={"persist","remove"}) - * @JoinTable(name="ddc422_customers_contacts", - * joinColumns={@JoinColumn(name="customer_id", referencedColumnName="id", onDelete="cascade" )}, - * inverseJoinColumns={@JoinColumn(name="contact_id", referencedColumnName="id", onDelete="cascade" )} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'ddc422_customers_contacts')] + #[JoinColumn(name: 'customer_id', referencedColumnName: 'id', onDelete: 'cascade')] + #[InverseJoinColumn(name: 'contact_id', referencedColumnName: 'id', onDelete: 'cascade')] + #[ManyToMany(targetEntity: 'DDC422Contact', cascade: ['persist', 'remove'])] public $contacts; public function __construct() @@ -92,14 +85,12 @@ public function __construct() } } -/** @Entity */ +#[Entity] class DDC422Contact { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC425Test.php b/tests/Tests/ORM/Functional/Ticket/DDC425Test.php index 0a1e0abd22b..230543b3a14 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC425Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC425Test.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC425Test extends OrmFunctionalTestCase { @@ -21,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels(DDC425Entity::class); } - /** @group DDC-425 */ + #[Group('DDC-425')] public function testIssue(): void { $num = $this->_em->createQuery('DELETE ' . __NAMESPACE__ . '\DDC425Entity e WHERE e.someDatetimeField > ?1') @@ -31,20 +32,16 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC425Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DateTime - * @Column(type="datetime") - */ + /** @var DateTime */ + #[Column(type: 'datetime')] public $someDatetimeField; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC440Test.php b/tests/Tests/ORM/Functional/Ticket/DDC440Test.php index 3da14f33a9c..c19f341bd52 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC440Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC440Test.php @@ -10,13 +10,13 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\OrderBy; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC440Test extends OrmFunctionalTestCase { @@ -27,7 +27,7 @@ protected function setUp(): void $this->createSchemaForModels(DDC440Phone::class, DDC440Client::class); } - /** @group DDC-440 */ + #[Group('DDC-440')] public function testOriginalEntityDataEmptyWhenProxyLoadedFromTwoAssociations(): void { /* The key of the problem is that the first phone is fetched via two association, mainPhone and phones. @@ -81,33 +81,23 @@ public function testOriginalEntityDataEmptyWhenProxyLoadedFromTwoAssociations(): } } -/** - * @Entity - * @Table(name="phone") - */ +#[Table(name: 'phone')] +#[Entity] class DDC440Phone { - /** - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - * @var int - */ + /** @var int */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var DDC440Client - * @ManyToOne(targetEntity="DDC440Client",inversedBy="phones") - * @JoinColumns({ - * @JoinColumn(name="client_id", referencedColumnName="id") - * }) - */ + /** @var DDC440Client */ + #[JoinColumn(name: 'client_id', referencedColumnName: 'id')] + #[ManyToOne(targetEntity: 'DDC440Client', inversedBy: 'phones')] protected $client; - /** - * @var string - * @Column(name="phonenumber", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'phonenumber', type: 'string', length: 255)] protected $number; public function setNumber(string $value): void @@ -144,40 +134,28 @@ public function setId(int $value): void } } -/** - * @Entity - * @Table(name="client") - */ +#[Table(name: 'client')] +#[Entity] class DDC440Client { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] protected $id; - /** - * @var DDC440Phone - * @OneToOne(targetEntity="DDC440Phone", fetch="EAGER") - * @JoinColumns({ - * @JoinColumn(name="main_phone_id", referencedColumnName="id",onDelete="SET NULL") - * }) - */ + /** @var DDC440Phone */ + #[JoinColumn(name: 'main_phone_id', referencedColumnName: 'id', onDelete: 'SET NULL')] + #[OneToOne(targetEntity: 'DDC440Phone', fetch: 'EAGER')] protected $mainPhone; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC440Phone", mappedBy="client", cascade={"persist", "remove"}, fetch="EAGER", indexBy="id") - * @OrderBy({"number"="ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC440Phone', mappedBy: 'client', cascade: ['persist', 'remove'], fetch: 'EAGER', indexBy: 'id')] + #[OrderBy(['number' => 'ASC'])] protected $phones; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255)] protected $name; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/DDC444Test.php b/tests/Tests/ORM/Functional/Ticket/DDC444Test.php index ff263faccd7..2c7f4e689eb 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC444Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC444Test.php @@ -62,24 +62,18 @@ public function testExplicitPolicy(): void } -/** - * @Entity - * @Table(name="ddc444") - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Table(name: 'ddc444')] +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class DDC444User { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(name="name", type="string", length=255) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255)] public $name; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC448Test.php b/tests/Tests/ORM/Functional/Ticket/DDC448Test.php index 872b01d73ab..800e8613bb8 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC448Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC448Test.php @@ -28,7 +28,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC448MainTable::class, DDC448ConnectedClass::class, - DDC448SubTable::class + DDC448SubTable::class, ); } @@ -37,58 +37,41 @@ public function testIssue(): void $q = $this->_em->createQuery('select b from ' . __NAMESPACE__ . '\\DDC448SubTable b where b.connectedClassId = ?1'); self::assertEquals( strtolower('SELECT d0_.id AS id_0, d0_.discr AS discr_1, d0_.connectedClassId AS connectedClassId_2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?'), - strtolower($q->getSQL()) + strtolower($q->getSQL()), ); } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="smallint") - * @DiscriminatorMap({ - * "0" = "DDC448MainTable", - * "1" = "DDC448SubTable" - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'smallint')] +#[DiscriminatorMap(['0' => 'DDC448MainTable', '1' => 'DDC448SubTable'])] class DDC448MainTable { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var DDC448ConnectedClass - * @ManyToOne(targetEntity="DDC448ConnectedClass", cascade={"all"}, fetch="EAGER") - * @JoinColumn(name="connectedClassId", referencedColumnName="id", onDelete="CASCADE", nullable=true) - */ - private $connectedClassId; + #[ManyToOne(targetEntity: 'DDC448ConnectedClass', cascade: ['all'], fetch: 'EAGER')] + #[JoinColumn(name: 'connectedClassId', referencedColumnName: 'id', onDelete: 'CASCADE', nullable: true)] + private DDC448ConnectedClass $connectedClassId; } -/** - * @Entity - * @Table(name="connectedClass") - * @HasLifecycleCallbacks - */ +#[Table(name: 'connectedClass')] +#[Entity] +#[HasLifecycleCallbacks] class DDC448ConnectedClass { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $id; // connected with DDC448MainTable } -/** - * @Entity - * @Table(name="SubTable") - */ +#[Table(name: 'SubTable')] +#[Entity] class DDC448SubTable extends DDC448MainTable { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC493Test.php b/tests/Tests/ORM/Functional/Ticket/DDC493Test.php index a1bd2e7bd45..4f5c6f5c871 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC493Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC493Test.php @@ -26,7 +26,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC493Customer::class, DDC493Distributor::class, - DDC493Contact::class + DDC493Contact::class, ); } @@ -35,52 +35,42 @@ public function testIssue(): void $q = $this->_em->createQuery('select u, c.data from ' . __NAMESPACE__ . '\\DDC493Distributor u JOIN u.contact c'); self::assertEquals( strtolower('SELECT d0_.id AS id_0, d1_.data AS data_1, d0_.discr AS discr_2, d0_.contact AS contact_3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id'), - strtolower($q->getSQL()) + strtolower($q->getSQL()), ); } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"distributor" = "DDC493Distributor", "customer" = "DDC493Customer"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['distributor' => 'DDC493Distributor', 'customer' => 'DDC493Customer'])] class DDC493Customer { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC493Contact - * @OneToOne(targetEntity="DDC493Contact", cascade={"remove","persist"}) - * @JoinColumn(name="contact", referencedColumnName="id") - */ + /** @var DDC493Contact */ + #[OneToOne(targetEntity: 'DDC493Contact', cascade: ['remove', 'persist'])] + #[JoinColumn(name: 'contact', referencedColumnName: 'id')] public $contact; } -/** @Entity */ +#[Entity] class DDC493Distributor extends DDC493Customer { } -/** @Entity */ +#[Entity] class DDC493Contact { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC501Test.php b/tests/Tests/ORM/Functional/Ticket/DDC501Test.php deleted file mode 100644 index bd4d026a712..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC501Test.php +++ /dev/null @@ -1,113 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testMergeUnitializedManyToManyAndOneToManyCollections(): void - { - // Create User - $user = $this->createAndPersistUser(); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user)); - $this->_em->clear(); - self::assertFalse($this->_em->contains($user)); - - unset($user); - - // Reload User from DB *without* any associations (i.e. an uninitialized PersistantCollection) - $userReloaded = $this->loadUserFromEntityManager(); - - self::assertTrue($this->_em->contains($userReloaded)); - $this->_em->clear(); - self::assertFalse($this->_em->contains($userReloaded)); - - // freeze and unfreeze - $userClone = unserialize(serialize($userReloaded)); - self::assertInstanceOf(CmsUser::class, $userClone); - - // detached user can't know about his phonenumbers - self::assertEquals(0, count($userClone->getPhonenumbers())); - self::assertFalse($userClone->getPhonenumbers()->isInitialized(), 'User::phonenumbers should not be marked initialized.'); - - // detached user can't know about his groups either - self::assertEquals(0, count($userClone->getGroups())); - self::assertFalse($userClone->getGroups()->isInitialized(), 'User::groups should not be marked initialized.'); - - // Merge back and flush - $userClone = $this->_em->merge($userClone); - - // Back in managed world I would expect to have my phonenumbers back but they aren't! - // Remember I didn't touch (and probably didn't need) them at all while in detached mode. - self::assertEquals(4, count($userClone->getPhonenumbers()), 'Phonenumbers are not available anymore'); - - // This works fine as long as cmUser::groups doesn't cascade "merge" - self::assertEquals(2, count($userClone->getGroups())); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertFalse($this->_em->contains($userClone)); - - // Reload user from DB - $userFromEntityManager = $this->loadUserFromEntityManager(); - - //Strange: Now the phonenumbers are back again - self::assertEquals(4, count($userFromEntityManager->getPhonenumbers())); - - // This works fine as long as cmUser::groups doesn't cascade "merge" - // Otherwise group memberships are physically deleted now! - self::assertEquals(2, count($userClone->getGroups())); - } - - protected function createAndPersistUser(): CmsUser - { - $user = new CmsUser(); - $user->name = 'Luka'; - $user->username = 'lukacho'; - $user->status = 'developer'; - - foreach ([1111, 2222, 3333, 4444] as $number) { - $phone = new CmsPhonenumber(); - $phone->phonenumber = $number; - $user->addPhonenumber($phone); - } - - foreach (['Moshers', 'Headbangers'] as $groupName) { - $group = new CmsGroup(); - $group->setName($groupName); - $user->addGroup($group); - } - - $this->_em->persist($user); - - return $user; - } - - protected function loadUserFromEntityManager(): CmsUser - { - return $this->_em - ->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name like :name') - ->setParameter('name', 'Luka') - ->getSingleResult(); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC512Test.php b/tests/Tests/ORM/Functional/Ticket/DDC512Test.php index c865e43ebdd..22cd3a015b7 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC512Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC512Test.php @@ -24,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC512Customer::class, DDC512OfferItem::class, - DDC512Item::class + DDC512Item::class, ); } @@ -58,15 +58,13 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC512Customer { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; /** @@ -74,30 +72,26 @@ class DDC512Customer * (item = item), this currently confuses Doctrine. * * @var DDC512OfferItem - * @OneToOne(targetEntity="DDC512OfferItem", cascade={"remove","persist"}) - * @JoinColumn(name="item_id", referencedColumnName="id") */ + #[OneToOne(targetEntity: 'DDC512OfferItem', cascade: ['remove', 'persist'])] + #[JoinColumn(name: 'item_id', referencedColumnName: 'id')] public $item; } -/** @Entity */ +#[Entity] class DDC512OfferItem extends DDC512Item { } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"item" = "DDC512Item", "offerItem" = "DDC512OfferItem"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['item' => 'DDC512Item', 'offerItem' => 'DDC512OfferItem'])] class DDC512Item { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC513Test.php b/tests/Tests/ORM/Functional/Ticket/DDC513Test.php index 7c745358b64..d556fd9d1f9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC513Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC513Test.php @@ -26,7 +26,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC513OfferItem::class, DDC513Item::class, - DDC513Price::class + DDC513Price::class, ); } @@ -35,54 +35,44 @@ public function testIssue(): void $q = $this->_em->createQuery('select u from ' . __NAMESPACE__ . '\\DDC513OfferItem u left join u.price p'); self::assertEquals( strtolower('SELECT d0_.id AS id_0, d0_.discr AS discr_1, d0_.price AS price_2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id'), - strtolower($q->getSQL()) + strtolower($q->getSQL()), ); } } -/** @Entity */ +#[Entity] class DDC513OfferItem extends DDC513Item { } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"item" = "DDC513Item", "offerItem" = "DDC513OfferItem"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['item' => 'DDC513Item', 'offerItem' => 'DDC513OfferItem'])] class DDC513Item { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC513Price - * @OneToOne(targetEntity="DDC513Price", cascade={"remove","persist"}) - * @JoinColumn(name="price", referencedColumnName="id") - */ + /** @var DDC513Price */ + #[OneToOne(targetEntity: 'DDC513Price', cascade: ['remove', 'persist'])] + #[JoinColumn(name: 'price', referencedColumnName: 'id')] public $price; } -/** @Entity */ +#[Entity] class DDC513Price { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $data; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC518Test.php b/tests/Tests/ORM/Functional/Ticket/DDC518Test.php deleted file mode 100644 index cdf4e638b92..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC518Test.php +++ /dev/null @@ -1,42 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testMergeWithRelatedNew(): void - { - $article = new CmsArticle(); - $article->text = 'foo'; - $article->topic = 'bar'; - - $this->_em->persist($article); - $this->_em->flush(); - $this->_em->detach($article); - $this->_em->clear(); - - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin Eberlei'; - $user->status = 'active'; - $article->user = $user; - - $this->_em->persist($user); - $managedArticle = $this->_em->merge($article); - - self::assertSame($article->user, $managedArticle->user); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC522Test.php b/tests/Tests/ORM/Functional/Ticket/DDC522Test.php index 7d9bdbba644..4fc79b04d06 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC522Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC522Test.php @@ -11,8 +11,7 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; /** * Tests that join columns (foreign keys) can be named the same as the association @@ -27,11 +26,11 @@ protected function setUp(): void $this->createSchemaForModels( DDC522Customer::class, DDC522Cart::class, - DDC522ForeignKeyTest::class + DDC522ForeignKeyTest::class, ); } - /** @group DDC-522 */ + #[Group('DDC-522')] public function testJoinColumnWithSameNameAsAssociationField(): void { $cust = new DDC522Customer(); @@ -61,15 +60,13 @@ public function testJoinColumnWithSameNameAsAssociationField(): void $this->_em->flush(); $this->_em->clear(); - $fkt2 = $this->_em->find(get_class($fkt), $fkt->id); + $fkt2 = $this->_em->find($fkt::class, $fkt->id); self::assertEquals($fkt->cart->id, $fkt2->cartId); self::assertTrue($this->isUninitializedObject($fkt2->cart)); } - /** - * @group DDC-522 - * @group DDC-762 - */ + #[Group('DDC-522')] + #[Group('DDC-762')] public function testJoinColumnWithNullSameNameAssociationField(): void { $fkCust = new DDC522ForeignKeyTest(); @@ -87,76 +84,58 @@ public function testJoinColumnWithNullSameNameAssociationField(): void } } -/** @Entity */ +#[Entity] class DDC522Customer { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var mixed - * @Column - */ + /** @var mixed */ + #[Column] public $name; - /** - * @var DDC522Cart - * @OneToOne(targetEntity="DDC522Cart", mappedBy="customer") - */ + /** @var DDC522Cart */ + #[OneToOne(targetEntity: 'DDC522Cart', mappedBy: 'customer')] public $cart; } -/** @Entity */ +#[Entity] class DDC522Cart { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $total; - /** - * @var DDC522Customer - * @OneToOne(targetEntity="DDC522Customer", inversedBy="cart") - * @JoinColumn(name="customer", referencedColumnName="id") - */ + /** @var DDC522Customer */ + #[OneToOne(targetEntity: 'DDC522Customer', inversedBy: 'cart')] + #[JoinColumn(name: 'customer', referencedColumnName: 'id')] public $customer; } -/** @Entity */ +#[Entity] class DDC522ForeignKeyTest { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var int|null - * @Column(type="integer", name="cart_id", nullable=true) - */ + /** @var int|null */ + #[Column(type: 'integer', name: 'cart_id', nullable: true)] public $cartId; - /** - * @var DDC522Cart|null - * @OneToOne(targetEntity="DDC522Cart") - * @JoinColumn(name="cart_id", referencedColumnName="id") - */ + /** @var DDC522Cart|null */ + #[OneToOne(targetEntity: 'DDC522Cart')] + #[JoinColumn(name: 'cart_id', referencedColumnName: 'id')] public $cart; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC531Test.php b/tests/Tests/ORM/Functional/Ticket/DDC531Test.php index ba6a460c791..9be32b0eca5 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC531Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC531Test.php @@ -26,7 +26,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC531Item::class, - DDC531SubItem::class + DDC531SubItem::class, ); } @@ -53,33 +53,25 @@ public function testIssue(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="integer") - * @DiscriminatorMap({"0" = "DDC531Item", "1" = "DDC531SubItem"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'integer')] +#[DiscriminatorMap(['0' => 'DDC531Item', '1' => 'DDC531SubItem'])] class DDC531Item { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC531Item", mappedBy="parent") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC531Item', mappedBy: 'parent')] protected $children; - /** - * @var DDC531Item - * @ManyToOne(targetEntity="DDC531Item", inversedBy="children") - * @JoinColumn(name="parentId", referencedColumnName="id") - */ + /** @var DDC531Item */ + #[ManyToOne(targetEntity: 'DDC531Item', inversedBy: 'children')] + #[JoinColumn(name: 'parentId', referencedColumnName: 'id')] public $parent; public function __construct() @@ -99,7 +91,7 @@ public function getChildren(): Collection } } -/** @Entity */ +#[Entity] class DDC531SubItem extends DDC531Item { } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC5684Test.php b/tests/Tests/ORM/Functional/Ticket/DDC5684Test.php index 181b7fad54d..c9271a0bd74 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC5684Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC5684Test.php @@ -12,14 +12,18 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; /** * This test verifies that custom post-insert identifiers respect type conversion semantics. * The generated identifier must be converted via DBAL types before populating the entity * identifier field. - * - * @group 5935 5684 6020 6152 */ +#[Group('5935')] +#[Group('5684')] +#[Group('6020')] +#[Group('6152')] class DDC5684Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -64,7 +68,7 @@ class DDC5684ObjectIdType extends DBALTypes\Type /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): DDC5684ObjectId { return new DDC5684ObjectId($value); } @@ -72,42 +76,26 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { return $value->value; } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::class; } - /** - * {@inheritDoc} - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - return true; - } - public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { return $platform->getIntegerTypeDeclarationSQL($column); } } -class DDC5684ObjectId +class DDC5684ObjectId implements Stringable { - /** @var mixed */ - public $value; - - /** @param mixed $value */ - public function __construct($value) + public function __construct(public mixed $value) { - $this->value = $value; } public function __toString(): string @@ -116,17 +104,13 @@ public function __toString(): string } } -/** - * @Entity - * @Table(name="ticket_5684_objects") - */ +#[Table(name: 'ticket_5684_objects')] +#[Entity] class DDC5684Object { - /** - * @var DDC5684ObjectIdType - * @Id - * @Column(type=DDC5684ObjectIdType::class) - * @GeneratedValue(strategy="AUTO") - */ + /** @var DDC5684ObjectIdType */ + #[Id] + #[Column(type: DDC5684ObjectIdType::class)] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC588Test.php b/tests/Tests/ORM/Functional/Ticket/DDC588Test.php index 1b8e2c5e380..dde7063c210 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC588Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC588Test.php @@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; class DDC588Test extends OrmFunctionalTestCase { @@ -19,6 +20,7 @@ protected function setUp(): void $this->createSchemaForModels(DDC588Site::class); } + #[DoesNotPerformAssertions] public function testIssue(): void { $site = new DDC588Site('Foo'); @@ -27,30 +29,21 @@ public function testIssue(): void $this->_em->flush(); // Following should not result in exception $this->_em->refresh($site); - - $this->addToAssertionCount(1); } } -/** @Entity */ +#[Entity] class DDC588Site { - /** - * @var int - * @Id - * @Column(type="integer", name="site_id") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer', name: 'site_id')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=45) - */ - protected $name = null; - - public function __construct($name = '') - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 45)] + protected string $name = '', + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC599Test.php b/tests/Tests/ORM/Functional/Ticket/DDC599Test.php index f3e1d114dc3..e827d3410b2 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC599Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC599Test.php @@ -24,11 +24,11 @@ protected function setUp(): void { parent::setUp(); - $this->createSchemaForModels( - DDC599Item::class, - DDC599Subitem::class, - DDC599Child::class - ); + $this->createSchemaForModels( + DDC599Item::class, + DDC599Subitem::class, + DDC599Child::class, + ); } public function testCascadeRemoveOnInheritanceHierarchy(): void @@ -78,30 +78,24 @@ public function testCascadeRemoveOnChildren(): void $class = $this->_em->getClassMetadata(DDC599Subitem::class); self::assertArrayHasKey('children', $class->associationMappings); - self::assertTrue($class->associationMappings['children']['isCascadeRemove']); + self::assertTrue($class->associationMappings['children']->isCascadeRemove()); } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="integer") - * @DiscriminatorMap({"0" = "DDC599Item", "1" = "DDC599Subitem"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'integer')] +#[DiscriminatorMap(['0' => 'DDC599Item', '1' => 'DDC599Subitem'])] class DDC599Item { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC599Child", mappedBy="parent", cascade={"remove"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC599Child', mappedBy: 'parent', cascade: ['remove'])] protected $children; public function __construct() @@ -116,31 +110,25 @@ public function getChildren(): Collection } } -/** @Entity */ +#[Entity] class DDC599Subitem extends DDC599Item { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $elem; } -/** @Entity */ +#[Entity] class DDC599Child { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var DDC599Item - * @ManyToOne(targetEntity="DDC599Item", inversedBy="children") - * @JoinColumn(name="parentId", referencedColumnName="id") - */ + /** @var DDC599Item */ + #[ManyToOne(targetEntity: 'DDC599Item', inversedBy: 'children')] + #[JoinColumn(name: 'parentId', referencedColumnName: 'id')] public $parent; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC618Test.php b/tests/Tests/ORM/Functional/Ticket/DDC618Test.php index ff540f8f8fd..4ade4445e1d 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC618Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC618Test.php @@ -15,8 +15,9 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-618 */ +#[Group('DDC-618')] class DDC618Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -44,7 +45,7 @@ protected function setUp(): void $this->_em->flush(); $this->_em->clear(); - } catch (UniqueConstraintViolationException $e) { + } catch (UniqueConstraintViolationException) { } } @@ -72,7 +73,7 @@ public function testIndexByHydrateArray(): void self::assertArrayHasKey('Alice', $result, "INDEX BY A.name should return an index by the name of 'Alice'."); } - /** @group DDC-1018 */ + #[Group('DDC-1018')] public function testIndexByJoin(): void { $dql = 'SELECT A, B FROM Doctrine\Tests\ORM\Functional\Ticket\DDC618Author A ' . @@ -94,7 +95,7 @@ public function testIndexByJoin(): void self::assertTrue(isset($result[0]['books']['Test']), 'Indexing by title should have books by title.'); } - /** @group DDC-1018 */ + #[Group('DDC-1018')] public function testIndexByToOneJoinSilentlyIgnored(): void { $dql = 'SELECT B, A FROM Doctrine\Tests\ORM\Functional\Ticket\DDC618Book B ' . @@ -111,7 +112,7 @@ public function testIndexByToOneJoinSilentlyIgnored(): void self::assertEquals('Alice', $result[0]['author']['name']); } - /** @group DDC-1018 */ + #[Group('DDC-1018')] public function testCombineIndexBy(): void { $dql = 'SELECT A, B FROM Doctrine\Tests\ORM\Functional\Ticket\DDC618Author A INDEX BY A.id ' . @@ -128,26 +129,20 @@ public function testCombineIndexBy(): void } } -/** @Entity */ +#[Entity] class DDC618Author { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC618Book", mappedBy="author", cascade={"persist"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC618Book', mappedBy: 'author', cascade: ['persist'])] public $books; public function __construct() @@ -162,32 +157,20 @@ public function addBook(string $title): void } } -/** @Entity */ +#[Entity] class DDC618Book { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $title; - - /** - * @var DDC618Author - * @ManyToOne(targetEntity="DDC618Author", inversedBy="books") - */ - public $author; - - public function __construct($title, $author) - { - $this->title = $title; - $this->author = $author; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $title, + #[ManyToOne(targetEntity: 'DDC618Author', inversedBy: 'books')] + public DDC618Author $author, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC6303Test.php b/tests/Tests/ORM/Functional/Ticket/DDC6303Test.php index e4c2677ad8a..f7c4dfa1c4b 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC6303Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC6303Test.php @@ -16,12 +16,13 @@ use Doctrine\ORM\OptimisticLockException; use Doctrine\Persistence\Mapping\MappingException; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_keys; use function array_walk; use function count; -/** @group #6303 */ +#[Group('#6303')] class DDC6303Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -31,7 +32,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC6303BaseClass::class, DDC6303ChildA::class, - DDC6303ChildB::class + DDC6303ChildB::class, ); } @@ -86,64 +87,45 @@ private function assertHydratedEntitiesSameToPersistedOnes(array $persistedEntit } /** - * @Entity - * @Table - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * DDC6303ChildB::class = DDC6303ChildB::class, - * DDC6303ChildA::class = DDC6303ChildA::class, - * }) - * * Note: discriminator map order *IS IMPORTANT* for this test */ +#[Table] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap([DDC6303ChildB::class => DDC6303ChildB::class, DDC6303ChildA::class => DDC6303ChildA::class])] abstract class DDC6303BaseClass { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $id; } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class DDC6303ChildA extends DDC6303BaseClass { - /** - * @var mixed - * @Column(type="string", length=255) - */ - private $originalData; - - /** @param mixed $originalData */ - public function __construct(string $id, $originalData) - { - $this->id = $id; - $this->originalData = $originalData; + public function __construct( + string $id, + #[Column(type: 'string', length: 255)] + private mixed $originalData, + ) { + $this->id = $id; } } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class DDC6303ChildB extends DDC6303BaseClass { - /** - * @var mixed[] - * @Column(type="simple_array", nullable=true) - */ - private $originalData; - /** @param mixed[] $originalData */ - public function __construct(string $id, array $originalData) - { - $this->id = $id; - $this->originalData = $originalData; + public function __construct( + string $id, + #[Column(type: 'simple_array', nullable: true)] + private array $originalData, + ) { + $this->id = $id; } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC633Test.php b/tests/Tests/ORM/Functional/Ticket/DDC633Test.php index a6d77040074..75fbc5ee60a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC633Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC633Test.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC633Test extends OrmFunctionalTestCase { @@ -19,15 +20,13 @@ protected function setUp(): void $this->createSchemaForModels( DDC633Patient::class, - DDC633Appointment::class + DDC633Appointment::class, ); } - /** - * @group DDC-633 - * @group DDC-952 - * @group DDC-914 - */ + #[Group('DDC-633')] + #[Group('DDC-952')] + #[Group('DDC-914')] public function testOneToOneEager(): void { $app = new DDC633Appointment(); @@ -47,10 +46,8 @@ public function testOneToOneEager(): void self::assertTrue($this->_em->contains($eagerAppointment->patient)); } - /** - * @group DDC-633 - * @group DDC-952 - */ + #[Group('DDC-633')] + #[Group('DDC-952')] public function testDQLDeferredEagerLoad(): void { for ($i = 0; $i < 10; $i++) { @@ -74,38 +71,30 @@ public function testDQLDeferredEagerLoad(): void } } -/** @Entity */ +#[Entity] class DDC633Appointment { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC633Patient - * @OneToOne(targetEntity="DDC633Patient", inversedBy="appointment", fetch="EAGER") - */ + /** @var DDC633Patient */ + #[OneToOne(targetEntity: 'DDC633Patient', inversedBy: 'appointment', fetch: 'EAGER')] public $patient; } -/** @Entity */ +#[Entity] class DDC633Patient { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var DDC633Appointment - * @OneToOne(targetEntity="DDC633Appointment", mappedBy="patient") - */ + /** @var DDC633Appointment */ + #[OneToOne(targetEntity: 'DDC633Appointment', mappedBy: 'patient')] public $appointment; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC6460Test.php b/tests/Tests/ORM/Functional/Ticket/DDC6460Test.php index 85680311845..b0366d1daec 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC6460Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC6460Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC6460Test extends OrmFunctionalTestCase { @@ -25,13 +26,13 @@ protected function setUp(): void [ DDC6460Entity::class, DDC6460ParentEntity::class, - ] + ], ); - } catch (SchemaException $e) { + } catch (SchemaException) { } } - /** @group DDC-6460 */ + #[Group('DDC-6460')] public function testInlineEmbeddable(): void { $isFieldMapped = $this->_em @@ -41,7 +42,7 @@ public function testInlineEmbeddable(): void self::assertTrue($isFieldMapped); } - /** @group DDC-6460 */ + #[Group('DDC-6460')] public function testInlineEmbeddableProxyInitialization(): void { $entity = new DDC6460Entity(); @@ -67,48 +68,38 @@ public function testInlineEmbeddableProxyInitialization(): void } } -/** @Embeddable() */ +#[Embeddable] class DDC6460Embeddable { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $field; } -/** @Entity() */ +#[Entity] class DDC6460Entity { - /** - * @var int - * @Id - * @GeneratedValue(strategy = "NONE") - * @Column(type = "integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'NONE')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC6460Embeddable - * @Embedded(class = "DDC6460Embeddable") - */ + /** @var DDC6460Embeddable */ + #[Embedded(class: 'DDC6460Embeddable')] public $embedded; } -/** @Entity() */ +#[Entity] class DDC6460ParentEntity { - /** - * @var int - * @Id - * @GeneratedValue(strategy = "NONE") - * @Column(type = "integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'NONE')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC6460Entity - * @ManyToOne(targetEntity="DDC6460Entity", fetch="EXTRA_LAZY", cascade={"persist"}) - */ + /** @var DDC6460Entity */ + #[ManyToOne(targetEntity: 'DDC6460Entity', fetch: 'EXTRA_LAZY', cascade: ['persist'])] public $lazyLoaded; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC6558Test.php b/tests/Tests/ORM/Functional/Ticket/DDC6558Test.php index 867203eb77d..a9c8c7e1f55 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC6558Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC6558Test.php @@ -6,10 +6,9 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-6558 - */ +#[Group('DDC-6558')] class DDC6558Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -42,55 +41,41 @@ public function testEmployeeIsPopulated(): void } } -/** - * @ORM\Entity() - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorColumn(name="discr", type="string") - * @ORM\DiscriminatorMap({"manager" = "DDC6558Manager", "staff" = "DDC6558Staff", "developer" = "DDC6558Developer"}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')] +#[ORM\DiscriminatorMap(['manager' => DDC6558Manager::class, 'staff' => DDC6558Staff::class, 'developer' => DDC6558Developer::class])] abstract class DDC6558Person { - /** - * @ORM\Id() - * @ORM\GeneratedValue() - * @ORM\Column(type="integer") - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; } -/** @ORM\Entity() */ +#[ORM\Entity] class DDC6558Manager extends DDC6558Person { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] abstract class DDC6558Employee extends DDC6558Person { - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $phoneNumber; } -/** @ORM\Entity() */ +#[ORM\Entity] class DDC6558Staff extends DDC6558Employee { } -/** @ORM\Entity() */ +#[ORM\Entity] class DDC6558Developer extends DDC6558Employee { - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $emailAddress; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC656Test.php b/tests/Tests/ORM/Functional/Ticket/DDC656Test.php index 97e58ce09b1..5ca23a20d10 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC656Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC656Test.php @@ -11,7 +11,6 @@ use Doctrine\Tests\OrmFunctionalTestCase; use function array_keys; -use function get_class; class DDC656Test extends OrmFunctionalTestCase { @@ -29,10 +28,10 @@ public function testRecomputeSingleEntityChangeSetPreservesFieldOrder(): void $entity->setType('type1'); $this->_em->persist($entity); - $this->_em->getUnitOfWork()->computeChangeSet($this->_em->getClassMetadata(get_class($entity)), $entity); + $this->_em->getUnitOfWork()->computeChangeSet($this->_em->getClassMetadata($entity::class), $entity); $data1 = $this->_em->getUnitOfWork()->getEntityChangeSet($entity); $entity->setType('type2'); - $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet($this->_em->getClassMetadata(get_class($entity)), $entity); + $this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet($this->_em->getClassMetadata($entity::class), $entity); $data2 = $this->_em->getUnitOfWork()->getEntityChangeSet($entity); self::assertEquals(array_keys($data1), array_keys($data2)); @@ -40,33 +39,27 @@ public function testRecomputeSingleEntityChangeSetPreservesFieldOrder(): void $this->_em->flush(); $this->_em->clear(); - $persistedEntity = $this->_em->find(get_class($entity), $entity->specificationId); + $persistedEntity = $this->_em->find($entity::class, $entity->specificationId); self::assertEquals('type2', $persistedEntity->getType()); self::assertEquals('test1', $persistedEntity->getName()); } } -/** @Entity */ +#[Entity] class DDC656Entity { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $type; - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $specificationId; public function getName(): string diff --git a/tests/Tests/ORM/Functional/Ticket/DDC6573Test.php b/tests/Tests/ORM/Functional/Ticket/DDC6573Test.php new file mode 100644 index 00000000000..4802e596bfb --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/DDC6573Test.php @@ -0,0 +1,108 @@ + */ + private $fixtures; + + protected function setUp(): void + { + parent::setUp(); + + $this->createSchemaForModels( + DDC6573Item::class, + ); + + $item1 = new DDC6573Item('Plate', new DDC6573Money(5, new DDC6573Currency('GBP'))); + $item2 = new DDC6573Item('Iron', new DDC6573Money(50, new DDC6573Currency('EUR'))); + $item3 = new DDC6573Item('Teapot', new DDC6573Money(10, new DDC6573Currency('GBP'))); + + $this->_em->persist($item1); + $this->_em->persist($item2); + $this->_em->persist($item3); + + $this->_em->flush(); + $this->_em->clear(); + + $this->fixtures = [$item1, $item2, $item3]; + } + + protected function tearDown(): void + { + $this->_em->createQuery('DELETE FROM Doctrine\Tests\Models\DDC6573\DDC6573Item i')->execute(); + } + + public static function provideDataForHydrationMode(): iterable + { + yield [AbstractQuery::HYDRATE_ARRAY]; + yield [AbstractQuery::HYDRATE_OBJECT]; + } + + #[DataProvider('provideDataForHydrationMode')] + public function testShouldSupportsMultipleNewOperator(int $hydrationMode): void + { + $dql = ' + SELECT + new Doctrine\Tests\Models\DDC6573\DDC6573Money( + i.priceAmount, + new Doctrine\Tests\Models\DDC6573\DDC6573Currency(i.priceCurrency) + ) + FROM + Doctrine\Tests\Models\DDC6573\DDC6573Item i + ORDER BY + i.priceAmount ASC'; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult($hydrationMode); + + self::assertCount(3, $result); + + self::assertInstanceOf(DDC6573Money::class, $result[0]); + self::assertInstanceOf(DDC6573Money::class, $result[1]); + self::assertInstanceOf(DDC6573Money::class, $result[2]); + + self::assertEquals($this->fixtures[0]->getPrice(), $result[0]); + self::assertEquals($this->fixtures[2]->getPrice(), $result[1]); + self::assertEquals($this->fixtures[1]->getPrice(), $result[2]); + } + + #[DataProvider('provideDataForHydrationMode')] + public function testShouldSupportsBasicUsage(int $hydrationMode): void + { + $dql = ' + SELECT + new Doctrine\Tests\Models\DDC6573\DDC6573Currency( + i.priceCurrency + ) + FROM + Doctrine\Tests\Models\DDC6573\DDC6573Item i + ORDER BY + i.priceAmount'; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult($hydrationMode); + + self::assertCount(3, $result); + + self::assertInstanceOf(DDC6573Currency::class, $result[0]); + self::assertInstanceOf(DDC6573Currency::class, $result[1]); + self::assertInstanceOf(DDC6573Currency::class, $result[2]); + + self::assertEquals($this->fixtures[0]->getPrice()->getCurrency(), $result[0]); + self::assertEquals($this->fixtures[1]->getPrice()->getCurrency(), $result[2]); + self::assertEquals($this->fixtures[2]->getPrice()->getCurrency(), $result[1]); + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC657Test.php b/tests/Tests/ORM/Functional/Ticket/DDC657Test.php index 3dd364186cf..9ca2ab9ccfd 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC657Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC657Test.php @@ -8,8 +8,9 @@ use DateTimeZone; use Doctrine\Tests\Models\Generic\DateTimeModel; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-657 */ +#[Group('DDC-657')] class DDC657Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -28,9 +29,9 @@ public function testEntitySingleResult(): void self::assertInstanceOf(DateTimeModel::class, $datetime); - self::assertInstanceOf('DateTime', $datetime->datetime); - self::assertInstanceOf('DateTime', $datetime->time); - self::assertInstanceOf('DateTime', $datetime->date); + self::assertInstanceOf(DateTime::class, $datetime->datetime); + self::assertInstanceOf(DateTime::class, $datetime->time); + self::assertInstanceOf(DateTime::class, $datetime->date); } public function testScalarResult(): void @@ -56,13 +57,13 @@ public function testaTicketEntityArrayResult(): void self::assertCount(2, $result); - self::assertInstanceOf('DateTime', $result[0]['datetime']); - self::assertInstanceOf('DateTime', $result[0]['time']); - self::assertInstanceOf('DateTime', $result[0]['date']); + self::assertInstanceOf(DateTime::class, $result[0]['datetime']); + self::assertInstanceOf(DateTime::class, $result[0]['time']); + self::assertInstanceOf(DateTime::class, $result[0]['date']); - self::assertInstanceOf('DateTime', $result[1]['datetime']); - self::assertInstanceOf('DateTime', $result[1]['time']); - self::assertInstanceOf('DateTime', $result[1]['date']); + self::assertInstanceOf(DateTime::class, $result[1]['datetime']); + self::assertInstanceOf(DateTime::class, $result[1]['time']); + self::assertInstanceOf(DateTime::class, $result[1]['date']); } public function testTicketSingleResult(): void @@ -72,9 +73,9 @@ public function testTicketSingleResult(): void self::assertIsArray($datetime); - self::assertInstanceOf('DateTime', $datetime['datetime']); - self::assertInstanceOf('DateTime', $datetime['time']); - self::assertInstanceOf('DateTime', $datetime['date']); + self::assertInstanceOf(DateTime::class, $datetime['datetime']); + self::assertInstanceOf(DateTime::class, $datetime['time']); + self::assertInstanceOf(DateTime::class, $datetime['date']); } public function testTicketResult(): void @@ -84,15 +85,15 @@ public function testTicketResult(): void self::assertCount(2, $result); - self::assertInstanceOf('DateTime', $result[0]['time']); - self::assertInstanceOf('DateTime', $result[0]['date']); - self::assertInstanceOf('DateTime', $result[0]['datetime']); + self::assertInstanceOf(DateTime::class, $result[0]['time']); + self::assertInstanceOf(DateTime::class, $result[0]['date']); + self::assertInstanceOf(DateTime::class, $result[0]['datetime']); self::assertEquals('2010-01-01 11:11:11', $result[0]['datetime']->format('Y-m-d G:i:s')); - self::assertInstanceOf('DateTime', $result[1]['time']); - self::assertInstanceOf('DateTime', $result[1]['date']); - self::assertInstanceOf('DateTime', $result[1]['datetime']); + self::assertInstanceOf(DateTime::class, $result[1]['time']); + self::assertInstanceOf(DateTime::class, $result[1]['date']); + self::assertInstanceOf(DateTime::class, $result[1]['datetime']); self::assertEquals('2010-02-02 12:12:12', $result[1]['datetime']->format('Y-m-d G:i:s')); } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC698Test.php b/tests/Tests/ORM/Functional/Ticket/DDC698Test.php index 7f2d0f2b6a1..6208322be2a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC698Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC698Test.php @@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -37,72 +38,53 @@ public function testTicket(): void self::assertEquals( strtolower('SELECT p0_.privilegeID AS privilegeID_0, p0_.name AS name_1, r1_.roleID AS roleID_2, r1_.name AS name_3, r1_.shortName AS shortName_4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID'), - strtolower($sql) + strtolower($sql), ); } } -/** - * @Table(name="Roles") - * @Entity - */ +#[Table(name: 'Roles')] +#[Entity] class DDC698Role { - /** - * @var int - * @Id - * @Column(name="roleID", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'roleID', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $roleID; - /** - * @var string - * @Column(name="name", type="string", length=45) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 45)] protected $name; - /** - * @var string - * @Column(name="shortName", type="string", length=45) - */ + /** @var string */ + #[Column(name: 'shortName', type: 'string', length: 45)] protected $shortName; - /** - * @var Collection - * @ManyToMany(targetEntity="DDC698Privilege", inversedBy="roles") - * @JoinTable(name="RolePrivileges", - * joinColumns={@JoinColumn(name="roleID", referencedColumnName="roleID")}, - * inverseJoinColumns={@JoinColumn(name="privilegeID", referencedColumnName="privilegeID")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'RolePrivileges')] + #[JoinColumn(name: 'roleID', referencedColumnName: 'roleID')] + #[InverseJoinColumn(name: 'privilegeID', referencedColumnName: 'privilegeID')] + #[ManyToMany(targetEntity: 'DDC698Privilege', inversedBy: 'roles')] protected $privilege; } -/** - * @Table(name="Privileges") - * @Entity() - */ +#[Table(name: 'Privileges')] +#[Entity] class DDC698Privilege { - /** - * @var int - * @Id - * @Column(name="privilegeID", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'privilegeID', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] protected $privilegeID; - /** - * @var string - * @Column(name="name", type="string", length=45) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 45)] protected $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC698Role", mappedBy="privilege") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC698Role', mappedBy: 'privilege')] protected $roles; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC69Test.php b/tests/Tests/ORM/Functional/Ticket/DDC69Test.php index 10c10776254..7ce3ec1131e 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC69Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC69Test.php @@ -105,33 +105,23 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="lemma") - */ +#[Table(name: 'lemma')] +#[Entity] class Lemma { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="lemma_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="lemma_name", unique=true, length=255) - */ - private $lemma; - - /** - * @var Collection - * @OneToMany(targetEntity="Relation", mappedBy="parent", cascade={"persist"}) - */ - private $relations; + #[Id] + #[Column(type: 'integer', name: 'lemma_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'lemma_name', unique: true, length: 255)] + private string|null $lemma = null; + + /** @var Collection */ + #[OneToMany(targetEntity: 'Relation', mappedBy: 'parent', cascade: ['persist'])] + private Collection $relations; public function __construct() { @@ -175,42 +165,28 @@ public function getRelations(): Collection } } -/** - * @Entity - * @Table(name="relation") - */ +#[Table(name: 'relation')] +#[Entity] class Relation { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="relation_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var Lemma|null - * @ManyToOne(targetEntity="Lemma", inversedBy="relations") - * @JoinColumn(name="relation_parent_id", referencedColumnName="lemma_id") - */ - private $parent; - - /** - * @var Lemma - * @OneToOne(targetEntity="Lemma") - * @JoinColumn(name="relation_child_id", referencedColumnName="lemma_id") - */ - private $child; - - /** - * @var RelationType - * @ManyToOne(targetEntity="RelationType", inversedBy="relations") - * @JoinColumn(name="relation_type_id", referencedColumnName="relation_type_id") - */ - private $type; + #[Id] + #[Column(type: 'integer', name: 'relation_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[ManyToOne(targetEntity: 'Lemma', inversedBy: 'relations')] + #[JoinColumn(name: 'relation_parent_id', referencedColumnName: 'lemma_id')] + private Lemma|null $parent = null; + + #[OneToOne(targetEntity: 'Lemma')] + #[JoinColumn(name: 'relation_child_id', referencedColumnName: 'lemma_id')] + private Lemma|null $child = null; + + #[ManyToOne(targetEntity: 'RelationType', inversedBy: 'relations')] + #[JoinColumn(name: 'relation_type_id', referencedColumnName: 'relation_type_id')] + private RelationType|null $type = null; public function setParent(Lemma $parent): void { @@ -261,38 +237,25 @@ public function removeType(): void } } -/** - * @Entity - * @Table(name="relation_type") - */ +#[Table(name: 'relation_type')] +#[Entity] class RelationType { public const CLASS_NAME = self::class; - /** - * @var int - * @Id - * @Column(type="integer", name="relation_type_id") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", name="relation_type_name", unique=true, length=255) - */ - private $type; - - /** - * @var string - * @Column(type="string", name="relation_type_abbreviation", unique=true, length=255) - */ - private $abbreviation; - - /** - * @var Collection - * @OneToMany(targetEntity="Relation", mappedBy="type", cascade={"persist"}) - */ + #[Id] + #[Column(type: 'integer', name: 'relation_type_id')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', name: 'relation_type_name', unique: true, length: 255)] + private string|null $type = null; + + #[Column(type: 'string', name: 'relation_type_abbreviation', unique: true, length: 255)] + private string|null $abbreviation = null; + + /** @var Collection */ + #[OneToMany(targetEntity: 'Relation', mappedBy: 'type', cascade: ['persist'])] private $relations; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/DDC719Test.php b/tests/Tests/ORM/Functional/Ticket/DDC719Test.php index ca5dbd457e4..41bc9879b7f 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC719Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC719Test.php @@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -35,20 +36,18 @@ public function testIsEmptySqlGeneration(): void self::assertEquals( strtolower($referenceSQL), - strtolower($q->getSQL()) + strtolower($q->getSQL()), ); } } -/** @MappedSuperclass */ +#[MappedSuperclass] class MyEntity { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] protected $id; public function getId(): int @@ -57,44 +56,31 @@ public function getId(): int } } -/** - * @Entity - * @Table(name="groups") - */ +#[Table(name: 'groups')] +#[Entity] class DDC719Group extends MyEntity { - /** - * @var string - * @Column(type="string", nullable=false) - */ + /** @var string */ + #[Column(type: 'string', nullable: false)] protected $name; - /** - * @var string - * @Column(type="string", nullable=true) - */ + /** @var string */ + #[Column(type: 'string', nullable: true)] protected $description; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC719Group", inversedBy="parents") - * @JoinTable(name="groups_groups", - * joinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="child_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'groups_groups')] + #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'child_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'DDC719Group', inversedBy: 'parents')] protected $children = null; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC719Group", mappedBy="children") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC719Group', mappedBy: 'children')] protected $parents = null; public function __construct() { - parent::__construct(); - $this->channels = new ArrayCollection(); $this->children = new ArrayCollection(); $this->parents = new ArrayCollection(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC729Test.php b/tests/Tests/ORM/Functional/Ticket/DDC729Test.php deleted file mode 100644 index 925986940d1..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC729Test.php +++ /dev/null @@ -1,198 +0,0 @@ -createSchemaForModels(DDC729A::class, DDC729B::class); - } - - public function testMergeManyToMany(): void - { - $a = new DDC729A(); - $b = new DDC729B(); - $a->related[] = $b; - - $this->_em->persist($a); - $this->_em->persist($b); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - self::assertInstanceOf(ArrayCollection::class, $a->related); - - $a = $this->_em->merge($a); - - self::assertInstanceOf(PersistentCollection::class, $a->related); - - self::assertFalse($a->related->isInitialized(), 'Collection should not be marked initialized.'); - self::assertFalse($a->related->isDirty(), 'Collection should not be marked as dirty.'); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(1, count($a->related)); - } - - public function testUnidirectionalMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - - $a->related->set(1, $this->_em->merge($b2)); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } - - public function testBidirectionalMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - $b1->related->set(0, $a); - - $a->related->set(1, $this->_em->merge($b2)); - $b2->related->set(0, $a); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } - - public function testBidirectionalMultiMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - $b1->related->set(0, $this->_em->merge($a)); - - $a->related->set(1, $this->_em->merge($b2)); - $b2->related->set(0, $this->_em->merge($a)); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } -} - -/** @Entity */ -class DDC729A -{ - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - public $id; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC729B", inversedBy="related") - */ - public $related; - - public function __construct() - { - $this->related = new ArrayCollection(); - } -} - -/** @Entity */ -class DDC729B -{ - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - public $id; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC729B", mappedBy="related") - */ - public $related; - - public function __construct() - { - $this->related = new ArrayCollection(); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC735Test.php b/tests/Tests/ORM/Functional/Ticket/DDC735Test.php index 6e0976e1970..b57d3cc6bbf 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC735Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC735Test.php @@ -55,26 +55,17 @@ public function testRemoveElementAppliesOrphanRemoval(): void } } -/** @Entity */ +#[Entity] class DDC735Product { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @phpstan-var Collection - * @OneToMany( - * targetEntity="DDC735Review", - * mappedBy="product", - * cascade={"persist"}, - * orphanRemoval=true - * ) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC735Review', mappedBy: 'product', cascade: ['persist'], orphanRemoval: true)] protected $reviews; public function __construct() @@ -99,26 +90,19 @@ public function removeReview(DDC735Review $review): void } } -/** @Entity */ +#[Entity] class DDC735Review { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var DDC735Product - * @ManyToOne(targetEntity="DDC735Product", inversedBy="reviews") - */ - protected $product; - - public function __construct(DDC735Product $product) - { - $this->product = $product; + public function __construct( + #[ManyToOne(targetEntity: 'DDC735Product', inversedBy: 'reviews')] + protected DDC735Product $product, + ) { $product->addReview($this); } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC736Test.php b/tests/Tests/ORM/Functional/Ticket/DDC736Test.php index b081651d66d..08c41e3a2eb 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC736Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC736Test.php @@ -5,12 +5,13 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\ORM\Query; -use Doctrine\ORM\Query\AST; use Doctrine\ORM\Query\AST\SelectExpression; +use Doctrine\ORM\Query\AST\SelectStatement; use Doctrine\ORM\Query\TreeWalkerAdapter; use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -23,7 +24,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-736 */ + #[Group('DDC-736')] public function testReorderEntityFetchJoinForHydration(): void { $cust = new ECommerceCustomer(); @@ -50,11 +51,9 @@ public function testReorderEntityFetchJoinForHydration(): void self::assertEquals(['name' => 'roman', 'payment' => 'cash'], $result); } - /** - * @group DDC-736 - * @group DDC-925 - * @group DDC-915 - */ + #[Group('DDC-736')] + #[Group('DDC-925')] + #[Group('DDC-915')] public function testDqlTreeWalkerReordering(): void { $cust = new ECommerceCustomer(); @@ -82,12 +81,12 @@ public function testDqlTreeWalkerReordering(): void class DisableFetchJoinTreeWalker extends TreeWalkerAdapter { - public function walkSelectStatement(AST\SelectStatement $AST): void + public function walkSelectStatement(SelectStatement $selectStatement): void { - foreach ($AST->selectClause->selectExpressions as $key => $selectExpr) { + foreach ($selectStatement->selectClause->selectExpressions as $key => $selectExpr) { assert($selectExpr instanceof SelectExpression); if ($selectExpr->expression === 'c') { - unset($AST->selectClause->selectExpressions[$key]); + unset($selectStatement->selectClause->selectExpressions[$key]); break; } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC742Test.php b/tests/Tests/ORM/Functional/Ticket/DDC742Test.php deleted file mode 100644 index 825a142e92e..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC742Test.php +++ /dev/null @@ -1,138 +0,0 @@ -_em->getMetadataFactory()->setCacheDriver(new FilesystemCache($testDir)); - - $this->createSchemaForModels(DDC742User::class, DDC742Comment::class); - - // make sure classes will be deserialized from caches - $this->_em->getMetadataFactory()->setMetadataFor(DDC742User::class, null); - $this->_em->getMetadataFactory()->setMetadataFor(DDC742Comment::class, null); - } - - public function testIssue(): void - { - $user = new DDC742User(); - $user->title = 'Foo'; - $user->favoriteComments = new ArrayCollection(); - - $comment1 = new DDC742Comment(); - $comment1->content = 'foo'; - - $comment2 = new DDC742Comment(); - $comment2->content = 'bar'; - - $comment3 = new DDC742Comment(); - $comment3->content = 'baz'; - - $user->favoriteComments->add($comment1); - $user->favoriteComments->add($comment2); - - $this->_em->persist($user); - $this->_em->persist($comment1); - $this->_em->persist($comment2); - $this->_em->persist($comment3); - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(DDC742User::class, $user->id); - $user->favoriteComments->add($this->_em->find(DDC742Comment::class, $comment3->id)); - - $this->_em->flush(); - $this->addToAssertionCount(1); - } -} - -/** - * @Entity - * @Table(name="ddc742_users") - */ -class DDC742User -{ - /** - * User Id - * - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - * @var int - */ - public $id; - - /** - * @Column(length=100, type="string") - * @var string - */ - public $title; - - /** - * @ManyToMany(targetEntity="DDC742Comment", cascade={"persist"}, fetch="EAGER") - * @JoinTable( - * name="user_comments", - * joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="comment_id", referencedColumnName="id")} - * ) - * @var PersistentCollection - */ - public $favoriteComments; -} - -/** - * @Entity - * @Table(name="ddc742_comments") - */ -class DDC742Comment -{ - /** - * User Id - * - * @Id - * @GeneratedValue(strategy="AUTO") - * @Column(type="integer") - * @var int - */ - public $id; - - /** - * @Column(length=100, type="string") - * @var string - */ - public $content; -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC758Test.php b/tests/Tests/ORM/Functional/Ticket/DDC758Test.php deleted file mode 100644 index 461cf116d90..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/DDC758Test.php +++ /dev/null @@ -1,186 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - /** - * Helper method to set cascade to merge only - */ - private function setCascadeMergeFor($class): void - { - $metadata = $this->_em->getMetadataFactory()->getMetadataFor($class); - foreach ($metadata->associationMappings as $key => $associationMapping) { - $metadata->associationMappings[$key]['isCascadePersist'] = false; - $metadata->associationMappings[$key]['isCascadeMerge'] = true; - $metadata->associationMappings[$key]['isCascadeRemove'] = false; - $metadata->associationMappings[$key]['isCascadeDetach'] = false; - } - } - - /** - * Test that changing associations on detached entities and then cascade merging them - * causes the database to be updated with the new associations. - * This specifically tests adding new associations. - */ - public function testManyToManyMergeAssociationAdds(): void - { - $this->setCascadeMergeFor(CmsUser::class); - $this->setCascadeMergeFor(CmsGroup::class); - - // Put entities in the database - $cmsUser = new CmsUser(); - $cmsUser->username = 'dave'; - $cmsUser->name = 'Dave Keen'; - $cmsUser->status = 'testing'; - - $group1 = new CmsGroup(); - $group1->name = 'Group 1'; - - $group2 = new CmsGroup(); - $group2->name = 'Group 2'; - - $this->_em->persist($cmsUser); - $this->_em->persist($group1); - $this->_em->persist($group2); - $this->_em->flush(); - - $cmsUserId = $cmsUser->id; - $group1Id = $group1->id; - $group2Id = $group2->id; - - $this->_em->clear(); - - // Now create detached versions of the entities with some new associations. - $cmsUser = new CmsUser(); - $cmsUser->id = $cmsUserId; - $cmsUser->username = 'dave'; - $cmsUser->name = 'Dave Keen'; - $cmsUser->status = 'testing'; - $cmsUser->groups = new ArrayCollection(); - - $group1 = new CmsGroup(); - $group1->id = $group1Id; - $group1->name = 'Group 1'; - $group1->users = new ArrayCollection(); - - $group2 = new CmsGroup(); - $group2->id = $group2Id; - $group2->name = 'Group 2'; - $group2->users = new ArrayCollection(); - - $cmsUser->addGroup($group1); - $cmsUser->addGroup($group2); - - // Cascade merge of cmsUser followed by a flush should add in the bidirectional new many-to-many associations between the user and the groups - $this->_em->merge($cmsUser); - $this->_em->flush(); - - $this->_em->clear(); - - $cmsUsers = $this->_em->getRepository(CmsUser::class)->findAll(); - $cmsGroups = $this->_em->getRepository(CmsGroup::class)->findAll(); - - // Check the entities are in the database - self::assertEquals(1, count($cmsUsers)); - self::assertEquals(2, count($cmsGroups)); - - // Check the associations between the entities are now in the database - self::assertEquals(2, count($cmsUsers[0]->groups)); - self::assertEquals(1, count($cmsGroups[0]->users)); - self::assertEquals(1, count($cmsGroups[1]->users)); - - self::assertSame($cmsUsers[0]->groups[0], $cmsGroups[0]); - self::assertSame($cmsUsers[0]->groups[1], $cmsGroups[1]); - self::assertSame($cmsGroups[0]->users[0], $cmsUsers[0]); - self::assertSame($cmsGroups[1]->users[0], $cmsUsers[0]); - } - - /** - * Test that changing associations on detached entities and then cascade merging them causes the - * database to be updated with the new associations. - */ - public function testManyToManyMergeAssociationRemoves(): void - { - $this->setCascadeMergeFor(CmsUser::class); - $this->setCascadeMergeFor(CmsGroup::class); - - $cmsUser = new CmsUser(); - $cmsUser->username = 'dave'; - $cmsUser->name = 'Dave Keen'; - $cmsUser->status = 'testing'; - - $group1 = new CmsGroup(); - $group1->name = 'Group 1'; - - $group2 = new CmsGroup(); - $group2->name = 'Group 2'; - - $cmsUser->addGroup($group1); - $cmsUser->addGroup($group2); - - $this->_em->persist($cmsUser); - $this->_em->persist($group1); - $this->_em->persist($group2); - $this->_em->flush(); - - $cmsUserId = $cmsUser->id; - $group1Id = $group1->id; - $group2Id = $group2->id; - - $this->_em->clear(); - - // Now create detached versions of the entities with NO associations. - $cmsUser = new CmsUser(); - $cmsUser->id = $cmsUserId; - $cmsUser->username = 'dave'; - $cmsUser->name = 'Dave Keen'; - $cmsUser->status = 'testing'; - $cmsUser->groups = new ArrayCollection(); - - $group1 = new CmsGroup(); - $group1->id = $group1Id; - $group1->name = 'Group 1'; - $group1->users = new ArrayCollection(); - - $group2 = new CmsGroup(); - $group2->id = $group2Id; - $group2->name = 'Group 2'; - $group2->users = new ArrayCollection(); - - // Cascade merge of cmsUser followed by a flush should result in the association array collection being empty - $this->_em->merge($cmsUser); - $this->_em->flush(); - - $this->_em->clear(); - - $cmsUsers = $this->_em->getRepository(CmsUser::class)->findAll(); - $cmsGroups = $this->_em->getRepository(CmsGroup::class)->findAll(); - - // Check the entities are in the database - self::assertEquals(1, count($cmsUsers)); - self::assertEquals(2, count($cmsGroups)); - - // Check the associations between the entities are now in the database - self::assertEquals(0, count($cmsUsers[0]->groups)); - self::assertEquals(0, count($cmsGroups[0]->users)); - self::assertEquals(0, count($cmsGroups[1]->users)); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/DDC767Test.php b/tests/Tests/ORM/Functional/Ticket/DDC767Test.php index 73ad3bb03af..033dd7199fa 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC767Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC767Test.php @@ -8,9 +8,9 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; use Exception; +use PHPUnit\Framework\Attributes\Group; use function assert; -use function get_class; class DDC767Test extends OrmFunctionalTestCase { @@ -21,7 +21,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-767 */ + #[Group('DDC-767')] public function testCollectionChangesInsideTransaction(): void { $user = new CmsUser(); @@ -49,7 +49,7 @@ public function testCollectionChangesInsideTransaction(): void $this->_em->flush(); $this->_em->clear(); - $pUser = $this->_em->find(get_class($user), $user->id); + $pUser = $this->_em->find($user::class, $user->id); assert($pUser instanceof CmsUser); self::assertNotNull($pUser, 'User not retrieved from database.'); @@ -65,12 +65,12 @@ public function testCollectionChangesInsideTransaction(): void // Add new foreach ($groups as $groupId) { - $pUser->addGroup($this->_em->find(get_class($group1), $groupId)); + $pUser->addGroup($this->_em->find($group1::class, $groupId)); } $this->_em->flush(); $this->_em->commit(); - } catch (Exception $e) { + } catch (Exception) { $this->_em->rollback(); } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC809Test.php b/tests/Tests/ORM/Functional/Ticket/DDC809Test.php index 844eb1671c8..319d3925769 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC809Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC809Test.php @@ -8,11 +8,13 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC809Test extends OrmFunctionalTestCase { @@ -22,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC809Variant::class, - DDC809SpecificationValue::class + DDC809SpecificationValue::class, ); $conn = $this->_em->getConnection(); @@ -47,7 +49,7 @@ protected function setUp(): void $conn->insert('var_spec_value_test', ['variant_id' => 545209, 'specification_value_id' => 94607]); } - /** @group DDC-809 */ + #[Group('DDC-809')] public function testIssue(): void { $result = $this->_em->createQueryBuilder() @@ -62,31 +64,20 @@ public function testIssue(): void } } -/** - * @Table(name="variant_test") - * @Entity - */ +#[Table(name: 'variant_test')] +#[Entity] class DDC809Variant { - /** - * @var int - * @Column(name="variant_id", type="integer") - * @Id - */ + /** @var int */ + #[Column(name: 'variant_id', type: 'integer')] + #[Id] protected $variantId; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC809SpecificationValue", inversedBy="Variants") - * @JoinTable(name="var_spec_value_test", - * joinColumns={ - * @JoinColumn(name="variant_id", referencedColumnName="variant_id") - * }, - * inverseJoinColumns={ - * @JoinColumn(name="specification_value_id", referencedColumnName="specification_value_id") - * } - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'var_spec_value_test')] + #[JoinColumn(name: 'variant_id', referencedColumnName: 'variant_id')] + #[InverseJoinColumn(name: 'specification_value_id', referencedColumnName: 'specification_value_id')] + #[ManyToMany(targetEntity: 'DDC809SpecificationValue', inversedBy: 'Variants')] protected $specificationValues; /** @phpstan-return Collection */ @@ -96,22 +87,16 @@ public function getSpecificationValues(): Collection } } -/** - * @Table(name="specification_value_test") - * @Entity - */ +#[Table(name: 'specification_value_test')] +#[Entity] class DDC809SpecificationValue { - /** - * @var int - * @Column(name="specification_value_id", type="integer") - * @Id - */ + /** @var int */ + #[Column(name: 'specification_value_id', type: 'integer')] + #[Id] protected $specificationValueId; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC809Variant", mappedBy="SpecificationValues") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC809Variant', mappedBy: 'SpecificationValues')] protected $variants; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC812Test.php b/tests/Tests/ORM/Functional/Ticket/DDC812Test.php index e5997813ead..1651600fa3a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC812Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC812Test.php @@ -7,8 +7,7 @@ use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsComment; use Doctrine\Tests\OrmFunctionalTestCase; - -use function get_class; +use PHPUnit\Framework\Attributes\Group; class DDC812Test extends OrmFunctionalTestCase { @@ -19,7 +18,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-812 */ + #[Group('DDC-812')] public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity(): void { $article = new CmsArticle(); @@ -36,10 +35,10 @@ public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManag $this->_em->flush(); $this->_em->clear(); - $article2 = $this->_em->find(get_class($article), $article->id); + $article2 = $this->_em->find($article::class, $article->id); $article2Again = $this->_em->createQuery( - 'select a, c from Doctrine\Tests\Models\CMS\CmsArticle a join a.comments c where a.id = ?1' + 'select a, c from Doctrine\Tests\Models\CMS\CmsArticle a join a.comments c where a.id = ?1', ) ->setParameter(1, $article->id) ->getSingleResult(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC832Test.php b/tests/Tests/ORM/Functional/Ticket/DDC832Test.php index 71be729a996..834c7a5cf5a 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC832Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC832Test.php @@ -16,6 +16,9 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; + +use function method_exists; class DDC832Test extends OrmFunctionalTestCase { @@ -30,7 +33,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC832JoinedIndex::class, DDC832JoinedTreeIndex::class, - DDC832Like::class + DDC832Like::class, ); } @@ -39,17 +42,18 @@ public function tearDown(): void $platform = $this->_em->getConnection()->getDatabasePlatform(); $sm = $this->createSchemaManager(); - $sm->dropTable($platform->quoteIdentifier('TREE_INDEX')); - $sm->dropTable($platform->quoteIdentifier('INDEX')); - $sm->dropTable($platform->quoteIdentifier('LIKE')); - - if ($platform instanceof PostgreSQLPlatform) { - $sm->dropSequence($platform->quoteIdentifier('INDEX_id_seq')); - $sm->dropSequence($platform->quoteIdentifier('LIKE_id_seq')); + $sm->dropTable($platform->quoteSingleIdentifier('TREE_INDEX')); + $sm->dropTable($platform->quoteSingleIdentifier('INDEX')); + $sm->dropTable($platform->quoteSingleIdentifier('LIKE')); + + // DBAL 3 + if ($platform instanceof PostgreSQLPlatform && method_exists($platform, 'getIdentitySequenceName')) { + $sm->dropSequence($platform->quoteSingleIdentifier('INDEX_id_seq')); + $sm->dropSequence($platform->quoteSingleIdentifier('LIKE_id_seq')); } } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableBasicUpdate(): void { $like = new DDC832Like('test'); @@ -63,7 +67,7 @@ public function testQuotedTableBasicUpdate(): void self::assertEquals($like, $this->_em->find(DDC832Like::class, $like->id)); } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableBasicRemove(): void { $like = new DDC832Like('test'); @@ -79,7 +83,7 @@ public function testQuotedTableBasicRemove(): void self::assertNull($this->_em->find(DDC832Like::class, $idToBeRemoved)); } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableJoinedUpdate(): void { $index = new DDC832JoinedIndex('test'); @@ -93,7 +97,7 @@ public function testQuotedTableJoinedUpdate(): void self::assertEquals($index, $this->_em->find(DDC832JoinedIndex::class, $index->id)); } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableJoinedRemove(): void { $index = new DDC832JoinedIndex('test'); @@ -109,7 +113,7 @@ public function testQuotedTableJoinedRemove(): void self::assertNull($this->_em->find(DDC832JoinedIndex::class, $idToBeRemoved)); } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableJoinedChildUpdate(): void { $index = new DDC832JoinedTreeIndex('test', 1, 2); @@ -123,7 +127,7 @@ public function testQuotedTableJoinedChildUpdate(): void self::assertEquals($index, $this->_em->find(DDC832JoinedTreeIndex::class, $index->id)); } - /** @group DDC-832 */ + #[Group('DDC-832')] public function testQuotedTableJoinedChildRemove(): void { $index = new DDC832JoinedTreeIndex('test', 1, 2); @@ -140,97 +144,64 @@ public function testQuotedTableJoinedChildRemove(): void } } -/** - * @Entity - * @Table(name="`LIKE`") - */ +#[Table(name: '`LIKE`')] +#[Entity] class DDC832Like { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $word; - - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; - public function __construct(string $word) - { - $this->word = $word; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $word, + ) { } } -/** - * @Entity - * @Table(name="`INDEX`") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"like" = "DDC832JoinedIndex", "fuzzy" = "DDC832JoinedTreeIndex"}) - */ +#[Table(name: '`INDEX`')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['like' => 'DDC832JoinedIndex', 'fuzzy' => 'DDC832JoinedTreeIndex'])] class DDC832JoinedIndex { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } -/** - * @Entity - * @Table(name="`TREE_INDEX`") - */ +#[Table(name: '`TREE_INDEX`')] +#[Entity] class DDC832JoinedTreeIndex extends DDC832JoinedIndex { - /** - * @var int - * @Column(type="integer") - */ - public $lft; - - /** - * @var int - * @Column(type="integer") - */ - public $rgt; - - public function __construct(string $name, int $lft, int $rgt) - { + public function __construct( + string $name, + #[Column(type: 'integer')] + public int $lft, + #[Column(type: 'integer')] + public int $rgt, + ) { $this->name = $name; - $this->lft = $lft; - $this->rgt = $rgt; } } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC837Test.php b/tests/Tests/ORM/Functional/Ticket/DDC837Test.php index 8fd52bf5302..3d02afd91f9 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC837Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC837Test.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC837Test extends OrmFunctionalTestCase { @@ -26,11 +27,11 @@ protected function setUp(): void DDC837Class1::class, DDC837Class2::class, DDC837Class3::class, - DDC837Aggregate::class + DDC837Aggregate::class, ); } - /** @group DDC-837 */ + #[Group('DDC-837')] public function testIssue(): void { $c1 = new DDC837Class1(); @@ -98,114 +99,84 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="DDC837Super") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"class1" = "DDC837Class1", "class2" = "DDC837Class2", "class3"="DDC837Class3"}) - */ +#[Table(name: 'DDC837Super')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['class1' => 'DDC837Class1', 'class2' => 'DDC837Class2', 'class3' => 'DDC837Class3'])] abstract class DDC837Super { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** @Entity */ +#[Entity] class DDC837Class1 extends DDC837Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $title; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $description; - /** - * @var DDC837Aggregate - * @OneToOne(targetEntity="DDC837Aggregate") - */ + /** @var DDC837Aggregate */ + #[OneToOne(targetEntity: 'DDC837Aggregate')] public $aggregate; } -/** @Entity */ +#[Entity] class DDC837Class2 extends DDC837Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $title; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $description; - /** - * @var string - * @Column(name="text", type="text") - */ + /** @var string */ + #[Column(name: 'text', type: 'text')] public $text; - /** - * @var DDC837Aggregate - * @OneToOne(targetEntity="DDC837Aggregate") - */ + /** @var DDC837Aggregate */ + #[OneToOne(targetEntity: 'DDC837Aggregate')] public $aggregate; } /** * An extra class to demonstrate why title and description aren't in Super - * - * @Entity */ +#[Entity] class DDC837Class3 extends DDC837Super { - /** - * @var string - * @Column(name="title", type="string", length=150) - */ + /** @var string */ + #[Column(name: 'title', type: 'string', length: 150)] public $apples; - /** - * @var string - * @Column(name="content", type="string", length=500) - */ + /** @var string */ + #[Column(name: 'content', type: 'string', length: 500)] public $bananas; } -/** @Entity */ +#[Entity] class DDC837Aggregate { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(name="sysname", type="string", length=255) - */ - protected $sysname; - - public function __construct(string $sysname) - { - $this->sysname = $sysname; + public function __construct( + #[Column(name: 'sysname', type: 'string', length: 255)] + protected string $sysname, + ) { } public function getSysname(): string diff --git a/tests/Tests/ORM/Functional/Ticket/DDC849Test.php b/tests/Tests/ORM/Functional/Ticket/DDC849Test.php index 1ea1a650cfb..87ac54973d6 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC849Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC849Test.php @@ -10,14 +10,11 @@ class DDC849Test extends OrmFunctionalTestCase { - /** @var CmsUser */ - private $user; + private CmsUser|null $user; - /** @var CmsGroup */ - private $group1; + private CmsGroup $group1; - /** @var CmsGroup */ - private $group2; + private CmsGroup $group2; protected function setUp(): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC881Test.php b/tests/Tests/ORM/Functional/Ticket/DDC881Test.php index 41e456c02db..49bacecee14 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC881Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC881Test.php @@ -11,11 +11,11 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinColumns; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\PersistentCollection; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC881Test extends OrmFunctionalTestCase { @@ -26,14 +26,12 @@ protected function setUp(): void $this->createSchemaForModels( DDC881User::class, DDC881PhoneNumber::class, - DDC881PhoneCall::class + DDC881PhoneCall::class, ); } - /** - * @group DDC-117 - * @group DDC-881 - */ + #[Group('DDC-117')] + #[Group('DDC-881')] public function testIssue(): void { /* Create two test users: albert and alfons */ @@ -101,27 +99,19 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class DDC881User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC881PhoneNumber",mappedBy="id") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[Column(type: 'string', length: 255)] + private string|null $name = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC881PhoneNumber', mappedBy: 'id')] private $phoneNumbers; public function getName(): string @@ -135,33 +125,22 @@ public function setName(string $name): void } } -/** @Entity */ +#[Entity] class DDC881PhoneNumber { - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var DDC881User - * @Id - * @ManyToOne(targetEntity="DDC881User",cascade={"all"}) - */ - private $user; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $phonenumber; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC881PhoneCall", mappedBy="phonenumber") - */ + #[Id] + #[Column(type: 'integer')] + private int|null $id = null; + + #[Id] + #[ManyToOne(targetEntity: 'DDC881User', cascade: ['all'])] + private DDC881User|null $user = null; + + #[Column(type: 'string', length: 255)] + private string|null $phonenumber = null; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC881PhoneCall', mappedBy: 'phonenumber')] private $calls; public function __construct() @@ -191,32 +170,21 @@ public function getCalls(): Collection } } -/** @Entity */ +#[Entity] class DDC881PhoneCall { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var DDC881PhoneNumber - * @ManyToOne(targetEntity="DDC881PhoneNumber", inversedBy="calls", cascade={"all"}) - * @JoinColumns({ - * @JoinColumn(name="phonenumber_id", referencedColumnName="id"), - * @JoinColumn(name="user_id", referencedColumnName="user_id") - * }) - */ - private $phonenumber; - - /** - * @var string - * @Column(type="string",nullable=true) - */ - private $callDate; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + #[JoinColumn(name: 'phonenumber_id', referencedColumnName: 'id')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'user_id')] + #[ManyToOne(targetEntity: 'DDC881PhoneNumber', inversedBy: 'calls', cascade: ['all'])] + private DDC881PhoneNumber|null $phonenumber = null; + + #[Column(type: 'string', nullable: true)] + private string $callDate; public function setPhoneNumber(DDC881PhoneNumber $phoneNumber): void { diff --git a/tests/Tests/ORM/Functional/Ticket/DDC933Test.php b/tests/Tests/ORM/Functional/Ticket/DDC933Test.php index 9a73e08462e..416f9c68bf4 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC933Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC933Test.php @@ -5,7 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\DBAL\LockMode; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\TransactionRequiredException; @@ -13,6 +13,7 @@ use Doctrine\Tests\Models\Company\CompanyManager; use Doctrine\Tests\OrmFunctionalTestCase; use Doctrine\Tests\TestUtil; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -25,10 +26,10 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-933 */ + #[Group('DDC-933')] public function testLockCTIClass(): void { - if ($this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('It should not run on in-memory databases'); } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC949Test.php b/tests/Tests/ORM/Functional/Ticket/DDC949Test.php index 634a60575e5..d73d32ff56e 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC949Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC949Test.php @@ -6,6 +6,7 @@ use Doctrine\Tests\Models\Generic\BooleanModel; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC949Test extends OrmFunctionalTestCase { @@ -16,7 +17,7 @@ protected function setUp(): void parent::setUp(); } - /** @group DDC-949 */ + #[Group('DDC-949')] public function testBooleanThroughRepository(): void { $true = new BooleanModel(); diff --git a/tests/Tests/ORM/Functional/Ticket/DDC960Test.php b/tests/Tests/ORM/Functional/Ticket/DDC960Test.php index 014484b6d4f..5bdaa6e801c 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC960Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC960Test.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class DDC960Test extends OrmFunctionalTestCase { @@ -22,7 +23,7 @@ protected function setUp(): void $this->createSchemaForModels(DDC960Root::class, DDC960Child::class); } - /** @group DDC-960 */ + #[Group('DDC-960')] public function testUpdateRootVersion(): void { $child = new DDC960Child('Test'); @@ -37,30 +38,19 @@ public function testUpdateRootVersion(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorMap({ - * "root" = "DDC960Root", - * "child" = "DDC960Child" - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap(['root' => 'DDC960Root', 'child' => 'DDC960Child'])] class DDC960Root { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - /** - * @var int - * @Column(type="integer") - * @Version - */ - private $version; + #[Column(type: 'integer')] + #[Version] + private int $version; public function getId(): int { @@ -73,18 +63,13 @@ public function getVersion(): int } } -/** @Entity */ +#[Entity] class DDC960Child extends DDC960Root { - /** - * @Column(type="string", length=255) - * @var string - */ - private $name; - - public function __construct($name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + private string $name, + ) { } public function setName($name): void diff --git a/tests/Tests/ORM/Functional/Ticket/DDC992Test.php b/tests/Tests/ORM/Functional/Ticket/DDC992Test.php index 709a76f1ff2..9648f278f6e 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC992Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC992Test.php @@ -12,16 +12,16 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function get_class; - -/** @group DDC-992 */ +#[Group('DDC-992')] class DDC992Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -31,7 +31,7 @@ protected function setUp(): void $this->createSchemaForModels( DDC992Role::class, DDC992Parent::class, - DDC992Child::class + DDC992Child::class, ); } @@ -50,7 +50,7 @@ public function testIssue(): void $this->_em->flush(); $this->_em->clear(); - $child = $this->_em->getRepository(get_class($role))->find($child->roleID); + $child = $this->_em->getRepository($role::class)->find($child->roleID); self::assertCount(1, $child->extends); foreach ($child->extends as $parent) { self::assertEquals($role->getRoleID(), $parent->getRoleID()); @@ -69,8 +69,8 @@ public function testOneToManyChild(): void $this->_em->flush(); $this->_em->clear(); - $parentRepository = $this->_em->getRepository(get_class($parent)); - $childRepository = $this->_em->getRepository(get_class($child)); + $parentRepository = $this->_em->getRepository($parent::class); + $childRepository = $this->_em->getRepository($child::class); $parent = $parentRepository->find($parent->id); self::assertCount(1, $parent->childs); @@ -91,35 +91,27 @@ public function testOneToManyChild(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorMap({"child" = "DDC992Child", "parent" = "DDC992Parent"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap(['child' => 'DDC992Child', 'parent' => 'DDC992Parent'])] class DDC992Parent { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var DDC992Parent - * @ManyToOne(targetEntity="DDC992Parent", inversedBy="childs") - */ + /** @var DDC992Parent */ + #[ManyToOne(targetEntity: 'DDC992Parent', inversedBy: 'childs')] public $parent; - /** - * @var Collection - * @OneToMany(targetEntity="DDC992Child", mappedBy="parent") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'DDC992Child', mappedBy: 'parent')] public $childs; } -/** @Entity */ +#[Entity] class DDC992Child extends DDC992Parent { public function childs(): Collection @@ -128,7 +120,7 @@ public function childs(): Collection } } -/** @Entity */ +#[Entity] class DDC992Role { public function getRoleID(): int @@ -136,34 +128,25 @@ public function getRoleID(): int return $this->roleID; } - /** - * @var int - * @Id - * @Column(name="roleID", type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(name: 'roleID', type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $roleID; - /** - * @var string - * @Column(name="name", type="string", length=45) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 45)] public $name; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="DDC992Role", mappedBy="extends") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'DDC992Role', mappedBy: 'extends')] public $extendedBy; - /** - * @phpstan-var Collection - * @ManyToMany (targetEntity="DDC992Role", inversedBy="extendedBy") - * @JoinTable(name="RoleRelations", - * joinColumns={@JoinColumn(name="roleID", referencedColumnName="roleID")}, - * inverseJoinColumns={@JoinColumn(name="extendsRoleID", referencedColumnName="roleID")}, - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'RoleRelations')] + #[JoinColumn(name: 'roleID', referencedColumnName: 'roleID')] + #[InverseJoinColumn(name: 'extendsRoleID', referencedColumnName: 'roleID')] + #[ManyToMany(targetEntity: 'DDC992Role', inversedBy: 'extendedBy')] public $extends; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/GH10049/GH10049Test.php b/tests/Tests/ORM/Functional/Ticket/GH10049/GH10049Test.php index d9658ee9413..7822a71c808 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10049/GH10049Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10049/GH10049Test.php @@ -6,9 +6,6 @@ use Doctrine\Tests\OrmFunctionalTestCase; -/** - * @requires PHP 8.1 - */ class GH10049Test extends OrmFunctionalTestCase { public function setUp(): void @@ -17,13 +14,11 @@ public function setUp(): void $this->createSchemaForModels( ReadOnlyPropertyOwner::class, - ReadOnlyPropertyInheritor::class + ReadOnlyPropertyInheritor::class, ); } - /** - * @doesNotPerformAssertions - */ + /** @doesNotPerformAssertions */ public function testInheritedReadOnlyPropertyValueCanBeSet(): void { $child = new ReadOnlyPropertyInheritor(10049); diff --git a/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyInheritor.php b/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyInheritor.php index 12b468a37f7..f5e65e010be 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyInheritor.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyInheritor.php @@ -6,9 +6,7 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - */ +#[ORM\Entity] class ReadOnlyPropertyInheritor extends ReadOnlyPropertyOwner { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyOwner.php b/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyOwner.php index b9a2b88b9a3..cbf7bc7c992 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyOwner.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10049/ReadOnlyPropertyOwner.php @@ -6,17 +6,13 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] abstract class ReadOnlyPropertyOwner { public function __construct( - /** - * @ORM\Id - * @ORM\Column(type="integer") - */ - public readonly int $id + #[ORM\Id] + #[ORM\Column(type: 'integer')] + public readonly int $id, ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10132Test.php b/tests/Tests/ORM/Functional/Ticket/GH10132Test.php index a46e081a20d..5a00b40e17a 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10132Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10132Test.php @@ -9,9 +9,6 @@ use Doctrine\Tests\Models\GH10132\ComplexChild; use Doctrine\Tests\OrmFunctionalTestCase; -/** - * @requires PHP 8.1 - */ class GH10132Test extends OrmFunctionalTestCase { public function setUp(): void @@ -20,7 +17,7 @@ public function setUp(): void $this->createSchemaForModels( Complex::class, - ComplexChild::class + ComplexChild::class, ); } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10288Test.php b/tests/Tests/ORM/Functional/Ticket/GH10288Test.php index e4154797973..27dc1a63448 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10288Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10288Test.php @@ -17,12 +17,11 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\Tests\Models\GH10288\GH10288People; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * This test makes sure that Discriminator columns can use both custom types using PHP enums as well as * enumType definition of enums. - * - * @requires PHP 8.1 */ class GH10288Test extends OrmFunctionalTestCase { @@ -42,17 +41,16 @@ protected function setUp(): void GH10288EmployeeWithEnumType::class, GH10288PersonCustomEnumType::class, GH10288BossCustomEnumType::class, - GH10288EmployeeCustomEnumType::class + GH10288EmployeeCustomEnumType::class, ); } - /** - * @param class-string $personType - * @phpstan-param GH10288BossWithEnumType|GH10288BossCustomEnumType $boss - * @phpstan-param GH10288EmployeeWithEnumType|GH10288EmployeeCustomEnumType $employee - */ - private function performEnumDiscriminatorTest($boss, $employee, string $personType): void - { + /** @param class-string $personType */ + private function performEnumDiscriminatorTest( + GH10288BossWithEnumType|GH10288BossCustomEnumType $boss, + GH10288EmployeeWithEnumType|GH10288EmployeeCustomEnumType $employee, + string $personType, + ): void { $boss->bossId = 1; $this->_em->persist($boss); @@ -74,14 +72,14 @@ private function performEnumDiscriminatorTest($boss, $employee, string $personTy self::assertEquals($boss, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)); self::assertEquals( GH10288People::BOSS, - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'] + $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'], ); $query->setParameter('name', 'Bob'); self::assertEquals($employee, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)); self::assertEquals( GH10288People::EMPLOYEE, - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'] + $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'], ); $this->_em->clear(); @@ -91,9 +89,7 @@ private function performEnumDiscriminatorTest($boss, $employee, string $personTy self::assertEquals($boss, $bossFetched); } - /** - * @group GH10288 - */ + #[Group('GH10288')] public function testEnumDiscriminatorWithEnumType(): void { $boss = new GH10288BossWithEnumType('John'); @@ -102,9 +98,7 @@ public function testEnumDiscriminatorWithEnumType(): void $this->performEnumDiscriminatorTest($boss, $employee, GH10288PersonWithEnumType::class); } - /** - * @group GH10288 - */ + #[Group('GH10288')] public function testEnumDiscriminatorWithCustomEnumType(): void { $boss = new GH10288BossCustomEnumType('John'); @@ -118,10 +112,7 @@ class GH10288PeopleType extends StringType { public const NAME = 'GH10288PeopleType'; - /** - * {@inheritDoc} - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string { if (! $value instanceof GH10288People) { $value = GH10288People::from($value); @@ -130,92 +121,68 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->value; } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): GH10288People { return GH10288People::from($value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", enumType=GH10288People::class) - * @DiscriminatorMap({ - * "boss" = GH10288BossWithEnumType::class, - * "employee" = GH10288EmployeeWithEnumType::class - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', enumType: GH10288People::class)] +#[DiscriminatorMap([ + 'boss' => GH10288BossWithEnumType::class, + 'employee' => GH10288EmployeeWithEnumType::class, +])] abstract class GH10288PersonWithEnumType { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct(string $name) - { - $this->name = $name; + #[Id] + #[Column] + #[GeneratedValue(strategy: 'AUTO')] + public int|null $id = null; + + public function __construct( + #[Column(length: 255)] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class GH10288BossWithEnumType extends GH10288PersonWithEnumType { - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $bossId; } -/** @Entity */ +#[Entity] class GH10288EmployeeWithEnumType extends GH10288PersonWithEnumType { } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="GH10288PeopleType") - * @DiscriminatorMap({ - * "boss" = GH10288BossCustomEnumType::class, - * "employee" = GH10288EmployeeCustomEnumType::class - * }) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'GH10288PeopleType')] +#[DiscriminatorMap([ + 'boss' => GH10288BossCustomEnumType::class, + 'employee' => GH10288EmployeeCustomEnumType::class, +])] abstract class GH10288PersonCustomEnumType { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; public function __construct(string $name) @@ -224,17 +191,15 @@ public function __construct(string $name) } } -/** @Entity */ +#[Entity] class GH10288BossCustomEnumType extends GH10288PersonCustomEnumType { - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $bossId; } -/** @Entity */ +#[Entity] class GH10288EmployeeCustomEnumType extends GH10288PersonCustomEnumType { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10334Test.php b/tests/Tests/ORM/Functional/Ticket/GH10334Test.php index 6a5d1440332..66aa028e092 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10334Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10334Test.php @@ -11,10 +11,6 @@ use Doctrine\Tests\Models\GH10334\GH10334ProductTypeId; use Doctrine\Tests\OrmFunctionalTestCase; -/** - * @group GH10334Test - * @requires PHP 8.1 - */ class GH10334Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH10336Test.php b/tests/Tests/ORM/Functional/Ticket/GH10336Test.php index 05b3c9556b7..ee020304533 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10336Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10336Test.php @@ -8,9 +8,6 @@ use Doctrine\Tests\Models\GH10336\GH10336Relation; use Doctrine\Tests\OrmFunctionalTestCase; -/** - * @requires PHP 7.4 - */ final class GH10336Test extends OrmFunctionalTestCase { public function setUp(): void @@ -19,7 +16,7 @@ public function setUp(): void $this->createSchemaForModels( GH10336Entity::class, - GH10336Relation::class + GH10336Relation::class, ); } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10348Test.php b/tests/Tests/ORM/Functional/Ticket/GH10348Test.php index 1e4f0c47c94..e571c431eb5 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10348Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10348Test.php @@ -43,54 +43,35 @@ public function testTheORMRemovesReferencedEmployeeBeforeReferencingEmployee(): } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10348Person { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var ?int - */ + /** @var ?int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id = null; - /** - * @ORM\ManyToOne(targetEntity="GH10348Company", inversedBy="employees") - * - * @var ?GH10348Company - */ + /** @var ?GH10348Company */ + #[ORM\ManyToOne(targetEntity: GH10348Company::class, inversedBy: 'employees')] public $employer = null; - /** - * @ORM\ManyToOne(targetEntity="GH10348Person", cascade={"remove"}) - * - * @var ?GH10348Person - */ + /** @var ?GH10348Person */ + #[ORM\ManyToOne(targetEntity: self::class, cascade: ['remove'])] public $mentor = null; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10348Company { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var ?int - */ + /** @var ?int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id = null; - /** - * @ORM\OneToMany(targetEntity="GH10348Person", mappedBy="emplo", cascade={"persist", "remove"}) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'employer', targetEntity: GH10348Person::class, cascade: ['persist', 'remove'])] private $employees; public function __construct() diff --git a/tests/Tests/ORM/Functional/Ticket/GH10387Test.php b/tests/Tests/ORM/Functional/Ticket/GH10387Test.php index 67e10cd3452..ccf802a6b05 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10387Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10387Test.php @@ -8,17 +8,15 @@ use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Tests\OrmTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function array_map; -/** - * @group GH-10387 - */ +#[Group('GH-10387')] class GH10387Test extends OrmTestCase { - /** - * @dataProvider classHierachies - */ + #[DataProvider('classHierachies')] public function testSchemaToolCreatesColumnForFieldInTheMiddleClass(array $classes): void { $em = $this->getTestEntityManager(); @@ -40,134 +38,89 @@ public static function classHierachies(): Generator } } -/** - * @ORM\Entity - * @ORM\Table(name="root") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({ "A": "GH10387EntitiesOnlyRoot", "B": "GH10387EntitiesOnlyMiddle", "C": "GH10387EntitiesOnlyLeaf"}) - */ +#[ORM\Entity] +#[ORM\Table(name: 'root')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorMap(['A' => GH10387EntitiesOnlyRoot::class, 'B' => GH10387EntitiesOnlyMiddle::class, 'C' => GH10387EntitiesOnlyLeaf::class])] class GH10387EntitiesOnlyRoot { - /** - * @ORM\Id - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Id] + #[ORM\Column] private $id; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10387EntitiesOnlyMiddle extends GH10387EntitiesOnlyRoot { - /** - * @ORM\Column(name="middle_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'middle_class_field')] private $parentValue; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10387EntitiesOnlyLeaf extends GH10387EntitiesOnlyMiddle { - /** - * @ORM\Column(name="leaf_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'leaf_class_field')] private $childValue; } -/** - * @ORM\Entity - * @ORM\Table(name="root") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({ "A": "GH10387MappedSuperclassRoot", "B": "GH10387MappedSuperclassLeaf"}) - * ^- This DiscriminatorMap contains the Entity classes only, not the Mapped Superclass - */ +/** ↓ This DiscriminatorMap contains the Entity classes only, not the Mapped Superclass */ +#[ORM\DiscriminatorMap(['A' => GH10387MappedSuperclassRoot::class, 'B' => GH10387MappedSuperclassLeaf::class])] +#[ORM\Entity] +#[ORM\Table(name: 'root')] +#[ORM\InheritanceType('SINGLE_TABLE')] class GH10387MappedSuperclassRoot { - /** - * @ORM\Id - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Id] + #[ORM\Column] private $id; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH10387MappedSuperclassMiddle extends GH10387MappedSuperclassRoot { - /** - * @ORM\Column(name="middle_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'middle_class_field')] private $parentValue; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10387MappedSuperclassLeaf extends GH10387MappedSuperclassMiddle { - /** - * @ORM\Column(name="leaf_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'leaf_class_field')] private $childValue; } -/** - * @ORM\Entity - * @ORM\Table(name="root") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({ "A": "GH10387AbstractEntitiesLeaf"}) - * ^- This DiscriminatorMap contains the single non-abstract Entity class only - */ +/** ↓ This DiscriminatorMap contains the single non-abstract Entity class only */ +#[ORM\DiscriminatorMap(['A' => GH10387AbstractEntitiesLeaf::class])] +#[ORM\Entity] +#[ORM\Table(name: 'root')] +#[ORM\InheritanceType('SINGLE_TABLE')] abstract class GH10387AbstractEntitiesRoot { - /** - * @ORM\Id - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Id] + #[ORM\Column] private $id; } -/** - * @ORM\Entity - */ +#[ORM\Entity] abstract class GH10387AbstractEntitiesMiddle extends GH10387AbstractEntitiesRoot { - /** - * @ORM\Column(name="middle_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'middle_class_field')] private $parentValue; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10387AbstractEntitiesLeaf extends GH10387AbstractEntitiesMiddle { - /** - * @ORM\Column(name="leaf_class_field") - * - * @var string - */ + /** @var string */ + #[ORM\Column(name: 'leaf_class_field')] private $childValue; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10450Test.php b/tests/Tests/ORM/Functional/Ticket/GH10450Test.php index afe75d3bcdf..79001b6dfe6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10450Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10450Test.php @@ -8,14 +8,12 @@ use Doctrine\ORM\Mapping\MappingException; use Doctrine\Tests\OrmTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; class GH10450Test extends OrmTestCase { - /** - * @param class-string $className - * - * @dataProvider classesThatOverrideFieldNames - */ + /** @param class-string $className */ + #[DataProvider('classesThatOverrideFieldNames')] public function testDuplicatePrivateFieldsShallBeRejected(string $className): void { $em = $this->getTestEntityManager(); @@ -25,7 +23,7 @@ public function testDuplicatePrivateFieldsShallBeRejected(string $className): vo $em->getClassMetadata($className); } - public function classesThatOverrideFieldNames(): Generator + public static function classesThatOverrideFieldNames(): Generator { yield 'Entity class that redeclares a private field inherited from a base entity' => [GH10450EntityChildPrivate::class]; yield 'Entity class that redeclares a private field inherited from a mapped superclass' => [GH10450MappedSuperclassChildPrivate::class]; @@ -43,183 +41,107 @@ public function testFieldsOfTransientClassesAreNotConsideredDuplicate(): void } } -/** - * @ORM\Entity - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorMap({ "base": "GH10450BaseEntityPrivate", "child": "GH10450EntityChildPrivate" }) - * @ORM\DiscriminatorColumn(name="type") - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorMap(['base' => GH10450BaseEntityPrivate::class, 'child' => GH10450EntityChildPrivate::class])] +#[ORM\DiscriminatorColumn(name: 'type')] class GH10450BaseEntityPrivate { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - private $id; - - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - private $field; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + private int $id; + + #[ORM\Column(type: 'text', name: 'base')] + private string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10450EntityChildPrivate extends GH10450BaseEntityPrivate { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - private $field; + #[ORM\Column(type: 'text', name: 'child')] + private string $field; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH10450BaseMappedSuperclassPrivate { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - private $id; - - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - private $field; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + private int $id; + + #[ORM\Column(type: 'text', name: 'base')] + private string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10450MappedSuperclassChildPrivate extends GH10450BaseMappedSuperclassPrivate { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - private $field; + #[ORM\Column(type: 'text', name: 'child')] + private string $field; } -/** - * @ORM\Entity - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorMap({ "base": "GH10450BaseEntityProtected", "child": "GH10450EntityChildProtected" }) - * @ORM\DiscriminatorColumn(name="type") - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorMap(['base' => GH10450BaseEntityProtected::class, 'child' => GH10450EntityChildProtected::class])] +#[ORM\DiscriminatorColumn(name: 'type')] class GH10450BaseEntityProtected { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - protected $id; - - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + protected int $id; + + #[ORM\Column(type: 'text', name: 'base')] + protected string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10450EntityChildProtected extends GH10450BaseEntityProtected { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'child')] + protected string $field; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH10450BaseMappedSuperclassProtected { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - protected $id; - - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + protected int $id; + + #[ORM\Column(type: 'text', name: 'base')] + protected string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10450MappedSuperclassChildProtected extends GH10450BaseMappedSuperclassProtected { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'child')] + protected string $field; } abstract class GH10450AbstractEntity { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - protected $id; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + protected int $id; } -/** - * @ORM\Entity - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({ "cat": "GH10450Cat" }) - * @ORM\DiscriminatorColumn(name="type") - */ +#[ORM\Entity] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorMap(['cat' => GH10450Cat::class])] +#[ORM\DiscriminatorColumn(name: 'type')] abstract class GH10450Animal extends GH10450AbstractEntity { - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - private $field; + #[ORM\Column(type: 'text', name: 'base')] + private string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10450Cat extends GH10450Animal { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10454Test.php b/tests/Tests/ORM/Functional/Ticket/GH10454Test.php index 18b6553e0ad..73e044434d1 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10454Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10454Test.php @@ -8,14 +8,12 @@ use Doctrine\ORM\Mapping\MappingException; use Doctrine\Tests\OrmTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; class GH10454Test extends OrmTestCase { - /** - * @param class-string $className - * - * @dataProvider classesThatOverrideFieldNames - */ + /** @param class-string $className */ + #[DataProvider('classesThatOverrideFieldNames')] public function testProtectedPropertyMustNotBeInheritedAndReconfigured(string $className): void { $em = $this->getTestEntityManager(); @@ -26,82 +24,50 @@ public function testProtectedPropertyMustNotBeInheritedAndReconfigured(string $c $em->getClassMetadata($className); } - public function classesThatOverrideFieldNames(): Generator + public static function classesThatOverrideFieldNames(): Generator { yield 'Entity class that redeclares a protected field inherited from a base entity' => [GH10454EntityChildProtected::class]; yield 'Entity class that redeclares a protected field inherited from a mapped superclass' => [GH10454MappedSuperclassChildProtected::class]; } } -/** - * @ORM\Entity - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorMap({ "base": "GH10454BaseEntityProtected", "child": "GH10454EntityChildProtected" }) - * @ORM\DiscriminatorColumn(name="type") - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorMap(['base' => GH10454BaseEntityProtected::class, 'child' => GH10454EntityChildProtected::class])] +#[ORM\DiscriminatorColumn(name: 'type')] class GH10454BaseEntityProtected { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - protected $id; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + protected int $id; - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'base')] + protected string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10454EntityChildProtected extends GH10454BaseEntityProtected { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'child')] + protected string $field; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH10454BaseMappedSuperclassProtected { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ - protected $id; + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] + protected int $id; - /** - * @ORM\Column(type="text", name="base") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'base')] + protected string $field; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10454MappedSuperclassChildProtected extends GH10454BaseMappedSuperclassProtected { - /** - * @ORM\Column(type="text", name="child") - * - * @var string - */ - protected $field; + #[ORM\Column(type: 'text', name: 'child')] + protected string $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10462Test.php b/tests/Tests/ORM/Functional/Ticket/GH10462Test.php index cd582188808..09820b12c2f 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10462Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10462Test.php @@ -38,27 +38,21 @@ public function testCharsetAndCollationOptionsOnDiscriminatedColumn(): void } } -/** - * @Entity - * @Table(name="gh10462_person") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string", options={"charset"="ascii", "collation"="ascii_general_ci"}) - * @DiscriminatorMap({"person"=GH10462Person::class, "employee"=GH10462Employee::class}) - */ +#[Entity] +#[Table(name: 'gh10462_person')] +#[InheritanceType(value: 'SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])] +#[DiscriminatorMap(['person' => GH10462Person::class, 'employee' => GH10462Employee::class])] class GH10462Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** - * @Entity - */ +#[Entity] class GH10462Employee extends GH10462Person { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10473Test.php b/tests/Tests/ORM/Functional/Ticket/GH10473Test.php index a08805f066a..550080d61f6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10473Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10473Test.php @@ -6,7 +6,6 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Tools\ResolveTargetEntityListener; use Doctrine\Tests\OrmTestCase; @@ -21,7 +20,7 @@ public function testMappedSuperclassAssociationsCanBeResolvedToEntities(): void $resolveTargetEntity->addResolveTargetEntity( GH10473BaseUser::class, GH10473UserImplementation::class, - [] + [], ); $em->getEventManager()->addEventSubscriber($resolveTargetEntity); @@ -32,25 +31,25 @@ public function testMappedSuperclassAssociationsCanBeResolvedToEntities(): void self::assertTrue($userMetadata->isInheritanceTypeNone()); $socialMediaAccountsMapping = $userMetadata->getAssociationMapping('socialMediaAccounts'); - self::assertArrayNotHasKey('inherited', $socialMediaAccountsMapping); - self::assertTrue((bool) ($socialMediaAccountsMapping['type'] & ClassMetadata::TO_MANY)); - self::assertFalse($socialMediaAccountsMapping['isOwningSide']); - self::assertSame(GH10473SocialMediaAccount::class, $socialMediaAccountsMapping['targetEntity']); - self::assertSame('user', $socialMediaAccountsMapping['mappedBy']); + self::assertNull($socialMediaAccountsMapping->inherited); + self::assertTrue($socialMediaAccountsMapping->isToMany()); + self::assertFalse($socialMediaAccountsMapping->isOwningSide()); + self::assertSame(GH10473SocialMediaAccount::class, $socialMediaAccountsMapping->targetEntity); + self::assertSame('user', $socialMediaAccountsMapping->mappedBy); $createdByMapping = $userMetadata->getAssociationMapping('createdBy'); - self::assertArrayNotHasKey('inherited', $createdByMapping); - self::assertTrue((bool) ($createdByMapping['type'] & ClassMetadata::TO_ONE)); - self::assertTrue($createdByMapping['isOwningSide']); - self::assertSame(GH10473UserImplementation::class, $createdByMapping['targetEntity']); - self::assertSame('createdUsers', $createdByMapping['inversedBy']); + self::assertNull($createdByMapping->inherited); + self::assertTrue($createdByMapping->isToOne()); + self::assertTrue($createdByMapping->isOwningSide()); + self::assertSame(GH10473UserImplementation::class, $createdByMapping->targetEntity); + self::assertSame('createdUsers', $createdByMapping->inversedBy); $createdUsersMapping = $userMetadata->getAssociationMapping('createdUsers'); - self::assertArrayNotHasKey('inherited', $createdUsersMapping); - self::assertTrue((bool) ($createdUsersMapping['type'] & ClassMetadata::TO_MANY)); - self::assertFalse($createdUsersMapping['isOwningSide']); - self::assertSame(GH10473UserImplementation::class, $createdUsersMapping['targetEntity']); - self::assertSame('createdBy', $createdUsersMapping['mappedBy']); + self::assertNull($createdUsersMapping->inherited); + self::assertTrue($createdUsersMapping->isToMany()); + self::assertFalse($createdUsersMapping->isOwningSide()); + self::assertSame(GH10473UserImplementation::class, $createdUsersMapping->targetEntity); + self::assertSame('createdBy', $createdUsersMapping->mappedBy); $socialMediaAccountMetadata = $em->getClassMetadata(GH10473SocialMediaAccount::class); @@ -58,73 +57,49 @@ public function testMappedSuperclassAssociationsCanBeResolvedToEntities(): void self::assertTrue($socialMediaAccountMetadata->isInheritanceTypeNone()); $userMapping = $socialMediaAccountMetadata->getAssociationMapping('user'); - self::assertArrayNotHasKey('inherited', $userMapping); - self::assertTrue((bool) ($userMapping['type'] & ClassMetadata::TO_ONE)); - self::assertTrue($userMapping['isOwningSide']); - self::assertSame(GH10473UserImplementation::class, $userMapping['targetEntity']); - self::assertSame('socialMediaAccounts', $userMapping['inversedBy']); + self::assertNull($userMapping->inherited); + self::assertTrue($userMapping->isToOne()); + self::assertTrue($userMapping->isOwningSide()); + self::assertSame(GH10473UserImplementation::class, $userMapping->targetEntity); + self::assertSame('socialMediaAccounts', $userMapping->inversedBy); } } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] abstract class GH10473BaseUser { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] private $id; - /** - * @ORM\OneToMany(targetEntity="GH10473SocialMediaAccount", mappedBy="user") - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(targetEntity: GH10473SocialMediaAccount::class, mappedBy: 'user')] private $socialMediaAccounts; - /** - * @ORM\ManyToOne(targetEntity="GH10473BaseUser", inversedBy="createdUsers") - * - * @var GH10473BaseUser - */ + /** @var GH10473BaseUser */ + #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'createdUsers')] private $createdBy; - /** - * @ORM\OneToMany(targetEntity="GH10473BaseUser", mappedBy="createdBy") - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'createdBy')] private $createdUsers; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10473SocialMediaAccount { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] private $id; - /** - * @ORM\ManyToOne(targetEntity="GH10473BaseUser", inversedBy="socialMediaAccounts") - * - * @var GH10473BaseUser - */ + /** @var GH10473BaseUser */ + #[ORM\ManyToOne(targetEntity: GH10473BaseUser::class, inversedBy: 'socialMediaAccounts')] private $user; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10473UserImplementation extends GH10473BaseUser { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10531Test.php b/tests/Tests/ORM/Functional/Ticket/GH10531Test.php index 2b0f4fa5c19..0f57e7c7838 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10531Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10531Test.php @@ -15,7 +15,7 @@ protected function setUp(): void $this->createSchemaForModels( GH10531A::class, - GH10531B::class + GH10531B::class, ); } @@ -113,39 +113,30 @@ public function testDeletes(): void } /** - * @ORM\Entity - * @ORM\Table(name="gh10531_a") - * @ORM\DiscriminatorColumn(name="discr", type="string") - * @ORM\DiscriminatorMap({ "A": "GH10531A", "B": "GH10531B" }) - * @ORM\InheritanceType("JOINED") - * * We are using JTI here, since STI would relax the not-nullable constraint for the "parent" * column (it has to be NULL when the row contains a GH10531A instance). Causes another error, * but not the constraint violation I'd like to point out. */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10531_a')] +#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')] +#[ORM\DiscriminatorMap(['A' => GH10531A::class, 'B' => GH10531B::class])] +#[ORM\InheritanceType('JOINED')] class GH10531A { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] public $id; } -/** - * @ORM\Entity - * @ORM\Table(name="gh10531_b") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10531_b')] class GH10531B extends GH10531A { - /** - * @ORM\ManyToOne(targetEntity="GH10531A") - * @ORM\JoinColumn(nullable=false, name="parent_id") - * - * @var GH10531A - */ + /** @var GH10531A */ + #[ORM\ManyToOne(targetEntity: GH10531A::class)] + #[ORM\JoinColumn(nullable: false, name: 'parent_id')] public $parent; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10532Test.php b/tests/Tests/ORM/Functional/Ticket/GH10532Test.php index b3762b068e4..a547fb3e436 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10532Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10532Test.php @@ -17,7 +17,7 @@ protected function setUp(): void GH10532A::class, GH10532B::class, GH10532C::class, - GH10532X::class + GH10532X::class, ); } @@ -117,70 +117,51 @@ public function testDeletes(): void } /** - * @ORM\Entity - * @ORM\Table(name="gh10532_x") - * @ORM\DiscriminatorColumn(name="discr", type="string") - * @ORM\DiscriminatorMap({ "B": "GH10532B", "C": "GH10532C" }) - * @ORM\InheritanceType("JOINED") - * * We are using JTI here, since STI would relax the not-nullable constraint for the "parent" * column. Causes another error, but not the constraint violation I'd like to point out. */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10532_x')] +#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')] +#[ORM\DiscriminatorMap(['B' => GH10532B::class, 'C' => GH10532C::class])] +#[ORM\InheritanceType('JOINED')] abstract class GH10532X { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] public $id; } -/** - * @ORM\Entity - * @ORM\Table(name="gh10532_b") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10532_b')] class GH10532B extends GH10532X { - /** - * @ORM\ManyToOne(targetEntity="GH10532A") - * @ORM\JoinColumn(nullable=false, name="gh10532a_id") - * - * @var GH10532A - */ + /** @var GH10532A */ + #[ORM\ManyToOne(targetEntity: GH10532A::class)] + #[ORM\JoinColumn(nullable: false, name: 'gh10532a_id')] public $a; } -/** - * @ORM\Entity - * @ORM\Table(name="gh10532_c") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10532_c')] class GH10532C extends GH10532X { } -/** - * @ORM\Entity - * @ORM\Table(name="gh10532_a") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10532_a')] class GH10532A { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH10532X") - * @ORM\JoinColumn(nullable=false, name="gh10532x_id") - * - * @var GH10532X - */ + /** @var GH10532X */ + #[ORM\ManyToOne(targetEntity: GH10532X::class)] + #[ORM\JoinColumn(nullable: false, name: 'gh10532x_id')] public $x; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10566Test.php b/tests/Tests/ORM/Functional/Ticket/GH10566Test.php index 91b6174a560..565ba91ea82 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10566Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10566Test.php @@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; use function is_a; @@ -19,13 +20,11 @@ protected function setUp(): void $this->createSchemaForModels( GH10566A::class, GH10566B::class, - GH10566C::class + GH10566C::class, ); } - /** - * @dataProvider provideEntityClasses - */ + #[DataProvider('provideEntityClasses')] public function testInsertion(string $startEntityClass): void { $a = new GH10566A(); @@ -51,9 +50,7 @@ public function testInsertion(string $startEntityClass): void self::assertNotNull($c->id); } - /** - * @dataProvider provideEntityClasses - */ + #[DataProvider('provideEntityClasses')] public function testRemoval(string $startEntityClass): void { $a = new GH10566A(); @@ -96,7 +93,8 @@ public function testRemoval(string $startEntityClass): void self::assertFalse($this->_em->getConnection()->fetchOne('SELECT id FROM gh10566_c WHERE id = ?', [$cId])); } - public function provideEntityClasses(): Generator + /** @return Generator */ + public static function provideEntityClasses(): Generator { yield [GH10566A::class]; yield [GH10566B::class]; @@ -104,74 +102,50 @@ public function provideEntityClasses(): Generator } } -/** - * @ORM\Entity - * @ORM\Table(name="gh10566_a") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10566_a')] class GH10566A { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToOne(targetEntity="GH10566B", cascade={"all"}) - * @ORM\JoinColumn(nullable=true, onDelete="SET NULL") - * - * @var GH10566B - */ + /** @var GH10566B */ + #[ORM\OneToOne(targetEntity: GH10566B::class, cascade: ['all'])] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] public $other; } -/** - * @ORM\Entity - * @ORM\Table(name="gh10566_b") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10566_b')] class GH10566B { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToOne(targetEntity="GH10566C", cascade={"all"}) - * @ORM\JoinColumn(nullable=true) - * - * @var GH10566C - */ + /** @var GH10566C */ + #[ORM\OneToOne(targetEntity: GH10566C::class, cascade: ['all'])] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] public $other; } -/** - * @ORM\Entity - * @ORM\Table(name="gh10566_c") - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh10566_c')] class GH10566C { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue() - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToOne(targetEntity="GH10566A", cascade={"all"}) - * @ORM\JoinColumn(nullable=true) - * - * @var GH10566A - */ + /** @var GH10566A */ + #[ORM\OneToOne(targetEntity: GH10566A::class, cascade: ['all'])] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] public $other; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10625Test.php b/tests/Tests/ORM/Functional/Ticket/GH10625Test.php index 13f3a544b48..a3c141eb465 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10625Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10625Test.php @@ -6,10 +6,10 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH-10625 - */ +#[Group('GH-10625')] class GH10625Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -19,13 +19,11 @@ protected function setUp(): void $this->createSchemaForModels( GH10625Root::class, GH10625Middle::class, - GH10625Leaf::class + GH10625Leaf::class, ); } - /** - * @dataProvider queryClasses - */ + #[DataProvider('queryClasses')] public function testLoadFieldsFromAllClassesInHierarchy(string $queryClass): void { $entity = new GH10625Leaf(); @@ -50,34 +48,23 @@ public static function queryClasses(): array } } -/** - * @ORM\Entity - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({ "1": "GH10625Leaf"}) - * ^- This DiscriminatorMap contains the single non-abstract Entity class only - */ +#[ORM\Entity] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorMap([1 => 'GH10625Leaf'])] // <- This DiscriminatorMap contains the single non-abstract Entity class only abstract class GH10625Root { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] + public int $id; } -/** - * @ORM\Entity - */ +#[ORM\Entity] abstract class GH10625Middle extends GH10625Root { } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10625Leaf extends GH10625Middle { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php b/tests/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php index 04576aa2619..b3274569692 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php @@ -8,9 +8,6 @@ use Doctrine\ORM\Tools\SchemaValidator; use Doctrine\Tests\OrmTestCase; -/** - * @requires PHP >= 7.4 - */ final class GH10661Test extends OrmTestCase { /** @var EntityManagerInterface */ @@ -28,7 +25,7 @@ public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void self::assertSame( ["The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type."], - $ce + $ce, ); } @@ -51,7 +48,7 @@ public function testMetadataFieldTypeNotCoherentWithEntityPropertyTypeWithInheri "The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidChildEntity#property2' has the property type 'int' that differs from the metadata field type 'string' returned by the 'string' DBAL type.", "The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidChildEntity#anotherProperty' has the property type 'string' that differs from the metadata field type 'bool' returned by the 'boolean' DBAL type.", ], - $ce + $ce, ); } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidChildEntity.php b/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidChildEntity.php index 123b8b0f436..83edeab6f44 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidChildEntity.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidChildEntity.php @@ -7,14 +7,12 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; -/** @Entity */ +#[Entity] class InvalidChildEntity extends InvalidEntity { - /** @Column(type="string") */ + #[Column(type: 'string')] protected int $property2; - /** - * @Column(type="boolean") - */ + #[Column(type: 'boolean')] private string $anotherProperty; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidEntity.php b/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidEntity.php index 7a93bab465b..c1b74056f89 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidEntity.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10661/InvalidEntity.php @@ -5,21 +5,23 @@ namespace Doctrine\Tests\ORM\Functional\Ticket\GH10661; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\DiscriminatorColumn; +use Doctrine\ORM\Mapping\DiscriminatorMap; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InheritanceType; -/** @Entity */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string', length: 255)] +#[DiscriminatorMap(['root' => 'InvalidEntity', 'child' => 'InvalidChildEntity'])] class InvalidEntity { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $key; - /** - * @Column(type="decimal") - */ + #[Column(type: 'decimal')] protected float $property1; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10747Test.php b/tests/Tests/ORM/Functional/Ticket/GH10747Test.php index 198341af789..90caec353bd 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10747Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10747Test.php @@ -15,15 +15,14 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\CustomIdObject; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function method_exists; use function str_replace; /** * Functional tests for asserting that orphaned children in a OneToMany relationship get removed with a custom identifier - * - * @group GH10747 */ +#[Group('GH10747')] final class GH10747Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -40,17 +39,17 @@ protected function setUp(): void public function testOrphanedOneToManyDeletesCollection(): void { $object = new GH10747Article( - new CustomIdObject('article') + new CustomIdObject('article'), ); $creditOne = new GH10747Credit( $object, - 'credit1' + 'credit1', ); $creditTwo = new GH10747Credit( $object, - 'credit2' + 'credit2', ); $object->setCredits(new ArrayCollection([$creditOne, $creditTwo])); @@ -66,7 +65,7 @@ public function testOrphanedOneToManyDeletesCollection(): void $creditThree = new GH10747Credit( $object2, - 'credit3' + 'credit3', ); $object2->setCredits(new ArrayCollection([$creditThree])); @@ -85,25 +84,17 @@ public function testOrphanedOneToManyDeletesCollection(): void } } -/** - * @Entity - * @Table - */ +#[Entity] +#[Table] class GH10747Article { - /** - * @Id - * @Column(type="Doctrine\Tests\ORM\Functional\Ticket\GH10747CustomIdObjectHashType") - * @var CustomIdObject - */ - public $id; - - /** - * @ORM\OneToMany(targetEntity="GH10747Credit", mappedBy="article", orphanRemoval=true) - * - * @var Collection - */ - public $credits; + #[Id] + #[Column(type: GH10747CustomIdObjectHashType::class, length: 24)] // strlen(PHP_INT_MAX . '_test') + public CustomIdObject $id; + + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'article', targetEntity: GH10747Credit::class, orphanRemoval: true)] + public Collection $credits; public function __construct(CustomIdObject $id) { @@ -123,72 +114,42 @@ public function getCredits(): Collection } } -/** - * @Entity - * @Table - */ +#[Entity] +#[Table] class GH10747Credit { - /** - * @ORM\Column(type="integer") - * @ORM\GeneratedValue() - * - * @Id() - * @var int|null - */ - public $id = null; - - /** @var string */ - public $name; - - /** - * @ORM\ManyToOne(targetEntity="GH10747Article", inversedBy="credits") - * - * @var GH10747Article - */ - public $article; - - public function __construct(GH10747Article $article, string $name) + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + #[Id] + public int|null $id = null; + + #[ORM\ManyToOne(inversedBy: 'credits', targetEntity: GH10747Article::class)] + public GH10747Article $article; + + public function __construct(GH10747Article $article, public string $name) { $this->article = $article; - $this->name = $name; } } class GH10747CustomIdObjectHashType extends DBALType { - /** - * {@inheritDoc} - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string { return $value->id . '_test'; } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): CustomIdObject { return new CustomIdObject(str_replace('_test', '', $value)); } - /** - * {@inheritDoc} - */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::class; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10752Test.php b/tests/Tests/ORM/Functional/Ticket/GH10752Test.php index afeefa4121b..7af5d7e97f7 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10752Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10752Test.php @@ -9,10 +9,9 @@ use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH10752 - */ +#[Group('GH10752')] class GH10752Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,10 +26,6 @@ protected function setUp(): void public function testThrowExceptionWhenRemovingPromotionThatIsInUse(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $order = new GH10752Order(); $promotion = new GH10752Promotion(); @@ -48,10 +43,6 @@ public function testThrowExceptionWhenRemovingPromotionThatIsInUse(): void public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotInMemory(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $order = new GH10752Order(); $promotion = new GH10752Promotion(); @@ -71,30 +62,19 @@ public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotI } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10752Order { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ - private $id = null; - - /** - * @ORM\ManyToMany(targetEntity="GH10752Promotion", cascade={"persist"}) - * @ORM\JoinTable(name="order_promotion", - * joinColumns={@ORM\JoinColumn(name="order_id", referencedColumnName="id", onDelete="CASCADE")}, - * inverseJoinColumns={@ORM\JoinColumn(name="promotion_id", referencedColumnName="id")} - * ) - * - * @var Collection - */ - private $promotions; + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + private int|null $id = null; + + #[ORM\ManyToMany(targetEntity: GH10752Promotion::class, cascade: ['persist'])] + #[ORM\JoinTable(name: 'order_promotion')] + #[ORM\JoinColumn(name: 'order_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[ORM\InverseJoinColumn(name: 'promotion_id', referencedColumnName: 'id')] + private Collection $promotions; public function __construct() { @@ -109,17 +89,11 @@ public function addPromotion(GH10752Promotion $promotion): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10752Promotion { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ - public $id = null; + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public int|null $id = null; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10808Test.php b/tests/Tests/ORM/Functional/Ticket/GH10808Test.php index 78966fe8073..0e893233442 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10808Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10808Test.php @@ -13,10 +13,11 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function get_class; -/** @group GH10808 */ +#[Group('GH10808')] class GH10808Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -25,7 +26,7 @@ protected function setUp(): void $this->createSchemaForModels( GH10808Appointment::class, - GH10808AppointmentChild::class + GH10808AppointmentChild::class, ); } @@ -39,7 +40,7 @@ public function testDQLDeferredEagerLoad(): void $query = $this->_em->createQuery( 'SELECT appointment from Doctrine\Tests\ORM\Functional\Ticket\GH10808Appointment appointment - JOIN appointment.child appointment_child' + JOIN appointment.child appointment_child', ); // By default, UnitOfWork::HINT_DEFEREAGERLOAD is set to 'true' @@ -53,35 +54,29 @@ public function testDQLDeferredEagerLoad(): void self::assertNotEquals( GH10808AppointmentChild::class, get_class($deferredLoadResult->child), - '$deferredLoadResult->child should be a proxy' + '$deferredLoadResult->child should be a proxy', ); self::assertEquals( GH10808AppointmentChild::class, get_class($eagerLoadResult->child), - '$eagerLoadResult->child should not be a proxy' + '$eagerLoadResult->child should not be a proxy', ); } } -/** - * @Entity - * @Table(name="gh10808_appointment") - */ +#[Entity] +#[Table(name: 'gh10808_appointment')] class GH10808Appointment { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH10808AppointmentChild - * @OneToOne(targetEntity="GH10808AppointmentChild", cascade={"persist", "remove"}, fetch="EAGER") - * @JoinColumn(name="child_id", referencedColumnName="id") - */ + /** @var GH10808AppointmentChild */ + #[OneToOne(targetEntity: GH10808AppointmentChild::class, cascade: ['persist', 'remove'], fetch: 'EAGER')] + #[JoinColumn(name: 'child_id', referencedColumnName: 'id')] public $child; public function __construct() @@ -90,17 +85,13 @@ public function __construct() } } -/** - * @Entity - * @Table(name="gh10808_appointment_child") - */ +#[Entity] +#[Table(name: 'gh10808_appointment_child')] class GH10808AppointmentChild { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] private $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10869Test.php b/tests/Tests/ORM/Functional/Ticket/GH10869Test.php index 937784df3d3..b3f92749fe7 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10869Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10869Test.php @@ -53,24 +53,16 @@ public function postPersist(PostPersistEventArgs $args): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10869Entity { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var ?int - */ + /** @var ?int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\Column(type="text", nullable=true) - * - * @var ?string - */ + /** @var ?string */ + #[ORM\Column(type: 'text', nullable: true)] public $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10880Test.php b/tests/Tests/ORM/Functional/Ticket/GH10880Test.php index dd299fdfdd6..57c6bc369dc 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10880Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10880Test.php @@ -61,59 +61,43 @@ private function removeTransactionCommandsFromQueryLog(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10880ProcessOwner { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; /** * fetch=EAGER is important to reach the part of \Doctrine\ORM\UnitOfWork::createEntity() * that is important for this regression test * - * @ORM\ManyToOne(targetEntity="GH10880Process", fetch="EAGER") - * * @var GH10880Process */ + #[ORM\ManyToOne(targetEntity: GH10880Process::class, fetch: 'EAGER')] public $process; } -/** - * @ORM\Entity() - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"process" = "GH10880Process"}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap(['process' => GH10880Process::class])] abstract class GH10880BaseProcess { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\Column(type="text") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'text')] public $description; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10880Process extends GH10880BaseProcess { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10889Test.php b/tests/Tests/ORM/Functional/Ticket/GH10889Test.php index 451fc887d20..fe7d6e8c53c 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10889Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10889Test.php @@ -6,12 +6,10 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @see https://github.com/doctrine/orm/issues/10889 - * - * @group GH10889 - */ +/** @see https://github.com/doctrine/orm/issues/10889 */ +#[Group('GH10889')] class GH10889Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -21,7 +19,7 @@ protected function setUp(): void $this->createSchemaForModels( GH10889Person::class, GH10889Company::class, - GH10889Resume::class + GH10889Resume::class, ); } @@ -49,59 +47,33 @@ public function testIssue(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10889Person { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + public int|null $id = null; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10889Company { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + public int|null $id = null; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH10889Resume { - /** - * @ORM\Id - * @ORM\OneToOne(targetEntity="GH10889Person") - * - * @var GH10889Person - */ - public $person; - - /** - * @ORM\ManyToOne(targetEntity="GH10889Company") - * - * @var GH10889Company|null - */ - public $currentCompany; - - public function __construct(GH10889Person $person, ?GH10889Company $currentCompany) - { - $this->person = $person; - $this->currentCompany = $currentCompany; + public function __construct( + #[ORM\Id] + #[ORM\OneToOne] + public GH10889Person $person, + #[ORM\ManyToOne] + public GH10889Company|null $currentCompany, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10912Test.php b/tests/Tests/ORM/Functional/Ticket/GH10912Test.php index b81f23f51ee..308cbf4f5cb 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10912Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10912Test.php @@ -89,32 +89,22 @@ public function testIssue(): void } } -/** @ORM\Entity */ +#[ORM\Entity] class GH10912User { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToMany(targetEntity=GH10912Room::class, mappedBy="user") - * - * @var Collection - */ - public $rooms; - - /** - * @ORM\OneToOne(targetEntity=GH10912Profile::class) - * @ORM\JoinColumn(onDelete="cascade") - * - * @var GH10912Profile - */ - public $profile; + /** @var Collection */ + #[ORM\OneToMany(targetEntity: GH10912Room::class, mappedBy: 'user')] + public Collection $rooms; + + #[ORM\OneToOne(targetEntity: GH10912Profile::class)] + #[ORM\JoinColumn(onDelete: 'cascade')] + public GH10912Profile $profile; public function __construct() { @@ -122,44 +112,29 @@ public function __construct() } } -/** @ORM\Entity */ +#[ORM\Entity] class GH10912Profile { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToOne(targetEntity=GH10912User::class) - * @ORM\JoinColumn(onDelete="cascade") - * - * @var GH10912User - */ - public $user; + #[ORM\OneToOne(targetEntity: GH10912User::class)] + #[ORM\JoinColumn(onDelete: 'cascade')] + public GH10912User $user; } -/** @ORM\Entity */ +#[ORM\Entity] class GH10912Room { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; - - /** - * @ORM\ManyToOne(targetEntity=GH10912User::class, inversedBy="rooms") - * @ORM\JoinColumn(nullable=false) - * - * @var GH10912User - */ - public $user; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int $id; + + #[ORM\ManyToOne(targetEntity: GH10912User::class, inversedBy: 'rooms')] + #[ORM\JoinColumn(nullable: false)] + public GH10912User $user; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10913Test.php b/tests/Tests/ORM/Functional/Ticket/GH10913Test.php index c75f7d9da7a..f220bf4e4ee 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10913Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10913Test.php @@ -122,9 +122,7 @@ private function flushAndAssertNumberOfDeleteQueries(int $expectedCount): void self::assertCount($expectedCount, $queries); } - /** - * @return list - */ + /** @return list */ private function createEntities(int $count = 1): array { $result = []; @@ -137,31 +135,19 @@ private function createEntities(int $count = 1): array } } -/** @ORM\Entity */ +#[ORM\Entity] class GH10913Entity { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; - - /** - * @ORM\ManyToOne(targetEntity=GH10913Entity::class) - * @ORM\JoinColumn(nullable=true, onDelete="CASCADE") - * - * @var GH10913Entity - */ - public $odc; - - /** - * @ORM\ManyToOne(targetEntity=GH10913Entity::class) - * @ORM\JoinColumn(nullable=true) - * - * @var GH10913Entity - */ - public $ref; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int $id; + + #[ORM\ManyToOne(targetEntity: self::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')] + public GH10913Entity $odc; + + #[ORM\ManyToOne(targetEntity: self::class)] + #[ORM\JoinColumn(nullable: true)] + public GH10913Entity $ref; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH10927Test.php b/tests/Tests/ORM/Functional/Ticket/GH10927Test.php index 5675674e512..a459cdea349 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH10927Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH10927Test.php @@ -7,10 +7,9 @@ use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH-10927 - */ +#[Group('GH-10927')] class GH10927Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -67,56 +66,38 @@ public function testSequenceGeneratorDefinitionForEntityC(): void } } -/** - * @ORM\MappedSuperclass() - */ +#[ORM\MappedSuperclass] class GH10927RootMappedSuperclass { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH10927EntityA extends GH10927RootMappedSuperclass { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="SEQUENCE") - * @ORM\Column(type="integer") - * - * @var int|null - */ - private $id = null; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'SEQUENCE')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; } -/** - * @ORM\MappedSuperclass() - */ +#[ORM\MappedSuperclass] class GH10927InheritedMappedSuperclass extends GH10927RootMappedSuperclass { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="SEQUENCE") - * @ORM\Column(type="integer") - * - * @var int|null - */ - private $id = null; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'SEQUENCE')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; } -/** - * @ORM\Entity() - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorColumn(name="discr", type="string") - * @ORM\DiscriminatorMap({"B" = "GH10927EntityB", "C" = "GH10927EntityC"}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')] +#[ORM\DiscriminatorMap(['B' => GH10927EntityB::class, 'C' => GH10927EntityC::class])] class GH10927EntityB extends GH10927InheritedMappedSuperclass { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH10927EntityC extends GH10927EntityB { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Entity.php b/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Entity.php index 25d0a90d733..b7190ad24f7 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Entity.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Entity.php @@ -6,24 +6,16 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH11017Entity { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var ?int - */ + /** @var ?int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\Column(type="string", enumType=GH11017Enum::class) - * - * @var GH11017Enum - */ + /** @var GH11017Enum */ + #[ORM\Column(type: 'string', enumType: GH11017Enum::class)] public $field; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Test.php b/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Test.php index 6844e97e7b3..66d2860deb0 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11017/GH11017Test.php @@ -10,9 +10,6 @@ use function sprintf; -/** - * @requires PHP 8.1 - */ class GH11017Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH11037/GH11037Test.php b/tests/Tests/ORM/Functional/Ticket/GH11037/GH11037Test.php index faaa698618d..b73e28ecfb4 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11037/GH11037Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11037/GH11037Test.php @@ -8,9 +8,6 @@ use Doctrine\ORM\Tools\SchemaValidator; use Doctrine\Tests\OrmTestCase; -/** - * @requires PHP >= 8.1 - */ final class GH11037Test extends OrmTestCase { /** @var EntityManagerInterface */ @@ -44,7 +41,7 @@ public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void "The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status2' has the property type 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\IntEntityStatus' that differs from the metadata enumType 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus'.", "The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status3' has the metadata enumType 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus' with a backing type of 'string' that differs from the metadata field type 'int'.", ], - $ce + $ce, ); } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11037/InvalidEntityWithTypedEnum.php b/tests/Tests/ORM/Functional/Ticket/GH11037/InvalidEntityWithTypedEnum.php index ad1c4b77cbe..411bc7436f9 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11037/InvalidEntityWithTypedEnum.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11037/InvalidEntityWithTypedEnum.php @@ -8,29 +8,19 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** - * @Entity - */ +#[Entity] class InvalidEntityWithTypedEnum { - /** - * @Id - * @Column - */ + #[Id] + #[Column] protected int $id; - /** - * @Column(type="integer", enumType=StringEntityStatus::class) - */ + #[Column(type: 'integer', enumType: StringEntityStatus::class)] protected StringEntityStatus $status1; - /** - * @Column(type="integer", enumType=StringEntityStatus::class) - */ + #[Column(type: 'integer', enumType: StringEntityStatus::class)] protected IntEntityStatus $status2; - /** - * @Column(type="integer", enumType=StringEntityStatus::class) - */ + #[Column(type: 'integer', enumType: StringEntityStatus::class)] protected EntityStatus $status3; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11037/ValidEntityWithTypedEnum.php b/tests/Tests/ORM/Functional/Ticket/GH11037/ValidEntityWithTypedEnum.php index 763e4f8bb6a..d4ac9ef3fd3 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11037/ValidEntityWithTypedEnum.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11037/ValidEntityWithTypedEnum.php @@ -8,29 +8,19 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -/** - * @Entity - */ +#[Entity] class ValidEntityWithTypedEnum { - /** - * @Id - * @Column - */ + #[Id] + #[Column] protected int $id; - /** - * @Column(type="string", enumType=StringEntityStatus::class) - */ + #[Column(type: 'string', enumType: StringEntityStatus::class)] protected StringEntityStatus $status1; - /** - * @Column(type="smallint", enumType=IntEntityStatus::class) - */ + #[Column(type: 'smallint', enumType: IntEntityStatus::class)] protected IntEntityStatus $status2; - /** - * @Column(type="string", enumType=StringEntityStatus::class) - */ + #[Column(type: 'string', enumType: StringEntityStatus::class)] protected EntityStatus $status3; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11058Test.php b/tests/Tests/ORM/Functional/Ticket/GH11058Test.php index a40ef8c2763..67491551721 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11058Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11058Test.php @@ -82,26 +82,18 @@ private function createParentWithTwoChildEntities(): array } } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH11058Parent { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToMany(targetEntity="GH11058Child", mappedBy="parent") - * - * @var Collection - */ - public $children; + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: GH11058Child::class)] + public Collection $children; public function __construct() { @@ -117,25 +109,17 @@ public function addChild(GH11058Child $child): void } } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH11058Child { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH11058Parent", inversedBy="children") - * - * @var GH11058Parent - */ + /** @var GH11058Parent */ + #[ORM\ManyToOne(inversedBy: 'children', targetEntity: GH11058Parent::class)] public $parent; public function setParent(GH11058Parent $parent): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityAdvanced.php b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityAdvanced.php index 4ca0dd0ef7f..00c33a6e005 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityAdvanced.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityAdvanced.php @@ -9,28 +9,24 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** - * @Entity - */ +#[Entity] class GH11072EntityAdvanced { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** @Column(type="json") */ + #[Column(type: 'json')] public mixed $anything; - /** @Column(type="json") */ + #[Column(type: 'json')] public true $alwaysTrue = true; - /** @Column(type="json") */ + #[Column(type: 'json')] public false $alwaysFalse = false; - /** @Column(type="json") */ + #[Column(type: 'json')] public null $alwaysNull = null; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityBasic.php b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityBasic.php index ef38b6b5c5b..1297a673980 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityBasic.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072EntityBasic.php @@ -9,31 +9,27 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; -/** - * @Entity - */ +#[Entity] class GH11072EntityBasic { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** @Column(type="json") */ + #[Column(type: 'json')] public string $jsonString = 'test'; - /** @Column(type="json") */ + #[Column(type: 'json')] public int $age = 99; - /** @Column(type="json") */ + #[Column(type: 'json')] public float $score = 0.0; - /** @Column(type="json", nullable=true) */ - public ?bool $trinary = null; + #[Column(type: 'json', nullable: true)] + public bool|null $trinary = null; - /** @Column(type="json") */ + #[Column(type: 'json')] public array $metadata = []; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072Test.php b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072Test.php index c11ba746bd6..31643f4b8c0 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11072/GH11072Test.php @@ -6,10 +6,9 @@ use Doctrine\ORM\Tools\SchemaValidator; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; -/** - * @requires PHP >= 7.4 - */ +#[RequiresPhp('8.2')] final class GH11072Test extends OrmFunctionalTestCase { /** @var SchemaValidator */ @@ -29,9 +28,6 @@ public function testAcceptsSubsetOfBuiltinTypesWithoutErrors(): void self::assertSame([], $ce); } - /** - * @requires PHP >= 8.2 - */ public function testAcceptsAdvancedSubsetOfBuiltinTypesWithoutErrors(): void { $class = $this->_em->getClassMetadata(GH11072EntityAdvanced::class); diff --git a/tests/Tests/ORM/Functional/Ticket/GH11112Test.php b/tests/Tests/ORM/Functional/Ticket/GH11112Test.php index 3dbdb533b33..d5a11cda6bf 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11112Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11112Test.php @@ -59,7 +59,7 @@ public function testSubqueryLimitAndOffsetAreIgnored(): void ->select('u') ->from(CmsUser::class, 'u') ->setFirstResult(10) - ->setMaxResults(20) + ->setMaxResults(20), )); $query = $queryBuilder->getQuery(); diff --git a/tests/Tests/ORM/Functional/Ticket/GH11135Test.php b/tests/Tests/ORM/Functional/Ticket/GH11135Test.php index 7a3e6a246bb..ae514cfaa4f 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11135Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11135Test.php @@ -25,49 +25,35 @@ public function testOverrideInheritsDeclaringClass(): void $cm1 = $this->_em->getClassMetadata(GH11135EntityWithOverride::class); $cm2 = $this->_em->getClassMetadata(GH11135EntityWithoutOverride::class); - self::assertSame($cm1->getFieldMapping('id')['declared'], $cm2->getFieldMapping('id')['declared']); - self::assertSame($cm1->getAssociationMapping('ref')['declared'], $cm2->getAssociationMapping('ref')['declared']); + self::assertSame($cm1->getFieldMapping('id')->declared, $cm2->getFieldMapping('id')->declared); + self::assertSame($cm1->getAssociationMapping('ref')->declared, $cm2->getAssociationMapping('ref')->declared); } } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH11135MappedSuperclass { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - private $id; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + private int $id; - /** - * @ORM\ManyToOne(targetEntity="GH11135EntityWithoutOverride") - * - * @var GH11135EntityWithoutOverride - */ - private $ref; + #[ORM\ManyToOne(targetEntity: GH11135EntityWithoutOverride::class)] + private GH11135EntityWithoutOverride $ref; } -/** - * @ORM\Entity() - * @ORM\AttributeOverrides({ - * @ORM\AttributeOverride(name="id", column=@ORM\Column(name="id_overridden")) - * }) - * @ORM\AssociationOverrides({ - * @ORM\AssociationOverride(name="ref", joinColumns=@ORM\JoinColumn(name="ref_overridden", referencedColumnName="id")) - * }) - */ +#[ORM\Entity] +#[ORM\AttributeOverrides([ + new ORM\AttributeOverride(name: 'id', column: new ORM\Column(name: 'id_overridden')), +])] +#[ORM\AssociationOverrides([ + new ORM\AssociationOverride(name: 'ref', joinColumns: [new ORM\JoinColumn(name: 'ref_overridden', referencedColumnName: 'id')]), +])] class GH11135EntityWithOverride extends GH11135MappedSuperclass { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH11135EntityWithoutOverride extends GH11135MappedSuperclass { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php b/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php index e2528089abf..0089d9a26aa 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php @@ -8,31 +8,22 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table("gh11149_eager_product") - */ +#[ORM\Entity] +#[ORM\Table('gh11149_eager_product')] class EagerProduct { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column] + public int $id; - /** - * @ORM\OneToMany( - * targetEntity=EagerProductTranslation::class, - * mappedBy="product", - * fetch="EAGER", - * indexBy="locale_code" - * ) - * - * @var Collection - */ - public $translations; + /** @var Collection */ + #[ORM\OneToMany( + targetEntity: EagerProductTranslation::class, + mappedBy: 'product', + fetch: 'EAGER', + indexBy: 'locale_code', + )] + public Collection $translations; public function __construct(int $id) { diff --git a/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProductTranslation.php b/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProductTranslation.php index 32c501868ec..8027010ca86 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProductTranslation.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11149/EagerProductTranslation.php @@ -6,37 +6,23 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table("gh11149_eager_product_translation") - */ +#[ORM\Entity] +#[ORM\Table('gh11149_eager_product_translation')] class EagerProductTranslation { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ - private $id; + #[ORM\Id] + #[ORM\Column] + private int $id; - /** - * @ORM\ManyToOne(targetEntity=EagerProduct::class, inversedBy="translations") - * @ORM\JoinColumn(nullable=false) - * - * @var EagerProduct - */ - public $product; + #[ORM\ManyToOne(inversedBy: 'translations')] + #[ORM\JoinColumn(nullable: false)] + public EagerProduct $product; - /** - * @ORM\ManyToOne(targetEntity=Locale::class) - * @ORM\JoinColumn(name="locale_code", referencedColumnName="code", nullable=false) - * - * @var Locale - */ - public $locale; + #[ORM\ManyToOne] + #[ORM\JoinColumn(name: 'locale_code', referencedColumnName: 'code', nullable: false)] + public Locale $locale; - public function __construct($id, EagerProduct $product, Locale $locale) + public function __construct(int $id, EagerProduct $product, Locale $locale) { $this->id = $id; $this->product = $product; diff --git a/tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php b/tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php index 8ec8a548bf1..75a4f583c83 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php @@ -6,19 +6,13 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table("gh11149_locale") - */ +#[ORM\Entity] +#[ORM\Table('gh11149_locale')] class Locale { - /** - * @ORM\Id - * @ORM\Column(type="string", length=5) - * - * @var string - */ - public $code; + #[ORM\Id] + #[ORM\Column(length: 5)] + public string $code; public function __construct(string $code) { diff --git a/tests/Tests/ORM/Functional/Ticket/GH11163Test.php b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php index 927457840c5..d4e75389c19 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11163Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php @@ -66,30 +66,21 @@ public function testFetchEagerModeWithOrderBy(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH11163Bucket { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ - private $id; - - /** - * @ORM\OneToMany( - * targetEntity=GH11163BucketItem::class, - * mappedBy="bucket", - * fetch="EAGER" - * ) - * @ORM\OrderBy({"position" = "ASC"}) - * - * @var Collection - */ - public $items; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + private int $id; + + /** @var Collection */ + #[ORM\OneToMany( + targetEntity: GH11163BucketItem::class, + mappedBy: 'bucket', + fetch: 'EAGER', + )] + #[ORM\OrderBy(['position' => 'ASC'])] + public Collection $items; public function __construct(int $id) { @@ -98,33 +89,19 @@ public function __construct(int $id) } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH11163BucketItem { - /** - * @ORM\ManyToOne(targetEntity=GH11163Bucket::class, inversedBy="items") - * @ORM\JoinColumn(nullable=false) - * - * @var GH11163Bucket - */ - private $bucket; - - /** - * @ORM\Id - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; - - /** - * @ORM\Column(type="integer", nullable=false) - * - * @var int - */ - public $position; + #[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) { diff --git a/tests/Tests/ORM/Functional/Ticket/GH11199Test.php b/tests/Tests/ORM/Functional/Ticket/GH11199Test.php index cac299e12b8..524ff932099 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11199Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11199Test.php @@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; class GH11199Test extends OrmFunctionalTestCase { @@ -23,7 +24,7 @@ protected function setUp(): void ]); } - public function dqlStatements(): Generator + public static function dqlStatements(): Generator { yield ['SELECT e FROM ' . GH11199Root::class . ' e', "/WHERE g0_.asset_type IN \('root', 'foo', 'baz'\)$/"]; yield ['SELECT e FROM ' . GH11199Parent::class . ' e', "/WHERE g0_.asset_type IN \('foo'\)$/"]; @@ -32,9 +33,7 @@ public function dqlStatements(): Generator yield ['SELECT e FROM ' . GH11199AbstractLeaf::class . ' e', '/WHERE 1=0/']; } - /** - * @dataProvider dqlStatements - */ + #[DataProvider('dqlStatements')] public function testGH11199(string $dql, string $expectedDiscriminatorValues): void { $query = $this->_em->createQuery($dql); @@ -44,53 +43,39 @@ public function testGH11199(string $dql, string $expectedDiscriminatorValues): v } } -/** - * @ORM\Entity() - * @ORM\Table(name="gh11199") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="asset_type", type="string") - * @ORM\DiscriminatorMap({ - * "root" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Root", - * "foo" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Foo", - * "baz" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Baz", - * }) - */ +#[ORM\Entity] +#[ORM\Table(name: 'gh11199')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'asset_type', type: 'string')] +#[ORM\DiscriminatorMap([ + 'root' => GH11199Root::class, + 'foo' => GH11199Foo::class, + 'baz' => GH11199Baz::class, +])] class GH11199Root { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="IDENTITY") - * @ORM\Column(type="integer") - * - * @var int|null - */ - private $id = null; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; } -/** - * @ORM\Entity() - */ +#[ORM\Entity] abstract class GH11199Parent extends GH11199Root { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH11199Foo extends GH11199Parent { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH11199Baz extends GH11199Root { } -/** - * @ORM\Entity() - */ +#[ORM\Entity] abstract class GH11199AbstractLeaf extends GH11199Root { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11341Test.php b/tests/Tests/ORM/Functional/Ticket/GH11341Test.php new file mode 100644 index 00000000000..f3527afb752 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11341Test.php @@ -0,0 +1,157 @@ +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]; + } + + /** + * @phpstan-param class-string $baseClass + * @phpstan-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 +{ +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCart.php b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCart.php new file mode 100644 index 00000000000..032171aac52 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCart.php @@ -0,0 +1,55 @@ +id; + } + + public function getAmount(): int|null + { + return $this->amount; + } + + public function setAmount(int $amount): static + { + $this->amount = $amount; + + return $this; + } + + public function getCustomer(): GH11386EntityCustomer|null + { + return $this->customer; + } + + public function setCustomer(GH11386EntityCustomer|null $customer): self + { + $this->customer = $customer; + + return $this; + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCustomer.php b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCustomer.php new file mode 100644 index 00000000000..3290a6f99bf --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EntityCustomer.php @@ -0,0 +1,80 @@ + true])] + private GH11386EnumType|null $type = null; + + #[OneToOne(mappedBy: 'customer')] + private GH11386EntityCart|null $cart = null; + + public function getId(): int|null + { + return $this->id; + } + + public function getName(): string|null + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getType(): GH11386EnumType|null + { + return $this->type; + } + + public function setType(GH11386EnumType $type): static + { + $this->type = $type; + + return $this; + } + + public function getCart(): GH11386EntityCart|null + { + return $this->cart; + } + + public function setCart(GH11386EntityCart|null $cart): self + { + // unset the owning side of the relation if necessary + if ($cart === null && $this->cart !== null) { + $this->cart->setCustomer(null); + } + + // set the owning side of the relation if necessary + if ($cart !== null && $cart->getCustomer() !== $this) { + $cart->setCustomer($this); + } + + $this->cart = $cart; + + return $this; + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EnumType.php b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EnumType.php new file mode 100644 index 00000000000..c07da865b95 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11386/GH11386EnumType.php @@ -0,0 +1,11 @@ +createSchemaForModels( + GH11386EntityCart::class, + GH11386EntityCustomer::class, + ); + } + + public function testInitializeClonedProxy(): void + { + $cart = new GH11386EntityCart(); + $cart->setAmount(1000); + + $customer = new GH11386EntityCustomer(); + $customer->setName('John Doe') + ->setType(GH11386EnumType::MALE) + ->setCart($cart); + + $this->_em->persist($cart); + $this->_em->flush(); + $this->_em->clear(); + + $cart = $this->_em->find(GH11386EntityCart::class, 1); + $customer = clone $cart->getCustomer(); + self::assertEquals('John Doe', $customer->getName()); + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH11487Test.php b/tests/Tests/ORM/Functional/Ticket/GH11487Test.php index ef036e48ada..3e622611454 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11487Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11487Test.php @@ -21,20 +21,14 @@ public function testItThrowsASyntaxErrorOnUnfinishedQuery(): void } } -/** @Entity */ +#[Entity] class TaxType { - /** - * @var int|null - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - public $id; + #[Column] + #[Id] + #[GeneratedValue] + public int|null $id = null; - /** - * @var bool - * @Column(type="boolean") - */ - public $default = false; + #[Column] + public bool $default = false; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH11500Test.php b/tests/Tests/ORM/Functional/Ticket/GH11500Test.php index 4be3bf2b76e..b2f8123fabd 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11500Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11500Test.php @@ -24,9 +24,7 @@ protected function setUp(): void ]); } - /** - * @throws ORMException - */ + /** @throws ORMException */ public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void { $testEntityOne = new GH11500TestEntityOne(); @@ -59,75 +57,49 @@ public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void -/** - * @ORM\Entity - * @ORM\Table(name="one_to_many_single_table_inheritance_test_entities") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"test_entity_one"="GH11500TestEntityOne", "test_entity_two"="GH11500TestEntityTwo"}) - */ +#[ORM\Entity] +#[ORM\Table(name: 'one_to_many_single_table_inheritance_test_entities')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap(['test_entity_one' => 'GH11500TestEntityOne', 'test_entity_two' => 'GH11500TestEntityTwo'])] class GH11500AbstractTestEntity { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + public int|null $id = null; } -/** @ORM\Entity */ +#[ORM\Entity] class GH11500TestEntityOne extends GH11500AbstractTestEntity { - /** - * @ORM\ManyToOne(targetEntity="GH11500TestEntityHolder", inversedBy="testEntityOnes") - * @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id") - * - * @var GH11500TestEntityHolder - */ - public $testEntityHolder; + #[ORM\ManyToOne(inversedBy:'testEntityOnes')] + #[ORM\JoinColumn(name:'test_entity_holder_id', referencedColumnName:'id')] + public GH11500TestEntityHolder|null $testEntityHolder = null; } -/** @ORM\Entity */ +#[ORM\Entity] class GH11500TestEntityTwo extends GH11500AbstractTestEntity { - /** - * @ORM\ManyToOne(targetEntity="GH11500TestEntityHolder", inversedBy="testEntityTwos") - * @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id") - * - * @var GH11500TestEntityHolder - */ - public $testEntityHolder; + #[ORM\ManyToOne(inversedBy:'testEntityTwos')] + #[ORM\JoinColumn(name:'test_entity_holder_id', referencedColumnName:'id')] + public GH11500TestEntityHolder|null $testEntityHolder = null; } -/** @ORM\Entity */ +#[ORM\Entity] class GH11500TestEntityHolder { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; - - /** - * @ORM\OneToMany(targetEntity="GH11500TestEntityOne", mappedBy="testEntityHolder", orphanRemoval=true) - * - * @var Collection - */ - public $testEntityOnes; - - /** - * @ORM\OneToMany(targetEntity="GH11500TestEntityTwo", mappedBy="testEntityHolder", orphanRemoval=true) - * - * @var Collection - */ - public $testEntityTwos; + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + public int|null $id = null; + + #[ORM\OneToMany(targetEntity: 'GH11500TestEntityOne', mappedBy: 'testEntityHolder', orphanRemoval: true)] + public Collection $testEntityOnes; + + #[ORM\OneToMany(targetEntity: 'GH11500TestEntityTwo', mappedBy: 'testEntityHolder', orphanRemoval: true)] + public Collection $testEntityTwos; public function __construct() { diff --git a/tests/Tests/ORM/Functional/Ticket/GH11501Test.php b/tests/Tests/ORM/Functional/Ticket/GH11501Test.php index 715137d43af..ffecab8c0de 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11501Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11501Test.php @@ -24,9 +24,7 @@ protected function setUp(): void ]); } - /** - * @throws ORMException - */ + /** @throws ORMException */ public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void { $testEntityOne = new GH11501TestEntityOne(); @@ -54,64 +52,51 @@ public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void } } - - -/** - * @ORM\Entity - * @ORM\Table(name="one_to_many_single_table_inheritance_test_entities_parent_join") - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"test_entity_one"="GH11501TestEntityOne", "test_entity_two"="GH11501TestEntityTwo"}) - */ +#[ORM\Entity] +#[ORM\Table(name: 'one_to_many_single_table_inheritance_test_entities_parent_join')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap([ + 'test_entity_one' => 'GH11501TestEntityOne', + 'test_entity_two' => 'GH11501TestEntityTwo', +])] class GH11501AbstractTestEntity { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; - - /** - * @ORM\ManyToOne(targetEntity="GH11501TestEntityHolder", inversedBy="testEntities") - * @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id") - * - * @var GH11501TestEntityHolder - */ - public $testEntityHolder; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int $id; + + #[ORM\ManyToOne(targetEntity: 'GH11501TestEntityHolder', inversedBy: 'testEntities')] + #[ORM\JoinColumn(name: 'test_entity_holder_id', referencedColumnName: 'id')] + public GH11501TestEntityHolder $testEntityHolder; } -/** @ORM\Entity */ +#[ORM\Entity] class GH11501TestEntityOne extends GH11501AbstractTestEntity { } -/** @ORM\Entity */ +#[ORM\Entity] class GH11501TestEntityTwo extends GH11501AbstractTestEntity { } -/** @ORM\Entity */ +#[ORM\Entity] class GH11501TestEntityHolder { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ - public $id; - - /** - * @ORM\OneToMany(targetEntity="GH11501AbstractTestEntity", mappedBy="testEntityHolder", orphanRemoval=true) - * - * @var Collection - */ - public $testEntities; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] + public int $id; + + #[ORM\OneToMany( + targetEntity: 'GH11501AbstractTestEntity', + mappedBy: 'testEntityHolder', + orphanRemoval: true, + )] + public Collection $testEntities; public function __construct() { diff --git a/tests/Tests/ORM/Functional/Ticket/GH2947Test.php b/tests/Tests/ORM/Functional/Ticket/GH2947Test.php index 6eb55cc9b92..9db15b1e0c3 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH2947Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH2947Test.php @@ -11,9 +11,11 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Query; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; use Symfony\Component\Cache\Adapter\ArrayAdapter; -/** @group GH-2947 */ +#[Group('GH-2947')] class GH2947Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -53,7 +55,7 @@ private function createQuery(): Query ->select('car') ->from(GH2947Car::class, 'car') ->getQuery() - ->useResultCache(true, 3600, 'foo-cache-id'); + ->enableResultCache(3600, 'foo-cache-id'); } private function createData(): void @@ -76,23 +78,16 @@ private function updateData(): void } } -/** - * @Entity - * @Table(name="GH2947_car") - */ -class GH2947Car +#[Table(name: 'GH2947_car')] +#[Entity] +class GH2947Car implements Stringable { - /** - * @var string - * @Id - * @Column(type="string", length=25) - * @GeneratedValue(strategy="NONE") - */ - public $brand; - - public function __construct(string $brand) - { - $this->brand = $brand; + public function __construct( + #[Id] + #[Column(type: 'string', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + public string $brand, + ) { } public function __toString(): string diff --git a/tests/Tests/ORM/Functional/Ticket/GH5562Test.php b/tests/Tests/ORM/Functional/Ticket/GH5562Test.php index b1895d9087d..6169bed1486 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5562Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5562Test.php @@ -13,8 +13,10 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function mt_rand; +use function mt_getrandmax; +use function random_int; final class GH5562Test extends OrmFunctionalTestCase { @@ -27,11 +29,11 @@ protected function setUp(): void $this->createSchemaForModels( GH5562User::class, GH5562Manager::class, - GH5562Merchant::class + GH5562Merchant::class, ); } - /** @group GH-5562 */ + #[Group('GH-5562')] public function testCacheShouldBeUpdatedWhenAssociationChanges(): void { $manager = new GH5562Manager(); @@ -50,7 +52,7 @@ public function testCacheShouldBeUpdatedWhenAssociationChanges(): void $merchant = $this->_em->find(GH5562Merchant::class, $merchant->id); - $merchant->name = mt_rand(); + $merchant->name = random_int(0, mt_getrandmax()); $merchant->manager->username = 'usernameUPDATE'; $this->_em->flush(); @@ -62,65 +64,47 @@ public function testCacheShouldBeUpdatedWhenAssociationChanges(): void } } -/** - * @Entity - * @Cache(usage="NONSTRICT_READ_WRITE") - */ +#[Entity] +#[Cache(usage: 'NONSTRICT_READ_WRITE')] class GH5562Merchant { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="IDENTITY") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'IDENTITY')] public $id; - /** - * @var GH5562Manager - * @OneToOne(targetEntity=GH5562Manager::class, mappedBy="merchant") - * @Cache(usage="NONSTRICT_READ_WRITE") - */ + /** @var GH5562Manager */ + #[OneToOne(targetEntity: GH5562Manager::class, mappedBy: 'merchant')] + #[Cache(usage: 'NONSTRICT_READ_WRITE')] public $manager; - /** - * @var string - * @Column(name="name", type="string", length=255, nullable=false) - */ + /** @var string */ + #[Column(name: 'name', type: 'string', length: 255, nullable: false)] public $name; } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"MANAGER" = GH5562Manager::class}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['MANAGER' => GH5562Manager::class])] abstract class GH5562User { - /** - * @var int - * @Id - * @Column(name="id", type="integer") - * @GeneratedValue(strategy="IDENTITY") - */ + /** @var int */ + #[Id] + #[Column(name: 'id', type: 'integer')] + #[GeneratedValue(strategy: 'IDENTITY')] public $id; } -/** - * @Entity - * @Cache(usage="NONSTRICT_READ_WRITE") - */ +#[Entity] +#[Cache(usage: 'NONSTRICT_READ_WRITE')] class GH5562Manager extends GH5562User { - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $username; - /** - * @var GH5562Merchant - * @OneToOne(targetEntity=GH5562Merchant::class, inversedBy="manager") - */ + /** @var GH5562Merchant */ + #[OneToOne(targetEntity: GH5562Merchant::class, inversedBy: 'manager')] public $merchant; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5742Test.php b/tests/Tests/ORM/Functional/Ticket/GH5742Test.php index ba62f2531bc..880296a977d 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5742Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5742Test.php @@ -18,7 +18,7 @@ protected function setUp(): void $this->createSchemaForModels( GH5742Person::class, GH5742Toothbrush::class, - GH5742ToothpasteBrand::class + GH5742ToothpasteBrand::class, ); } @@ -88,38 +88,24 @@ public function testManyToManyCollectionUpdateBeforeRemoval(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5742Person { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; - - /** - * @ORM\OneToOne(targetEntity="GH5742Toothbrush", cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var GH5742Toothbrush - */ - public $toothbrush; - - /** - * @ORM\ManyToMany(targetEntity="GH5742ToothpasteBrand") - * @ORM\JoinTable(name="gh5742person_gh5742toothpastebrand", - * joinColumns={@ORM\JoinColumn(name="person_id", referencedColumnName="id", onDelete="CASCADE")}, - * inverseJoinColumns={@ORM\JoinColumn(name="brand_id", referencedColumnName="id")} - * ) - * - * @var Collection - */ - public $preferredBrands; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] + public int $id; + + #[ORM\OneToOne(targetEntity: 'GH5742Toothbrush', cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] + public GH5742Toothbrush $toothbrush; + + /** @var Collection */ + #[ORM\ManyToMany(targetEntity: 'GH5742ToothpasteBrand')] + #[ORM\JoinTable('gh5742person_gh5742toothpastebrand')] + #[ORM\JoinColumn(name: 'person_id', referencedColumnName: 'id', onDelete: 'CASCADE')] + #[ORM\InverseJoinColumn(name: 'brand_id', referencedColumnName: 'id')] + public Collection $preferredBrands; public function __construct() { @@ -127,32 +113,20 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5742Toothbrush { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] + public int $id; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5742ToothpasteBrand { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] + public int $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5762Test.php b/tests/Tests/ORM/Functional/Ticket/GH5762Test.php index bd5c9d65dd2..3956e06d57f 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5762Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5762Test.php @@ -16,11 +16,12 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\PersistentCollection; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_unique; use function count; -/** @group GH-5762 */ +#[Group('GH-5762')] class GH5762Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -30,7 +31,7 @@ protected function setUp(): void $this->createSchemaForModels( GH5762Driver::class, GH5762DriverRide::class, - GH5762Car::class + GH5762Car::class, ); } @@ -57,8 +58,7 @@ public function testIssue(): void self::assertContains('Volvo', $cars); } - /** @return mixed */ - private function fetchData() + private function fetchData(): mixed { $this->createData(); @@ -107,102 +107,61 @@ private function createData(): void } } -/** - * @Entity - * @Table(name="driver") - */ +#[Table(name: 'driver')] +#[Entity] class GH5762Driver { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="GH5762DriverRide", mappedBy="driver") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'GH5762DriverRide', mappedBy: 'driver')] public $driverRides; - public function __construct(int $id, string $name) - { + public function __construct( + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'NONE')] + public int $id, + #[Column(type: 'string', length: 255)] + public string $name, + ) { $this->driverRides = new ArrayCollection(); - $this->id = $id; - $this->name = $name; } } -/** - * @Entity - * @Table(name="driver_ride") - */ +#[Table(name: 'driver_ride')] +#[Entity] class GH5762DriverRide { - /** - * @var GH5762Driver - * @Id - * @ManyToOne(targetEntity="GH5762Driver", inversedBy="driverRides") - * @JoinColumn(name="driver_id", referencedColumnName="id") - */ - public $driver; - - /** - * @var GH5762Car - * @Id - * @ManyToOne(targetEntity="GH5762Car", inversedBy="carRides") - * @JoinColumn(name="car", referencedColumnName="brand") - */ - public $car; - - public function __construct(GH5762Driver $driver, GH5762Car $car) - { - $this->driver = $driver; - $this->car = $car; - + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'GH5762Driver', inversedBy: 'driverRides')] + #[JoinColumn(name: 'driver_id', referencedColumnName: 'id')] + public GH5762Driver $driver, + #[Id] + #[ManyToOne(targetEntity: 'GH5762Car', inversedBy: 'carRides')] + #[JoinColumn(name: 'car', referencedColumnName: 'brand')] + public GH5762Car $car, + ) { $this->driver->driverRides->add($this); $this->car->carRides->add($this); } } -/** - * @Entity - * @Table(name="car") - */ +#[Table(name: 'car')] +#[Entity] class GH5762Car { - /** - * @var string - * @Id - * @Column(type="string", length=25) - * @GeneratedValue(strategy="NONE") - */ - public $brand; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $model; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="GH5762DriverRide", mappedBy="car") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'GH5762DriverRide', mappedBy: 'car')] public $carRides; - public function __construct($brand, $model) - { + public function __construct( + #[Id] + #[Column(type: 'string', length: 25)] + #[GeneratedValue(strategy: 'NONE')] + public string $brand, + #[Column(type: 'string', length: 255)] + public string $model, + ) { $this->carRides = new ArrayCollection(); - $this->brand = $brand; - $this->model = $model; } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5804Test.php b/tests/Tests/ORM/Functional/Ticket/GH5804Test.php index 5790a54ad1b..5dfb90e28c6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5804Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5804Test.php @@ -15,10 +15,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function method_exists; - -/** @group GH-5804 */ +#[Group('GH-5804')] final class GH5804Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -49,10 +48,7 @@ public function testTextColumnSaveAndRetrieve2(): void final class GH5804Generator extends AbstractIdGenerator { - /** - * {@inheritDoc} - */ - public function generateId(EntityManagerInterface $em, $entity) + public function generateId(EntityManagerInterface $em, object|null $entity): string { return 'test5804'; } @@ -62,10 +58,7 @@ final class GH5804Type extends Type { public const NAME = 'GH5804Type'; - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } @@ -73,19 +66,15 @@ public function getName() /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string|null { if (empty($value)) { return null; @@ -95,28 +84,22 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) } } -/** @Entity */ +#[Entity] class GH5804Article { - /** - * @var string - * @Id - * @Column(type="GH5804Type", length=255) - * @GeneratedValue(strategy="CUSTOM") - * @CustomIdGenerator(class=GH5804Generator::class) - */ + /** @var string */ + #[Id] + #[Column(type: 'GH5804Type', length: 255)] + #[GeneratedValue(strategy: 'CUSTOM')] + #[CustomIdGenerator(class: GH5804Generator::class)] public $id; - /** - * @var int - * @Version - * @Column(type="integer") - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; - /** - * @var string - * @Column(type="text") - */ + /** @var string */ + #[Column(type: 'text')] public $text; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5887Test.php b/tests/Tests/ORM/Functional/Ticket/GH5887Test.php index 1cc6169dff0..10c28a9acbc 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5887Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5887Test.php @@ -14,10 +14,12 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use Stringable; use function assert; -/** @group GH-5887 */ +#[Group('GH-5887')] class GH5887Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -59,25 +61,20 @@ public function testLazyLoadsForeignEntitiesInOneToOneRelationWhileHavingCustomI } } -/** @Entity */ +#[Entity] class GH5887Cart { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'NONE')] + private int|null $id = null; /** * One Cart has One Customer. - * - * @var GH5887Customer - * @OneToOne(targetEntity="GH5887Customer", inversedBy="cart") - * @JoinColumn(name="customer_id", referencedColumnName="id") */ - private $customer; + #[OneToOne(targetEntity: 'GH5887Customer', inversedBy: 'cart')] + #[JoinColumn(name: 'customer_id', referencedColumnName: 'id')] + private GH5887Customer|null $customer = null; public function getId(): int { @@ -103,24 +100,19 @@ public function setCustomer(GH5887Customer $customer): void } } -/** @Entity */ +#[Entity] class GH5887Customer { - /** - * @var GH5887CustomIdObject - * @Id - * @Column(type="GH5887CustomIdObject", length=255) - * @GeneratedValue(strategy="NONE") - */ - private $id; + #[Id] + #[Column(type: 'GH5887CustomIdObject', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + private GH5887CustomIdObject|null $id = null; /** * One Customer has One Cart. - * - * @var GH5887Cart - * @OneToOne(targetEntity="GH5887Cart", mappedBy="customer") */ - private $cart; + #[OneToOne(targetEntity: 'GH5887Cart', mappedBy: 'customer')] + private GH5887Cart|null $cart = null; public function getId(): GH5887CustomIdObject { @@ -146,14 +138,10 @@ public function setCart(GH5887Cart $cart): void } } -class GH5887CustomIdObject +class GH5887CustomIdObject implements Stringable { - /** @var int */ - private $id; - - public function __construct(int $id) + public function __construct(private int $id) { - $this->id = $id; } public function getId(): int @@ -174,7 +162,7 @@ class GH5887CustomIdObjectType extends StringType /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { return $value->getId(); } @@ -182,15 +170,12 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): GH5887CustomIdObject { return new GH5887CustomIdObject((int) $value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5988Test.php b/tests/Tests/ORM/Functional/Ticket/GH5988Test.php index 4e731b54353..75c8f1026ca 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5988Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5988Test.php @@ -15,15 +15,14 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\DbalTypes\CustomIdObject; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -use function method_exists; use function str_replace; /** * Functional tests for the Class Table Inheritance mapping strategy with custom id object types. - * - * @group GH5988 */ +#[Group('GH5988')] final class GH5988Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -61,7 +60,7 @@ class GH5988CustomIdObjectHashType extends DBALType /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { return $value->id . '_test'; } @@ -69,7 +68,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): CustomIdObject { return new CustomIdObject(str_replace('_test', '', $value)); } @@ -77,54 +76,37 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::class; } } -/** - * @Entity - * @Table - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"child" = GH5988CustomIdObjectTypeChild::class}) - */ +#[Table] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['child' => GH5988CustomIdObjectTypeChild::class])] abstract class GH5988CustomIdObjectTypeParent { - /** - * @Id - * @Column(type="Doctrine\Tests\ORM\Functional\Ticket\GH5988CustomIdObjectHashType", length=255) - * @var CustomIdObject - */ + /** @var CustomIdObject */ + #[Id] + #[Column(type: 'Doctrine\Tests\ORM\Functional\Ticket\GH5988CustomIdObjectHashType', length: 255)] public $id; } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class GH5988CustomIdObjectTypeChild extends GH5988CustomIdObjectTypeParent { - /** @var string */ - public $name; - - public function __construct(CustomIdObject $id, string $name) + public function __construct(CustomIdObject $id, public string $name) { - $this->id = $id; - $this->name = $name; + $this->id = $id; } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH5998Test.php b/tests/Tests/ORM/Functional/Ticket/GH5998Test.php index ab031886124..f37e4c82ade 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH5998Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH5998Test.php @@ -7,10 +7,9 @@ use Doctrine\DBAL\LockMode; use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH-5998 - */ +#[Group('GH-5998')] class GH5998Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -60,7 +59,7 @@ private function classTests($className): void self::assertNotNull($child); // Test lock and update - $this->_em->transactional(static function ($em) use ($child): void { + $this->_em->wrapInTransaction(static function ($em) use ($child): void { $em->lock($child, LockMode::NONE); $child->firstName = 'Bob'; $child->status = 0; @@ -79,76 +78,52 @@ private function classTests($className): void } } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH5998Common { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\ManyToOne(targetEntity=GH5998Related::class) - * @ORM\JoinColumn(name="related_id", referencedColumnName="id") - * - * @var GH5998Related - */ + + /** @var GH5998Related */ + #[ORM\ManyToOne(targetEntity: GH5998Related::class)] + #[ORM\JoinColumn(name: 'related_id', referencedColumnName: 'id')] public $rel; - /** - * @ORM\Version - * @ORM\Column(type="integer") - * - * @var int - */ + + /** @var int */ + #[ORM\Version] + #[ORM\Column(type: 'integer')] public $version; /** @var mixed */ public $other; } -/** - * @ORM\Entity - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorMap({"child" = GH5998JTIChild::class}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorMap(['child' => GH5998JTIChild::class])] abstract class GH5998JTI extends GH5998Common { - /** - * @ORM\Column(type="string", length=255) - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string', length: 255)] public $firstName; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH5998JTICommon extends GH5998JTI { - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $status; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5998JTIChild extends GH5998JTICommon { - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $type; public function __construct(string $firstName, int $type, int $status) @@ -159,44 +134,29 @@ public function __construct(string $firstName, int $type, int $status) } } -/** - * @ORM\Entity - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorMap({"child" = GH5998STIChild::class}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorMap(['child' => GH5998STIChild::class])] abstract class GH5998STI extends GH5998Common { - /** - * @ORM\Column(type="string", length=255) - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string', length: 255)] public $firstName; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH5998STICommon extends GH5998STI { - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $status; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5998STIChild extends GH5998STICommon { - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $type; public function __construct(string $firstName, int $type, int $status) @@ -207,30 +167,19 @@ public function __construct(string $firstName, int $type, int $status) } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH5998Basic extends GH5998Common { - /** - * @ORM\Column(type="string", length=255) - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string', length: 255)] public $firstName; - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $status; - /** - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] public $type; public function __construct(string $firstName, int $type, int $status) @@ -241,17 +190,12 @@ public function __construct(string $firstName, int $type, int $status) } } -/** - * @ORM\Entity() - */ +#[ORM\Entity] class GH5998Related { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6029Test.php b/tests/Tests/ORM/Functional/Ticket/GH6029Test.php index 416d509b028..5ac7908cc14 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6029Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6029Test.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function sprintf; @@ -32,16 +33,15 @@ protected function setUp(): void GH6029Group2::class, GH6029Product::class, GH6029Feature::class, - ] + ], ); } /** * Verifies that when wrong entity is persisted via relationship field, the error message does not correctly state * the expected class name. - * - * @group GH-6029 */ + #[Group('GH-6029')] public function testManyToManyAssociation(): void { $user = new GH6029User(); @@ -53,8 +53,8 @@ public function testManyToManyAssociation(): void 'Expected value of type "%s" for association field "%s#$groups", got "%s" instead.', GH6029Group::class, GH6029User::class, - GH6029Group2::class - ) + GH6029Group2::class, + ), ); $this->_em->persist($user); @@ -64,9 +64,8 @@ public function testManyToManyAssociation(): void /** * Verifies that when wrong entity is persisted via relationship field, the error message does not correctly state * the expected class name. - * - * @group GH-6029 */ + #[Group('GH-6029')] public function testOneToManyAssociation(): void { $product = new GH6029Product(); @@ -78,8 +77,8 @@ public function testOneToManyAssociation(): void 'Expected value of type "%s" for association field "%s#$features", got "%s" instead.', GH6029Feature::class, GH6029Product::class, - GH6029Group2::class - ) + GH6029Group2::class, + ), ); $this->_em->persist($product); @@ -87,21 +86,17 @@ public function testOneToManyAssociation(): void } } -/** @Entity */ +#[Entity] class GH6029User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity=GH6029Group::class, cascade={"all"}) - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: GH6029Group::class, cascade: ['all'])] public $groups; public function __construct() @@ -110,45 +105,37 @@ public function __construct() } } -/** @Entity */ +#[Entity] class GH6029Group { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH6029Group2 { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH6029Product { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity=GH6029Feature::class, mappedBy="product", cascade={"all"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: GH6029Feature::class, mappedBy: 'product', cascade: ['all'])] public $features; public function __construct() @@ -157,21 +144,17 @@ public function __construct() } } -/** @Entity */ +#[Entity] class GH6029Feature { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH6029Product - * @ManyToOne(targetEntity=GH6029Product::class, inversedBy="features") - * @JoinColumn(name="product_id", referencedColumnName="id") - */ + /** @var GH6029Product */ + #[ManyToOne(targetEntity: GH6029Product::class, inversedBy: 'features')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] public $product; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6123Test.php b/tests/Tests/ORM/Functional/Ticket/GH6123Test.php index 05a1a765f27..60bea99a7e6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6123Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6123Test.php @@ -16,7 +16,7 @@ protected function setUp(): void parent::setUp(); $this->createSchemaForModels( - GH6123Entity::class + GH6123Entity::class, ); } @@ -57,7 +57,7 @@ public function testRemovedEntityCanBePersistedAgain(): void $this->_em->flush(); } - private function loadEntityFromDatabase(int $id): ?GH6123Entity + private function loadEntityFromDatabase(int $id): GH6123Entity|null { return $this->_em->createQueryBuilder() ->select('e') @@ -69,20 +69,11 @@ private function loadEntityFromDatabase(int $id): ?GH6123Entity } } -/** - * @ORM\Entity - */ #[ORM\Entity] class GH6123Entity { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * @var int - */ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: Types::INTEGER)] - public $id; + public int $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6141Test.php b/tests/Tests/ORM/Functional/Ticket/GH6141Test.php index 858fbddfcda..408eff79223 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6141Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6141Test.php @@ -17,6 +17,8 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\Tests\OrmFunctionalTestCase; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\Group; +use Stringable; use function in_array; @@ -31,7 +33,7 @@ protected function setUp(): void $this->createSchemaForModels( GH6141Person::class, GH6141Boss::class, - GH6141Employee::class + GH6141Employee::class, ); } @@ -39,9 +41,8 @@ protected function setUp(): void * The intent of this test is to ensure that the ORM is capable * of using objects as discriminators (which makes things a bit * more dynamic as you can see on the mapping of `GH6141Person`) - * - * @group GH-6141 */ + #[Group('GH-6141')] public function testEnumDiscriminatorsShouldBeConvertedToString(): void { $boss = new GH6141Boss('John'); @@ -64,14 +65,14 @@ public function testEnumDiscriminatorsShouldBeConvertedToString(): void self::assertEquals($boss, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)); self::assertEquals( GH6141People::get(GH6141People::BOSS), - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'] + $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'], ); $query->setParameter('name', 'Bob'); self::assertEquals($employee, $query->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT)); self::assertEquals( GH6141People::get(GH6141People::EMPLOYEE), - $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'] + $query->getOneOrNullResult(AbstractQuery::HYDRATE_ARRAY)['discr'], ); } } @@ -83,7 +84,7 @@ class GH6141PeopleType extends StringType /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): string { if (! $value instanceof GH6141People) { $value = GH6141People::get($value); @@ -95,28 +96,22 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): GH6141People { return GH6141People::get($value); } - /** - * {@inheritDoc} - */ - public function getName() + public function getName(): string { return self::NAME; } } -class GH6141People +class GH6141People implements Stringable { public const BOSS = 'boss'; public const EMPLOYEE = 'employee'; - /** @var string */ - private $value; - /** @throws InvalidArgumentException */ public static function get(string $value): GH6141People { @@ -132,9 +127,8 @@ private static function isValid(string $valid): bool return in_array($valid, [self::BOSS, self::EMPLOYEE], true); } - private function __construct(string $value) + private function __construct(private string $value) { - $this->value = $value; } public function getValue(): string @@ -148,43 +142,31 @@ public function __toString(): string } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="gh6141people") - * @DiscriminatorMap({ - * GH6141People::BOSS = GH6141Boss::class, - * GH6141People::EMPLOYEE = GH6141Employee::class - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'gh6141people')] +#[DiscriminatorMap([GH6141People::BOSS => GH6141Boss::class, GH6141People::EMPLOYEE => GH6141Employee::class])] abstract class GH6141Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class GH6141Boss extends GH6141Person { } -/** @Entity */ +#[Entity] class GH6141Employee extends GH6141Person { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6217Test.php b/tests/Tests/ORM/Functional/Ticket/GH6217Test.php index 128727eb228..d23f5e68acd 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6217Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6217Test.php @@ -11,10 +11,11 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function uniqid; -/** @group #6217 */ +#[Group('#6217')] final class GH6217Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -25,7 +26,7 @@ protected function setUp(): void $this->createSchemaForModels( GH6217AssociatedEntity::class, - GH6217FetchedEntity::class + GH6217FetchedEntity::class, ); } @@ -58,18 +59,14 @@ public function testLoadingOfSecondLevelCacheOnEagerAssociations(): void } } -/** - * @Entity - * @Cache(usage="NONSTRICT_READ_WRITE") - */ +#[Entity] +#[Cache(usage: 'NONSTRICT_READ_WRITE')] class GH6217AssociatedEntity { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] public $id; public function __construct() @@ -78,31 +75,19 @@ public function __construct() } } -/** - * @Entity - * @Cache(usage="NONSTRICT_READ_WRITE") - */ +#[Entity] +#[Cache(usage: 'NONSTRICT_READ_WRITE')] class GH6217FetchedEntity { - /** - * @var GH6217AssociatedEntity - * @Id - * @Cache("NONSTRICT_READ_WRITE") - * @ManyToOne(targetEntity=GH6217AssociatedEntity::class) - */ - public $lazy; - - /** - * @var GH6217AssociatedEntity - * @Id - * @Cache("NONSTRICT_READ_WRITE") - * @ManyToOne(targetEntity=GH6217AssociatedEntity::class, fetch="EAGER") - */ - public $eager; - - public function __construct(GH6217AssociatedEntity $lazy, GH6217AssociatedEntity $eager) - { - $this->lazy = $lazy; - $this->eager = $eager; + public function __construct( + #[Id] + #[Cache('NONSTRICT_READ_WRITE')] + #[ManyToOne(targetEntity: GH6217AssociatedEntity::class)] + public GH6217AssociatedEntity $lazy, + #[Id] + #[Cache('NONSTRICT_READ_WRITE')] + #[ManyToOne(targetEntity: GH6217AssociatedEntity::class, fetch: 'EAGER')] + public GH6217AssociatedEntity $eager, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6362Test.php b/tests/Tests/ORM/Functional/Ticket/GH6362Test.php index 86ce1adf73d..ccc734a1150 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6362Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6362Test.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Common\Collections\Collection; +use Doctrine\DBAL\Connection; use Doctrine\ORM\Internal\Hydration\ObjectHydrator; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\DiscriminatorColumn; @@ -18,6 +19,7 @@ use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH6362Test extends OrmFunctionalTestCase { @@ -29,19 +31,18 @@ protected function setUp(): void GH6362Start::class, GH6362Base::class, GH6362Child::class, - GH6362Join::class + GH6362Join::class, ); } /** - * @group GH-6362 - * * SELECT a as base, b, c, d * FROM Start a * LEFT JOIN a.bases b * LEFT JOIN Child c WITH b.id = c.id * LEFT JOIN c.joins d */ + #[Group('GH-6362')] public function testInheritanceJoinAlias(): void { $rsm = new ResultSetMapping(); @@ -76,7 +77,7 @@ public function testInheritanceJoinAlias(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class)); $hydrator = new ObjectHydrator($this->_em); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -85,71 +86,52 @@ public function testInheritanceJoinAlias(): void } } -/** @Entity */ +#[Entity] class GH6362Start { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] protected $id; - /** - * @var GH6362Base - * @ManyToOne(targetEntity="GH6362Base", inversedBy="starts") - */ - private $bases; + #[ManyToOne(targetEntity: 'GH6362Base', inversedBy: 'starts')] + private GH6362Base $bases; } -/** - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"child" = "GH6362Child"}) - * @Entity - */ +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string')] +#[DiscriminatorMap(['child' => 'GH6362Child'])] +#[Entity] abstract class GH6362Base { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] protected $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="GH6362Start", mappedBy="bases") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'GH6362Start', mappedBy: 'bases')] private $starts; } -/** @Entity */ +#[Entity] class GH6362Child extends GH6362Base { - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="GH6362Join", mappedBy="child") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'GH6362Join', mappedBy: 'child')] private $joins; } -/** @Entity */ +#[Entity] class GH6362Join { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - private $id; + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] + private int $id; - /** - * @var GH6362Child - * @ManyToOne(targetEntity="GH6362Child", inversedBy="joins") - */ - private $child; + #[ManyToOne(targetEntity: 'GH6362Child', inversedBy: 'joins')] + private GH6362Child $child; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6394Test.php b/tests/Tests/ORM/Functional/Ticket/GH6394Test.php index 999aa8a79e6..1bcfe4a69b1 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6394Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6394Test.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class GH6394Test extends OrmFunctionalTestCase { @@ -24,9 +25,8 @@ protected function setUp(): void /** * Test the the version of an entity can be fetched, when the id field and * the id column are different. - * - * @group 6393 */ + #[Group('6393')] public function testFetchVersionValueForDifferentIdFieldAndColumn(): void { $a = new A(1); @@ -45,56 +45,37 @@ public function testFetchVersionValueForDifferentIdFieldAndColumn(): void } } -/** @Entity */ +#[Entity] class A { - /** - * @Id - * @Column(type="integer") - * @var int - */ - public $id; - - /** - * @Version - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; - public function __construct(int $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + ) { } } -/** @Entity */ +#[Entity] class B { - /** - * @Id - * @ManyToOne(targetEntity="A") - * @JoinColumn(name="aid", referencedColumnName="id") - * @var A - */ - public $a; - - /** - * @Column(type="string", length=255) - * @var string - */ - public $something; - - /** - * @Version - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] public $version; - public function __construct(A $a, string $something) - { - $this->a = $a; - $this->something = $something; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'A')] + #[JoinColumn(name: 'aid', referencedColumnName: 'id')] + public A $a, + #[Column(type: 'string', length: 255)] + public string $something, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6402Test.php b/tests/Tests/ORM/Functional/Ticket/GH6402Test.php index 74db31ebce8..7e302f9e52b 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6402Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6402Test.php @@ -9,8 +9,9 @@ use Doctrine\Tests\Models\Quote\FullAddress; use Doctrine\Tests\Models\Quote\User; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-6402 */ +#[Group('GH-6402')] class GH6402Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH6464Test.php b/tests/Tests/ORM/Functional/Ticket/GH6464Test.php index eae77f9d0d9..4a2b51a3170 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6464Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6464Test.php @@ -12,8 +12,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-6464 */ +#[Group('GH-6464')] class GH6464Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -23,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels( GH6464Post::class, GH6464User::class, - GH6464Author::class + GH6464Author::class, ); } @@ -44,7 +45,7 @@ public function testIssue(): void self::assertDoesNotMatchRegularExpression( '/INNER JOIN \w+ \w+ INNER JOIN/', $query->getSQL(), - 'As of GH-6464, every INNER JOIN should have an ON clause, which is missing here' + 'As of GH-6464, every INNER JOIN should have an ON clause, which is missing here', ); // Query shouldn't yield a result, yet it shouldn't crash (anymore) @@ -52,42 +53,34 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class GH6464Post { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $authorId; } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"author" = "GH6464Author"}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['author' => 'GH6464Author'])] abstract class GH6464User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH6464Author extends GH6464User { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6499OneToManyRelationshipTest.php b/tests/Tests/ORM/Functional/Ticket/GH6499OneToManyRelationshipTest.php index f638e79f013..5c5d4bfc17b 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6499OneToManyRelationshipTest.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6499OneToManyRelationshipTest.php @@ -8,10 +8,9 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH6499 - */ +#[Group('GH6499')] class GH6499OneToManyRelationshipTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -49,27 +48,19 @@ public function testIssue(): void } } -/** - * @ORM\Entity - * @ORM\Table("GH6499OTM_application") - */ +#[ORM\Entity] +#[ORM\Table(name: 'GH6499OTM_application')] class Application { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\OneToMany(targetEntity=ApplicationPerson::class, mappedBy="application", orphanRemoval=true, cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'application', targetEntity: ApplicationPerson::class, orphanRemoval: true, cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] private $applicationPeople; public function __construct() @@ -82,27 +73,20 @@ public function getApplicationPeople(): Collection return $this->applicationPeople; } } -/** - * @ORM\Entity() - * @ORM\Table("GH6499OTM_person") - */ + +#[ORM\Entity] +#[ORM\Table(name: 'GH6499OTM_person')] class Person { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\OneToMany(targetEntity=ApplicationPerson::class, mappedBy="person", orphanRemoval=true, cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'person', targetEntity: ApplicationPerson::class, orphanRemoval: true, cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] private $applicationPeople; public function __construct() @@ -116,35 +100,24 @@ public function getApplicationPeople(): Collection } } -/** - * @ORM\Entity() - * @ORM\Table("GH6499OTM_application_person") - */ +#[ORM\Entity] +#[ORM\Table(name: 'GH6499OTM_application_person')] class ApplicationPerson { - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\ManyToOne(targetEntity=Application::class, inversedBy="applicationPeople", cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var Application - */ + /** @var Application */ + #[ORM\ManyToOne(targetEntity: Application::class, inversedBy: 'applicationPeople', cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] public $application; - /** - * @ORM\ManyToOne(targetEntity=Person::class, inversedBy="applicationPeople", cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var Person - */ + /** @var Person */ + #[ORM\ManyToOne(targetEntity: Person::class, inversedBy: 'applicationPeople', cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] public $person; public function __construct(Person $person, Application $application) diff --git a/tests/Tests/ORM/Functional/Ticket/GH6499OneToOneRelationshipTest.php b/tests/Tests/ORM/Functional/Ticket/GH6499OneToOneRelationshipTest.php index 2922c674733..e7018c85e72 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6499OneToOneRelationshipTest.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6499OneToOneRelationshipTest.php @@ -6,10 +6,9 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH6499 - */ +#[Group('GH6499')] class GH6499OneToOneRelationshipTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -33,29 +32,23 @@ public function testIssue(): void self::assertEquals( $this->_em->find(GH6499OTOA::class, $a->id)->b->id, $a->b->id, - 'Issue #6499 will result in an integrity constraint violation before reaching this point.' + 'Issue #6499 will result in an integrity constraint violation before reaching this point.', ); } } -/** @ORM\Entity */ +#[ORM\Entity] class GH6499OTOA { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToOne(targetEntity="GH6499OTOB", cascade={"persist"}) - * @ORM\JoinColumn(nullable=false) - * - * @var GH6499OTOB - */ + /** @var GH6499OTOB */ + #[ORM\OneToOne(targetEntity: GH6499OTOB::class, cascade: ['persist'])] + #[ORM\JoinColumn(nullable: false)] public $b; public function __construct() @@ -64,15 +57,12 @@ public function __construct() } } -/** @ORM\Entity */ +#[ORM\Entity] class GH6499OTOB { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6499Test.php b/tests/Tests/ORM/Functional/Ticket/GH6499Test.php index a6672801ca4..c03077d3e1b 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6499Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6499Test.php @@ -6,14 +6,14 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** - * @group GH6499 - * * Specifically, GH6499B has a dependency on GH6499A, and GH6499A * has a dependency on GH6499B. Since GH6499A#b is not nullable, * the database row for GH6499B should be inserted first. */ +#[Group('GH6499')] class GH6499Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -57,47 +57,31 @@ public function testIssueReversed(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH6499A { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\JoinColumn(nullable=false) - * @ORM\OneToOne(targetEntity=GH6499B::class) - * - * @var GH6499B - */ + /** @var GH6499B */ + #[ORM\JoinColumn(nullable: false)] + #[ORM\OneToOne(targetEntity: GH6499B::class)] public $b; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH6499B { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\ManyToOne(targetEntity=GH6499A::class) - * - * @var GH6499A - */ + /** @var GH6499A */ + #[ORM\ManyToOne(targetEntity: GH6499A::class)] private $a; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6531Test.php b/tests/Tests/ORM/Functional/Ticket/GH6531Test.php index ea3dca1c7bb..27f2a0a8f47 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6531Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6531Test.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH6531Test extends OrmFunctionalTestCase { @@ -30,11 +31,11 @@ protected function setUp(): void GH6531Order::class, GH6531OrderItem::class, GH6531Product::class, - ] + ], ); } - /** @group GH-6531 */ + #[Group('GH-6531')] public function testSimpleDerivedIdentity(): void { $user = new GH6531User(); @@ -49,7 +50,7 @@ public function testSimpleDerivedIdentity(): void self::assertSame($address, $this->_em->find(GH6531Address::class, $user)); } - /** @group GH-6531 */ + #[Group('GH-6531')] public function testDynamicAttributes(): void { $article = new GH6531Article(); @@ -60,11 +61,11 @@ public function testDynamicAttributes(): void self::assertSame( $article->attributes['name'], - $this->_em->find(GH6531ArticleAttribute::class, ['article' => $article, 'attribute' => 'name']) + $this->_em->find(GH6531ArticleAttribute::class, ['article' => $article, 'attribute' => 'name']), ); } - /** @group GH-6531 */ + #[Group('GH-6531')] public function testJoinTableWithMetadata(): void { $product = new GH6531Product(); @@ -79,49 +80,41 @@ public function testJoinTableWithMetadata(): void self::assertSame( $order->items->first(), - $this->_em->find(GH6531OrderItem::class, ['product' => $product, 'order' => $order]) + $this->_em->find(GH6531OrderItem::class, ['product' => $product, 'order' => $order]), ); } } -/** @Entity */ +#[Entity] class GH6531User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH6531Address { - /** - * @var GH6531User - * @Id - * @OneToOne(targetEntity=GH6531User::class) - */ + /** @var GH6531User */ + #[Id] + #[OneToOne(targetEntity: GH6531User::class)] public $user; } -/** @Entity */ +#[Entity] class GH6531Article { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity=GH6531ArticleAttribute::class, mappedBy="article", cascade={"ALL"}, indexBy="attribute") - * */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: GH6531ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')] public $attributes; public function addAttribute(string $name, string $value): void @@ -130,52 +123,33 @@ public function addAttribute(string $name, string $value): void } } -/** @Entity */ +#[Entity] class GH6531ArticleAttribute { - /** - * @var GH6531Article - * @Id - * @ManyToOne(targetEntity=GH6531Article::class, inversedBy="attributes") - */ - public $article; - - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - public $attribute; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $value; - - public function __construct(string $name, string $value, GH6531Article $article) - { - $this->attribute = $name; - $this->value = $value; - $this->article = $article; + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + public string $attribute, + #[Column(type: 'string', length: 255)] + public string $value, + #[Id] + #[ManyToOne(targetEntity: GH6531Article::class, inversedBy: 'attributes')] + public GH6531Article $article, + ) { } } -/** @Entity */ +#[Entity] class GH6531Order { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity=GH6531OrderItem::class, mappedBy="order", cascade={"ALL"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: GH6531OrderItem::class, mappedBy: 'order', cascade: ['ALL'])] public $items; public function __construct() @@ -189,45 +163,28 @@ public function addItem(GH6531Product $product, int $amount): void } } -/** @Entity */ +#[Entity] class GH6531Product { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH6531OrderItem { - /** - * @var GH6531Order - * @Id - * @ManyToOne(targetEntity=GH6531Order::class) - */ - public $order; - - /** - * @var GH6531Product - * @Id - * @ManyToOne(targetEntity=GH6531Product::class) - */ - public $product; - - /** - * @var int - * @Column(type="integer") - */ - public $amount = 1; - - public function __construct(GH6531Order $order, GH6531Product $product, int $amount = 1) - { - $this->order = $order; - $this->product = $product; - $this->amount = $amount; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: GH6531Order::class)] + public GH6531Order $order, + #[Id] + #[ManyToOne(targetEntity: GH6531Product::class)] + public GH6531Product $product, + #[Column(type: 'integer')] + public int $amount = 1, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6682Test.php b/tests/Tests/ORM/Functional/Ticket/GH6682Test.php index e0812da0faa..440441eb854 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6682Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6682Test.php @@ -6,10 +6,11 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH6682Test extends OrmFunctionalTestCase { - /** @group GH-6682 */ + #[Group('GH-6682')] public function testIssue(): void { $parsedDefinition = [ @@ -23,7 +24,7 @@ public function testIssue(): void self::assertSame( ['sequenceName' => 'test_sequence', 'allocationSize' => '1', 'initialValue' => '1'], - $classMetadata->sequenceGeneratorDefinition + $classMetadata->sequenceGeneratorDefinition, ); } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6699Test.php b/tests/Tests/ORM/Functional/Ticket/GH6699Test.php index 70088a4b719..5944c1a879a 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6699Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6699Test.php @@ -6,8 +6,9 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-6699 */ +#[Group('GH-6699')] final class GH6699Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH6740Test.php b/tests/Tests/ORM/Functional/Ticket/GH6740Test.php index 11c6a6a5306..1eb82f85baf 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6740Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6740Test.php @@ -8,17 +8,15 @@ use Doctrine\Tests\Models\ECommerce\ECommerceCategory; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH6740Test extends OrmFunctionalTestCase { - /** @var int */ - private $productId; + private int $productId; - /** @var int */ - private $firstCategoryId; + private int $firstCategoryId; - /** @var int */ - private $secondCategoryId; + private int $secondCategoryId; protected function setUp(): void { @@ -47,7 +45,7 @@ protected function setUp(): void $this->secondCategoryId = $secondCategory->getId(); } - /** @group GH-6740 */ + #[Group('GH-6740')] public function testCollectionFilteringLteOperator(): void { $product = $this->_em->find(ECommerceProduct::class, $this->productId); @@ -56,7 +54,7 @@ public function testCollectionFilteringLteOperator(): void self::assertCount(2, $product->getCategories()->matching($criteria)); } - /** @group GH-6740 */ + #[Group('GH-6740')] public function testCollectionFilteringLtOperator(): void { $product = $this->_em->find(ECommerceProduct::class, $this->productId); @@ -65,7 +63,7 @@ public function testCollectionFilteringLtOperator(): void self::assertCount(1, $product->getCategories()->matching($criteria)); } - /** @group GH-6740 */ + #[Group('GH-6740')] public function testCollectionFilteringGteOperator(): void { $product = $this->_em->find(ECommerceProduct::class, $this->productId); @@ -74,7 +72,7 @@ public function testCollectionFilteringGteOperator(): void self::assertCount(2, $product->getCategories()->matching($criteria)); } - /** @group GH-6740 */ + #[Group('GH-6740')] public function testCollectionFilteringGtOperator(): void { $product = $this->_em->find(ECommerceProduct::class, $this->productId); @@ -83,7 +81,7 @@ public function testCollectionFilteringGtOperator(): void self::assertCount(1, $product->getCategories()->matching($criteria)); } - /** @group GH-6740 */ + #[Group('GH-6740')] public function testCollectionFilteringEqualsOperator(): void { $product = $this->_em->find(ECommerceProduct::class, $this->productId); diff --git a/tests/Tests/ORM/Functional/Ticket/GH6823Test.php b/tests/Tests/ORM/Functional/Ticket/GH6823Test.php index ae11ab913ec..9a63a8b6b37 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6823Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6823Test.php @@ -5,11 +5,11 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Common\Collections\Collection; -use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -17,8 +17,6 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; -use function method_exists; - class GH6823Test extends OrmFunctionalTestCase { public function testCharsetCollationWhenCreatingForeignRelations(): void @@ -27,132 +25,92 @@ public function testCharsetCollationWhenCreatingForeignRelations(): void self::markTestSkipped('This test is useful for all databases, but designed only for mysql.'); } - if (method_exists(AbstractPlatform::class, 'getGuidExpression')) { - self::markTestSkipped('Test valid for doctrine/dbal:3.x only.'); - } - $this->createSchemaForModels( GH6823User::class, GH6823Group::class, - GH6823Status::class + GH6823Status::class, ); $schemaManager = $this->createSchemaManager(); /* gh6823_user.group_id should use charset ascii and collation * ascii_general_ci, because that is what gh6823_group.id falls back to */ - $userGroupIdOptions = $schemaManager->listTableDetails('gh6823_user')->getColumn('group_id')->toArray(); + $userGroupIdOptions = $schemaManager->introspectTable('gh6823_user')->getColumn('group_id')->toArray(); self::assertSame('ascii', $userGroupIdOptions['charset']); self::assertSame('ascii_general_ci', $userGroupIdOptions['collation']); /* gh6823_user.status_id should use charset latin1 and collation * latin1_bin, because that is what gh6823_status.id uses */ - $userStatusIdOptions = $schemaManager->listTableDetails('gh6823_user')->getColumn('status_id')->toArray(); + $userStatusIdOptions = $schemaManager->introspectTable('gh6823_user')->getColumn('status_id')->toArray(); self::assertSame('latin1', $userStatusIdOptions['charset']); self::assertSame('latin1_bin', $userStatusIdOptions['collation']); /* gh6823_user_tags.user_id should use charset utf8mb4 and collation * utf8mb4_bin, because that is what gh6823_user.id falls back to */ - $userTagsUserIdOptions = $schemaManager->listTableDetails('gh6823_user_tags')->getColumn('user_id')->toArray(); + $userTagsUserIdOptions = $schemaManager->introspectTable('gh6823_user_tags')->getColumn('user_id')->toArray(); self::assertSame('utf8mb4', $userTagsUserIdOptions['charset']); self::assertSame('utf8mb4_bin', $userTagsUserIdOptions['collation']); /* gh6823_user_tags.tag_id should use charset latin1 and collation * latin1_bin, because that is what gh6823_tag.id falls back to */ - $userTagsTagIdOption = $schemaManager->listTableDetails('gh6823_user_tags')->getColumn('tag_id')->toArray(); + $userTagsTagIdOption = $schemaManager->introspectTable('gh6823_user_tags')->getColumn('tag_id')->toArray(); self::assertSame('latin1', $userTagsTagIdOption['charset']); self::assertSame('latin1_bin', $userTagsTagIdOption['collation']); } } -/** - * @Entity - * @Table(name="gh6823_user", options={ - * "charset"="utf8mb4", - * "collation"="utf8mb4_bin" - * }) - */ +#[Table(name: 'gh6823_user', options: ['charset' => 'utf8mb4', 'collation' => 'utf8mb4_bin'])] +#[Entity] class GH6823User { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] public $id; - /** - * @var GH6823Group - * @ManyToOne(targetEntity="GH6823Group") - * @JoinColumn(name="group_id", referencedColumnName="id", options={"charset"="ascii", "collation"="ascii_general_ci"}) - */ + /** @var GH6823Group */ + #[ManyToOne(targetEntity: 'GH6823Group')] + #[JoinColumn(name: 'group_id', referencedColumnName: 'id', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])] public $group; - /** - * @var GH6823Status - * @ManyToOne(targetEntity="GH6823Status") - * @JoinColumn(name="status_id", referencedColumnName="id", options={"charset"="latin1", "collation"="latin1_bin"}) - */ + /** @var GH6823Status */ + #[ManyToOne(targetEntity: 'GH6823Status')] + #[JoinColumn(name: 'status_id', referencedColumnName: 'id', options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])] public $status; - /** - * @var Collection - * @ManyToMany(targetEntity="GH6823Tag") - * @JoinTable(name="gh6823_user_tags", joinColumns={ - * @JoinColumn(name="user_id", referencedColumnName="id", options={"charset"="utf8mb4", "collation"="utf8mb4_bin"}) - * }, inverseJoinColumns={ - * @JoinColumn(name="tag_id", referencedColumnName="id", options={"charset"="latin1", "collation"="latin1_bin"}) - * }, options={"charset"="ascii", "collation"="ascii_general_ci"}) - */ + /** @var Collection */ + #[JoinTable(name: 'gh6823_user_tags', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id', options: ['charset' => 'utf8mb4', 'collation' => 'utf8mb4_bin'])] + #[InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id', options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])] + #[ManyToMany(targetEntity: 'GH6823Tag')] public $tags; } -/** - * @Entity - * @Table(name="gh6823_group", options={ - * "charset"="ascii", - * "collation"="ascii_general_ci" - * }) - */ +#[Table(name: 'gh6823_group', options: ['charset' => 'ascii', 'collation' => 'ascii_general_ci'])] +#[Entity] class GH6823Group { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] public $id; } -/** - * @Entity - * @Table(name="gh6823_status", options={ - * "charset"="koi8r", - * "collation"="koi8r_bin" - * }) - */ +#[Table(name: 'gh6823_status', options: ['charset' => 'koi8r', 'collation' => 'koi8r_bin'])] +#[Entity] class GH6823Status { - /** - * @var string - * @Id - * @Column(type="string", length=255, options={"charset"="latin1", "collation"="latin1_bin"}) - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255, options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])] public $id; } -/** - * @Entity - * @Table(name="gh6823_tag", options={ - * "charset"="koi8r", - * "collation"="koi8r_bin" - * }) - */ +#[Table(name: 'gh6823_tag', options: ['charset' => 'koi8r', 'collation' => 'koi8r_bin'])] +#[Entity] class GH6823Tag { - /** - * @var string - * @Id - * @Column(type="string", length=255, options={"charset"="latin1", "collation"="latin1_bin"}) - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255, options: ['charset' => 'latin1', 'collation' => 'latin1_bin'])] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH6937Test.php b/tests/Tests/ORM/Functional/Ticket/GH6937Test.php index 0eb32c23360..0c6dc146875 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH6937Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH6937Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-6937 */ +#[Group('GH-6937')] final class GH6937Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -89,45 +90,35 @@ public function testPhoneNumberIsPopulatedWithQueryBuilder(): void } } -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"employee"=GH6937Employee::class, "manager"=GH6937Manager::class}) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['employee' => GH6937Employee::class, 'manager' => GH6937Manager::class])] abstract class GH6937Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; } -/** @Entity */ +#[Entity] abstract class GH6937Employee extends GH6937Person { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $phoneNumber; } -/** @Entity */ +#[Entity] class GH6937Manager extends GH6937Employee { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $department; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7006Test.php b/tests/Tests/ORM/Functional/Ticket/GH7006Test.php index e4be3ed83b8..8fceab6a6cc 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7006Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7006Test.php @@ -39,64 +39,42 @@ public function testIssue(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH7006Book { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\Column(type="string", length=255, nullable=true) - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string', length: 255, nullable: true)] public $exchangeCode; - /** - * @ORM\OneToOne(targetEntity="GH7006PCT", cascade={"persist", "remove"}) - * @ORM\JoinColumn(name="paymentCardTransactionId", referencedColumnName="id") - * - * @var GH7006PCT - */ + /** @var GH7006PCT */ + #[ORM\OneToOne(targetEntity: GH7006PCT::class, cascade: ['persist', 'remove'])] + #[ORM\JoinColumn(name: 'paymentCardTransactionId', referencedColumnName: 'id')] public $paymentCardTransaction; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH7006PCT { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH7006Book") - * @ORM\JoinColumn(name="bookingId", referencedColumnName="id", nullable=false) - * - * @var GH7006Book - */ + /** @var GH7006Book */ + #[ORM\ManyToOne(targetEntity: GH7006Book::class)] + #[ORM\JoinColumn(name: 'bookingId', referencedColumnName: 'id', nullable: false)] public $book; - /** - * @ORM\OneToMany(targetEntity="GH7006PCTFee", mappedBy="pct", cascade={"persist", "remove"}) - * @ORM\OrderBy({"id" = "ASC"}) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(targetEntity: GH7006PCTFee::class, mappedBy: 'pct', cascade: ['persist', 'remove'])] + #[ORM\OrderBy(['id' => 'ASC'])] public $fees; public function __construct() @@ -105,26 +83,18 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH7006PCTFee { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH7006PCT", inversedBy="fees") - * @ORM\JoinColumn(name="paymentCardTransactionId", referencedColumnName="id", nullable=false) - * - * @var GH7006PCT - */ + /** @var GH7006PCT */ + #[ORM\ManyToOne(targetEntity: GH7006PCT::class, inversedBy: 'fees')] + #[ORM\JoinColumn(name: 'paymentCardTransactionId', referencedColumnName: 'id', nullable: false)] public $pct; public function __construct(GH7006PCT $pct) diff --git a/tests/Tests/ORM/Functional/Ticket/GH7012Test.php b/tests/Tests/ORM/Functional/Ticket/GH7012Test.php index 836f40ce253..6f24cc8bf60 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7012Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7012Test.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\Models\Quote\User as QuotedUser; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH7012Test extends OrmFunctionalTestCase { @@ -24,7 +25,7 @@ protected function setUp(): void $this->setUpEntitySchema([GH7012UserData::class]); } - /** @group GH-7012 */ + #[Group('GH-7012')] public function testUpdateEntityWithIdentifierAssociationWithQuotedJoinColumn(): void { $user = new QuotedUser(); @@ -43,35 +44,23 @@ public function testUpdateEntityWithIdentifierAssociationWithQuotedJoinColumn(): self::assertSame( '4321', - $this->_em->getRepository(GH7012UserData::class)->findOneBy(['user' => $user])->name + $this->_em->getRepository(GH7012UserData::class)->findOneBy(['user' => $user])->name, ); } } -/** - * @Entity - * @Table(name="`quote-user-data`") - */ +#[Table(name: '`quote-user-data`')] +#[Entity] class GH7012UserData { - /** - * @var QuotedUser - * @Id - * @OneToOne(targetEntity=QuotedUser::class) - * @JoinColumn(name="`user-id`", referencedColumnName="`user-id`", onDelete="CASCADE") - */ - public $user; - - /** - * @var string - * @Column(type="string", name="`name`") - */ - public $name; - - public function __construct(QuotedUser $user, string $name) - { - $this->user = $user; - $this->name = $name; + public function __construct( + #[Id] + #[OneToOne(targetEntity: QuotedUser::class)] + #[JoinColumn(name: '`user-id`', referencedColumnName: '`user-id`', onDelete: 'CASCADE')] + public QuotedUser $user, + #[Column(type: 'string', name: '`name`')] + public string $name, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7062Test.php b/tests/Tests/ORM/Functional/Ticket/GH7062Test.php index 42122bf7222..b8de944a086 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7062Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7062Test.php @@ -15,6 +15,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -33,11 +34,11 @@ protected function setUp(): void GH7062Season::class, GH7062Ranking::class, GH7062RankingPosition::class, - ] + ], ); } - /** @group GH-7062 */ + #[Group('GH-7062')] public function testEntityWithAssociationKeyIdentityCanBeUpdated(): void { $this->createInitialRankingWithRelatedEntities(); @@ -92,31 +93,26 @@ private function verifyRanking(): void /** * Simple Entity whose identity is defined through another Entity (Season) - * - * @Entity - * @Table(name="soccer_rankings") */ +#[Table(name: 'soccer_rankings')] +#[Entity] class GH7062Ranking { /** - * @Id - * @OneToOne(targetEntity=GH7062Season::class, inversedBy="ranking") - * @JoinColumn(name="season", referencedColumnName="id") - * @var GH7062Season - */ - public $season; - - /** - * @OneToMany(targetEntity=GH7062RankingPosition::class, mappedBy="ranking", cascade={"all"}) * @var Collection|GH7062RankingPosition[] * @phpstan-var Collection */ + #[OneToMany(targetEntity: GH7062RankingPosition::class, mappedBy: 'ranking', cascade: ['all'])] public $positions; /** @param GH7062Team[] $teams */ - public function __construct(GH7062Season $season, array $teams) - { - $this->season = $season; + public function __construct( + #[Id] + #[OneToOne(targetEntity: GH7062Season::class, inversedBy: 'ranking')] + #[JoinColumn(name: 'season', referencedColumnName: 'id')] + public GH7062Season $season, + array $teams, + ) { $this->positions = new ArrayCollection(); foreach ($teams as $team) { @@ -127,86 +123,59 @@ public function __construct(GH7062Season $season, array $teams) /** * Entity which serves as a identity provider for other entities - * - * @Entity - * @Table(name="soccer_seasons") */ +#[Table(name: 'soccer_seasons')] +#[Entity] class GH7062Season { - /** - * @Id - * @Column(type="string", length=255) - * @var string - */ - public $id; - - /** - * @var GH7062Ranking|null - * @OneToOne(targetEntity=GH7062Ranking::class, mappedBy="season", cascade={"all"}) - */ + /** @var GH7062Ranking|null */ + #[OneToOne(targetEntity: GH7062Ranking::class, mappedBy: 'season', cascade: ['all'])] public $ranking; - public function __construct(string $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + public string $id, + ) { } } /** * Entity which serves as a identity provider for other entities - * - * @Entity - * @Table(name="soccer_teams") */ +#[Table(name: 'soccer_teams')] +#[Entity] class GH7062Team { - /** - * @Id - * @Column(type="string", length=255) - * @var string - */ - public $id; - - public function __construct(string $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + public string $id, + ) { } } /** * Entity whose identity is defined through two other entities - * - * @Entity - * @Table(name="soccer_ranking_positions") */ +#[Table(name: 'soccer_ranking_positions')] +#[Entity] class GH7062RankingPosition { - /** - * @Id - * @ManyToOne(targetEntity=GH7062Ranking::class, inversedBy="positions") - * @JoinColumn(name="season", referencedColumnName="season") - * @var GH7062Ranking - */ - public $ranking; - - /** - * @Id - * @ManyToOne(targetEntity=GH7062Team::class) - * @JoinColumn(name="team_id", referencedColumnName="id") - * @var GH7062Team - */ - public $team; - - /** - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Column(type: 'integer')] public $points; - public function __construct(GH7062Ranking $ranking, GH7062Team $team) - { - $this->ranking = $ranking; - $this->team = $team; - $this->points = 0; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: GH7062Ranking::class, inversedBy: 'positions')] + #[JoinColumn(name: 'season', referencedColumnName: 'season')] + public GH7062Ranking $ranking, + #[Id] + #[ManyToOne(targetEntity: GH7062Team::class)] + #[JoinColumn(name: 'team_id', referencedColumnName: 'id')] + public GH7062Team $team, + ) { + $this->points = 0; } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7067Test.php b/tests/Tests/ORM/Functional/Ticket/GH7067Test.php index 91b5aabc05e..b5c6345cfb8 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7067Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7067Test.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Version; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -26,7 +27,7 @@ protected function setUp(): void $this->setUpEntitySchema([GH7067Entity::class]); } - /** @group GH-7067 */ + #[Group('GH-7067')] public function testSLCWithVersion(): void { $entity = new GH7067Entity(); @@ -48,30 +49,22 @@ public function testSLCWithVersion(): void } } -/** - * @Entity() - * @Cache(usage="NONSTRICT_READ_WRITE") - */ +#[Entity] +#[Cache(usage: 'NONSTRICT_READ_WRITE')] class GH7067Entity { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @Column(type="datetime") - * @var DateTime - */ + /** @var DateTime */ + #[Column(type: 'datetime')] public $lastUpdate; - /** - * @Column(type="datetime") - * @Version - * @var DateTime - */ + /** @var DateTime */ + #[Column(type: 'datetime')] + #[Version] public $version; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7068Test.php b/tests/Tests/ORM/Functional/Ticket/GH7068Test.php index da83bfd15d1..7d3d44ae2fb 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7068Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7068Test.php @@ -21,7 +21,7 @@ protected function setUp(): void $this->setUpEntitySchema( [ SomeEntity::class, - ] + ], ); } @@ -39,14 +39,12 @@ public function testLockModeIsRespected(): void } } -/** @Entity */ +#[Entity] final class SomeEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7079Test.php b/tests/Tests/ORM/Functional/Ticket/GH7079Test.php index d477301ba64..508d77ef14e 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7079Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7079Test.php @@ -16,15 +16,14 @@ use Doctrine\ORM\Mapping\Table; use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH7079 */ +#[Group('GH7079')] final class GH7079Test extends OrmFunctionalTestCase { - /** @var DefaultQuoteStrategy */ - private $strategy; + private DefaultQuoteStrategy $strategy; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; protected function setUp(): void { @@ -61,23 +60,18 @@ public function testJoinTableName(): void 'targetEntity' => 'DDC7079CmsUser', 'inversedBy' => 'users', 'joinTable' => $table, - ] + ], ); self::assertEquals( $this->getTableFullName($table), - $this->strategy->getJoinTableName($cm->associationMappings['user'], $cm, $this->platform) + $this->strategy->getJoinTableName($cm->associationMappings['user'], $cm, $this->platform), ); } private function getTableFullName(array $table): string { - $join = '.'; - if (! $this->platform->supportsSchemas() && $this->platform->canEmulateSchemas()) { - $join = '__'; - } - - return $table['schema'] . $join . $table['name']; + return $table['schema'] . '.' . $table['name']; } private function createClassMetadata(string $className): ClassMetadata @@ -89,45 +83,33 @@ private function createClassMetadata(string $className): ClassMetadata } } -/** - * @Entity - * @Table(name="cms_users", schema="cms") - */ +#[Table(name: 'cms_users', schema: 'cms')] +#[Entity] class GH7079CmsUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH7079CmsAddress - * @OneToOne(targetEntity=GH7079CmsAddress::class, mappedBy="user", cascade={"persist"}, orphanRemoval=true) - */ + /** @var GH7079CmsAddress */ + #[OneToOne(targetEntity: GH7079CmsAddress::class, mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] public $address; } -/** - * @Entity - * @Table(name="cms_addresses", schema="cms") - */ +#[Table(name: 'cms_addresses', schema: 'cms')] +#[Entity] class GH7079CmsAddress { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] public $id; - /** - * @var GH7079CmsUser - * @OneToOne(targetEntity=GH7079CmsUser::class, inversedBy="address") - * @JoinColumn(referencedColumnName="id") - */ + /** @var GH7079CmsUser */ + #[OneToOne(targetEntity: GH7079CmsUser::class, inversedBy: 'address')] + #[JoinColumn(referencedColumnName: 'id')] public $user; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7180Test.php b/tests/Tests/ORM/Functional/Ticket/GH7180Test.php index 46431647f75..f9e524d9c30 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7180Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7180Test.php @@ -12,13 +12,13 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Tests suggested in https://github.com/doctrine/orm/pull/7180#issuecomment-380841413 and * https://github.com/doctrine/orm/pull/7180#issuecomment-381067448. - * - * @group 7180 */ +#[Group('GH7180')] final class GH7180Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -75,149 +75,106 @@ public function testIssue3NodeCycle(): void } } -/** - * @Entity - */ +#[Entity] class GH7180A { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @OneToOne(targetEntity=GH7180B::class, inversedBy="a") - * @JoinColumn(nullable=false) - * @var GH7180B - */ + /** @var GH7180B */ + #[OneToOne(targetEntity: GH7180B::class, inversedBy: 'a')] + #[JoinColumn(nullable: false)] public $b; } -/** - * @Entity - */ +#[Entity] class GH7180B { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @OneToOne(targetEntity=GH7180A::class, mappedBy="b") - * @JoinColumn(nullable=true) - * @var GH7180A - */ + /** @var GH7180A */ + #[OneToOne(targetEntity: GH7180A::class, mappedBy: 'b')] public $a; } -/** - * @Entity - */ +#[Entity] class GH7180C { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7180A::class) - * @JoinColumn(nullable=false) - * @var GH7180A - */ + /** @var GH7180A */ + #[ManyToOne(targetEntity: GH7180A::class)] + #[JoinColumn(nullable: false)] public $a; } -/** - * @Entity - */ +#[Entity] class GH7180D { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @OneToOne(targetEntity=GH7180E::class) - * @JoinColumn(nullable=false) - * @var GH7180E - */ + /** @var GH7180E */ + #[OneToOne(targetEntity: GH7180E::class)] + #[JoinColumn(nullable: false)] public $e; } -/** - * @Entity - */ +#[Entity] class GH7180E { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @OneToOne(targetEntity=GH7180F::class) - * @JoinColumn(nullable=false) - * @var GH7180F - */ + /** @var GH7180F */ + #[OneToOne(targetEntity: GH7180F::class)] + #[JoinColumn(nullable: false)] public $f; } -/** - * @Entity - */ +#[Entity] class GH7180F { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7180D::class) - * @JoinColumn(nullable=true) - * @var GH7180D - */ + /** @var GH7180D */ + #[ManyToOne(targetEntity: GH7180D::class)] + #[JoinColumn(nullable: true)] public $d; } -/** - * @Entity - */ +#[Entity] class GH7180G { - /** - * @GeneratedValue() - * @Id - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[GeneratedValue] + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7180D::class) - * @JoinColumn(nullable=false) - * @var GH7180D - */ + /** @var GH7180D */ + #[ManyToOne(targetEntity: GH7180D::class)] + #[JoinColumn(nullable: false)] public $d; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7259Test.php b/tests/Tests/ORM/Functional/Ticket/GH7259Test.php index 2bbb9bd4f91..2945bcef595 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7259Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7259Test.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH7259Test extends OrmFunctionalTestCase { @@ -21,7 +22,7 @@ protected function setUp(): void $this->setUpEntitySchema([GH7259Space::class, GH7259File::class, GH7259FileVersion::class, GH7259Feed::class]); } - /** @group GH-7259 */ + #[Group('GH-7259')] public function testPersistFileBeforeVersion(): void { $space = new GH7259Space(); @@ -46,7 +47,7 @@ public function testPersistFileBeforeVersion(): void self::assertNotNull($fileVersion->id); } - /** @group GH-7259 */ + #[Group('GH-7259')] public function testPersistFileAfterVersion(): void { $space = new GH7259Space(); @@ -75,78 +76,62 @@ public function testPersistFileAfterVersion(): void } } -/** @Entity() */ +#[Entity] class GH7259File { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7259Space::class) - * @JoinColumn(nullable=false) - * @var GH7259Space|null - */ + /** @var GH7259Space|null */ + #[ManyToOne(targetEntity: GH7259Space::class)] + #[JoinColumn(nullable: false)] public $space; } -/** @Entity() */ +#[Entity] class GH7259FileVersion { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7259File::class) - * @JoinColumn(nullable=false) - * @var GH7259File|null - */ + /** @var GH7259File|null */ + #[ManyToOne(targetEntity: GH7259File::class)] + #[JoinColumn(nullable: false)] public $file; } -/** @Entity() */ +#[Entity] class GH7259Space { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7259File::class) - * @JoinColumn(nullable=true) - * @var GH7259File|null - */ + /** @var GH7259File|null */ + #[ManyToOne(targetEntity: GH7259File::class)] + #[JoinColumn(nullable: true)] public $ruleFile; } -/** @Entity() */ +#[Entity] class GH7259Feed { - /** - * @Id - * @GeneratedValue - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @ManyToOne(targetEntity=GH7259Space::class) - * @JoinColumn(nullable=false) - * @var GH7259Space|null - */ + /** @var GH7259Space|null */ + #[ManyToOne(targetEntity: GH7259Space::class)] + #[JoinColumn(nullable: false)] public $space; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7286Test.php b/tests/Tests/ORM/Functional/Ticket/GH7286Test.php index e46c963277b..497f1d50143 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7286Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7286Test.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TokenType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH7286Test extends OrmFunctionalTestCase { @@ -24,7 +25,7 @@ protected function setUp(): void $this->setUpEntitySchema( [ GH7286Entity::class, - ] + ], ); $this->_em->persist(new GH7286Entity('foo', 1)); @@ -42,7 +43,7 @@ public function testAggregateExpressionInFunction(): void . ' FROM ' . GH7286Entity::class . ' e' . ' WHERE e.type IS NOT NULL' . ' GROUP BY e.type' - . ' ORDER BY e.type' + . ' ORDER BY e.type', ); self::assertSame( @@ -50,11 +51,11 @@ public function testAggregateExpressionInFunction(): void ['pair' => 'bar3'], ['pair' => 'foo1'], ], - $query->getArrayResult() + $query->getArrayResult(), ); } - /** @group DDC-1091 */ + #[Group('DDC-1091')] public function testAggregateFunctionInCustomFunction(): void { $this->_em->getConfiguration()->addCustomStringFunction('CC', GH7286CustomConcat::class); @@ -63,54 +64,40 @@ public function testAggregateFunctionInCustomFunction(): void 'SELECT CC(e.type, MIN(e.version)) pair' . ' FROM ' . GH7286Entity::class . ' e' . ' WHERE e.type IS NOT NULL AND e.type != :type' - . ' GROUP BY e.type' + . ' GROUP BY e.type', ); $query->setParameter('type', 'bar'); self::assertSame( ['pair' => 'foo1'], - $query->getSingleResult() + $query->getSingleResult(), ); } } -/** @Entity */ +#[Entity] class GH7286Entity { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @Column(nullable=true) - * @var string|null - */ - public $type; - - /** - * @Column(type="integer") - * @var int - */ - public $version; - - public function __construct(?string $type, int $version) - { - $this->type = $type; - $this->version = $version; + public function __construct( + #[Column(nullable: true)] + public string|null $type, + #[Column(type: 'integer')] + public int $version, + ) { } } class GH7286CustomConcat extends FunctionNode { - /** @var Node */ - private $first; + private Node|null $first = null; - /** @var Node */ - private $second; + private Node|null $second = null; public function parse(Parser $parser): void { @@ -128,7 +115,7 @@ public function getSql(SqlWalker $walker): string { return $walker->getConnection()->getDatabasePlatform()->getConcatExpression( $this->first->dispatch($walker), - $this->second->dispatch($walker) + $this->second->dispatch($walker), ); } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7366Test.php b/tests/Tests/ORM/Functional/Ticket/GH7366Test.php index a3bc4d83475..23ed7700763 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7366Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7366Test.php @@ -22,7 +22,7 @@ protected function setUp(): void $this->setUpEntitySchema( [ GH7366Entity::class, - ] + ], ); $this->_em->persist(new GH7366Entity('baz')); @@ -34,7 +34,7 @@ public function testOptimisticLockNoExceptionOnFind(): void { try { $entity = $this->_em->find(GH7366Entity::class, 1, LockMode::OPTIMISTIC); - } catch (TransactionRequiredException $e) { + } catch (TransactionRequiredException) { self::fail('EntityManager::find() threw TransactionRequiredException with LockMode::OPTIMISTIC'); } @@ -42,33 +42,24 @@ public function testOptimisticLockNoExceptionOnFind(): void } } -/** @Entity */ +#[Entity] class GH7366Entity { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var int - * @Column(type="integer") - * @Version - */ + /** @var int */ + #[Column(type: 'integer')] + #[Version] protected $lockVersion = 1; - /** - * @Column(length=32) - * @var string - */ - protected $name; - - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column(length: 32)] + protected string $name, + ) { } public function getName(): string diff --git a/tests/Tests/ORM/Functional/Ticket/GH7407Test.php b/tests/Tests/ORM/Functional/Ticket/GH7407Test.php deleted file mode 100644 index c12d3da3279..00000000000 --- a/tests/Tests/ORM/Functional/Ticket/GH7407Test.php +++ /dev/null @@ -1,88 +0,0 @@ -useModelSet('cms'); - - parent::setUp(); - } - - public function testMergingEntitiesDoesNotCreateUnmanagedProxyReferences(): void - { - // 1. Create an article with a user; persist, flush and clear the entity manager - $user = new CmsUser(); - $user->username = 'Test'; - $user->name = 'Test'; - $this->_em->persist($user); - - $article = new CmsArticle(); - $article->topic = 'Test'; - $article->text = 'Test'; - $article->setAuthor($user); - $this->_em->persist($article); - - $this->_em->flush(); - $this->_em->clear(); - - // 2. Merge the user object back in: - // We get a new (different) entity object that represents the user instance - // which is now (through this object instance) managed by the EM/UoW - $mergedUser = $this->_em->merge($user); - $mergedUserOid = spl_object_id($mergedUser); - - // 3. Merge the article object back in, - // the returned entity object is the article instance as it is managed by the EM/UoW - $mergedArticle = $this->_em->merge($article); - $mergedArticleOid = spl_object_id($mergedArticle); - - self::assertSame($mergedUser, $mergedArticle->user, 'The $mergedArticle\'s #user property should hold the $mergedUser we obtained previously, since that\'s the only legitimate object instance representing the user from the UoW\'s point of view.'); - - // Inspect internal UoW state - $uow = $this->_em->getUnitOfWork(); - $entityIdentifiers = $this->grabProperty('entityIdentifiers', $uow); - $identityMap = $this->grabProperty('identityMap', $uow); - $entityStates = $this->grabProperty('entityStates', $uow); - - self::assertCount(2, $entityIdentifiers, 'UoW#entityIdentifiers contains exactly two OID -> ID value mapping entries one for the article, one for the user object'); - self::assertArrayHasKey($mergedArticleOid, $entityIdentifiers); - self::assertArrayHasKey($mergedUserOid, $entityIdentifiers); - - self::assertSame([ - $mergedUserOid => UnitOfWork::STATE_MANAGED, - $mergedArticleOid => UnitOfWork::STATE_MANAGED, - ], $entityStates, 'UoW#entityStates contains two OID -> state entries, one for the article, one for the user object'); - - self::assertCount(2, $entityIdentifiers); - self::assertArrayHasKey($mergedArticleOid, $entityIdentifiers); - self::assertArrayHasKey($mergedUserOid, $entityIdentifiers); - - self::assertSame([ - CmsUser::class => [$user->id => $mergedUser], - CmsArticle::class => [$article->id => $mergedArticle], - ], $identityMap, 'The identity map contains exactly two objects, the article and the user.'); - } - - /** @return mixed */ - private function grabProperty(string $name, UnitOfWork $uow) - { - $reflection = new ReflectionClass($uow); - $property = $reflection->getProperty($name); - $property->setAccessible(true); - - return $property->getValue($uow); - } -} diff --git a/tests/Tests/ORM/Functional/Ticket/GH7496WithToIterableTest.php b/tests/Tests/ORM/Functional/Ticket/GH7496WithToIterableTest.php index 20a6d0c7577..be502f3a792 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7496WithToIterableTest.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7496WithToIterableTest.php @@ -24,7 +24,7 @@ protected function setUp(): void GH7496EntityA::class, GH7496EntityB::class, GH7496EntityAinB::class, - ] + ], ); $this->_em->persist($a1 = new GH7496EntityA(1, 'A#1')); @@ -40,11 +40,11 @@ protected function setUp(): void public function testNonUniqueObjectHydrationDuringIteration(): void { $q = $this->_em->createQuery( - 'SELECT b FROM ' . GH7496EntityAinB::class . ' aib JOIN ' . GH7496EntityB::class . ' b WITH aib.eB = b' + 'SELECT b FROM ' . GH7496EntityAinB::class . ' aib JOIN ' . GH7496EntityB::class . ' b WITH aib.eB = b', ); $bs = IterableTester::iterableToArray( - $q->toIterable([], AbstractQuery::HYDRATE_OBJECT) + $q->toIterable([], AbstractQuery::HYDRATE_OBJECT), ); self::assertCount(2, $bs); @@ -54,7 +54,7 @@ public function testNonUniqueObjectHydrationDuringIteration(): void self::assertEquals(1, $bs[1]->id); $bs = IterableTester::iterableToArray( - $q->toIterable([], AbstractQuery::HYDRATE_ARRAY) + $q->toIterable([], AbstractQuery::HYDRATE_ARRAY), ); self::assertCount(2, $bs); @@ -63,80 +63,45 @@ public function testNonUniqueObjectHydrationDuringIteration(): void } } -/** @Entity */ +#[Entity] class GH7496EntityA { - /** - * @var int - * @Id - * @Column(type="integer", name="a_id") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct(int $id, string $name) - { - $this->id = $id; - $this->name = $name; + public function __construct( + #[Id] + #[Column(type: 'integer', name: 'a_id')] + public int $id, + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class GH7496EntityB { - /** - * @var int - * @Id - * @Column(type="integer", name="b_id") - */ - public $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - public function __construct(int $id, $name) - { - $this->id = $id; - $this->name = $name; + public function __construct( + #[Id] + #[Column(type: 'integer', name: 'b_id')] + public int $id, + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class GH7496EntityAinB { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; - - /** - * @var GH7496EntityA - * @ManyToOne(targetEntity=GH7496EntityA::class) - * @JoinColumn(name="a_id", referencedColumnName="a_id", nullable=false) - */ - public $eA; - - /** - * @var GH7496EntityB - * @ManyToOne(targetEntity=GH7496EntityB::class) - * @JoinColumn(name="b_id", referencedColumnName="b_id", nullable=false) - */ - public $eB; - - public function __construct(int $id, $a, $b) - { - $this->id = $id; - $this->eA = $a; - $this->eB = $b; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + #[ManyToOne(targetEntity: GH7496EntityA::class)] + #[JoinColumn(name: 'a_id', referencedColumnName: 'a_id', nullable: false)] + public GH7496EntityA $eA, + #[ManyToOne(targetEntity: GH7496EntityB::class)] + #[JoinColumn(name: 'b_id', referencedColumnName: 'b_id', nullable: false)] + public GH7496EntityB $eB, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7505Test.php b/tests/Tests/ORM/Functional/Ticket/GH7505Test.php index 3cda57fbad7..58bc559dad1 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7505Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7505Test.php @@ -13,10 +13,11 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; -/** @group GH7505 */ +#[Group('GH7505')] final class GH7505Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -53,43 +54,32 @@ public function testSimpleArrayTypeHydratedCorrectly(): void } } -/** - * @Entity() - * @Table(name="gh7505_responses") - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "array" = GH7505ArrayResponse::class, - * "text" = GH7505TextResponse::class, - * }) - */ +#[Table(name: 'gh7505_responses')] +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['array' => GH7505ArrayResponse::class, 'text' => GH7505TextResponse::class])] abstract class GH7505AbstractResponse { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; } -/** @Entity() */ +#[Entity] class GH7505ArrayResponse extends GH7505AbstractResponse { - /** - * @var mixed[] - * @Column(name="value_array", type="simple_array") - */ + /** @var mixed[] */ + #[Column(name: 'value_array', type: 'simple_array')] public $value = []; } -/** @Entity() */ +#[Entity] class GH7505TextResponse extends GH7505AbstractResponse { - /** - * @Column(name="value_string", type="string", length=255) - * @var string|null - */ + /** @var string|null */ + #[Column(name: 'value_string', type: 'string', length: 255)] public $value; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7512Test.php b/tests/Tests/ORM/Functional/Ticket/GH7512Test.php index fa58570274f..a3305c5ef3e 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7512Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7512Test.php @@ -44,50 +44,37 @@ public function testFindEntityByAssociationPropertyJoinedChildWithClearMetadata( } } -/** - * @Entity() - * @InheritanceType("JOINED") - * @DiscriminatorMap({ - * "entitya"=GH7512EntityA::class, - * "entityB"=GH7512EntityB::class - * }) - */ +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorMap(['entitya' => GH7512EntityA::class, 'entityB' => GH7512EntityB::class])] class GH7512EntityA { - /** - * @Column(type="integer") - * @Id() - * @GeneratedValue(strategy="AUTO") - * @var int - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @OneToMany(targetEntity="GH7512EntityC", mappedBy="entityA") - * @var Collection - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'GH7512EntityC', mappedBy: 'entityA')] public $entityCs; } -/** @Entity() */ +#[Entity] class GH7512EntityB extends GH7512EntityA { } -/** @Entity() */ +#[Entity] class GH7512EntityC { - /** - * @Column(type="integer") - * @Id() - * @GeneratedValue(strategy="AUTO") - * @var int - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ManyToOne(targetEntity="GH7512EntityA", inversedBy="entityCs") - * @var GH7512EntityA - */ + /** @var GH7512EntityA */ + #[ManyToOne(targetEntity: 'GH7512EntityA', inversedBy: 'entityCs')] public $entityA; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7629Test.php b/tests/Tests/ORM/Functional/Ticket/GH7629Test.php index afe4057cc7d..53133516bbe 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7629Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7629Test.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class GH7629Test extends OrmFunctionalTestCase { @@ -36,7 +37,7 @@ public function testClearScheduledForSynchronizationWhenCommitEmpty(): void self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDirtyCheck($entity)); } - /** @group GH-8231 */ + #[Group('GH-8231')] public function testPersistAfterRemoveSchedulesForSynchronization(): void { $entity = $this->_em->find(GH7629Entity::class, 1); @@ -49,17 +50,13 @@ public function testPersistAfterRemoveSchedulesForSynchronization(): void } } -/** - * @Entity - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class GH7629Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7661Test.php b/tests/Tests/ORM/Functional/Ticket/GH7661Test.php index 6d01f9dc95b..a267cc6071d 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7661Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7661Test.php @@ -11,10 +11,11 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_keys; -/** @group GH-7661 */ +#[Group('GH-7661')] class GH7661Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -51,59 +52,43 @@ public function testIndexByAssociation(): void } } -/** @Entity */ +#[Entity] class GH7661User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH7661Event { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH7661Participant[] - * @OneToMany(targetEntity=GH7661Participant::class, mappedBy="event", indexBy="user_id") - */ + /** @var GH7661Participant[] */ + #[OneToMany(targetEntity: GH7661Participant::class, mappedBy: 'event', indexBy: 'user_id')] public $participants; } -/** @Entity */ +#[Entity] class GH7661Participant { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH7661User - * @ManyToOne(targetEntity=GH7661User::class) - */ - public $user; - /** - * @var GH7661Event - * @ManyToOne(targetEntity=GH7661Event::class) - */ - public $event; - public function __construct(GH7661User $user, GH7661Event $event) - { - $this->user = $user; - $this->event = $event; + public function __construct( + #[ManyToOne(targetEntity: GH7661User::class)] + public GH7661User $user, + #[ManyToOne(targetEntity: GH7661Event::class)] + public GH7661Event $event, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7684Test.php b/tests/Tests/ORM/Functional/Ticket/GH7684Test.php index d0c0b824b69..654b1cd8655 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7684Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7684Test.php @@ -16,10 +16,6 @@ class GH7684Test extends DatabaseDriverTestCase { public function testIssue(): void { - if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { - self::markTestSkipped('Platform does not support foreign keys.'); - } - $table1 = new Table('GH7684_identity_test_table'); $table1->addColumn('id', 'integer'); $table1->setPrimaryKey(['id']); diff --git a/tests/Tests/ORM/Functional/Ticket/GH7717Test.php b/tests/Tests/ORM/Functional/Ticket/GH7717Test.php index 96c519e8624..81e5c1d6202 100755 --- a/tests/Tests/ORM/Functional/Ticket/GH7717Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7717Test.php @@ -10,9 +10,6 @@ use Doctrine\Tests\Models\GH7717\GH7717Parent; use Doctrine\Tests\OrmFunctionalTestCase; -/** - * @requires PHP 7.4 - */ final class GH7717Test extends OrmFunctionalTestCase { public function setUp(): void @@ -21,7 +18,7 @@ public function setUp(): void $this->createSchemaForModels( GH7717Parent::class, - GH7717Child::class + GH7717Child::class, ); } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7735Test.php b/tests/Tests/ORM/Functional/Ticket/GH7735Test.php index e58c48947a5..99dc13b87d9 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7735Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7735Test.php @@ -12,6 +12,8 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use function assert; @@ -26,7 +28,7 @@ protected function setUp(): void $this->createSchemaForModels( GH7735Car::class, GH7735Power::class, - GH7735Engine::class + GH7735Engine::class, ); $this->_em->persist(new GH7735Car(1, new GH7735Engine(1, 'turbo', new GH7735Power(1)))); @@ -34,10 +36,8 @@ protected function setUp(): void $this->_em->clear(); } - /** - * @test - * @group GH7735 - */ + #[Test] + #[Group('GH7735')] public function findByReturnsCachedEntity(): void { $this->_em->getCache()->evictEntityRegion(GH7735Power::class); @@ -50,31 +50,19 @@ public function findByReturnsCachedEntity(): void } } -/** - * @Entity - * @Cache(usage="READ_ONLY") - */ +#[Entity] +#[Cache(usage: 'READ_ONLY')] class GH7735Car { - /** - * @Id - * @Column(type="integer") - * @var int - */ - private $id; - - /** - * @ManyToOne(targetEntity=GH7735Engine::class, cascade={"all"}) - * @JoinColumn(nullable=false) - * @Cache("READ_ONLY") - * @var GH7735Engine - */ - private $engine; - - public function __construct(int $id, GH7735Engine $engine) - { - $this->id = $id; - $this->engine = $engine; + public function __construct( + #[Id] + #[Column(type: 'integer')] + private int $id, + #[ManyToOne(targetEntity: GH7735Engine::class, cascade: ['all'])] + #[JoinColumn(nullable: false)] + #[Cache('READ_ONLY')] + private GH7735Engine $engine, + ) { } public function getId(): int @@ -88,38 +76,20 @@ public function getEngine(): GH7735Engine } } -/** - * @Entity - * @Cache(usage="READ_ONLY") - */ +#[Entity] +#[Cache(usage: 'READ_ONLY')] class GH7735Engine { - /** - * @Id - * @Column(type="integer") - * @var int - */ - private $id; - - /** - * @var GH7735Power - * @OneToOne(targetEntity=GH7735Power::class, mappedBy="engine", cascade={"all"}) - * @Cache("READ_ONLY") - */ - private $power; - - /** - * @Column - * @var string - */ - private $model; - - public function __construct(int $id, string $model, GH7735Power $power) - { - $this->id = $id; - $this->model = $model; - $this->power = $power; - + public function __construct( + #[Id] + #[Column(type: 'integer')] + private int $id, + #[Column] + private string $model, + #[OneToOne(targetEntity: GH7735Power::class, mappedBy: 'engine', cascade: ['all'])] + #[Cache('READ_ONLY')] + private GH7735Power $power, + ) { $power->setEngine($this); } @@ -139,29 +109,19 @@ public function getModel(): string } } -/** - * @Entity - * @Cache(usage="READ_ONLY") - */ +#[Entity] +#[Cache(usage: 'READ_ONLY')] class GH7735Power { - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var GH7735Engine - * @OneToOne(targetEntity=GH7735Engine::class, inversedBy="power") - * @Cache("READ_ONLY") - */ - private $engine; - - public function __construct(int $id) - { - $this->id = $id; + #[OneToOne(targetEntity: GH7735Engine::class, inversedBy: 'power')] + #[Cache('READ_ONLY')] + private GH7735Engine|null $engine = null; + + public function __construct( + #[Id] + #[Column(type: 'integer')] + private int $id, + ) { } public function getId(): int diff --git a/tests/Tests/ORM/Functional/Ticket/GH7737Test.php b/tests/Tests/ORM/Functional/Ticket/GH7737Test.php index 1f4fd5580d4..774820eb5d6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7737Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7737Test.php @@ -10,12 +10,14 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; -/** @group GH7737 */ +#[Group('GH7737')] class GH7737Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -35,7 +37,7 @@ protected function setUp(): void $this->_em->clear(); } - /** @test */ + #[Test] public function memberOfCriteriaShouldBeCompatibleWithQueryBuilder(): void { $query = $this->_em->createQueryBuilder() @@ -57,49 +59,33 @@ public function memberOfCriteriaShouldBeCompatibleWithQueryBuilder(): void } } -/** @Entity */ +#[Entity] class GH7737Group { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; - - /** - * @var string - * @Column - */ - public $name; - - public function __construct(int $id, string $name) - { - $this->id = $id; - $this->name = $name; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + #[Column] + public string $name, + ) { } } -/** @Entity */ +#[Entity] class GH7737Person { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; - - /** - * @var Collection - * @ManyToMany(targetEntity=GH7737Group::class) - * @JoinTable(inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id", unique=true)}) - */ + /** @var Collection */ + #[JoinTable] + #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', unique: true)] + #[ManyToMany(targetEntity: GH7737Group::class)] public $groups; - public function __construct(int $id) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'integer')] + public int $id, + ) { $this->groups = new ArrayCollection(); } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7761Test.php b/tests/Tests/ORM/Functional/Ticket/GH7761Test.php index 93fe7d714f7..02cad1fee19 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7761Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7761Test.php @@ -11,10 +11,12 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -53,7 +55,7 @@ public function testCollectionClearDoesNotClearIfNotPersisted(): void self::assertCount(1, $entity->children); } - /** @group GH-7862 */ + #[Group('GH-7862')] public function testCollectionClearDoesClearIfPersisted(): void { $entity = $this->_em->find(GH7761Entity::class, 1); @@ -69,28 +71,21 @@ public function testCollectionClearDoesClearIfPersisted(): void } } -/** - * @Entity - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class GH7761Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Collection - * @ManyToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH7761ChildEntity", cascade={"all"}) - * @JoinTable(name="gh7761_to_child", - * joinColumns={@JoinColumn(name="entity_id")}, - * inverseJoinColumns={@JoinColumn(name="child_id")} - * ) - */ + /** @var Collection */ + #[JoinTable(name: 'gh7761_to_child')] + #[JoinColumn(name: 'entity_id')] + #[InverseJoinColumn(name: 'child_id')] + #[ManyToMany(targetEntity: 'Doctrine\Tests\ORM\Functional\Ticket\GH7761ChildEntity', cascade: ['all'])] public $children; public function __construct() @@ -99,17 +94,13 @@ public function __construct() } } -/** - * @Entity - * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") - */ +#[Entity] +#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')] class GH7761ChildEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7767Test.php b/tests/Tests/ORM/Functional/Ticket/GH7767Test.php index cd222700bed..2eb32dd7c92 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7767Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7767Test.php @@ -16,11 +16,12 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OrderBy; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; use function class_exists; -/** @group GH7767 */ +#[Group('GH7767')] class GH7767Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -57,7 +58,7 @@ public function testMatchingOverrulesCollectionOrdering(): void assert($parent instanceof GH7767ParentEntity); $children = $parent->getChildren()->matching( - Criteria::create()->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC']) + Criteria::create()->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC']), ); self::assertEquals(300, $children[0]->position); @@ -66,22 +67,17 @@ public function testMatchingOverrulesCollectionOrdering(): void } } -/** @Entity */ +#[Entity] class GH7767ParentEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @phpstan-var Collection&Selectable - * @OneToMany(targetEntity=GH7767ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"}) - * @OrderBy({"position" = "ASC"}) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection&Selectable */ + #[OneToMany(targetEntity: GH7767ChildEntity::class, mappedBy: 'parent', fetch: 'EXTRA_LAZY', cascade: ['persist'])] + #[OrderBy(['position' => 'ASC'])] private $children; public function addChild(int $position): void @@ -96,32 +92,19 @@ public function getChildren(): Collection } } -/** @Entity */ +#[Entity] class GH7767ChildEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var int - * @Column(type="integer") - */ - public $position; - - /** - * @var GH7767ParentEntity - * @ManyToOne(targetEntity=GH7767ParentEntity::class, inversedBy="children") - */ - private $parent; - - public function __construct(GH7767ParentEntity $parent, int $position) - { - $this->parent = $parent; - $this->position = $position; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + public function __construct( + #[ManyToOne(targetEntity: GH7767ParentEntity::class, inversedBy: 'children')] + private GH7767ParentEntity $parent, + #[Column(type: 'integer')] + public int $position, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7820Test.php b/tests/Tests/ORM/Functional/Ticket/GH7820Test.php index 2b5987bce8e..3b3aeb0bd2b 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7820Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7820Test.php @@ -14,7 +14,9 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Attributes\Group; use Psr\Cache\CacheItemInterface; +use Stringable; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\CacheItem; @@ -23,8 +25,6 @@ use function iterator_to_array; /** - * @group GH7820 - * * When using a {@see \Doctrine\ORM\Tools\Pagination\Paginator} to iterate over a query * that has entities with a custom DBAL type used in the identifier, then `$id->__toString()` * is used implicitly by {@see \PDOStatement::bindValue()}, instead of being converted by the @@ -36,10 +36,10 @@ * * If `#__toString()` and the DBAL type conversions are asymmetric, then the paginator will fail * to find records. - * * Tricky situation, but this very much affects `ramsey/uuid-doctrine` and anyone relying on (for * example) the {@see \Ramsey\Uuid\Doctrine\UuidBinaryType} type. */ +#[Group('GH7820')] class GH7820Test extends OrmFunctionalTestCase { private const SONG = [ @@ -78,7 +78,7 @@ public function testWillFindSongsInPaginator(): void self::assertSame(self::SONG, $lines); } - /** @group GH7837 */ + #[Group('GH7837')] public function testWillFindSongsInPaginatorEvenWithCachedQueryParsing(): void { // Enable the query cache @@ -149,32 +149,20 @@ private function fetchSongLinesWithPaginator(): array ->orderBy('l.lineNumber', Criteria::ASC) ->setMaxResults(100); - return array_map(static function (GH7820Line $line): string { - return $line->toString(); - }, iterator_to_array(new Paginator($query))); + return array_map(static fn (GH7820Line $line): string => $line->toString(), iterator_to_array(new Paginator($query))); } } -/** @Entity */ +#[Entity] class GH7820Line { - /** - * @var GH7820LineText - * @Id() - * @Column(type="Doctrine\Tests\ORM\Functional\Ticket\GH7820LineTextType", length=255) - */ - private $text; - - /** - * @var int - * @Column(type="integer") - */ - private $lineNumber; - - public function __construct(GH7820LineText $text, int $index) - { - $this->text = $text; - $this->lineNumber = $index; + public function __construct( + #[Id] + #[Column(type: 'Doctrine\Tests\ORM\Functional\Ticket\GH7820LineTextType', length: 255)] + private GH7820LineText $text, + #[Column(type: 'integer')] + private int $lineNumber, + ) { } public function toString(): string @@ -183,14 +171,10 @@ public function toString(): string } } -final class GH7820LineText +final class GH7820LineText implements Stringable { - /** @var string */ - private $text; - - private function __construct(string $text) + private function __construct(private string $text) { - $this->text = $text; } public static function fromText(string $text): self @@ -214,7 +198,7 @@ final class GH7820LineTextType extends StringType /** * {@inheritDoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): mixed { $text = parent::convertToPHPValue($value, $platform); @@ -228,7 +212,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritDoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { if (! $value instanceof GH7820LineText) { return parent::convertToDatabaseValue($value, $platform); diff --git a/tests/Tests/ORM/Functional/Ticket/GH7829Test.php b/tests/Tests/ORM/Functional/Ticket/GH7829Test.php index 58c14bb4a2c..abf121e7bb7 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7829Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7829Test.php @@ -7,8 +7,9 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH7829 */ +#[Group('GH7829')] final class GH7829Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/GH7836Test.php b/tests/Tests/ORM/Functional/Ticket/GH7836Test.php index 408748d890f..1cdaea018a1 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7836Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7836Test.php @@ -16,11 +16,12 @@ use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\OrderBy; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; use function class_exists; -/** @group GH7836 */ +#[Group('GH7836')] class GH7836Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -63,8 +64,8 @@ public function testMatchingOverrulesCollectionOrdering(): void Criteria::create()->orderBy( class_exists(Order::class) ? ['position' => Order::Descending, 'name' => Order::Ascending] - : ['position' => 'DESC', 'name' => 'ASC'] - ) + : ['position' => 'DESC', 'name' => 'ASC'], + ), ); self::assertSame(200, $children[0]->position); @@ -84,8 +85,8 @@ public function testMatchingKeepsOrderOfCriteriaOrderingKeys(): void Criteria::create()->orderBy( class_exists(Order::class) ? ['name' => Order::Ascending, 'position' => Order::Ascending] - : ['name' => 'ASC', 'position' => 'ASC'] - ) + : ['name' => 'ASC', 'position' => 'ASC'], + ), ); self::assertSame(100, $children[0]->position); @@ -97,22 +98,17 @@ class_exists(Order::class) } } -/** @Entity */ +#[Entity] class GH7836ParentEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var Collection&Selectable - * @OneToMany(targetEntity=GH7836ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"}) - * @OrderBy({"position" = "ASC", "name" = "ASC"}) - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + /** @var Collection&Selectable */ + #[OneToMany(targetEntity: GH7836ChildEntity::class, mappedBy: 'parent', fetch: 'EXTRA_LAZY', cascade: ['persist'])] + #[OrderBy(['position' => 'ASC', 'name' => 'ASC'])] private $children; public function addChild(int $position, string $name): void @@ -127,39 +123,21 @@ public function getChildren(): Collection } } -/** @Entity */ +#[Entity] class GH7836ChildEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var int - * @Column(type="integer") - */ - public $position; - - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - /** - * @var GH7836ParentEntity - * @ManyToOne(targetEntity=GH7836ParentEntity::class, inversedBy="children") - */ - private $parent; - - public function __construct(GH7836ParentEntity $parent, int $position, string $name) - { - $this->parent = $parent; - $this->position = $position; - $this->name = $name; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; + + public function __construct( + #[ManyToOne(targetEntity: GH7836ParentEntity::class, inversedBy: 'children')] + private GH7836ParentEntity $parent, + #[Column(type: 'integer')] + public int $position, + #[Column(type: 'string', length: 255)] + public string $name, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7864Test.php b/tests/Tests/ORM/Functional/Ticket/GH7864Test.php index 91eb188fab5..a21e59e2f1d 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7864Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7864Test.php @@ -13,10 +13,11 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function array_values; -/** @group gh7864 */ +#[Group('gh7864')] class GH7864Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -27,7 +28,7 @@ protected function setUp(): void [ GH7864User::class, GH7864Tweet::class, - ] + ], ); } @@ -55,35 +56,27 @@ public function testExtraLazyRemoveElement(): void $user->tweets->removeElement($tweet); - $tweets = $user->tweets->map(static function (GH7864Tweet $tweet) { - return $tweet->content; - }); + $tweets = $user->tweets->map(static fn (GH7864Tweet $tweet) => $tweet->content); self::assertEquals(['Goodbye, and thanks for all the fish'], array_values($tweets->toArray())); } } -/** @Entity */ +#[Entity] class GH7864User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var Collection - * @OneToMany(targetEntity="GH7864Tweet", mappedBy="user", fetch="EXTRA_LAZY") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'GH7864Tweet', mappedBy: 'user', fetch: 'EXTRA_LAZY')] public $tweets; public function __construct() @@ -98,26 +91,20 @@ public function addTweet(GH7864Tweet $tweet): void } } -/** @Entity */ +#[Entity] class GH7864Tweet { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $content; - /** - * @var GH7864User - * @ManyToOne(targetEntity="GH7864User", inversedBy="tweets") - */ + /** @var GH7864User */ + #[ManyToOne(targetEntity: 'GH7864User', inversedBy: 'tweets')] public $user; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7869Test.php b/tests/Tests/ORM/Functional/Ticket/GH7869Test.php index 534bb6c1c37..00329604aa4 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7869Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7869Test.php @@ -17,8 +17,11 @@ use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH7869 */ +use function method_exists; + +#[Group('GH7869')] class GH7869Test extends OrmTestCase { public function testDQLDeferredEagerLoad(): void @@ -30,8 +33,11 @@ public function testDQLDeferredEagerLoad(): void $connection = $this->createMock(Connection::class); $connection->method('getDatabasePlatform') ->willReturn($platform); - $connection->method('getEventManager') - ->willReturn(new EventManager()); + + if (method_exists($connection, 'getEventManager')) { + $connection->method('getEventManager') + ->willReturn(new EventManager()); + } $em = new class (new EntityManagerMock($connection)) extends EntityManagerDecorator { /** @var int */ @@ -59,38 +65,30 @@ public function getClassMetadata($className): ClassMetadata } } -/** @Entity */ +#[Entity] class GH7869Appointment { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH7869Patient - * @OneToOne(targetEntity="GH7869Patient", inversedBy="appointment", fetch="EAGER") - */ + /** @var GH7869Patient */ + #[OneToOne(targetEntity: 'GH7869Patient', inversedBy: 'appointment', fetch: 'EAGER')] public $patient; } -/** @Entity */ +#[Entity] class GH7869Patient { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH7869Appointment - * @OneToOne(targetEntity="GH7869Appointment", mappedBy="patient") - */ + /** @var GH7869Appointment */ + #[OneToOne(targetEntity: 'GH7869Appointment', mappedBy: 'patient')] public $appointment; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7875Test.php b/tests/Tests/ORM/Functional/Ticket/GH7875Test.php index fa947ace060..b19f9fed601 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7875Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7875Test.php @@ -4,7 +4,6 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; -use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; @@ -12,18 +11,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\Group; use function array_filter; use function current; -use function method_exists; use function sprintf; use function str_contains; use function str_starts_with; -/** @group GH7875 */ +#[Group('GH7875')] final class GH7875Test extends OrmFunctionalTestCase { - /** @after */ + #[After] public function cleanUpSchema(): void { $connection = $this->_em->getConnection(); @@ -45,9 +45,7 @@ public function cleanUpSchema(): void */ private function filterCreateTable(array $sqls, string $tableName): array { - return array_filter($sqls, static function (string $sql) use ($tableName): bool { - return str_starts_with($sql, sprintf('CREATE TABLE %s (', $tableName)); - }); + return array_filter($sqls, static fn (string $sql): bool => str_starts_with($sql, sprintf('CREATE TABLE %s (', $tableName))); } public function testUpdateSchemaSql(): void @@ -60,9 +58,7 @@ public function testUpdateSchemaSql(): void $this->_em->getConnection()->executeStatement(current($sqls)); - $sqls = array_filter($this->getUpdateSchemaSqlForModels(...$classes), static function (string $sql): bool { - return str_contains($sql, ' gh7875_my_entity '); - }); + $sqls = array_filter($this->getUpdateSchemaSqlForModels(...$classes), static fn (string $sql): bool => str_contains($sql, ' gh7875_my_entity ')); self::assertSame([], $sqls); @@ -74,76 +70,45 @@ public function testUpdateSchemaSql(): void self::assertCount(1, $this->filterCreateTable($sqls, 'gh7875_my_other_entity')); } - /** @return array> */ - public static function provideUpdateSchemaSqlWithSchemaAssetFilter(): array + public function testUpdateSchemaSqlWithSchemaAssetFilter(): void { - return [ - ['/^(?!my_enti)/', null], - [ - null, - static function ($assetName): bool { - return $assetName !== 'gh7875_my_entity'; - }, - ], - ]; - } - - /** @dataProvider provideUpdateSchemaSqlWithSchemaAssetFilter */ - public function testUpdateSchemaSqlWithSchemaAssetFilter(?string $filterRegex, ?callable $filterCallback): void - { - if ($filterRegex && ! method_exists(Configuration::class, 'setFilterSchemaAssetsExpression')) { - self::markTestSkipped(sprintf('Test require %s::setFilterSchemaAssetsExpression method', Configuration::class)); - } + $filterCallback = static fn ($assetName): bool => $assetName !== 'gh7875_my_entity'; $class = GH7875MyEntity::class; $this->createSchemaForModels($class); $config = $this->_em->getConnection()->getConfiguration(); - if ($filterRegex) { - $config->setFilterSchemaAssetsExpression($filterRegex); - } else { - $config->setSchemaAssetsFilter($filterCallback); - } + $config->setSchemaAssetsFilter($filterCallback); $previousFilter = $config->getSchemaAssetsFilter(); $sqls = $this->getUpdateSchemaSqlForModels($class); - $sqls = array_filter($sqls, static function (string $sql): bool { - return str_contains($sql, ' gh7875_my_entity '); - }); + $sqls = array_filter($sqls, static fn (string $sql): bool => str_contains($sql, ' gh7875_my_entity ')); self::assertCount(0, $sqls); self::assertSame($previousFilter, $config->getSchemaAssetsFilter()); } } -/** - * @Entity - * @Table(name="gh7875_my_entity") - */ +#[Table(name: 'gh7875_my_entity')] +#[Entity] class GH7875MyEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** - * @Entity - * @Table(name="gh7875_my_other_entity") - */ +#[Table(name: 'gh7875_my_other_entity')] +#[Entity] class GH7875MyOtherEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH7941Test.php b/tests/Tests/ORM/Functional/Ticket/GH7941Test.php index bbd0b167045..68249de9897 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH7941Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH7941Test.php @@ -11,11 +11,13 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use function ltrim; use function strlen; -/** @group GH7941 */ +#[Group('GH7941')] final class GH7941Test extends OrmFunctionalTestCase { private const PRODUCTS = [ @@ -41,7 +43,7 @@ protected function setUp(): void $this->_em->clear(); } - /** @test */ + #[Test] public function typesShouldBeConvertedForDQLFunctions(): void { $query = $this->_em->createQuery( @@ -49,7 +51,7 @@ public function typesShouldBeConvertedForDQLFunctions(): void COUNT(product.id) as count, SUM(product.price) as sales, AVG(product.price) as average - FROM ' . GH7941Product::class . ' product' + FROM ' . GH7941Product::class . ' product', ); $result = $query->getSingleResult(); @@ -69,7 +71,7 @@ public function typesShouldBeConvertedForDQLFunctions(): void ABS(product.price) as absolute, SQRT(ABS(product.price)) as square_root, LENGTH(product.name) as length - FROM ' . GH7941Product::class . ' product' + FROM ' . GH7941Product::class . ' product', ); foreach ($query->getResult() as $i => $item) { @@ -86,42 +88,26 @@ public function typesShouldBeConvertedForDQLFunctions(): void } } -/** - * @Entity - * @Table() - */ +#[Table] +#[Entity] class GH7941Product { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ - public $name; - - /** - * @var string - * @Column(type="decimal", precision=10) - */ - public $price; - - /** - * @var DateTimeImmutable - * @Column(type="datetime_immutable") - */ + /** @var DateTimeImmutable */ + #[Column(type: 'datetime_immutable')] public $createdAt; - public function __construct(string $name, string $price) - { - $this->name = $name; - $this->price = $price; + public function __construct( + #[Column(type: 'string', length: 255)] + public string $name, + #[Column(type: 'decimal', precision: 10)] + public string $price, + ) { $this->createdAt = new DateTimeImmutable(); } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8055Test.php b/tests/Tests/ORM/Functional/Ticket/GH8055Test.php index 9e22d4f9283..6c6f4cd9a6d 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8055Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8055Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH8055 */ +#[Group('GH8055')] final class GH8055Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -42,33 +43,24 @@ public function testNumericDescriminatorColumn(): void } } -/** - * @Entity() - * @Table(name="gh8055") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="integer") - * @DiscriminatorMap({ - * "1" = GH8055BaseClass::class, - * "2" = GH8055SubClass::class - * }) - */ +#[Table(name: 'gh8055')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'integer')] +#[DiscriminatorMap([1 => GH8055BaseClass::class, 2 => GH8055SubClass::class])] class GH8055BaseClass { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; } -/** @Entity() */ +#[Entity] class GH8055SubClass extends GH8055BaseClass { - /** - * @Column(name="test", type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(name: 'test', type: 'string', length: 255)] public $value; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8061Test.php b/tests/Tests/ORM/Functional/Ticket/GH8061Test.php index 3f548c765f7..9e02de58b70 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8061Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8061Test.php @@ -11,11 +11,11 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -use function method_exists; use function sprintf; -/** @group GH8061 */ +#[Group('GH8061')] final class GH8061Test extends OrmTestCase { public static function setUpBeforeClass(): void @@ -33,33 +33,25 @@ public function testConvertToPHPValueSQLForNewObjectExpression(): void } } -/** @Entity */ +#[Entity] final class GH8061Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var mixed - * @Column(type="GH8061Type", length=255) - */ + /** @var mixed */ + #[Column(type: 'GH8061Type', length: 255)] public $field; } final class GH8061Type extends Type { - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - if (method_exists($platform, 'getStringTypeDeclarationSQL')) { - return $platform->getStringTypeDeclarationSQL($fieldDeclaration); - } - - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getStringTypeDeclarationSQL($column); } public function getName(): string @@ -67,11 +59,6 @@ public function getName(): string return 'GH8061'; } - public function canRequireSQLConversion(): bool - { - return true; - } - public function convertToPHPValueSQL($sqlExpr, $platform): string { return sprintf('DatabaseFunction(%s)', $sqlExpr); @@ -80,11 +67,7 @@ public function convertToPHPValueSQL($sqlExpr, $platform): string final class GH8061Class { - /** @var string */ - public $field; - - public function __construct(string $field) + public function __construct(public string $field) { - $this->field = $field; } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8127Test.php b/tests/Tests/ORM/Functional/Ticket/GH8127Test.php index 101d07790fe..c88053901c7 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8127Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8127Test.php @@ -6,6 +6,7 @@ use Doctrine\ORM\Mapping as ORM; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; class GH8127Test extends OrmFunctionalTestCase { @@ -16,13 +17,11 @@ protected function setUp(): void $this->createSchemaForModels( GH8127Root::class, GH8127Middle::class, - GH8127Leaf::class + GH8127Leaf::class, ); } - /** - * @dataProvider queryClasses - */ + #[DataProvider('queryClasses')] public function testLoadFieldsFromAllClassesInHierarchy(string $queryClass): void { $entity = new GH8127Leaf(); @@ -50,53 +49,35 @@ public static function queryClasses(): array } } -/** - * @ORM\Entity - * @ORM\Table(name="root") - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorMap({ "leaf": "GH8127Leaf" }) - */ +#[ORM\Entity] +#[ORM\Table(name: 'root')] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorMap(['leaf' => GH8127Leaf::class])] abstract class GH8127Root { - /** - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - * @ORM\Column(type="integer") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Column] public $root; } -/** - * @ORM\Entity - */ +#[ORM\Entity] abstract class GH8127Middle extends GH8127Root { - /** - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Column] public $middle; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH8127Leaf extends GH8127Middle { - /** - * @ORM\Column - * - * @var string - */ + /** @var string */ + #[ORM\Column] public $leaf; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8217Test.php b/tests/Tests/ORM/Functional/Ticket/GH8217Test.php index 530db9202d2..6747f5e084b 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8217Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8217Test.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\OneToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; final class GH8217Test extends OrmFunctionalTestCase { @@ -25,11 +26,11 @@ protected function setUp(): void [ GH8217Collection::class, GH8217CollectionItem::class, - ] + ], ); } - /** @group GH-8217 */ + #[Group('GH-8217')] public function testNoQueriesAfterSecondFlush(): void { $collection = new GH8217Collection(); @@ -44,22 +45,17 @@ public function testNoQueriesAfterSecondFlush(): void } } -/** @Entity */ +#[Entity] class GH8217Collection { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="GH8217CollectionItem", mappedBy="collection", - * cascade={"persist", "remove"}, orphanRemoval=true) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'GH8217CollectionItem', mappedBy: 'collection', cascade: ['persist', 'remove'], orphanRemoval: true)] public $items; public function __construct() @@ -73,27 +69,17 @@ public function addItem(GH8217CollectionItem $item): void } } -/** @Entity */ +#[Entity] class GH8217CollectionItem { - /** - * @var GH8217Collection - * @Id - * @ManyToOne(targetEntity="GH8217Collection", inversedBy="items") - * @JoinColumn(name="id", referencedColumnName="id") - */ - public $collection; - - /** - * @var int - * @Id - * @Column(type="integer", options={"unsigned": true}) - */ - public $collectionIndex; - - public function __construct(GH8217Collection $collection, int $collectionIndex) - { - $this->collection = $collection; - $this->collectionIndex = $collectionIndex; + public function __construct( + #[Id] + #[ManyToOne(targetEntity: 'GH8217Collection', inversedBy: 'items')] + #[JoinColumn(name: 'id', referencedColumnName: 'id')] + public GH8217Collection $collection, + #[Id] + #[Column(type: 'integer', options: ['unsigned' => true])] + public int $collectionIndex, + ) { } } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8415Test.php b/tests/Tests/ORM/Functional/Ticket/GH8415Test.php index 9ce30802660..35bde6deb10 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8415Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8415Test.php @@ -13,14 +13,12 @@ protected function setUp(): void { parent::setUp(); - $this->setUpEntitySchema( - [ - GH8415BaseClass::class, - GH8415MiddleMappedSuperclass::class, - GH8415LeafClass::class, - GH8415AssociationTarget::class, - ] - ); + $this->setUpEntitySchema([ + GH8415BaseClass::class, + GH8415MiddleMappedSuperclass::class, + GH8415LeafClass::class, + GH8415AssociationTarget::class, + ]); } public function testAssociationIsBasedOnBaseClass(): void @@ -47,75 +45,49 @@ public function testAssociationIsBasedOnBaseClass(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH8415AssociationTarget { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; } -/** - * @ORM\Entity - * @ORM\InheritanceType("JOINED") - * @ORM\DiscriminatorColumn(name="discriminator", type="string") - * @ORM\DiscriminatorMap({"1" = "Doctrine\Tests\ORM\Functional\Ticket\GH8415BaseClass", "2" = "Doctrine\Tests\ORM\Functional\Ticket\GH8415LeafClass"}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('JOINED')] +#[ORM\DiscriminatorColumn(name: 'discriminator', type: 'string')] +#[ORM\DiscriminatorMap(['1' => GH8415BaseClass::class, '2' => GH8415LeafClass::class])] class GH8415BaseClass { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH8415AssociationTarget") - * - * @var GH8415AssociationTarget - */ + /** @var GH8415AssociationTarget */ + #[ORM\ManyToOne(targetEntity: GH8415AssociationTarget::class)] public $target; - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $baseField; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH8415MiddleMappedSuperclass extends GH8415BaseClass { - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $middleField; } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH8415LeafClass extends GH8415MiddleMappedSuperclass { - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $leafField; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8415ToManyAssociationTest.php b/tests/Tests/ORM/Functional/Ticket/GH8415ToManyAssociationTest.php index c5a7c35f3df..444955c10cb 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8415ToManyAssociationTest.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8415ToManyAssociationTest.php @@ -19,69 +19,46 @@ public function testToManyAssociationOnBaseClassAllowedWhenThereAreMappedSupercl } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH8415ToManyAssociationTarget { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH8415ToManyBaseClass", inversedBy="targets") - * - * @var GH8415ToManyBaseClass - */ + /** @var GH8415ToManyBaseClass */ + #[ORM\ManyToOne(targetEntity: GH8415ToManyBaseClass::class, inversedBy: 'targets')] public $base; } -/** - * @ORM\Entity - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="discriminator", type="string") - * @ORM\DiscriminatorMap({"1" = "Doctrine\Tests\ORM\Functional\Ticket\GH8415ToManyBaseClass", "2" = "Doctrine\Tests\ORM\Functional\Ticket\GH8415ToManyLeafClass"}) - */ +#[ORM\Entity] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'discriminator', type: 'string')] +#[ORM\DiscriminatorMap(['1' => GH8415ToManyBaseClass::class, '2' => GH8415ToManyLeafClass::class])] class GH8415ToManyBaseClass { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue - * - * @var int - */ + /** @var int */ + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue] public $id; - /** - * @ORM\OneToMany(targetEntity="GH8415ToManyAssociationTarget", mappedBy="base") - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(targetEntity: GH8415ToManyAssociationTarget::class, mappedBy: 'base')] public $targets; } -/** - * @ORM\MappedSuperclass - */ +#[ORM\MappedSuperclass] class GH8415ToManyMappedSuperclass extends GH8415ToManyBaseClass { } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH8415ToManyLeafClass extends GH8415ToManyMappedSuperclass { - /** - * @ORM\Column(type="string") - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string')] public $leafField; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8443Test.php b/tests/Tests/ORM/Functional/Ticket/GH8443Test.php index e3d74ca6412..44695f1856e 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8443Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8443Test.php @@ -18,6 +18,7 @@ use Doctrine\Tests\Models\Company\CompanyManager; use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -32,7 +33,7 @@ protected function setUp(): void $this->createSchemaForModels(GH8443Foo::class); } - /** @group GH-8443 */ + #[Group('GH-8443')] public function testJoinRootEntityWithForcePartialLoad(): void { $person = new CompanyPerson(); @@ -54,14 +55,14 @@ public function testJoinRootEntityWithForcePartialLoad(): void $manager = $this->_em->createQuery( "SELECT m from Doctrine\Tests\Models\Company\CompanyManager m JOIN m.spouse s - WITH s.name = 'John'" + WITH s.name = 'John'", )->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)->getSingleResult(); $this->_em->refresh($manager); $this->assertEquals('John', $manager->getSpouse()->getName()); } - /** @group GH-8443 */ + #[Group('GH-8443')] public function testJoinRootEntityWithOnlyOneEntityInHierarchy(): void { $bar = new GH8443Foo('bar'); @@ -75,7 +76,7 @@ public function testJoinRootEntityWithOnlyOneEntityInHierarchy(): void $this->_em->clear(); $foo = $this->_em->createQuery( - 'SELECT f from ' . GH8443Foo::class . " f JOIN f.bar b WITH b.name = 'bar'" + 'SELECT f from ' . GH8443Foo::class . " f JOIN f.bar b WITH b.name = 'bar'", )->getSingleResult(); assert($foo instanceof GH8443Foo); @@ -84,44 +85,30 @@ public function testJoinRootEntityWithOnlyOneEntityInHierarchy(): void $this->assertEquals('bar', $bar->getName()); } } -/** - * @Entity - * @Table(name="GH2947_foo") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "foo" = "GH8443Foo" - * }) - */ +#[Table(name: 'GH2947_foo')] +#[Entity] +#[InheritanceType('JOINED')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['foo' => 'GH8443Foo'])] class GH8443Foo { - /** - * @var int|null - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column - */ - private $name; - - /** - * @var GH8443Foo|null - * @OneToOne(targetEntity="GH8443Foo") - * @JoinColumn(name="bar_id", referencedColumnName="id") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int|null $id = null; + + /** @var GH8443Foo|null */ + #[OneToOne(targetEntity: 'GH8443Foo')] + #[JoinColumn(name: 'bar_id', referencedColumnName: 'id')] private $bar; - public function __construct(string $name) - { - $this->name = $name; + public function __construct( + #[Column] + private string $name, + ) { } - public function getName(): ?string + public function getName(): string|null { return $this->name; } @@ -134,7 +121,7 @@ public function setBar(GH8443Foo $bar): void } } - public function getBar(): ?GH8443Foo + public function getBar(): GH8443Foo|null { return $this->bar; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8499Test.php b/tests/Tests/ORM/Functional/Ticket/GH8499Test.php index 8e5e19ae627..9f6f3066caa 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8499Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8499Test.php @@ -16,6 +16,8 @@ use Doctrine\ORM\Mapping\Version; use Doctrine\ORM\OptimisticLockException; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; use function date; use function strtotime; @@ -37,7 +39,7 @@ protected function createSchema(): void $this->createSchemaForModels(GH8499VersionableEntity::class); } - /** @group GH-8499 */ + #[Group('GH-8499')] public function testOptimisticTimestampSetsDefaultValue(): GH8499VersionableEntity { $this->createSchema(); @@ -54,10 +56,8 @@ public function testOptimisticTimestampSetsDefaultValue(): GH8499VersionableEnti return $entity; } - /** - * @group GH-8499 - * @depends testOptimisticTimestampSetsDefaultValue - */ + #[Depends('testOptimisticTimestampSetsDefaultValue')] + #[Group('GH-8499')] public function testOptimisticLockWithDateTimeForVersion(GH8499VersionableEntity $entity): void { $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Ticket\GH8499VersionableEntity t WHERE t.id = :id'); @@ -67,12 +67,12 @@ public function testOptimisticLockWithDateTimeForVersion(GH8499VersionableEntity $format = $this->_em->getConnection()->getDatabasePlatform()->getDateTimeFormatString(); $modifiedDate = new DateTime(date( $format, - strtotime($test->getRevision()->format($format)) - 3600 + strtotime($test->getRevision()->format($format)) - 3600, )); $this->conn->executeQuery( 'UPDATE GH8499VersionableEntity SET revision = ? WHERE id = ?', - [$modifiedDate->format($format), $test->id] + [$modifiedDate->format($format), $test->id], ); $this->_em->refresh($test); @@ -85,16 +85,16 @@ public function testOptimisticLockWithDateTimeForVersion(GH8499VersionableEntity self::assertEquals( 'Test Entity Locked', $test->getName(), - 'Entity not modified after persist/flush,' + 'Entity not modified after persist/flush,', ); self::assertGreaterThan( $modifiedDate->getTimestamp(), $test->getRevision()->getTimestamp(), - 'Current version timestamp is not greater than previous one.' + 'Current version timestamp is not greater than previous one.', ); } - /** @group GH-8499 */ + #[Group('GH-8499')] public function testOptimisticLockWithDateTimeForVersionThrowsException(): void { $this->createSchema(); @@ -109,37 +109,27 @@ public function testOptimisticLockWithDateTimeForVersionThrowsException(): void } } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class GH8499VersionableEntity { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @Column(type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @Column(type="string", length=255) - * @var string - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $description; - /** - * @Version - * @Column(type="datetime") - * @var DateTimeInterface - */ + /** @var DateTimeInterface */ + #[Version] + #[Column(type: 'datetime')] public $revision; public function getId(): int @@ -167,7 +157,7 @@ public function setDescription(string $description): void $this->description = $description; } - public function getRevision(): ?DateTimeInterface + public function getRevision(): DateTimeInterface|null { return $this->revision; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8663Test.php b/tests/Tests/ORM/Functional/Ticket/GH8663Test.php index 8b111677d04..d827c74cdbf 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8663Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8663Test.php @@ -29,21 +29,17 @@ public function testDeletedEntity(): void } } -/** @Entity */ +#[Entity] class GH8663VersionedEntity { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @Version - * @Column(type="integer") - * @var int - */ + /** @var int */ + #[Version] + #[Column(type: 'integer')] protected $version; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH8914Test.php b/tests/Tests/ORM/Functional/Ticket/GH8914Test.php index f2745305968..328aadcdfaa 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH8914Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH8914Test.php @@ -13,13 +13,13 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\MappedSuperclass; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\Group; final class GH8914Test extends OrmTestCase { - /** - * @group GH-8914 - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] + #[Group('GH-8914')] public function testDiscriminatorMapWithSeveralLevelsIsSupported(): void { $entityManager = $this->getTestEntityManager(); @@ -27,29 +27,25 @@ public function testDiscriminatorMapWithSeveralLevelsIsSupported(): void } } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class GH8914BaseEntity { } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"person" = "GH8914Person", "employee" = "GH8914Employee"}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'discr', type: 'string')] +#[DiscriminatorMap(['person' => 'GH8914Person', 'employee' => 'GH8914Employee'])] class GH8914Person extends GH8914BaseEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class GH8914Employee extends GH8914Person { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9027Test.php b/tests/Tests/ORM/Functional/Ticket/GH9027Test.php index 6b5f1067a14..64a04535984 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9027Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9027Test.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class GH9027Test extends OrmFunctionalTestCase { @@ -23,7 +24,7 @@ protected function setUp(): void $this->createSchemaForModels(GH9027Cart::class, GH9027Customer::class); } - /** @group GH-9027 */ + #[Group('GH-9027')] public function testUnitOfWorkHandlesNullRelations(): void { $uow = new UnitOfWork($this->_em); @@ -32,46 +33,38 @@ public function testUnitOfWorkHandlesNullRelations(): void $cart = $uow->createEntity( GH9027Cart::class, ['id' => 1, 'customer' => 24252], - $hints + $hints, ); $this->assertEquals(null, $cart->customer); } } -/** @Entity */ +#[Entity] class GH9027Customer { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH9027Cart - * @OneToOne(targetEntity="GH9027Cart", mappedBy="customer") - */ + /** @var GH9027Cart */ + #[OneToOne(targetEntity: 'GH9027Cart', mappedBy: 'customer')] public $cart; } -/** @Entity */ +#[Entity] class GH9027Cart { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH9027Customer - * @OneToOne(targetEntity="GH9027Customer", inversedBy="cart") - * @JoinColumn(name="customer", referencedColumnName="id") - */ + /** @var GH9027Customer */ + #[OneToOne(targetEntity: 'GH9027Customer', inversedBy: 'cart')] + #[JoinColumn(name: 'customer', referencedColumnName: 'id')] public $customer; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9109Test.php b/tests/Tests/ORM/Functional/Ticket/GH9109Test.php index e8d7ae3e8cb..cf32fbf0a58 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9109Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9109Test.php @@ -13,8 +13,9 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-9109 */ +#[Group('GH-9109')] class GH9109Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -86,28 +87,22 @@ public function testIssue(): void } } -/** @Entity */ +#[Entity] class GH9109Product { - /** - * @var int $id - * @Column(name="`id`", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(name: '`id`', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string $title - * @Column(name="`title`", type="string", length=255) - */ - private $title; + #[Column(name: '`title`', type: 'string', length: 255)] + private string|null $title = null; /** * @var Collection|GH9109User[] * @phpstan-var Collection - * @ManyToMany(targetEntity="GH9109User") */ + #[ManyToMany(targetEntity: 'GH9109User')] private $buyers; public function __construct() @@ -142,28 +137,19 @@ public function addBuyer(GH9109User $buyer): void } } -/** @Entity */ +#[Entity] class GH9109User { - /** - * @var int - * @Column(name="`id`", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Column(name: '`id`', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(name="`first_name`", type="string", length=255) - */ - private $firstName; + #[Column(name: '`first_name`', type: 'string', length: 255)] + private string|null $firstName = null; - /** - * @var string - * @Column(name="last_name", type="string", length=255) - */ - private $lastName; + #[Column(name: 'last_name', type: 'string', length: 255)] + private string|null $lastName = null; public function getId(): int { diff --git a/tests/Tests/ORM/Functional/Ticket/GH9192Test.php b/tests/Tests/ORM/Functional/Ticket/GH9192Test.php index 216c4f2dff9..a173ebc962e 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9192Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9192Test.php @@ -44,33 +44,22 @@ public function testIssue(): void } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH9192A { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\OneToMany(targetEntity="GH9192B", mappedBy="a", cascade={"remove"}) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'a', targetEntity: GH9192B::class, cascade: ['remove'])] public $bs; - /** - * @ORM\OneToOne(targetEntity="GH9192C") - * @ORM\JoinColumn(nullable=true, onDelete="SET NULL") - * - * @var GH9192C - */ + /** @var GH9192C */ + #[ORM\OneToOne(targetEntity: GH9192C::class)] + #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')] public $c; public function __construct() @@ -79,32 +68,21 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH9192B { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\OneToMany(targetEntity="GH9192C", mappedBy="b", cascade={"remove"}) - * - * @var Collection - */ + /** @var Collection */ + #[ORM\OneToMany(mappedBy: 'b', targetEntity: GH9192C::class, cascade: ['remove'])] public $cs; - /** - * @ORM\ManyToOne(targetEntity="GH9192A", inversedBy="bs") - * - * @var GH9192A - */ + /** @var GH9192A */ + #[ORM\ManyToOne(inversedBy: 'bs', targetEntity: GH9192A::class)] public $a; public function __construct() @@ -113,24 +91,16 @@ public function __construct() } } -/** - * @ORM\Entity - */ +#[ORM\Entity] class GH9192C { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ + /** @var int */ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @ORM\ManyToOne(targetEntity="GH9192B", inversedBy="cs") - * - * @var GH9192B - */ + /** @var GH9192B */ + #[ORM\ManyToOne(inversedBy: 'cs', targetEntity: GH9192B::class)] public $b; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9230Test.php b/tests/Tests/ORM/Functional/Ticket/GH9230Test.php index 16d986b3ded..3354982113d 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9230Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9230Test.php @@ -9,8 +9,10 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-9230 */ +#[Group('GH-9230')] class GH9230Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -81,10 +83,8 @@ public static function succeedingValuesBeforeFix(): array ]; } - /** - * @dataProvider failingValuesBeforeFix - * @dataProvider succeedingValuesBeforeFix - */ + #[DataProvider('failingValuesBeforeFix')] + #[DataProvider('succeedingValuesBeforeFix')] public function testIssue(string $property, $falsyValue, $truthyValue): void { $counter1 = new GH9230Entity(); @@ -126,44 +126,32 @@ public function testIssue(string $property, $falsyValue, $truthyValue): void } -/** @Entity */ +#[Entity] class GH9230Entity { - /** - * @var int - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Column(name: 'id', type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var ?string - * @Column(name="name", type="string", nullable=true) - */ + /** @var ?string */ + #[Column(name: 'name', type: 'string', nullable: true)] public $name; - /** - * @var ?int - * @Column(name="counter", type="integer", nullable=true) - */ + /** @var ?int */ + #[Column(name: 'counter', type: 'integer', nullable: true)] public $counter; - /** - * @var ?bool - * @Column(name="enabled", type="boolean", nullable=true) - */ + /** @var ?bool */ + #[Column(name: 'enabled', type: 'boolean', nullable: true)] public $enabled; - /** - * @var ?float - * @Column(name="price", type="decimal", scale=1, precision=2, nullable=true) - */ + /** @var ?float */ + #[Column(name: 'price', type: 'decimal', scale: 1, precision: 2, nullable: true)] public $price; - /** - * @var mixed[] - * @Column(name="extra", type="json", nullable=true) - */ + /** @var mixed[] */ + #[Column(name: 'extra', type: 'json', nullable: true)] public $extra; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9335Test.php b/tests/Tests/ORM/Functional/Ticket/GH9335Test.php index a1f96233fba..44b082d4290 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9335Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9335Test.php @@ -13,9 +13,11 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; +use Doctrine\Tests\Mocks\CompatibilityType; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH9335 */ +#[Group('GH9335')] final class GH9335Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -52,6 +54,8 @@ public function testFlattenIdentifierWithObjectId(): void class GH9335IntObjectType extends Type { + use CompatibilityType; + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { return $platform->getIntegerTypeDeclarationSQL($column); @@ -72,7 +76,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform): GH9335Int return new GH9335IntObject((int) $value); } - public function getBindingType(): int + private function doGetBindingType(): ParameterType|int { return ParameterType::INTEGER; } @@ -99,36 +103,30 @@ public function __toString(): string } } -/** @Entity */ +#[Entity] class GH9335Book { - /** - * @var GH9335IntObject - * @Id - * @Column(type=GH9335IntObjectType::class, unique=true) - */ + /** @var GH9335IntObject */ + #[Id] + #[Column(type: GH9335IntObjectType::class, unique: true)] private $id; - /** - * @Column(type="string") - * @var string - */ + /** @var string */ + #[Column(type: 'string')] private $title; - /** - * @OneToOne(targetEntity="GH9335Author", mappedBy="book", cascade={"persist", "remove"}) - * @var GH9335Author - */ + /** @var GH9335Author */ + #[OneToOne(targetEntity: 'GH9335Author', mappedBy: 'book', cascade: ['persist', 'remove'])] private $author; - public function __construct(GH9335IntObject $id, string $title, ?GH9335Author $author = null) + public function __construct(GH9335IntObject $id, string $title, GH9335Author|null $author = null) { $this->setId($id); $this->setTitle($title); $this->setAuthor($author); } - public function getId(): ?GH9335IntObject + public function getId(): GH9335IntObject|null { return $this->id; } @@ -138,7 +136,7 @@ public function setId($id): void $this->id = $id; } - public function getTitle(): ?string + public function getTitle(): string|null { return $this->title; } @@ -148,12 +146,12 @@ public function setTitle($title): void $this->title = $title; } - public function getAuthor(): ?GH9335Author + public function getAuthor(): GH9335Author|null { return $this->author; } - public function setAuthor(?GH9335Author $author): self + public function setAuthor(GH9335Author|null $author): self { $this->author = $author; @@ -166,29 +164,25 @@ public function setAuthor(?GH9335Author $author): self } } -/** @Entity */ +#[Entity] class GH9335Author { - /** - * @var GH9335Book - * @Id - * @OneToOne(targetEntity="GH9335Book", inversedBy="author") - * @JoinColumn(name="book") - */ + /** @var GH9335Book */ + #[Id] + #[OneToOne(targetEntity: 'GH9335Book', inversedBy: 'author')] + #[JoinColumn(name: 'book')] private $book; - /** - * @Column(type="string", nullable="true" ) - * @var string - */ + /** @var string */ + #[Column(type: 'string', nullable: true)] private $name; - public function __construct(?string $name) + public function __construct(string|null $name) { $this->setName($name); } - public function getBook(): ?GH9335Book + public function getBook(): GH9335Book|null { return $this->book; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/GH9467Test.php b/tests/Tests/ORM/Functional/Ticket/GH9467/GH9467Test.php index 01a6acae51e..7d05338f129 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/GH9467Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/GH9467Test.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket\GH9467; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; class GH9467Test extends OrmFunctionalTestCase { @@ -18,7 +19,7 @@ protected function setUp(): void JoinedInheritanceWritableColumn::class, JoinedInheritanceNonWritableColumn::class, JoinedInheritanceNonInsertableColumn::class, - JoinedInheritanceNonUpdatableColumn::class + JoinedInheritanceNonUpdatableColumn::class, ); } @@ -51,7 +52,7 @@ public function testRootColumnsInsert(): int return $entity->id; } - /** @depends testRootColumnsInsert */ + #[Depends('testRootColumnsInsert')] public function testRootColumnsUpdate(int $entityId): void { $entity = $this->_em->find(JoinedInheritanceChild::class, $entityId); @@ -102,7 +103,7 @@ public function testChildWritableColumnInsert(): int return $entity->id; } - /** @depends testChildWritableColumnInsert */ + #[Depends('testChildWritableColumnInsert')] public function testChildWritableColumnUpdate(int $entityId): void { $entity = $this->_em->find(JoinedInheritanceWritableColumn::class, $entityId); @@ -144,7 +145,7 @@ public function testChildNonWritableColumnInsert(): int return $entity->id; } - /** @depends testChildNonWritableColumnInsert */ + #[Depends('testChildNonWritableColumnInsert')] public function testChildNonWritableColumnUpdate(int $entityId): void { $entity = $this->_em->find(JoinedInheritanceNonWritableColumn::class, $entityId); @@ -190,7 +191,7 @@ public function testChildNonInsertableColumnInsert(): int return $entity->id; } - /** @depends testChildNonInsertableColumnInsert */ + #[Depends('testChildNonInsertableColumnInsert')] public function testChildNonInsertableColumnUpdate(int $entityId): void { $entity = $this->_em->find(JoinedInheritanceNonInsertableColumn::class, $entityId); @@ -232,7 +233,7 @@ public function testChildNonUpdatableColumnInsert(): int return $entity->id; } - /** @depends testChildNonUpdatableColumnInsert */ + #[Depends('testChildNonUpdatableColumnInsert')] public function testChildNonUpdatableColumnUpdate(int $entityId): void { $entity = $this->_em->find(JoinedInheritanceNonUpdatableColumn::class, $entityId); diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceChild.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceChild.php index 0b30c6972a7..5689b43b422 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceChild.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceChild.php @@ -7,10 +7,6 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_child") - */ #[Entity] #[Table(name: 'joined_inheritance_child')] class JoinedInheritanceChild extends JoinedInheritanceRoot diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonInsertableColumn.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonInsertableColumn.php index 1bf575e4289..dc16314d6d6 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonInsertableColumn.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonInsertableColumn.php @@ -8,18 +8,11 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_non_insertable_column") - */ #[Entity] #[Table(name: 'joined_inheritance_non_insertable_column')] class JoinedInheritanceNonInsertableColumn extends JoinedInheritanceRoot { - /** - * @var string - * @Column(type="string", insertable=false, updatable=true, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: false, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $nonInsertableContent; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonUpdatableColumn.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonUpdatableColumn.php index c8b1c671f6e..9a4997b8cd2 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonUpdatableColumn.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonUpdatableColumn.php @@ -8,18 +8,11 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_non_updatable_column") - */ #[Entity] #[Table(name: 'joined_inheritance_non_updatable_column')] class JoinedInheritanceNonUpdatableColumn extends JoinedInheritanceRoot { - /** - * @var string - * @Column(type="string", insertable=true, updatable=false, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: true, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $nonUpdatableContent; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonWritableColumn.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonWritableColumn.php index 2f78f6b5e5f..722bdedc634 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonWritableColumn.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceNonWritableColumn.php @@ -8,18 +8,11 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_non_writable_column") - */ #[Entity] #[Table(name: 'joined_inheritance_non_writable_column')] class JoinedInheritanceNonWritableColumn extends JoinedInheritanceRoot { - /** - * @var string - * @Column(type="string", insertable=false, updatable=false, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: false, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $nonWritableContent; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceRoot.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceRoot.php index ba7f39b7f6e..83998f799d3 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceRoot.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceRoot.php @@ -13,69 +13,42 @@ use Doctrine\ORM\Mapping\InheritanceType; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_root") - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({ - * "child" = "JoinedInheritanceChild", - * "writable" = "JoinedInheritanceWritableColumn", - * "nonWritable" = "JoinedInheritanceNonWritableColumn", - * "nonInsertable" = "JoinedInheritanceNonInsertableColumn", - * "nonUpdatable" = "JoinedInheritanceNonUpdatableColumn" - * }) - */ #[Entity] #[Table(name: 'joined_inheritance_root')] #[InheritanceType('JOINED')] #[DiscriminatorColumn(name: 'discr', type: 'string')] -#[DiscriminatorMap(['child' => JoinedInheritanceChild::class, 'writable' => JoinedInheritanceWritableColumn::class, 'nonWritable' => JoinedInheritanceNonWritableColumn::class, 'nonInsertable' => JoinedInheritanceNonInsertableColumn::class, 'nonUpdatable' => JoinedInheritanceNonUpdatableColumn::class])] +#[DiscriminatorMap([ + 'child' => JoinedInheritanceChild::class, + 'writable' => JoinedInheritanceWritableColumn::class, + 'nonWritable' => JoinedInheritanceNonWritableColumn::class, + 'nonInsertable' => JoinedInheritanceNonInsertableColumn::class, + 'nonUpdatable' => JoinedInheritanceNonUpdatableColumn::class, +])] class JoinedInheritanceRoot { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ #[Id] #[GeneratedValue] #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column(type="string") - */ + /** @var string */ #[Column(type: 'string')] public $rootField = ''; - /** - * @var string - * @Column(type="string", insertable=true, updatable=true, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: true, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $rootWritableContent = ''; - /** - * @var string - * @Column(type="string", insertable=false, updatable=false, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: false, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $rootNonWritableContent; - /** - * @var string - * @Column(type="string", insertable=false, updatable=true, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: false, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $rootNonInsertableContent; - /** - * @var string - * @Column(type="string", insertable=true, updatable=false, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: true, updatable: false, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $rootNonUpdatableContent = ''; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceWritableColumn.php b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceWritableColumn.php index 874df49e6b6..be6290bc39c 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceWritableColumn.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9467/JoinedInheritanceWritableColumn.php @@ -8,18 +8,11 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="joined_inheritance_writable_column") - */ #[Entity] #[Table(name: 'joined_inheritance_writable_column')] class JoinedInheritanceWritableColumn extends JoinedInheritanceRoot { - /** - * @var string - * @Column(type="string", insertable=true, updatable=true, options={"default": "dbDefault"}, generated="ALWAYS") - */ + /** @var string */ #[Column(type: 'string', insertable: true, updatable: true, options: ['default' => 'dbDefault'], generated: 'ALWAYS')] public $writableContent; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9516Test.php b/tests/Tests/ORM/Functional/Ticket/GH9516Test.php index dce4164d9af..7f5e019b9c3 100755 --- a/tests/Tests/ORM/Functional/Ticket/GH9516Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9516Test.php @@ -22,56 +22,41 @@ public function testEntityCanHaveInverseOneToManyAssociationWithChildMappedSuper } } -/** @Entity */ +#[Entity] class GH9516Passenger { - /** - * @Id - * @Column(type="integer") - * @var int $id - */ + /** @var int $id */ + #[Id] + #[Column(type: 'integer')] private $id; - /** - * @ManyToOne(targetEntity="GH9516Vehicle", inversedBy="passengers") - * @var GH9516Vehicle $vehicle - */ + /** @var GH9516Vehicle $vehicle */ + #[ManyToOne(targetEntity: GH9516Vehicle::class, inversedBy: 'passengers')] private $vehicle; } -/** - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({ "sports" = "\Doctrine\Tests\ORM\Functional\Ticket\GH9516SportsCar" }) - * @ORM\InheritanceType("SINGLE_TABLE") - * - * @Entity - */ +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap(['sports' => GH9516SportsCar::class])] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[Entity] abstract class GH9516Vehicle { - /** - * @Id - * @Column(type="integer") - * @var int $id - */ + /** @var int $id */ + #[Id] + #[Column(type: 'integer')] private $id; - /** - * @OneToMany(targetEntity="GH9516Passenger", mappedBy="vehicle") - * @var GH9516Passenger[] $passengers - */ + /** @var GH9516Passenger[] $passengers */ + #[OneToMany(targetEntity: GH9516Passenger::class, mappedBy: 'vehicle')] private $passengers; } -/** - * @MappedSuperclass - */ +#[MappedSuperclass] abstract class GH9516Car extends GH9516Vehicle { } -/** - * @Entity - */ +#[Entity] class GH9516SportsCar extends GH9516Car { } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9579Test.php b/tests/Tests/ORM/Functional/Ticket/GH9579Test.php index 0cb15ae6582..61b59e7f097 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9579Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9579Test.php @@ -15,8 +15,9 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group GH-9579 */ +#[Group('GH-9579')] class GH9579Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -26,7 +27,7 @@ protected function setUp(): void $this->createSchemaForModels( GH9579Container::class, GH9579Item::class, - GH9579Part::class + GH9579Part::class, ); $container = new GH9579Container(); @@ -47,7 +48,7 @@ protected function setUp(): void $this->_em->clear(); } - /** @group GH-9579 */ + #[Group('GH-9579')] public function testIssue(): void { $dql = <<<'DQL' @@ -65,24 +66,18 @@ public function testIssue(): void } } -/** - * @Entity - * @Table(name="GH9579_containers") - */ +#[Table(name: 'GH9579_containers')] +#[Entity] class GH9579Container { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Collection - * @OneToMany (targetEntity="GH9579Item", mappedBy="container") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'GH9579Item', mappedBy: 'container')] public $items; public function __construct() @@ -90,18 +85,14 @@ public function __construct() $this->items = new ArrayCollection(); } - /** - * @var GH9579Item - * @OneToOne(targetEntity="GH9579Item") - * @JoinColumn(name="item_id", referencedColumnName="id") - */ + /** @var GH9579Item */ + #[OneToOne(targetEntity: 'GH9579Item')] + #[JoinColumn(name: 'item_id', referencedColumnName: 'id')] public $currentItem; } -/** - * @Entity - * @Table(name="GH9579_items") - */ +#[Table(name: 'GH9579_items')] +#[Entity] class GH9579Item { public function __construct() @@ -109,46 +100,34 @@ public function __construct() $this->parts = new ArrayCollection(); } - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Collection - * @OneToMany(targetEntity="GH9579Part", mappedBy="item") - */ + /** @var Collection */ + #[OneToMany(targetEntity: 'GH9579Part', mappedBy: 'item')] public $parts; - /** - * @var GH9579Container - * @ManyToOne (targetEntity="GH9579Container", inversedBy="items") - * @JoinColumn(name="container_id", referencedColumnName="id") - */ + /** @var GH9579Container */ + #[ManyToOne(targetEntity: 'GH9579Container', inversedBy: 'items')] + #[JoinColumn(name: 'container_id', referencedColumnName: 'id')] public $container; } -/** - * @Entity - * @Table(name="GH9579_parts") - */ +#[Table(name: 'GH9579_parts')] +#[Entity] class GH9579Part { - /** - * @Id - * @Column(type="integer") - * @GeneratedValue - * @var int - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var GH9579Item - * @ManyToOne (targetEntity="GH9579Item", inversedBy="parts") - * @JoinColumn(name="item_id", referencedColumnName="id") - */ + /** @var GH9579Item */ + #[ManyToOne(targetEntity: 'GH9579Item', inversedBy: 'parts')] + #[JoinColumn(name: 'item_id', referencedColumnName: 'id')] public $item; } diff --git a/tests/Tests/ORM/Functional/Ticket/GH9807Test.php b/tests/Tests/ORM/Functional/Ticket/GH9807Test.php index c8c4fed0d80..17b43256662 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH9807Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH9807Test.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Common\Collections\Collection; +use Doctrine\DBAL\Connection; use Doctrine\ORM\Internal\Hydration\ObjectHydrator; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Column; @@ -40,7 +41,7 @@ public function testHydrateJoinedCollectionWithFirstNullishRow(): void $uow->createEntity( GH9807Main::class, - ['id' => 1] + ['id' => 1], ); $resultSet = [ @@ -61,7 +62,7 @@ public function testHydrateJoinedCollectionWithFirstNullishRow(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class)); /** @var GH9807Main[] $result */ $result = $hydrator->hydrateAll($stmt, $rsm); @@ -71,22 +72,17 @@ public function testHydrateJoinedCollectionWithFirstNullishRow(): void } } -/** @Entity */ +#[Entity] class GH9807Main { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] private $id; - /** - * @ORM\ManyToMany(targetEntity="GH9807Join", inversedBy="starts") - * - * @var Collection - */ + /** @var Collection */ + #[ORM\ManyToMany(targetEntity: 'GH9807Join', inversedBy: 'starts')] private $joins; /** @return Collection */ @@ -96,28 +92,20 @@ public function getJoins(): Collection } } -/** @Entity */ +#[Entity] class GH9807Join { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue] private $id; - /** - * @ORM\ManyToMany(targetEntity="GH9807Main", mappedBy="bases") - * - * @var Collection - */ + /** @var Collection */ + #[ORM\ManyToMany(targetEntity: 'GH9807Main', mappedBy: 'bases')] private $mains; - /** - * @ORM\Column(type="string", nullable=false) - * - * @var string - */ + /** @var string */ + #[ORM\Column(type: 'string', nullable: false)] private $value; } diff --git a/tests/Tests/ORM/Functional/Ticket/Issue5989Test.php b/tests/Tests/ORM/Functional/Ticket/Issue5989Test.php index d2eaa3d734e..b0ec1866b91 100644 --- a/tests/Tests/ORM/Functional/Ticket/Issue5989Test.php +++ b/tests/Tests/ORM/Functional/Ticket/Issue5989Test.php @@ -8,8 +8,9 @@ use Doctrine\Tests\Models\Issue5989\Issue5989Manager; use Doctrine\Tests\Models\Issue5989\Issue5989Person; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group issue-5989 */ +#[Group('issue-5989')] class Issue5989Test extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/Functional/Ticket/Issue9300Test.php b/tests/Tests/ORM/Functional/Ticket/Issue9300Test.php index 5a6309e433c..48f15924331 100644 --- a/tests/Tests/ORM/Functional/Ticket/Issue9300Test.php +++ b/tests/Tests/ORM/Functional/Ticket/Issue9300Test.php @@ -8,10 +8,9 @@ use Doctrine\Tests\Models\Issue9300\Issue9300Child; use Doctrine\Tests\Models\Issue9300\Issue9300Parent; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group GH-9300 - */ +#[Group('GH-9300')] class Issue9300Test extends OrmFunctionalTestCase { protected function setUp(): void @@ -21,9 +20,6 @@ protected function setUp(): void parent::setUp(); } - /** - * @group GH-9300 - */ public function testPersistedCollectionIsPresentInOriginalDataAfterFlush(): void { $parent = new Issue9300Parent(); @@ -45,9 +41,6 @@ public function testPersistedCollectionIsPresentInOriginalDataAfterFlush(): void self::assertArrayHasKey('parents', $this->_em->getUnitOfWork()->getOriginalEntityData($child)); } - /** - * @group GH-9300 - */ public function testPersistingCollectionAfterFlushWorksAsExpected(): void { $parentOne = new Issue9300Parent(); diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/ChangeFiltersTest.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/ChangeFiltersTest.php index 7ce97442b28..df48e013d12 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/ChangeFiltersTest.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/ChangeFiltersTest.php @@ -24,9 +24,7 @@ public function setUp(): void ]); } - /** - * @return non-empty-array<"companyA"|"companyB", array{orderId: int, userId: int}> - */ + /** @return non-empty-array<"companyA"|"companyB", array{orderId: int, userId: int}> */ private function prepareData(): array { $user1 = new User(self::COMPANY_A); @@ -97,7 +95,7 @@ public function testUseQueryBuilder(): void ['companyA' => $companyA, 'companyB' => $companyB] = $this->prepareData(); - $getOrderByIdCache = function (int $orderId): ?Order { + $getOrderByIdCache = function (int $orderId): Order|null { return $this->_em->createQueryBuilder() ->select('orderMaster, user') ->from(Order::class, 'orderMaster') diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/CompanySQLFilter.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/CompanySQLFilter.php index e65188334ac..d1e38528bf1 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/CompanySQLFilter.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/CompanySQLFilter.php @@ -14,11 +14,11 @@ class CompanySQLFilter extends SQLFilter public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string { if ($targetEntity->getName() === User::class) { - return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']['fieldName'], $this->getParameter('company')); + return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']->fieldName, $this->getParameter('company')); } if ($targetEntity->getName() === Order::class) { - return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']['fieldName'], $this->getParameter('company')); + return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['company']->fieldName, $this->getParameter('company')); } return ''; diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Order.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Order.php index a6d86dca8a2..37709a94cd2 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Order.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/Order.php @@ -6,34 +6,20 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="Order_Master") - */ +#[ORM\Entity] +#[ORM\Table(name: 'Order_Master')] class Order { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $company; + #[ORM\Column(type: 'string')] + public string $company; - /** - * @ORM\ManyToOne(targetEntity="User", fetch="EAGER") - * - * @var User - */ - public $user; + #[ORM\ManyToOne(targetEntity: User::class, fetch: 'EAGER')] + public User $user; public function __construct(User $user) { diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/User.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/User.php index 294bfdf87aa..660fb345a49 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/User.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilter/User.php @@ -6,27 +6,17 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="User_Master") - */ +#[ORM\Entity] +#[ORM\Table(name: 'User_Master')] class User { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $company; + #[ORM\Column(type: 'string')] + public string $company; public function __construct(string $company) { diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Category.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Category.php index f80d89bc98c..fb9a2eb6e22 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Category.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Category.php @@ -6,34 +6,20 @@ use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="Category_Master") - */ +#[ORM\Entity] +#[ORM\Table(name: 'Category_Master')] class Category { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $name; + #[ORM\Column(type: 'string')] + public string $name; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $type; + #[ORM\Column(type: 'string')] + public string $type; public function __construct(string $name, string $type) { diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/CategoryTypeSQLFilter.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/CategoryTypeSQLFilter.php index 459e0c2f2f7..d9a96470a38 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/CategoryTypeSQLFilter.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/CategoryTypeSQLFilter.php @@ -14,7 +14,7 @@ class CategoryTypeSQLFilter extends SQLFilter public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string { if ($targetEntity->getName() === Category::class) { - return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['type']['fieldName'], $this->getParameter('type')); + return sprintf('%s.%s = %s', $targetTableAlias, $targetEntity->fieldMappings['type']->fieldName, $this->getParameter('type')); } return ''; diff --git a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Company.php b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Company.php index c0a54cfbeeb..0c85555d4fb 100644 --- a/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Company.php +++ b/tests/Tests/ORM/Functional/Ticket/SwitchContextWithFilterAndIndexedRelation/Company.php @@ -8,34 +8,21 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -/** - * @ORM\Entity - * @ORM\Table(name="Company_Master") - */ +#[ORM\Entity] +#[ORM\Table(name: 'Company_Master')] class Company { - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ - public $id; + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public int $id; - /** - * @ORM\Column(type="string") - * - * @var string - */ - public $name; + #[ORM\Column(type: 'string')] + public string $name; - /** - * @ORM\ManyToMany(targetEntity="Category", fetch="EAGER", indexBy="type") - * - * @var Collection - */ - public $categories; + /** @var Collection */ + #[ORM\ManyToMany(targetEntity: Category::class, fetch: 'EAGER', indexBy: 'type')] + public Collection $categories; /** @param Category[] $categories */ public function __construct(string $name, array $categories) diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket2481Test.php b/tests/Tests/ORM/Functional/Ticket/Ticket2481Test.php index b1702fbaf19..b3882dfccb3 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket2481Test.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket2481Test.php @@ -30,17 +30,13 @@ public function testEmptyInsert(): void } } -/** - * @Entity - * @Table(name="ticket_2481_products") - */ +#[Table(name: 'ticket_2481_products')] +#[Entity] class Ticket2481Product { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php index 7903fe0827d..c9aea38997e 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php @@ -22,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels( PersonTicket4646Abstract::class, - EmployeeTicket4646Abstract::class + EmployeeTicket4646Abstract::class, ); } @@ -41,35 +41,26 @@ public function testInstanceOf(): void } } -/** - * @Entity() - * @Table(name="instance_of_abstract_test_person") - * @InheritanceType(value="JOINED") - * @DiscriminatorColumn(name="kind", type="string") - * @DiscriminatorMap(value={ - * "employee": EmployeeTicket4646Abstract::class - * }) - */ +#[Table(name: 'instance_of_abstract_test_person')] +#[Entity] +#[InheritanceType(value: 'JOINED')] +#[DiscriminatorColumn(name: 'kind', type: 'string')] +#[DiscriminatorMap(value: ['employee' => EmployeeTicket4646Abstract::class])] abstract class PersonTicket4646Abstract { - /** - * @var int - * @Id() - * @GeneratedValue() - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - public function getId(): ?int + public function getId(): int|null { return $this->id; } } -/** - * @Entity() - * @Table(name="instance_of_abstract_test_employee") - */ +#[Table(name: 'instance_of_abstract_test_employee')] +#[Entity] class EmployeeTicket4646Abstract extends PersonTicket4646Abstract { } diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php index 612052fa647..fd234d8fb21 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php @@ -23,7 +23,7 @@ protected function setUp(): void $this->createSchemaForModels( PersonTicket4646MultiLevel::class, EmployeeTicket4646MultiLevel::class, - EngineerTicket4646MultiLevel::class + EngineerTicket4646MultiLevel::class, ); } @@ -44,45 +44,32 @@ public function testInstanceOf(): void } } -/** - * @Entity() - * @Table(name="instance_of_multi_level_test_person") - * @InheritanceType(value="JOINED") - * @DiscriminatorColumn(name="kind", type="string") - * @DiscriminatorMap(value={ - * "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646MultiLevel", - * "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646MultiLevel", - * "engineer": "Doctrine\Tests\ORM\Functional\Ticket\EngineerTicket4646MultiLevel", - * }) - */ +#[Table(name: 'instance_of_multi_level_test_person')] +#[Entity] +#[InheritanceType(value: 'JOINED')] +#[DiscriminatorColumn(name: 'kind', type: 'string')] +#[DiscriminatorMap(value: ['person' => 'Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646MultiLevel', 'employee' => 'Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646MultiLevel', 'engineer' => 'Doctrine\Tests\ORM\Functional\Ticket\EngineerTicket4646MultiLevel'])] class PersonTicket4646MultiLevel { - /** - * @var int - * @Id() - * @GeneratedValue() - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - public function getId(): ?int + public function getId(): int|null { return $this->id; } } -/** - * @Entity() - * @Table(name="instance_of_multi_level_employee") - */ +#[Table(name: 'instance_of_multi_level_employee')] +#[Entity] class EmployeeTicket4646MultiLevel extends PersonTicket4646MultiLevel { } -/** - * @Entity() - * @Table(name="instance_of_multi_level_engineer") - */ +#[Table(name: 'instance_of_multi_level_engineer')] +#[Entity] class EngineerTicket4646MultiLevel extends EmployeeTicket4646MultiLevel { } diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php index cf0b243ae81..2bd7f165361 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php @@ -22,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels( PersonTicket4646Parametric::class, - EmployeeTicket4646Parametric::class + EmployeeTicket4646Parametric::class, ); } @@ -36,7 +36,7 @@ public function testInstanceOf(): void $query = $this->_em->createQuery($dql); $query->setParameter( 'parameter', - $this->_em->getClassMetadata(PersonTicket4646Parametric::class) + $this->_em->getClassMetadata(PersonTicket4646Parametric::class), ); $result = $query->getResult(); self::assertCount(2, $result); @@ -44,36 +44,26 @@ public function testInstanceOf(): void } } -/** - * @Entity() - * @Table(name="instance_of_parametric_person") - * @InheritanceType(value="JOINED") - * @DiscriminatorColumn(name="kind", type="string") - * @DiscriminatorMap(value={ - * "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Parametric", - * "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646Parametric" - * }) - */ +#[Table(name: 'instance_of_parametric_person')] +#[Entity] +#[InheritanceType(value: 'JOINED')] +#[DiscriminatorColumn(name: 'kind', type: 'string')] +#[DiscriminatorMap(value: ['person' => 'Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Parametric', 'employee' => 'Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646Parametric'])] class PersonTicket4646Parametric { - /** - * @var int - * @Id() - * @GeneratedValue() - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - public function getId(): ?int + public function getId(): int|null { return $this->id; } } -/** - * @Entity() - * @Table(name="instance_of_parametric_employee") - */ +#[Table(name: 'instance_of_parametric_employee')] +#[Entity] class EmployeeTicket4646Parametric extends PersonTicket4646Parametric { } diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfTest.php b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfTest.php index 865f38b7ade..803892768e1 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfTest.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfTest.php @@ -22,7 +22,7 @@ protected function setUp(): void $this->createSchemaForModels( PersonTicket4646::class, - EmployeeTicket4646::class + EmployeeTicket4646::class, ); } @@ -42,36 +42,26 @@ public function testInstanceOf(): void } } -/** - * @Entity() - * @Table(name="instance_of_test_person") - * @InheritanceType(value="JOINED") - * @DiscriminatorColumn(name="kind", type="string") - * @DiscriminatorMap(value={ - * "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646", - * "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646" - * }) - */ +#[Table(name: 'instance_of_test_person')] +#[Entity] +#[InheritanceType(value: 'JOINED')] +#[DiscriminatorColumn(name: 'kind', type: 'string')] +#[DiscriminatorMap(value: ['person' => 'Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646', 'employee' => 'Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646'])] class PersonTicket4646 { - /** - * @var int - * @Id() - * @GeneratedValue() - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - public function getId(): ?int + public function getId(): int|null { return $this->id; } } -/** - * @Entity() - * @Table(name="instance_of_test_employee") - */ +#[Table(name: 'instance_of_test_employee')] +#[Entity] class EmployeeTicket4646 extends PersonTicket4646 { } diff --git a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfWithMultipleParametersTest.php b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfWithMultipleParametersTest.php index 50dce0475df..1bece72951f 100644 --- a/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfWithMultipleParametersTest.php +++ b/tests/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfWithMultipleParametersTest.php @@ -24,7 +24,7 @@ protected function setUp(): void PersonTicket4646Multiple::class, EmployeeTicket4646Multiple::class, ManagerTicket4646Multiple::class, - InternTicket4646Multiple::class + InternTicket4646Multiple::class, ); } @@ -46,27 +46,17 @@ public function testInstanceOf(): void } } -/** - * @Entity() - * @Table(name="instance_of_test_multiple_person") - * @InheritanceType(value="JOINED") - * @DiscriminatorColumn(name="kind", type="string") - * @DiscriminatorMap(value={ - * "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Multiple", - * "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646Multiple", - * "manager": "Doctrine\Tests\ORM\Functional\Ticket\ManagerTicket4646Multiple", - * "intern": "Doctrine\Tests\ORM\Functional\Ticket\InternTicket4646Multiple" - * }) - */ +#[Table(name: 'instance_of_test_multiple_person')] +#[Entity] +#[InheritanceType(value: 'JOINED')] +#[DiscriminatorColumn(name: 'kind', type: 'string')] +#[DiscriminatorMap(value: ['person' => 'Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Multiple', 'employee' => 'Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646Multiple', 'manager' => 'Doctrine\Tests\ORM\Functional\Ticket\ManagerTicket4646Multiple', 'intern' => 'Doctrine\Tests\ORM\Functional\Ticket\InternTicket4646Multiple'])] class PersonTicket4646Multiple { - /** - * @var int - * @Id() - * @GeneratedValue() - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; public function getId(): int { @@ -74,26 +64,20 @@ public function getId(): int } } -/** - * @Entity() - * @Table(name="instance_of_test_multiple_employee") - */ +#[Table(name: 'instance_of_test_multiple_employee')] +#[Entity] class EmployeeTicket4646Multiple extends PersonTicket4646Multiple { } -/** - * @Entity() - * @Table(name="instance_of_test_multiple_manager") - */ +#[Table(name: 'instance_of_test_multiple_manager')] +#[Entity] class ManagerTicket4646Multiple extends PersonTicket4646Multiple { } -/** - * @Entity() - * @Table(name="instance_of_test_multiple_intern") - */ +#[Table(name: 'instance_of_test_multiple_intern')] +#[Entity] class InternTicket4646Multiple extends PersonTicket4646Multiple { } diff --git a/tests/Tests/ORM/Functional/TypeTest.php b/tests/Tests/ORM/Functional/TypeTest.php index 325bd650840..9743ebcfb17 100644 --- a/tests/Tests/ORM/Functional/TypeTest.php +++ b/tests/Tests/ORM/Functional/TypeTest.php @@ -6,14 +6,19 @@ use DateTime; use DateTimeZone; +use Doctrine\DBAL\Types\ArrayType; +use Doctrine\DBAL\Types\ObjectType; use Doctrine\DBAL\Types\Types; use Doctrine\Tests\Models\Generic\BooleanModel; use Doctrine\Tests\Models\Generic\DateTimeModel; use Doctrine\Tests\Models\Generic\DecimalModel; use Doctrine\Tests\Models\Generic\SerializationModel; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use stdClass; +use function class_exists; + class TypeTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -40,7 +45,7 @@ public function testDecimal(): void self::assertSame('0.1515', $decimal->highScale); } - /** @group DDC-1394 */ + #[Group('DDC-1394')] public function testBoolean(): void { $bool = new BooleanModel(); @@ -68,6 +73,10 @@ public function testBoolean(): void public function testArray(): void { + if (! class_exists(ArrayType::class)) { + self::markTestSkipped('Test valid for doctrine/dbal:3.x only.'); + } + $serialize = new SerializationModel(); $serialize->array['foo'] = 'bar'; $serialize->array['bar'] = 'baz'; @@ -86,6 +95,10 @@ public function testArray(): void public function testObject(): void { + if (! class_exists(ObjectType::class)) { + self::markTestSkipped('Test valid for doctrine/dbal:3.x only.'); + } + $serialize = new SerializationModel(); $serialize->object = new stdClass(); @@ -98,7 +111,7 @@ public function testObject(): void $dql = 'SELECT s FROM ' . SerializationModel::class . ' s'; $serialize = $this->_em->createQuery($dql)->getSingleResult(); - self::assertInstanceOf('stdClass', $serialize->object); + self::assertInstanceOf(stdClass::class, $serialize->object); } public function testDate(): void diff --git a/tests/Tests/ORM/Functional/TypeValueSqlTest.php b/tests/Tests/ORM/Functional/TypeValueSqlTest.php index a9960a75c22..c20f4dd0427 100644 --- a/tests/Tests/ORM/Functional/TypeValueSqlTest.php +++ b/tests/Tests/ORM/Functional/TypeValueSqlTest.php @@ -11,6 +11,7 @@ use Doctrine\Tests\Models\CustomType\CustomTypeParent; use Doctrine\Tests\Models\CustomType\CustomTypeUpperCase; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; class TypeValueSqlTest extends OrmFunctionalTestCase { @@ -45,13 +46,13 @@ public function testUpperCaseStringType(): void $this->_em->clear(); - $entity = $this->_em->find('\Doctrine\Tests\Models\CustomType\CustomTypeUpperCase', $id); + $entity = $this->_em->find(CustomTypeUpperCase::class, $id); self::assertEquals('foo', $entity->lowerCaseString, 'Entity holds lowercase string'); self::assertEquals('FOO', $this->_em->getConnection()->fetchOne('select lowerCaseString from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string'); } - /** @group DDC-1642 */ + #[Group('DDC-1642')] public function testUpperCaseStringTypeWhenColumnNameIsDefined(): void { $entity = new CustomTypeUpperCase(); @@ -65,7 +66,7 @@ public function testUpperCaseStringTypeWhenColumnNameIsDefined(): void $this->_em->clear(); - $entity = $this->_em->find('\Doctrine\Tests\Models\CustomType\CustomTypeUpperCase', $id); + $entity = $this->_em->find(CustomTypeUpperCase::class, $id); self::assertEquals('foo', $entity->namedLowerCaseString, 'Entity holds lowercase string'); self::assertEquals('FOO', $this->_em->getConnection()->fetchOne('select named_lower_case_string from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string'); @@ -78,7 +79,7 @@ public function testUpperCaseStringTypeWhenColumnNameIsDefined(): void $this->_em->clear(); - $entity = $this->_em->find('\Doctrine\Tests\Models\CustomType\CustomTypeUpperCase', $id); + $entity = $this->_em->find(CustomTypeUpperCase::class, $id); self::assertEquals('bar', $entity->namedLowerCaseString, 'Entity holds lowercase string'); self::assertEquals('BAR', $this->_em->getConnection()->fetchOne('select named_lower_case_string from customtype_uppercases where id=' . $entity->id . ''), 'Database holds uppercase string'); } diff --git a/tests/Tests/ORM/Functional/UUIDGeneratorTest.php b/tests/Tests/ORM/Functional/UUIDGeneratorTest.php deleted file mode 100644 index f7e1cdeba61..00000000000 --- a/tests/Tests/ORM/Functional/UUIDGeneratorTest.php +++ /dev/null @@ -1,84 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/7312'); - $this->_em->getClassMetadata(UUIDEntity::class); - } - - public function testGenerateUUID(): void - { - if (! method_exists(AbstractPlatform::class, 'getGuidExpression')) { - self::markTestSkipped('Test valid for doctrine/dbal:2.x only.'); - } - - if (! $this->_em->getConnection()->getDatabasePlatform() instanceof MySQLPlatform) { - self::markTestSkipped('Currently restricted to MySQL platform.'); - } - - $this->createSchemaForModels(UUIDEntity::class); - $entity = new UUIDEntity(); - - $this->_em->persist($entity); - self::assertNotNull($entity->getId()); - self::assertGreaterThan(0, strlen($entity->getId())); - } - - public function testItCannotBeInitialised(): void - { - if (method_exists(AbstractPlatform::class, 'getGuidExpression')) { - self::markTestSkipped('Test valid for doctrine/dbal:3.x only.'); - } - - $this->expectException(NotSupported::class); - $this->_em->getClassMetadata(UUIDEntity::class); - } -} - -/** @Entity */ -class UUIDEntity -{ - /** - * @var string - * @Id - * @Column(type="string") - * @GeneratedValue(strategy="UUID") - */ - private $id; - - /** - * Get id. - * - * @return string. - */ - public function getId(): string - { - return $this->id; - } -} diff --git a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php index a3ae19cc8e3..8ce7163e3c8 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdForeignKeyTest.php @@ -4,9 +4,13 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). @@ -14,9 +18,8 @@ * * Test that ManyToMany associations with composite id of which one is a * association itself work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class ManyToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -72,45 +75,45 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('tuv', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany_compositeid_foreignkey LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedManyToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToManyCompositeIdForeignKeyEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\AuxiliaryEntity::class, $auxiliary); - self::assertInstanceOf(Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity::class, $owning); + self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary); + self::assertInstanceOf(InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(OwningManyToManyCompositeIdForeignKeyEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedManyToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToManyCompositeIdForeignKeyEntity::class, + 'ghi', ); self::assertEquals('abc', $auxiliary->id4); @@ -119,48 +122,46 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id2); } - /** @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')] public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => $auxiliary] + InversedManyToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => $auxiliary], ); - self::assertInstanceOf(Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(InversedManyToManyCompositeIdForeignKeyEntity::class, $inversed); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToManyCompositeIdForeignKeyEntity::class, + 'ghi', ); self::assertCount(1, $owning->associatedEntities); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedManyToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); self::assertCount(1, $inversed->associatedEntities); } - /** - * @depends testThatTheCollectionFromOwningToInversedIsLoaded - * @depends testThatTheCollectionFromInversedToOwningIsLoaded - */ + #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')] + #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')] public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void { $conn = $this->_em->getConnection(); @@ -168,8 +169,8 @@ public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): // remove association $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedManyToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); foreach ($inversed->associatedEntities as $owning) { diff --git a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php index c158b7de2fa..9f9d5b28f53 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyCompositeIdTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that ManyToMany associations with composite id work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class ManyToManyCompositeIdTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -64,34 +66,34 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('tuv', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany_compositeid LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedManyToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdEntity::class, - 'ghi' + OwningManyToManyCompositeIdEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToManyCompositeIdEntity::class, $owning); + self::assertInstanceOf(InversedManyToManyCompositeIdEntity::class, $inversed); + self::assertInstanceOf(OwningManyToManyCompositeIdEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedManyToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdEntity::class, - 'ghi' + OwningManyToManyCompositeIdEntity::class, + 'ghi', ); self::assertEquals('abc', $inversed->id1); @@ -99,32 +101,30 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id3); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyCompositeIdEntity::class, - 'ghi' + OwningManyToManyCompositeIdEntity::class, + 'ghi', ); self::assertCount(1, $owning->associatedEntities); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedManyToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); self::assertCount(1, $inversed->associatedEntities); } - /** - * @depends testThatTheCollectionFromOwningToInversedIsLoaded - * @depends testThatTheCollectionFromInversedToOwningIsLoaded - */ + #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')] + #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')] public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void { $conn = $this->_em->getConnection(); @@ -132,8 +132,8 @@ public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): // remove association $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedManyToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); foreach ($inversed->associatedEntities as $owning) { diff --git a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php index 248aaaeae36..c1adbdf8e1b 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyExtraLazyTest.php @@ -4,9 +4,11 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). @@ -14,9 +16,8 @@ * * Test that ManyToMany associations work correctly, focusing on EXTRA_LAZY * functionality. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class ManyToManyExtraLazyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -68,8 +69,8 @@ public static function tearDownAfterClass(): void public function testThatTheExtraLazyCollectionFromOwningToInversedIsCounted(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, - 'ghi' + OwningManyToManyExtraLazyEntity::class, + 'ghi', ); self::assertEquals(2, $owning->associatedEntities->count()); @@ -78,8 +79,8 @@ public function testThatTheExtraLazyCollectionFromOwningToInversedIsCounted(): v public function testThatTheExtraLazyCollectionFromInversedToOwningIsCounted(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - 'abc' + InversedManyToManyExtraLazyEntity::class, + 'abc', ); self::assertEquals(2, $inversed->associatedEntities->count()); @@ -88,13 +89,13 @@ public function testThatTheExtraLazyCollectionFromInversedToOwningIsCounted(): v public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnEntity(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, - 'ghi' + OwningManyToManyExtraLazyEntity::class, + 'ghi', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - 'abc' + InversedManyToManyExtraLazyEntity::class, + 'abc', ); self::assertTrue($owning->associatedEntities->contains($inversed)); @@ -103,13 +104,13 @@ public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnEnti public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnEntity(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - 'abc' + InversedManyToManyExtraLazyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, - 'ghi' + OwningManyToManyExtraLazyEntity::class, + 'ghi', ); self::assertTrue($inversed->associatedEntities->contains($owning)); @@ -118,8 +119,8 @@ public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnEnti public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnIndexByKey(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, - 'ghi' + OwningManyToManyExtraLazyEntity::class, + 'ghi', ); self::assertTrue($owning->associatedEntities->containsKey('abc')); @@ -128,8 +129,8 @@ public function testThatTheExtraLazyCollectionFromOwningToInversedContainsAnInde public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnIndexByKey(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - 'abc' + InversedManyToManyExtraLazyEntity::class, + 'abc', ); self::assertTrue($inversed->associatedEntities->containsKey('ghi')); @@ -138,8 +139,8 @@ public function testThatTheExtraLazyCollectionFromInversedToOwningContainsAnInde public function testThatASliceOfTheExtraLazyCollectionFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, - 'ghi' + OwningManyToManyExtraLazyEntity::class, + 'ghi', ); self::assertCount(1, $owning->associatedEntities->slice(0, 1)); @@ -148,8 +149,8 @@ public function testThatASliceOfTheExtraLazyCollectionFromOwningToInversedIsLoad public function testThatASliceOfTheExtraLazyCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - 'abc' + InversedManyToManyExtraLazyEntity::class, + 'abc', ); self::assertCount(1, $inversed->associatedEntities->slice(1, 1)); diff --git a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php index 6063312eb64..f8370239d1a 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/ManyToManyTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that ManyToMany associations work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class ManyToManyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -61,66 +63,64 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('qrs', $conn->fetchOne('SELECT owning_id FROM vct_xref_manytomany LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyEntity::class, - 'abc' + InversedManyToManyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyEntity::class, - 'def' + OwningManyToManyEntity::class, + 'def', ); - self::assertInstanceOf(Models\ValueConversionType\InversedManyToManyEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToManyEntity::class, $owning); + self::assertInstanceOf(InversedManyToManyEntity::class, $inversed); + self::assertInstanceOf(OwningManyToManyEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyEntity::class, - 'abc' + InversedManyToManyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyEntity::class, - 'def' + OwningManyToManyEntity::class, + 'def', ); self::assertEquals('abc', $inversed->id1); self::assertEquals('def', $owning->id2); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToManyEntity::class, - 'def' + OwningManyToManyEntity::class, + 'def', ); self::assertCount(1, $owning->associatedEntities); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyEntity::class, - 'abc' + InversedManyToManyEntity::class, + 'abc', ); self::assertCount(1, $inversed->associatedEntities); } - /** - * @depends testThatTheCollectionFromOwningToInversedIsLoaded - * @depends testThatTheCollectionFromInversedToOwningIsLoaded - */ + #[Depends('testThatTheCollectionFromOwningToInversedIsLoaded')] + #[Depends('testThatTheCollectionFromInversedToOwningIsLoaded')] public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): void { $conn = $this->_em->getConnection(); @@ -128,8 +128,8 @@ public function testThatTheJoinTableRowsAreRemovedWhenRemovingTheAssociation(): // remove association $inversed = $this->_em->find( - Models\ValueConversionType\InversedManyToManyEntity::class, - 'abc' + InversedManyToManyEntity::class, + 'abc', ); foreach ($inversed->associatedEntities as $owning) { diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php index f5aa0f9833b..a346881ed96 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdForeignKeyTest.php @@ -4,9 +4,13 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). @@ -14,9 +18,8 @@ * * Test that OneToMany associations with composite id of which one is a * association itself work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToManyCompositeIdForeignKeyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -70,45 +73,45 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('nop', $conn->fetchOne('SELECT associated_foreign_id FROM vct_owning_manytoone_compositeid_foreignkey LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\AuxiliaryEntity::class, $auxiliary); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity::class, $owning); + self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary); + self::assertInstanceOf(InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(OwningManyToOneCompositeIdForeignKeyEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); self::assertEquals('abc', $auxiliary->id4); @@ -117,28 +120,28 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id2); } - /** @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')] public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => $auxiliary] + InversedOneToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => $auxiliary], ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(InversedOneToManyCompositeIdForeignKeyEntity::class, $inversed); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningManyToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); $inversedProxy = $owning->associatedEntity; @@ -148,12 +151,12 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToManyCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); self::assertCount(1, $inversed->associatedEntities); diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php index 625baf990a8..8304e0c311e 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyCompositeIdTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that OneToMany associations with composite id work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToManyCompositeIdTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -62,34 +64,34 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id2 FROM vct_owning_manytoone_compositeid LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdEntity::class, - 'ghi' + OwningManyToOneCompositeIdEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToManyCompositeIdEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToOneCompositeIdEntity::class, $owning); + self::assertInstanceOf(InversedOneToManyCompositeIdEntity::class, $inversed); + self::assertInstanceOf(OwningManyToOneCompositeIdEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdEntity::class, - 'ghi' + OwningManyToOneCompositeIdEntity::class, + 'ghi', ); self::assertEquals('abc', $inversed->id1); @@ -97,12 +99,12 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id3); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneCompositeIdEntity::class, - 'ghi' + OwningManyToOneCompositeIdEntity::class, + 'ghi', ); $inversedProxy = $owning->associatedEntity; @@ -110,12 +112,12 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToManyCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); self::assertCount(1, $inversed->associatedEntities); diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php index 72833ea6d60..9b3e2fe929e 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyExtraLazyTest.php @@ -4,9 +4,11 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneExtraLazyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). @@ -14,9 +16,8 @@ * * Test that OneToMany associations work correctly, focusing on EXTRA_LAZY * functionality. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToManyExtraLazyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -64,8 +65,8 @@ public static function tearDownAfterClass(): void public function testThatExtraLazyCollectionIsCounted(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyExtraLazyEntity::class, - 'abc' + InversedOneToManyExtraLazyEntity::class, + 'abc', ); self::assertEquals(3, $inversed->associatedEntities->count()); @@ -74,13 +75,13 @@ public function testThatExtraLazyCollectionIsCounted(): void public function testThatExtraLazyCollectionContainsAnEntity(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyExtraLazyEntity::class, - 'abc' + InversedOneToManyExtraLazyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneExtraLazyEntity::class, - 'def' + OwningManyToOneExtraLazyEntity::class, + 'def', ); self::assertTrue($inversed->associatedEntities->contains($owning)); @@ -89,8 +90,8 @@ public function testThatExtraLazyCollectionContainsAnEntity(): void public function testThatExtraLazyCollectionContainsAnIndexbyKey(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyExtraLazyEntity::class, - 'abc' + InversedOneToManyExtraLazyEntity::class, + 'abc', ); self::assertTrue($inversed->associatedEntities->containsKey('def')); @@ -99,8 +100,8 @@ public function testThatExtraLazyCollectionContainsAnIndexbyKey(): void public function testThatASliceOfTheExtraLazyCollectionIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyExtraLazyEntity::class, - 'abc' + InversedOneToManyExtraLazyEntity::class, + 'abc', ); self::assertCount(2, $inversed->associatedEntities->slice(0, 2)); diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php index 1a80dd9ce21..f91dae6ceda 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToManyTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that OneToMany associations work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToManyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -59,46 +61,46 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('nop', $conn->fetchOne('SELECT associated_id FROM vct_owning_manytoone LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyEntity::class, - 'abc' + InversedOneToManyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneEntity::class, - 'def' + OwningManyToOneEntity::class, + 'def', ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToManyEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningManyToOneEntity::class, $owning); + self::assertInstanceOf(InversedOneToManyEntity::class, $inversed); + self::assertInstanceOf(OwningManyToOneEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyEntity::class, - 'abc' + InversedOneToManyEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneEntity::class, - 'def' + OwningManyToOneEntity::class, + 'def', ); self::assertEquals('abc', $inversed->id1); self::assertEquals('def', $owning->id2); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningManyToOneEntity::class, - 'def' + OwningManyToOneEntity::class, + 'def', ); $inversedProxy = $owning->associatedEntity; @@ -106,12 +108,12 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheCollectionFromInversedToOwningIsLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToManyEntity::class, - 'abc' + InversedOneToManyEntity::class, + 'abc', ); self::assertCount(1, $inversed->associatedEntities); diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php index 56b33fef72f..8f8ac3846a7 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdForeignKeyTest.php @@ -4,9 +4,13 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). @@ -14,9 +18,8 @@ * * Test that OneToOne associations with composite id of which one is a * association itself work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToOneCompositeIdForeignKeyTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -70,45 +73,45 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('nop', $conn->fetchOne('SELECT associated_foreign_id FROM vct_owning_onetoone_compositeid_foreignkey LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToOneCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningOneToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\AuxiliaryEntity::class, $auxiliary); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, $owning); + self::assertInstanceOf(AuxiliaryEntity::class, $auxiliary); + self::assertInstanceOf(InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(OwningOneToOneCompositeIdForeignKeyEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToOneCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningOneToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); self::assertEquals('abc', $auxiliary->id4); @@ -117,28 +120,28 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id2); } - /** @depends testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase')] public function testThatInversedEntityIsFetchedFromTheDatabaseUsingAuxiliaryEntityAsId(): void { $auxiliary = $this->_em->find( - Models\ValueConversionType\AuxiliaryEntity::class, - 'abc' + AuxiliaryEntity::class, + 'abc', ); $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => $auxiliary] + InversedOneToOneCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => $auxiliary], ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed); + self::assertInstanceOf(InversedOneToOneCompositeIdForeignKeyEntity::class, $inversed); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, - 'ghi' + OwningOneToOneCompositeIdForeignKeyEntity::class, + 'ghi', ); $inversedProxy = $owning->associatedEntity; @@ -146,14 +149,14 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, - ['id1' => 'def', 'foreignEntity' => 'abc'] + InversedOneToOneCompositeIdForeignKeyEntity::class, + ['id1' => 'def', 'foreignEntity' => 'abc'], ); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, $inversed->associatedEntity); + self::assertInstanceOf(OwningOneToOneCompositeIdForeignKeyEntity::class, $inversed->associatedEntity); } } diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php index 950b0da5481..0a609e23966 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneCompositeIdTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that OneToOne associations with composite id work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToOneCompositeIdTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -62,34 +64,34 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('qrs', $conn->fetchOne('SELECT associated_id2 FROM vct_owning_onetoone_compositeid LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToOneCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, - 'ghi' + OwningOneToOneCompositeIdEntity::class, + 'ghi', ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToOneCompositeIdEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, $owning); + self::assertInstanceOf(InversedOneToOneCompositeIdEntity::class, $inversed); + self::assertInstanceOf(OwningOneToOneCompositeIdEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToOneCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, - 'ghi' + OwningOneToOneCompositeIdEntity::class, + 'ghi', ); self::assertEquals('abc', $inversed->id1); @@ -97,12 +99,12 @@ public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFr self::assertEquals('ghi', $owning->id3); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, - 'ghi' + OwningOneToOneCompositeIdEntity::class, + 'ghi', ); $inversedProxy = $owning->associatedEntity; @@ -110,14 +112,14 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneCompositeIdEntity::class, - ['id1' => 'abc', 'id2' => 'def'] + InversedOneToOneCompositeIdEntity::class, + ['id1' => 'abc', 'id2' => 'def'], ); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, $inversed->associatedEntity); + self::assertInstanceOf(OwningOneToOneCompositeIdEntity::class, $inversed->associatedEntity); } } diff --git a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php index dba57c35327..f91f6caff06 100644 --- a/tests/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php +++ b/tests/Tests/ORM/Functional/ValueConversionType/OneToOneTest.php @@ -4,18 +4,20 @@ namespace Doctrine\Tests\ORM\Functional\ValueConversionType; -use Doctrine\Tests\Models; use Doctrine\Tests\Models\ValueConversionType as Entity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Depends; +use PHPUnit\Framework\Attributes\Group; /** * The entities all use a custom type that converst the value as identifier(s). * {@see \Doctrine\Tests\DbalTypes\Rot13Type} * * Test that OneToOne associations work correctly. - * - * @group DDC-3380 */ +#[Group('DDC-3380')] class OneToOneTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -59,46 +61,46 @@ public function testThatTheValueOfIdentifiersAreConvertedInTheDatabase(): void self::assertEquals('nop', $conn->fetchOne('SELECT associated_id FROM vct_owning_onetoone LIMIT 1')); } - /** @depends testThatTheValueOfIdentifiersAreConvertedInTheDatabase */ + #[Depends('testThatTheValueOfIdentifiersAreConvertedInTheDatabase')] public function testThatEntitiesAreFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneEntity::class, - 'abc' + InversedOneToOneEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneEntity::class, - 'def' + OwningOneToOneEntity::class, + 'def', ); - self::assertInstanceOf(Models\ValueConversionType\InversedOneToOneEntity::class, $inversed); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneEntity::class, $owning); + self::assertInstanceOf(InversedOneToOneEntity::class, $inversed); + self::assertInstanceOf(OwningOneToOneEntity::class, $owning); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheValueOfIdentifiersAreConvertedBackAfterBeingFetchedFromTheDatabase(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneEntity::class, - 'abc' + InversedOneToOneEntity::class, + 'abc', ); $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneEntity::class, - 'def' + OwningOneToOneEntity::class, + 'def', ); self::assertEquals('abc', $inversed->id1); self::assertEquals('def', $owning->id2); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheProxyFromOwningToInversedIsLoaded(): void { $owning = $this->_em->find( - Models\ValueConversionType\OwningOneToOneEntity::class, - 'def' + OwningOneToOneEntity::class, + 'def', ); $inversedProxy = $owning->associatedEntity; @@ -106,14 +108,14 @@ public function testThatTheProxyFromOwningToInversedIsLoaded(): void self::assertEquals('some value to be loaded', $inversedProxy->someProperty); } - /** @depends testThatEntitiesAreFetchedFromTheDatabase */ + #[Depends('testThatEntitiesAreFetchedFromTheDatabase')] public function testThatTheEntityFromInversedToOwningIsEagerLoaded(): void { $inversed = $this->_em->find( - Models\ValueConversionType\InversedOneToOneEntity::class, - 'abc' + InversedOneToOneEntity::class, + 'abc', ); - self::assertInstanceOf(Models\ValueConversionType\OwningOneToOneEntity::class, $inversed->associatedEntity); + self::assertInstanceOf(OwningOneToOneEntity::class, $inversed->associatedEntity); } } diff --git a/tests/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Tests/ORM/Functional/ValueObjectsTest.php index 096751aa610..ad77a29b29f 100644 --- a/tests/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Tests/ORM/Functional/ValueObjectsTest.php @@ -21,12 +21,14 @@ use Doctrine\ORM\Query\QueryException; use Doctrine\Persistence\Reflection\RuntimeReflectionProperty; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use ReflectionProperty; use function class_exists; use function sprintf; -/** @group DDC-93 */ +#[Group('DDC-93')] class ValueObjectsTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -39,7 +41,7 @@ protected function setUp(): void DDC93Vehicle::class, DDC93Car::class, DDC3027Animal::class, - DDC3027Dog::class + DDC3027Dog::class, ); } @@ -50,17 +52,17 @@ public function testMetadataHasReflectionEmbeddablesAccessible(): void if (class_exists(CommonRuntimePublicReflectionProperty::class)) { self::assertInstanceOf( CommonRuntimePublicReflectionProperty::class, - $classMetadata->getReflectionProperty('address') + $classMetadata->getReflectionProperty('address'), ); } elseif (class_exists(RuntimeReflectionProperty::class)) { self::assertInstanceOf( RuntimeReflectionProperty::class, - $classMetadata->getReflectionProperty('address') + $classMetadata->getReflectionProperty('address'), ); } else { self::assertInstanceOf( ReflectionProperty::class, - $classMetadata->getReflectionProperty('address') + $classMetadata->getReflectionProperty('address'), ); } @@ -159,7 +161,7 @@ public function testLoadDql(): void } } - /** @group dql */ + #[Group('dql')] public function testDqlOnEmbeddedObjectsField(): void { if ($this->isSecondLevelCacheEnabled) { @@ -182,7 +184,7 @@ public function testDqlOnEmbeddedObjectsField(): void $this->_em->createQuery($selectDql) ->setParameter('city', 'asdf') ->setParameter('country', 'Germany') - ->getOneOrNullResult() + ->getOneOrNullResult(), ); // UPDATE @@ -327,18 +329,18 @@ public function testInlineEmbeddableInMappedSuperClass(): void self::assertTrue($isFieldMapped); } - /** @dataProvider getInfiniteEmbeddableNestingData */ + #[DataProvider('getInfiniteEmbeddableNestingData')] public function testThrowsExceptionOnInfiniteEmbeddableNesting( string $embeddableClassName, - string $declaredEmbeddableClassName + string $declaredEmbeddableClassName, ): void { $this->expectException(MappingException::class); $this->expectExceptionMessage( sprintf( 'Infinite nesting detected for embedded property %s::nested. ' . 'You cannot embed an embeddable from the same type inside an embeddable.', - __NAMESPACE__ . '\\' . $declaredEmbeddableClassName - ) + __NAMESPACE__ . '\\' . $declaredEmbeddableClassName, + ), ); $this->createSchemaForModels(__NAMESPACE__ . '\\' . $embeddableClassName); @@ -355,406 +357,267 @@ public static function getInfiniteEmbeddableNestingData(): array } -/** @Entity */ +#[Entity] class DDC93Person { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] public $id; - /** - * @var string|null - * @Column(type="string", length=255) - */ - public $name; - - /** - * @var DDC93Address|null - * @Embedded(class="DDC93Address") - */ - public $address; - - /** - * @var DDC93Timestamps - * @Embedded(class = "DDC93Timestamps") - */ + /** @var DDC93Timestamps */ + #[Embedded(class: 'DDC93Timestamps')] public $timestamps; - public function __construct(?string $name = null, ?DDC93Address $address = null) - { - $this->name = $name; - $this->address = $address; + public function __construct( + /** @var string|null */ + #[Column(type: 'string', length: 255)] + public $name = null, + #[Embedded(class: 'DDC93Address')] + public DDC93Address|null $address = null, + ) { $this->timestamps = new DDC93Timestamps(new DateTime()); } } -/** @Embeddable */ +#[Embeddable] class DDC93Timestamps { - /** - * @var DateTime - * @Column(type = "datetime") - */ - public $createdAt; - - public function __construct(DateTime $createdAt) - { - $this->createdAt = $createdAt; + public function __construct( + #[Column(type: 'datetime')] + public DateTime $createdAt, + ) { } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name = "t", type = "string", length = 10) - * @DiscriminatorMap({ - * "v" = "Doctrine\Tests\ORM\Functional\DDC93Car", - * }) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 't', type: 'string', length: 10)] +#[DiscriminatorMap(['v' => 'Doctrine\Tests\ORM\Functional\DDC93Car'])] abstract class DDC93Vehicle { - /** - * @var int - * @Id - * @GeneratedValue(strategy = "AUTO") - * @Column(type = "integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC93Address - * @Embedded(class = "DDC93Address") - */ - public $address; - - public function __construct(DDC93Address $address) - { - $this->address = $address; + public function __construct( + #[Embedded(class: 'DDC93Address')] + public DDC93Address $address, + ) { } } -/** @Entity */ +#[Entity] class DDC93Car extends DDC93Vehicle { } -/** @Embeddable */ +#[Embeddable] class DDC93Country { - /** - * @var string|null - * @Column(type="string", nullable=true) - */ - public $name; - - public function __construct(?string $name = null) - { - $this->name = $name; + public function __construct( + #[Column(type: 'string', nullable: true)] + public string|null $name = null, + ) { } } -/** @Embeddable */ +#[Embeddable] class DDC93Address { - /** - * @var string|null - * @Column(type="string", length=255) - */ - public $street; - - /** - * @var string|null - * @Column(type="string", length=255) - */ - public $zip; + #[Embedded(class: DDC93Country::class)] + public DDC93Country|null $country = null; /** - * @var string|null - * @Column(type="string", length=255) + * @param string|null $street + * @param string|null $zip */ - public $city; - - /** - * @var DDC93Country|null - * @Embedded(class = "DDC93Country") - */ - public $country; - public function __construct( - ?string $street = null, - ?string $zip = null, - ?string $city = null, - ?DDC93Country $country = null + #[Column(type: 'string', length: 255)] + public $street = null, + #[Column(type: 'string', length: 255)] + public $zip = null, + #[Column(type: 'string', length: 255)] + public string|null $city = null, + DDC93Country|null $country = null, ) { - $this->street = $street; - $this->zip = $zip; - $this->city = $city; $this->country = $country; } } -/** @Entity */ +#[Entity] class DDC93Customer { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'integer')] + private int $id; - /** - * @var DDC93ContactInfo - * @Embedded(class = "DDC93ContactInfo", columnPrefix = "contact_info_") - */ - private $contactInfo; + #[Embedded(class: 'DDC93ContactInfo', columnPrefix: 'contact_info_')] + private DDC93ContactInfo $contactInfo; } -/** @Embeddable */ +#[Embeddable] class DDC93ContactInfo { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $email; - /** - * @var DDC93Address - * @Embedded(class = "DDC93Address") - */ + /** @var DDC93Address */ + #[Embedded(class: 'DDC93Address')] public $address; } -/** @Entity */ +#[Entity] class DDC3028PersonWithPrefix { - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = "foobar_") - */ - public $id; - - /** - * @var DDC3028NestedEmbeddable|null - * @Embedded(class="DDC3028NestedEmbeddable", columnPrefix = "bloo_") - */ - public $nested; - - public function __construct(?DDC3028Id $id = null, ?DDC3028NestedEmbeddable $nested = null) - { - $this->id = $id; - $this->nested = $nested; + public function __construct( + #[Embedded(class: 'DDC3028Id', columnPrefix: 'foobar_')] + public DDC3028Id|null $id = null, + #[Embedded(class: 'DDC3028NestedEmbeddable', columnPrefix: 'bloo_')] + public DDC3028NestedEmbeddable|null $nested = null, + ) { } } -/** @Entity */ +#[Entity] class DDC3028PersonEmptyPrefix { - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = "") - */ - public $id; - - /** - * @var DDC3028NestedEmbeddable|null - * @Embedded(class="DDC3028NestedEmbeddable", columnPrefix = "") - */ - public $nested; - - public function __construct(?DDC3028Id $id = null, ?DDC3028NestedEmbeddable $nested = null) - { - $this->id = $id; - $this->nested = $nested; + public function __construct( + #[Embedded(class: 'DDC3028Id', columnPrefix: '')] + public DDC3028Id|null $id = null, + #[Embedded(class: 'DDC3028NestedEmbeddable', columnPrefix: '')] + public DDC3028NestedEmbeddable|null $nested = null, + ) { } } -/** @Entity */ +#[Entity] class DDC3028PersonPrefixFalse { - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = false) - */ - public $id; - - public function __construct(?DDC3028Id $id = null) - { - $this->id = $id; + public function __construct( + #[Embedded(class: 'DDC3028Id', columnPrefix: false)] + public DDC3028Id|null $id = null, + ) { } } -/** @Embeddable */ +#[Embeddable] class DDC3028Id { - /** - * @var string|null - * @Id - * @Column(type="string", length=255) - */ - public $id; - - public function __construct(?string $id = null) - { - $this->id = $id; + public function __construct( + #[Id] + #[Column(type: 'string', length: 255)] + public string|null $id = null, + ) { } } -/** @Embeddable */ +#[Embeddable] class DDC3028NestedEmbeddable { - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = "foo_") - */ - public $nestedWithPrefix; - - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = "") - */ - public $nestedWithEmptyPrefix; - - /** - * @var DDC3028Id|null - * @Embedded(class="DDC3028Id", columnPrefix = false) - */ - public $nestedWithPrefixFalse; - public function __construct( - ?DDC3028Id $nestedWithPrefix = null, - ?DDC3028Id $nestedWithEmptyPrefix = null, - ?DDC3028Id $nestedWithPrefixFalse = null + #[Embedded(class: 'DDC3028Id', columnPrefix: 'foo_')] + public DDC3028Id|null $nestedWithPrefix = null, + #[Embedded(class: 'DDC3028Id', columnPrefix: '')] + public DDC3028Id|null $nestedWithEmptyPrefix = null, + #[Embedded(class: 'DDC3028Id', columnPrefix: false)] + public DDC3028Id|null $nestedWithPrefixFalse = null, ) { - $this->nestedWithPrefix = $nestedWithPrefix; - $this->nestedWithEmptyPrefix = $nestedWithEmptyPrefix; - $this->nestedWithPrefixFalse = $nestedWithPrefixFalse; } } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class DDC3027Animal { - /** - * @var int - * @Id - * @GeneratedValue(strategy = "AUTO") - * @Column(type = "integer") - */ + /** @var int */ + #[Id] + #[GeneratedValue(strategy: 'AUTO')] + #[Column(type: 'integer')] public $id; - /** - * @var DDC93Address - * @Embedded(class = "DDC93Address") - */ + /** @var DDC93Address */ + #[Embedded(class: 'DDC93Address')] public $address; } -/** @Entity */ +#[Entity] class DDC3027Dog extends DDC3027Animal { } -/** @Embeddable */ +#[Embeddable] class DDCInfiniteNestingEmbeddable { - /** - * @var DDCInfiniteNestingEmbeddable - * @Embedded(class="DDCInfiniteNestingEmbeddable") - */ + /** @var DDCInfiniteNestingEmbeddable */ + #[Embedded(class: 'DDCInfiniteNestingEmbeddable')] public $nested; } -/** @Embeddable */ +#[Embeddable] class DDCNestingEmbeddable1 { - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id1; - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id2; - /** - * @var DDCNestingEmbeddable2 - * @Embedded(class="DDCNestingEmbeddable2") - */ + /** @var DDCNestingEmbeddable2 */ + #[Embedded(class: 'DDCNestingEmbeddable2')] public $nested; } -/** @Embeddable */ +#[Embeddable] class DDCNestingEmbeddable2 { - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id1; - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id2; - /** - * @var DDCNestingEmbeddable3 - * @Embedded(class="DDCNestingEmbeddable3") - */ + /** @var DDCNestingEmbeddable3 */ + #[Embedded(class: 'DDCNestingEmbeddable3')] public $nested; } -/** @Embeddable */ +#[Embeddable] class DDCNestingEmbeddable3 { - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id1; - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id2; - /** - * @var DDCNestingEmbeddable4 - * @Embedded(class="DDCNestingEmbeddable4") - */ + /** @var DDCNestingEmbeddable4 */ + #[Embedded(class: 'DDCNestingEmbeddable4')] public $nested; } -/** @Embeddable */ +#[Embeddable] class DDCNestingEmbeddable4 { - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id1; - /** - * @var DDC3028Id - * @Embedded(class="DDC3028Id") - */ + /** @var DDC3028Id */ + #[Embedded(class: 'DDC3028Id')] public $id2; - /** - * @var DDCNestingEmbeddable1 - * @Embedded(class="DDCNestingEmbeddable1") - */ + /** @var DDCNestingEmbeddable1 */ + #[Embedded(class: 'DDCNestingEmbeddable1')] public $nested; } diff --git a/tests/Tests/ORM/Functional/VersionedOneToOneTest.php b/tests/Tests/ORM/Functional/VersionedOneToOneTest.php index d008e17ef79..8029695f3b5 100644 --- a/tests/Tests/ORM/Functional/VersionedOneToOneTest.php +++ b/tests/Tests/ORM/Functional/VersionedOneToOneTest.php @@ -7,12 +7,12 @@ use Doctrine\Tests\Models\VersionedOneToOne\FirstRelatedEntity; use Doctrine\Tests\Models\VersionedOneToOne\SecondRelatedEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Tests that an entity with a OneToOne relationship defined as the id, with a version field can be created. - * - * @group VersionedOneToOne */ +#[Group('VersionedOneToOne')] class VersionedOneToOneTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -21,7 +21,7 @@ protected function setUp(): void $this->createSchemaForModels( FirstRelatedEntity::class, - SecondRelatedEntity::class + SecondRelatedEntity::class, ); } diff --git a/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.Person.dcm.xml b/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.Person.dcm.xml deleted file mode 100644 index 661061c1c4c..00000000000 --- a/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.Person.dcm.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.PhoneNumber.dcm.xml b/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.PhoneNumber.dcm.xml deleted file mode 100644 index 7b19b1a62a2..00000000000 --- a/tests/Tests/ORM/Functional/xml/Doctrine.Tests.Models.OrnementalOrphanRemoval.PhoneNumber.dcm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/tests/Tests/ORM/Hydration/AbstractHydratorTest.php b/tests/Tests/ORM/Hydration/AbstractHydratorTest.php index c08c24c0e5c..d83ee1a8647 100644 --- a/tests/Tests/ORM/Hydration/AbstractHydratorTest.php +++ b/tests/Tests/ORM/Hydration/AbstractHydratorTest.php @@ -2,36 +2,32 @@ declare(strict_types=1); -namespace Doctrine\Tests\ORM\Functional\Ticket; +namespace Doctrine\Tests\ORM\Hydration; use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Result; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; +use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Internal\Hydration\AbstractHydrator; -use Doctrine\ORM\ORMException; 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; use PHPUnit\Framework\MockObject\MockObject; use function iterator_to_array; -/** @covers \Doctrine\ORM\Internal\Hydration\AbstractHydrator */ +#[CoversClass(AbstractHydrator::class)] class AbstractHydratorTest extends OrmFunctionalTestCase { - /** @var EventManager&MockObject */ - private $mockEventManager; - - /** @var Result&MockObject */ - private $mockResult; - - /** @var ResultSetMapping&MockObject */ - private $mockResultMapping; - - /** @var AbstractHydrator&MockObject */ - private $hydrator; + private EventManager&MockObject $mockEventManager; + private Result&MockObject $mockResult; + private ResultSetMapping&MockObject $mockResultMapping; + private AbstractHydrator&MockObject $hydrator; protected function setUp(): void { @@ -43,16 +39,16 @@ protected function setUp(): void $this->mockResult = $this->createMock(Result::class); $this->mockResultMapping = $this->createMock(ResultSetMapping::class); + $mockConnection + ->method('getDatabasePlatform') + ->willReturn($this->createMock(AbstractPlatform::class)); $mockEntityManagerInterface - ->expects(self::any()) ->method('getEventManager') ->willReturn($this->mockEventManager); $mockEntityManagerInterface - ->expects(self::any()) ->method('getConnection') ->willReturn($mockConnection); $this->mockResult - ->expects(self::any()) ->method('fetchAssociative') ->willReturn(false); @@ -63,12 +59,11 @@ protected function setUp(): void } /** - * @group DDC-3146 - * @group #1515 - * * Verify that the number of added events to the event listener from the abstract hydrator class is equal to the * number of removed events */ + #[Group('DDC-3146')] + #[Group('#1515')] public function testOnClearEventListenerIsDetachedOnCleanup(): void { $eventListenerHasBeenRegistered = false; @@ -92,10 +87,10 @@ public function testOnClearEventListenerIsDetachedOnCleanup(): void $this->assertTrue($eventListenerHasBeenRegistered); }); - iterator_to_array($this->hydrator->iterate($this->mockResult, $this->mockResultMapping)); + iterator_to_array($this->hydrator->toIterable($this->mockResult, $this->mockResultMapping)); } - /** @group #6623 */ + #[Group('#6623')] public function testHydrateAllRegistersAndClearsAllAttachedListeners(): void { $eventListenerHasBeenRegistered = false; @@ -122,7 +117,7 @@ public function testHydrateAllRegistersAndClearsAllAttachedListeners(): void $this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping); } - /** @group #8482 */ + #[Group('#8482')] public function testHydrateAllClearsAllAttachedListenersEvenOnError(): void { $eventListenerHasBeenRegistered = false; @@ -150,7 +145,7 @@ public function testHydrateAllClearsAllAttachedListenersEvenOnError(): void ->hydrator ->expects(self::once()) ->method('hydrateAllData') - ->willThrowException(new ORMException()); + ->willThrowException($this->createStub(ORMException::class)); $this->expectException(ORMException::class); $this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping); diff --git a/tests/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Tests/ORM/Hydration/ArrayHydratorTest.php index 72926a06e02..db3c5bd4659 100644 --- a/tests/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -6,17 +6,18 @@ use Doctrine\ORM\Internal\Hydration\ArrayHydrator; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsComment; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Forum\ForumBoard; use Doctrine\Tests\Models\Forum\ForumCategory; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; class ArrayHydratorTest extends HydrationTestCase { - /** @phpstan-return list */ + /** @phpstan-return list */ public static function provideDataForUserEntityResult(): array { return [ @@ -51,7 +52,7 @@ public function testSimpleEntityQuery(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -68,10 +69,9 @@ public function testSimpleEntityQuery(): void /** * SELECT PARTIAL scalars.{id, name}, UPPER(scalars.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser scalars - * - * @dataProvider provideDataForUserEntityResult */ - public function testSimpleEntityWithScalarQuery($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + public function testSimpleEntityWithScalarQuery(int|string $userEntityKey): void { $alias = $userEntityKey ?: 'u'; $rsm = new ResultSetMapping(); @@ -95,7 +95,7 @@ public function testSimpleEntityWithScalarQuery($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -143,7 +143,7 @@ public function testSimpleEntityQueryWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -190,7 +190,7 @@ public function testSimpleMultipleRootEntityQuery(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -240,7 +240,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -294,7 +294,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedArticleEntity(): voi ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -348,7 +348,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -376,10 +376,9 @@ public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void * FROM Doctrine\Tests\Models\CMS\CmsUser u * JOIN u.phonenumbers p * GROUP BY u.status, u.id - * - * @dataProvider provideDataForUserEntityResult */ - public function testMixedQueryNormalJoin($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + public function testMixedQueryNormalJoin(int|string $userEntityKey): void { $rsm = new ResultSetMapping(); @@ -403,7 +402,7 @@ public function testMixedQueryNormalJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -425,10 +424,9 @@ public function testMixedQueryNormalJoin($userEntityKey): void * SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * JOIN u.phonenumbers p - * - * @dataProvider provideDataForUserEntityResult */ - public function testMixedQueryFetchJoin($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + public function testMixedQueryFetchJoin(int|string $userEntityKey): void { $rsm = new ResultSetMapping(); @@ -437,7 +435,7 @@ public function testMixedQueryFetchJoin($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -467,7 +465,7 @@ public function testMixedQueryFetchJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -496,10 +494,9 @@ public function testMixedQueryFetchJoin($userEntityKey): void * INDEX BY u.id * JOIN u.phonenumbers p * INDEX BY p.phonenumber - * - * @dataProvider provideDataForUserEntityResult */ - public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + public function testMixedQueryFetchJoinCustomIndex(int|string $userEntityKey): void { $rsm = new ResultSetMapping(); @@ -508,7 +505,7 @@ public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -540,7 +537,7 @@ public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -586,13 +583,13 @@ public function testMixedQueryMultipleFetchJoin(): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addJoinedEntityResult( CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -654,7 +651,7 @@ public function testMixedQueryMultipleFetchJoin(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -705,19 +702,19 @@ public function testMixedQueryMultipleDeepMixedFetchJoin(): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addJoinedEntityResult( CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addJoinedEntityResult( CmsComment::class, 'c', 'a', - 'comments' + 'comments', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -793,7 +790,7 @@ public function testMixedQueryMultipleDeepMixedFetchJoin(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -862,7 +859,7 @@ public function testEntityQueryCustomResultSetOrder(): void ForumBoard::class, 'b', 'c', - 'boards' + 'boards', ); $rsm->addFieldResult('c', 'c__id', 'id'); @@ -907,7 +904,7 @@ public function testEntityQueryCustomResultSetOrder(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -926,10 +923,9 @@ public function testEntityQueryCustomResultSetOrder(): void * FROM Doctrine\Tests\Models\CMS\CmsUser u * LEFT JOIN u.articles a * LEFT JOIN a.comments c - * - * @dataProvider provideDataForUserEntityResult */ - public function testChainedJoinWithScalars($entityKey): void + #[DataProvider('provideDataForUserEntityResult')] + public function testChainedJoinWithScalars(int|string $entityKey): void { $rsm = new ResultSetMapping(); @@ -970,7 +966,7 @@ public function testChainedJoinWithScalars($entityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1019,21 +1015,18 @@ public function testResultIteration(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); - $iterator = $hydrator->iterate($stmt, $rsm); + $iterator = $hydrator->toIterable($stmt, $rsm); $rowNum = 0; - while (($row = $iterator->next()) !== false) { - self::assertCount(1, $row); - self::assertIsArray($row[0]); - + foreach ($iterator as $row) { if ($rowNum === 0) { - self::assertEquals(1, $row[0]['id']); - self::assertEquals('romanb', $row[0]['name']); + self::assertEquals(1, $row['id']); + self::assertEquals('romanb', $row['name']); } elseif ($rowNum === 1) { - self::assertEquals(2, $row[0]['id']); - self::assertEquals('jwage', $row[0]['name']); + self::assertEquals(2, $row['id']); + self::assertEquals('jwage', $row['name']); } ++$rowNum; @@ -1064,22 +1057,20 @@ public function testResultIterationWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); - $iterator = $hydrator->iterate($stmt, $rsm); + $iterator = $hydrator->toIterable($stmt, $rsm); $rowNum = 0; - while (($row = $iterator->next()) !== false) { - self::assertCount(1, $row); - self::assertArrayHasKey(0, $row); - self::assertArrayHasKey('user', $row[0]); + foreach ($iterator as $row) { + self::assertArrayHasKey('user', $row); if ($rowNum === 0) { - self::assertEquals(1, $row[0]['user']['id']); - self::assertEquals('romanb', $row[0]['user']['name']); + self::assertEquals(1, $row['user']['id']); + self::assertEquals('romanb', $row['user']['name']); } elseif ($rowNum === 1) { - self::assertEquals(2, $row[0]['user']['id']); - self::assertEquals('jwage', $row[0]['user']['name']); + self::assertEquals(2, $row['user']['id']); + self::assertEquals('jwage', $row['user']['name']); } ++$rowNum; @@ -1089,9 +1080,8 @@ public function testResultIterationWithAliasedUserEntity(): void /** * SELECT PARTIAL u.{id, name} * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-644 */ + #[Group('DDC-644')] public function testSkipUnknownColumns(): void { $rsm = new ResultSetMapping(); @@ -1109,7 +1099,7 @@ public function testSkipUnknownColumns(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1122,11 +1112,10 @@ public function testSkipUnknownColumns(): void /** * SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-1358 - * @dataProvider provideDataForUserEntityResult */ - public function testMissingIdForRootEntity($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1358')] + public function testMissingIdForRootEntity(int|string $userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1160,7 +1149,7 @@ public function testMissingIdForRootEntity($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1181,11 +1170,10 @@ public function testMissingIdForRootEntity($userEntityKey): void * SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * INDEX BY u.id - * - * @group DDC-1385 - * @dataProvider provideDataForUserEntityResult */ - public function testIndexByAndMixedResult($userEntityKey): void + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1385')] + public function testIndexByAndMixedResult(int|string $userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1210,7 +1198,7 @@ public function testIndexByAndMixedResult($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ArrayHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); diff --git a/tests/Tests/ORM/Hydration/CustomHydratorTest.php b/tests/Tests/ORM/Hydration/CustomHydratorTest.php index d5e9ad7d96f..2e2dcf3eb10 100644 --- a/tests/Tests/ORM/Hydration/CustomHydratorTest.php +++ b/tests/Tests/ORM/Hydration/CustomHydratorTest.php @@ -25,7 +25,7 @@ class CustomHydrator extends AbstractHydrator /** * {@inheritDoc} */ - protected function hydrateAllData() + protected function hydrateAllData(): array { return $this->_stmt->fetchAllAssociative(); } diff --git a/tests/Tests/ORM/Hydration/HydrationTestCase.php b/tests/Tests/ORM/Hydration/HydrationTestCase.php index a1128709682..4e8e9709257 100644 --- a/tests/Tests/ORM/Hydration/HydrationTestCase.php +++ b/tests/Tests/ORM/Hydration/HydrationTestCase.php @@ -4,13 +4,14 @@ namespace Doctrine\Tests\ORM\Hydration; +use Doctrine\DBAL\Result; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\OrmTestCase; class HydrationTestCase extends OrmTestCase { - /** @var EntityManagerInterface */ - protected $entityManager; + protected EntityManagerInterface $entityManager; protected function setUp(): void { @@ -18,4 +19,9 @@ protected function setUp(): void $this->entityManager = $this->getTestEntityManager(); } + + protected function createResultMock(array $resultSet): Result + { + return ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->entityManager->getConnection()); + } } diff --git a/tests/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Tests/ORM/Hydration/ObjectHydratorTest.php index e1479547746..526817263a7 100644 --- a/tests/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -4,9 +4,12 @@ namespace Doctrine\Tests\ORM\Hydration; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Internal\Hydration\HydrationException; use Doctrine\ORM\Internal\Hydration\ObjectHydrator; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Proxy\InternalProxy; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Tests\Mocks\ArrayResultFactory; @@ -25,15 +28,16 @@ use Doctrine\Tests\Models\Forum\ForumCategory; use Doctrine\Tests\Models\Hydration\EntityWithArrayDefaultArrayValueM2M; use Doctrine\Tests\Models\Hydration\SimpleEntity; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; use function count; use function property_exists; +use function sys_get_temp_dir; class ObjectHydratorTest extends HydrationTestCase { - use MockBuilderCompatibilityTools; - /** @phpstan-return list */ public static function provideDataForUserEntityResult(): array { @@ -86,7 +90,7 @@ public function testSimpleEntityQuery(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -125,7 +129,7 @@ public function testSimpleEntityQueryWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -174,7 +178,7 @@ public function testSimpleMultipleRootEntityQuery(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -228,7 +232,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -289,7 +293,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedArticleEntity(): voi ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -350,7 +354,7 @@ public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -386,9 +390,8 @@ public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void * FROM User u * JOIN u.phonenumbers p * GROUP BY u.id - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testMixedQueryNormalJoin($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -412,7 +415,7 @@ public function testMixedQueryNormalJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -435,9 +438,8 @@ public function testMixedQueryNormalJoin($userEntityKey): void * SELECT u, p, UPPER(u.name) nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * JOIN u.phonenumbers p - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testMixedQueryFetchJoin($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -446,7 +448,7 @@ public function testMixedQueryFetchJoin($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -476,7 +478,7 @@ public function testMixedQueryFetchJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -513,9 +515,8 @@ public function testMixedQueryFetchJoin($userEntityKey): void * INDEX BY u.id * JOIN u.phonenumbers p * INDEX BY p.phonenumber - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -524,7 +525,7 @@ public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -556,7 +557,7 @@ public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -591,9 +592,8 @@ public function testMixedQueryFetchJoinCustomIndex($userEntityKey): void * FROM User u * JOIN u.phonenumbers p * JOIN u.articles a - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testMixedQueryMultipleFetchJoin($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -602,13 +602,13 @@ public function testMixedQueryMultipleFetchJoin($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addJoinedEntityResult( CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -670,7 +670,7 @@ public function testMixedQueryMultipleFetchJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -701,9 +701,8 @@ public function testMixedQueryMultipleFetchJoin($userEntityKey): void * JOIN u.phonenumbers p * JOIN u.articles a * LEFT JOIN a.comments c - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testMixedQueryMultipleDeepMixedFetchJoin($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -712,19 +711,19 @@ public function testMixedQueryMultipleDeepMixedFetchJoin($userEntityKey): void CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addJoinedEntityResult( CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addJoinedEntityResult( CmsComment::class, 'c', 'a', - 'comments' + 'comments', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -800,7 +799,7 @@ public function testMixedQueryMultipleDeepMixedFetchJoin($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -869,7 +868,7 @@ public function testEntityQueryCustomResultSetOrder(): void ForumBoard::class, 'b', 'c', - 'boards' + 'boards', ); $rsm->addFieldResult('c', 'c__id', 'id'); $rsm->addFieldResult('c', 'c__position', 'position'); @@ -913,7 +912,7 @@ public function testEntityQueryCustomResultSetOrder(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -937,9 +936,8 @@ public function testEntityQueryCustomResultSetOrder(): void /** * SELECT u * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-644 */ + #[Group('DDC-644')] public function testSkipUnknownColumns(): void { $rsm = new ResultSetMapping(); @@ -956,7 +954,7 @@ public function testSkipUnknownColumns(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -967,9 +965,8 @@ public function testSkipUnknownColumns(): void /** * SELECT u.id, u.name * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] public function testScalarQueryWithoutResultVariables($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -989,7 +986,7 @@ public function testScalarQueryWithoutResultVariables($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1026,25 +1023,29 @@ public function testCreatesProxyForLazyLoadingWithForeignKeys(): void ], ]; - $proxyInstance = new ECommerceShipping(); - - // mocking the proxy factory - $proxyFactory = $this->getMockBuilderWithOnlyMethods(ProxyFactory::class, ['getProxy']) - ->disableOriginalConstructor() - ->getMock(); - - $proxyFactory->expects(self::once()) - ->method('getProxy') - ->with(self::equalTo(ECommerceShipping::class), ['id' => 42]) - ->willReturn($proxyInstance); + // extending the proxy factory to spy on getProxy() + $proxyFactory = new class ( + $this->entityManager, + sys_get_temp_dir(), + 'Proxies', + ProxyFactory::AUTOGENERATE_ALWAYS, + ) extends ProxyFactory { + public function getProxy(string $className, array $identifier): InternalProxy + { + TestCase::assertSame(ECommerceShipping::class, $className); + TestCase::assertSame(['id' => 42], $identifier); + + return parent::getProxy($className, $identifier); + } + }; $this->entityManager->setProxyFactory($proxyFactory); // configuring lazy loading - $metadata = $this->entityManager->getClassMetadata(ECommerceProduct::class); - $metadata->associationMappings['shipping']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->entityManager->getClassMetadata(ECommerceProduct::class); + $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1076,23 +1077,29 @@ public function testCreatesProxyForLazyLoadingWithForeignKeysWithAliasedProductE $proxyInstance = new ECommerceShipping(); - // mocking the proxy factory - $proxyFactory = $this->getMockBuilderWithOnlyMethods(ProxyFactory::class, ['getProxy']) - ->disableOriginalConstructor() - ->getMock(); - - $proxyFactory->expects(self::once()) - ->method('getProxy') - ->with(self::equalTo(ECommerceShipping::class), ['id' => 42]) - ->willReturn($proxyInstance); + // extending the proxy factory to spy on getProxy() + $proxyFactory = new class ( + $this->entityManager, + sys_get_temp_dir(), + 'Proxies', + ProxyFactory::AUTOGENERATE_ALWAYS, + ) extends ProxyFactory { + public function getProxy(string $className, array $identifier): InternalProxy + { + TestCase::assertSame(ECommerceShipping::class, $className); + TestCase::assertSame(['id' => 42], $identifier); + + return parent::getProxy($className, $identifier); + } + }; $this->entityManager->setProxyFactory($proxyFactory); // configuring lazy loading - $metadata = $this->entityManager->getClassMetadata(ECommerceProduct::class); - $metadata->associationMappings['shipping']['fetch'] = ClassMetadata::FETCH_LAZY; + $metadata = $this->entityManager->getClassMetadata(ECommerceProduct::class); + $metadata->associationMappings['shipping']->fetch = ClassMetadata::FETCH_LAZY; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1116,13 +1123,13 @@ public function testChainedJoinWithEmptyCollections(): void CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addJoinedEntityResult( CmsComment::class, 'c', 'a', - 'comments' + 'comments', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -1152,7 +1159,7 @@ public function testChainedJoinWithEmptyCollections(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1179,13 +1186,13 @@ public function testChainedJoinWithEmptyCollectionsWithAliasedUserEntity(): void CmsArticle::class, 'a', 'u', - 'articles' + 'articles', ); $rsm->addJoinedEntityResult( CmsComment::class, 'c', 'a', - 'comments' + 'comments', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -1215,7 +1222,7 @@ public function testChainedJoinWithEmptyCollectionsWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1256,22 +1263,21 @@ public function testResultIteration(): void $hydrator = new ObjectHydrator($this->entityManager); - $iterableResult = $hydrator->iterate( - ArrayResultFactory::createFromArray($resultSet), - $rsm + $iterableResult = $hydrator->toIterable( + $this->createResultMock($resultSet), + $rsm, ); $rowNum = 0; - while (($row = $iterableResult->next()) !== false) { - self::assertEquals(1, count($row)); - self::assertInstanceOf(CmsUser::class, $row[0]); + foreach ($iterableResult as $row) { + self::assertInstanceOf(CmsUser::class, $row); if ($rowNum === 0) { - self::assertEquals(1, $row[0]->id); - self::assertEquals('romanb', $row[0]->name); + self::assertEquals(1, $row->id); + self::assertEquals('romanb', $row->name); } elseif ($rowNum === 1) { - self::assertEquals(2, $row[0]->id); - self::assertEquals('jwage', $row[0]->name); + self::assertEquals(2, $row->id); + self::assertEquals('jwage', $row->name); } ++$rowNum; @@ -1280,8 +1286,8 @@ public function testResultIteration(): void self::assertSame(2, $rowNum); $iterableResult = $hydrator->toIterable( - ArrayResultFactory::createFromArray($resultSet), - $rsm + $this->createResultMock($resultSet), + $rsm, ); $rowNum = 0; @@ -1329,23 +1335,22 @@ public function testResultIterationWithAliasedUserEntity(): void $hydrator = new ObjectHydrator($this->entityManager); $rowNum = 0; - $iterableResult = $hydrator->iterate( - ArrayResultFactory::createFromArray($resultSet), - $rsm + $iterableResult = $hydrator->toIterable( + ArrayResultFactory::createWrapperResultFromArray($resultSet, $this->createMock(Connection::class)), + $rsm, ); - while (($row = $iterableResult->next()) !== false) { - self::assertEquals(1, count($row)); - self::assertArrayHasKey(0, $row); - self::assertArrayHasKey('user', $row[0]); - self::assertInstanceOf(CmsUser::class, $row[0]['user']); + foreach ($iterableResult as $row) { + self::assertCount(1, $row); + self::assertArrayHasKey('user', $row); + self::assertInstanceOf(CmsUser::class, $row['user']); if ($rowNum === 0) { - self::assertEquals(1, $row[0]['user']->id); - self::assertEquals('romanb', $row[0]['user']->name); + self::assertEquals(1, $row['user']->id); + self::assertEquals('romanb', $row['user']->name); } elseif ($rowNum === 1) { - self::assertEquals(2, $row[0]['user']->id); - self::assertEquals('jwage', $row[0]['user']->name); + self::assertEquals(2, $row['user']->id); + self::assertEquals('jwage', $row['user']->name); } ++$rowNum; @@ -1355,8 +1360,8 @@ public function testResultIterationWithAliasedUserEntity(): void $rowNum = 0; $iterableResult = $hydrator->toIterable( - ArrayResultFactory::createFromArray($resultSet), - $rsm + $this->createResultMock($resultSet), + $rsm, ); foreach ($iterableResult as $row) { @@ -1385,9 +1390,8 @@ public function testResultIterationWithAliasedUserEntity(): void * * SELECT u, g, p * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-809 */ + #[Group('DDC-809')] public function testManyToManyHydration(): void { $rsm = new ResultSetMapping(); @@ -1488,7 +1492,7 @@ public function testManyToManyHydration(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1508,9 +1512,8 @@ public function testManyToManyHydration(): void * * SELECT u As user, g, p * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-809 */ + #[Group('DDC-809')] public function testManyToManyHydrationWithAliasedUserEntity(): void { $rsm = new ResultSetMapping(); @@ -1611,7 +1614,7 @@ public function testManyToManyHydrationWithAliasedUserEntity(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1632,10 +1635,9 @@ public function testManyToManyHydrationWithAliasedUserEntity(): void /** * SELECT u, UPPER(u.name) as nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-1358 - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1358')] public function testMissingIdForRootEntity($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1669,7 +1671,7 @@ public function testMissingIdForRootEntity($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1691,10 +1693,9 @@ public function testMissingIdForRootEntity($userEntityKey): void * SELECT u, p, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * LEFT JOIN u.phonenumbers u - * - * @group DDC-1358 - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1358')] public function testMissingIdForCollectionValuedChildEntity($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1703,7 +1704,7 @@ public function testMissingIdForCollectionValuedChildEntity($userEntityKey): voi CmsPhonenumber::class, 'p', 'u', - 'phonenumbers' + 'phonenumbers', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -1739,7 +1740,7 @@ public function testMissingIdForCollectionValuedChildEntity($userEntityKey): voi ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1753,10 +1754,9 @@ public function testMissingIdForCollectionValuedChildEntity($userEntityKey): voi * SELECT u, a, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * JOIN u.address a - * - * @group DDC-1358 - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1358')] public function testMissingIdForSingleValuedChildEntity($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1765,7 +1765,7 @@ public function testMissingIdForSingleValuedChildEntity($userEntityKey): void CmsAddress::class, 'a', 'u', - 'address' + 'address', ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -1793,7 +1793,7 @@ public function testMissingIdForSingleValuedChildEntity($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1807,10 +1807,9 @@ public function testMissingIdForSingleValuedChildEntity($userEntityKey): void * SELECT u, UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u * INDEX BY u.id - * - * @group DDC-1385 - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1385')] public function testIndexByAndMixedResult($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1835,7 +1834,7 @@ public function testIndexByAndMixedResult($userEntityKey): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1851,10 +1850,9 @@ public function testIndexByAndMixedResult($userEntityKey): void /** * SELECT UPPER(u.name) AS nameUpper * FROM Doctrine\Tests\Models\CMS\CmsUser u - * - * @group DDC-1385 - * @dataProvider provideDataForUserEntityResult */ + #[DataProvider('provideDataForUserEntityResult')] + #[Group('DDC-1385')] public function testIndexByScalarsOnly($userEntityKey): void { $rsm = new ResultSetMapping(); @@ -1869,7 +1867,7 @@ public function testIndexByScalarsOnly($userEntityKey): void ['sclr0' => 'JWAGE'], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -1878,14 +1876,14 @@ public function testIndexByScalarsOnly($userEntityKey): void 'ROMANB' => ['nameUpper' => 'ROMANB'], 'JWAGE' => ['nameUpper' => 'JWAGE'], ], - $result + $result, ); } - /** @group DDC-1470 */ + #[Group('DDC-1470')] public function testMissingMetaMappingException(): void { - $this->expectException('Doctrine\ORM\Internal\Hydration\HydrationException'); + $this->expectException(HydrationException::class); $this->expectExceptionMessage('The meta mapping for the discriminator column "c_discr" is missing for "Doctrine\Tests\Models\Company\CompanyFixContract" using the DQL alias "c".'); $rsm = new ResultSetMapping(); @@ -1901,15 +1899,15 @@ public function testMissingMetaMappingException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } - /** @group DDC-1470 */ + #[Group('DDC-1470')] public function testMissingDiscriminatorColumnException(): void { - $this->expectException('Doctrine\ORM\Internal\Hydration\HydrationException'); + $this->expectException(HydrationException::class); $this->expectExceptionMessage('The discriminator column "discr" is missing for "Doctrine\Tests\Models\Company\CompanyEmployee" using the DQL alias "e".'); $rsm = new ResultSetMapping(); @@ -1932,15 +1930,15 @@ public function testMissingDiscriminatorColumnException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } - /** @group DDC-3076 */ + #[Group('DDC-3076')] public function testInvalidDiscriminatorValueException(): void { - $this->expectException('Doctrine\ORM\Internal\Hydration\HydrationException'); + $this->expectException(HydrationException::class); $this->expectExceptionMessage('The discriminator value "subworker" is invalid. It must be one of "person", "manager", "employee".'); $rsm = new ResultSetMapping(); @@ -1958,7 +1956,7 @@ public function testInvalidDiscriminatorValueException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } @@ -1979,7 +1977,7 @@ public function testFetchJoinCollectionValuedAssociationWithDefaultArrayValue(): ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); diff --git a/tests/Tests/ORM/Hydration/ResultSetMappingTest.php b/tests/Tests/ORM/Hydration/ResultSetMappingTest.php index 1e9c86921f3..0c20eab0866 100644 --- a/tests/Tests/ORM/Hydration/ResultSetMappingTest.php +++ b/tests/Tests/ORM/Hydration/ResultSetMappingTest.php @@ -5,27 +5,21 @@ namespace Doctrine\Tests\ORM\Hydration; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\ORM\Query\ResultSetMappingBuilder; -use Doctrine\Persistence\Mapping\RuntimeReflectionService; -use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Legacy\LegacyUser; use Doctrine\Tests\Models\Legacy\LegacyUserReference; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Description of ResultSetMappingTest */ class ResultSetMappingTest extends OrmTestCase { - /** @var ResultSetMapping */ - private $_rsm; - - /** @var EntityManagerInterface */ - private $entityManager; + private ResultSetMapping $_rsm; + private EntityManagerInterface $entityManager; protected function setUp(): void { @@ -42,7 +36,7 @@ public function testBasicResultSetMapping(): void { $this->_rsm->addEntityResult( CmsUser::class, - 'u' + 'u', ); $this->_rsm->addFieldResult('u', 'id', 'id'); $this->_rsm->addFieldResult('u', 'status', 'status'); @@ -70,10 +64,9 @@ public function testBasicResultSetMapping(): void } /** - * @group DDC-1057 - * * Fluent interface test, not a real result set mapping */ + #[Group('DDC-1057')] public function testFluentInterface(): void { $rms = $this->_rsm; @@ -98,168 +91,7 @@ public function testFluentInterface(): void self::assertTrue($rms->isMixedResult()); } - /** @group DDC-1663 */ - public function testAddNamedNativeQueryResultSetMapping(): void - { - $cm = new ClassMetadata(CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->mapOneToOne( - [ - 'fieldName' => 'email', - 'targetEntity' => CmsEmail::class, - 'cascade' => ['persist'], - 'inversedBy' => 'user', - 'orphanRemoval' => false, - 'joinColumns' => [ - [ - 'nullable' => true, - 'referencedColumnName' => 'id', - ], - ], - ] - ); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT u.id AS user_id, e.id AS email_id, u.name, e.email, u.id + e.id AS scalarColumn FROM cms_users u INNER JOIN cms_emails e ON e.id = u.email_id', - 'resultSetMapping' => 'find-all', - ] - ); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => '__CLASS__', - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'user_id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - ], - ], - [ - 'entityClass' => 'CmsEmail', - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'email_id', - ], - [ - 'name' => 'email', - 'column' => 'email', - ], - ], - ], - ], - 'columns' => [ - ['name' => 'scalarColumn'], - ], - ] - ); - - $queryMapping = $cm->getNamedNativeQuery('find-all'); - - $rsm = new ResultSetMappingBuilder($this->entityManager); - $rsm->addNamedNativeQueryMapping($cm, $queryMapping); - - self::assertEquals('scalarColumn', $rsm->getScalarAlias('scalarColumn')); - - self::assertEquals('c0', $rsm->getEntityAlias('user_id')); - self::assertEquals('c0', $rsm->getEntityAlias('name')); - self::assertEquals(CmsUser::class, $rsm->getClassName('c0')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('name')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('user_id')); - - self::assertEquals('c1', $rsm->getEntityAlias('email_id')); - self::assertEquals('c1', $rsm->getEntityAlias('email')); - self::assertEquals(CmsEmail::class, $rsm->getClassName('c1')); - self::assertEquals(CmsEmail::class, $rsm->getDeclaringClass('email')); - self::assertEquals(CmsEmail::class, $rsm->getDeclaringClass('email_id')); - } - - /** @group DDC-1663 */ - public function testAddNamedNativeQueryResultSetMappingWithoutFields(): void - { - $cm = new ClassMetadata(CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT u.id AS user_id, e.id AS email_id, u.name, e.email, u.id + e.id AS scalarColumn FROM cms_users u INNER JOIN cms_emails e ON e.id = u.email_id', - 'resultSetMapping' => 'find-all', - ] - ); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - ['entityClass' => '__CLASS__'], - ], - 'columns' => [ - ['name' => 'scalarColumn'], - ], - ] - ); - - $queryMapping = $cm->getNamedNativeQuery('find-all'); - $rsm = new ResultSetMappingBuilder($this->entityManager); - - $rsm->addNamedNativeQueryMapping($cm, $queryMapping); - - self::assertEquals('scalarColumn', $rsm->getScalarAlias('scalarColumn')); - self::assertEquals('c0', $rsm->getEntityAlias('id')); - self::assertEquals('c0', $rsm->getEntityAlias('name')); - self::assertEquals('c0', $rsm->getEntityAlias('status')); - self::assertEquals('c0', $rsm->getEntityAlias('username')); - self::assertEquals(CmsUser::class, $rsm->getClassName('c0')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('id')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('name')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('status')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('username')); - } - - /** @group DDC-1663 */ - public function testAddNamedNativeQueryResultClass(): void - { - $cm = new ClassMetadata(CmsUser::class); - - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'resultClass' => '__CLASS__', - 'query' => 'SELECT * FROM cms_users', - ] - ); - - $queryMapping = $cm->getNamedNativeQuery('find-all'); - $rsm = new ResultSetMappingBuilder($this->entityManager); - - $rsm->addNamedNativeQueryMapping($cm, $queryMapping); - - self::assertEquals('c0', $rsm->getEntityAlias('id')); - self::assertEquals('c0', $rsm->getEntityAlias('name')); - self::assertEquals('c0', $rsm->getEntityAlias('status')); - self::assertEquals('c0', $rsm->getEntityAlias('username')); - self::assertEquals(CmsUser::class, $rsm->getClassName('c0')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('id')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('name')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('status')); - self::assertEquals(CmsUser::class, $rsm->getDeclaringClass('username')); - } - - /** @group DDC-117 */ + #[Group('DDC-117')] public function testIndexByMetadataColumn(): void { $this->_rsm->addEntityResult(LegacyUser::class, 'u'); diff --git a/tests/Tests/ORM/Hydration/ScalarColumnHydratorTest.php b/tests/Tests/ORM/Hydration/ScalarColumnHydratorTest.php index 0e82b58213f..b87bc6c1496 100644 --- a/tests/Tests/ORM/Hydration/ScalarColumnHydratorTest.php +++ b/tests/Tests/ORM/Hydration/ScalarColumnHydratorTest.php @@ -7,7 +7,6 @@ use Doctrine\ORM\Exception\MultipleSelectorsFoundException; use Doctrine\ORM\Internal\Hydration\ScalarColumnHydrator; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\Models\CMS\CmsUser; use function sprintf; @@ -24,7 +23,7 @@ public function testEmptyResultTest(): void $rsm->addEntityResult(CmsUser::class, 'u'); $rsm->addFieldResult('u', 'u__id', 'id'); - $stmt = ArrayResultFactory::createFromArray($emptyResultSet = []); + $stmt = $this->createResultMock([]); $hydrator = new ScalarColumnHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -47,7 +46,7 @@ public function testSingleColumnEntityQueryWithoutScalarMap(): void ['u__id' => '2'], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarColumnHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -75,7 +74,7 @@ public function testSingleColumnEntityQueryWithScalarMap(): void ['u__id' => '2'], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarColumnHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -108,13 +107,13 @@ public function testMultipleColumnEntityQueryThrowsException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarColumnHydrator($this->entityManager); $this->expectException(MultipleSelectorsFoundException::class); $this->expectExceptionMessage(sprintf( MultipleSelectorsFoundException::MULTIPLE_SELECTORS_FOUND_EXCEPTION, - 'id, name' + 'id, name', )); $hydrator->hydrateAll($stmt, $rsm); diff --git a/tests/Tests/ORM/Hydration/ScalarHydratorTest.php b/tests/Tests/ORM/Hydration/ScalarHydratorTest.php index 9d33b9c8d59..baa27f3bd08 100644 --- a/tests/Tests/ORM/Hydration/ScalarHydratorTest.php +++ b/tests/Tests/ORM/Hydration/ScalarHydratorTest.php @@ -6,8 +6,8 @@ use Doctrine\ORM\Internal\Hydration\ScalarHydrator; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\Models\CMS\CmsUser; +use PHPUnit\Framework\Attributes\Group; class ScalarHydratorTest extends HydrationTestCase { @@ -33,7 +33,7 @@ public function testNewHydrationSimpleEntityQuery(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -46,7 +46,7 @@ public function testNewHydrationSimpleEntityQuery(): void self::assertEquals(2, $result[1]['u_id']); } - /** @group DDC-407 */ + #[Group('DDC-407')] public function testHydrateScalarResults(): void { $rsm = new ResultSetMapping(); @@ -62,13 +62,13 @@ public function testHydrateScalarResults(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarHydrator($this->entityManager); self::assertCount(1, $hydrator->hydrateAll($stmt, $rsm)); } - /** @group DDC-644 */ + #[Group('DDC-644')] public function testSkipUnknownColumns(): void { $rsm = new ResultSetMapping(); @@ -90,7 +90,7 @@ public function testSkipUnknownColumns(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new ScalarHydrator($this->entityManager); self::assertCount(1, $hydrator->hydrateAll($stmt, $rsm)); diff --git a/tests/Tests/ORM/Hydration/SimpleObjectHydratorTest.php b/tests/Tests/ORM/Hydration/SimpleObjectHydratorTest.php index f4d3a9ac9fd..bf2f6586aa0 100644 --- a/tests/Tests/ORM/Hydration/SimpleObjectHydratorTest.php +++ b/tests/Tests/ORM/Hydration/SimpleObjectHydratorTest.php @@ -22,10 +22,11 @@ use Doctrine\Tests\Models\Issue5989\Issue5989Employee; use Doctrine\Tests\Models\Issue5989\Issue5989Manager; use Doctrine\Tests\Models\Issue5989\Issue5989Person; +use PHPUnit\Framework\Attributes\Group; class SimpleObjectHydratorTest extends HydrationTestCase { - /** @group DDC-1470 */ + #[Group('DDC-1470')] public function testMissingDiscriminatorColumnException(): void { $this->expectException(HydrationException::class); @@ -43,7 +44,7 @@ public function testMissingDiscriminatorColumnException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } @@ -66,16 +67,16 @@ public function testExtraFieldInResultSetShouldBeIgnore(): void $expectedEntity->id = 1; $expectedEntity->city = 'Cracow'; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); self::assertEquals($result[0], $expectedEntity); } - /** @group DDC-3076 */ + #[Group('DDC-3076')] public function testInvalidDiscriminatorValueException(): void { - $this->expectException('Doctrine\ORM\Internal\Hydration\HydrationException'); + $this->expectException(HydrationException::class); $this->expectExceptionMessage('The discriminator value "subworker" is invalid. It must be one of "person", "manager", "employee".'); $rsm = new ResultSetMapping(); @@ -94,12 +95,12 @@ public function testInvalidDiscriminatorValueException(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } - /** @group issue-5989 */ + #[Group('issue-5989')] public function testNullValueShouldNotOverwriteFieldWithSameNameInJoinedInheritance(): void { $rsm = new ResultSetMapping(); @@ -121,7 +122,7 @@ public function testNullValueShouldNotOverwriteFieldWithSameNameInJoinedInherita $expectedEntity->id = 1; $expectedEntity->tags = ['tag1', 'tag2']; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); self::assertEquals($result[0], $expectedEntity); @@ -153,15 +154,12 @@ public function testWrongValuesShouldNotBeConvertedToPhpValue(): void $expectedEntity->id = 1; $expectedEntity->type = 'type field'; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); self::assertEquals($result[0], $expectedEntity); } - /** - * @requires PHP 8.1 - */ public function testNotListedValueInEnumArray(): void { $this->expectException(MappingException::class); @@ -178,7 +176,7 @@ public function testNotListedValueInEnumArray(): void ], ]; - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = ArrayResultFactory::createWrapperResultFromArray($resultSet); $hydrator = new SimpleObjectHydrator($this->entityManager); $hydrator->hydrateAll($stmt, $rsm); } diff --git a/tests/Tests/ORM/Hydration/SingleScalarHydratorTest.php b/tests/Tests/ORM/Hydration/SingleScalarHydratorTest.php index 44f83ce8b5d..87d8edec9ac 100644 --- a/tests/Tests/ORM/Hydration/SingleScalarHydratorTest.php +++ b/tests/Tests/ORM/Hydration/SingleScalarHydratorTest.php @@ -7,9 +7,9 @@ use Doctrine\ORM\Internal\Hydration\SingleScalarHydrator; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\Models\CMS\CmsUser; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; class SingleScalarHydratorTest extends HydrationTestCase { @@ -48,39 +48,31 @@ public static function validResultSetProvider(): Generator ]; } - /** - * @param list> $resultSet - * @param mixed $expectedResult - * - * @dataProvider validResultSetProvider - */ - public function testHydrateSingleScalarFromFieldMappingWithValidResultSet(array $resultSet, $expectedResult): void + /** @param list> $resultSet */ + #[DataProvider('validResultSetProvider')] + public function testHydrateSingleScalarFromFieldMappingWithValidResultSet(array $resultSet, mixed $expectedResult): void { $rsm = new ResultSetMapping(); $rsm->addEntityResult(CmsUser::class, 'u'); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__name', 'name'); - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SingleScalarHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); $this->assertEquals($expectedResult, $result); } - /** - * @param list> $resultSet - * @param mixed $expectedResult - * - * @dataProvider validResultSetProvider - */ - public function testHydrateSingleScalarFromScalarMappingWithValidResultSet(array $resultSet, $expectedResult): void + /** @param list> $resultSet */ + #[DataProvider('validResultSetProvider')] + public function testHydrateSingleScalarFromScalarMappingWithValidResultSet(array $resultSet, mixed $expectedResult): void { $rsm = new ResultSetMapping(); $rsm->addScalarResult('u__id', 'id', 'string'); $rsm->addScalarResult('u__name', 'name', 'string'); - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SingleScalarHydrator($this->entityManager); $result = $hydrator->hydrateAll($stmt, $rsm); @@ -138,11 +130,8 @@ public static function invalidResultSetProvider(): Generator ]; } - /** - * @param list> $resultSet - * - * @dataProvider invalidResultSetProvider - */ + /** @param list> $resultSet */ + #[DataProvider('invalidResultSetProvider')] public function testHydrateSingleScalarFromFieldMappingWithInvalidResultSet(array $resultSet): void { $rsm = new ResultSetMapping(); @@ -150,25 +139,22 @@ public function testHydrateSingleScalarFromFieldMappingWithInvalidResultSet(arra $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__name', 'name'); - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SingleScalarHydrator($this->entityManager); $this->expectException(NonUniqueResultException::class); $hydrator->hydrateAll($stmt, $rsm); } - /** - * @param list> $resultSet - * - * @dataProvider invalidResultSetProvider - */ + /** @param list> $resultSet */ + #[DataProvider('invalidResultSetProvider')] public function testHydrateSingleScalarFromScalarMappingWithInvalidResultSet(array $resultSet): void { $rsm = new ResultSetMapping(); $rsm->addScalarResult('u__id', 'id', 'string'); $rsm->addScalarResult('u__name', 'name', 'string'); - $stmt = ArrayResultFactory::createFromArray($resultSet); + $stmt = $this->createResultMock($resultSet); $hydrator = new SingleScalarHydrator($this->entityManager); $this->expectException(NonUniqueResultException::class); diff --git a/tests/Tests/ORM/Id/AbstractIdGeneratorTest.php b/tests/Tests/ORM/Id/AbstractIdGeneratorTest.php deleted file mode 100644 index 79ccca18d7e..00000000000 --- a/tests/Tests/ORM/Id/AbstractIdGeneratorTest.php +++ /dev/null @@ -1,72 +0,0 @@ -receivedEm = $em; - $this->receivedEntity = $entity; - - return '4711'; - } - }; - - $em = $this->createMock(EntityManager::class); - $entity = new stdClass(); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9325'); - - self::assertSame('4711', $generator->generateId($em, $entity)); - self::assertSame($em, $generator->receivedEm); - self::assertSame($entity, $generator->receivedEntity); - } - - public function testNoEndlessRecursionOnGenerateId(): void - { - $generator = new class extends AbstractIdGenerator - { - }; - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Endless recursion detected in Doctrine\ORM\Id\AbstractIdGenerator@anonymous. Please implement generateId() without calling the parent implementation.'); - - $generator->generateId($this->createMock(EntityManager::class), (object) []); - } - - public function testNoEndlessRecursionOnGenerate(): void - { - $generator = new class extends AbstractIdGenerator - { - }; - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Endless recursion detected in Doctrine\ORM\Id\AbstractIdGenerator@anonymous. Please implement generateId() without calling the parent implementation.'); - - $generator->generate($this->createMock(EntityManager::class), (object) []); - } -} diff --git a/tests/Tests/ORM/Id/AssignedGeneratorTest.php b/tests/Tests/ORM/Id/AssignedGeneratorTest.php index d70a76fa8e8..09755b2595d 100644 --- a/tests/Tests/ORM/Id/AssignedGeneratorTest.php +++ b/tests/Tests/ORM/Id/AssignedGeneratorTest.php @@ -5,23 +5,22 @@ namespace Doctrine\Tests\ORM\Id; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Id\AssignedGenerator; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\ORMException; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; /** * AssignedGeneratorTest */ class AssignedGeneratorTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; - /** @var AssignedGenerator */ - private $assignedGen; + private AssignedGenerator $assignedGen; protected function setUp(): void { @@ -29,7 +28,7 @@ protected function setUp(): void $this->assignedGen = new AssignedGenerator(); } - /** @dataProvider entitiesWithoutId */ + #[DataProvider('entitiesWithoutId')] public function testThrowsExceptionIfIdNotAssigned($entity): void { $this->expectException(ORMException::class); @@ -60,31 +59,25 @@ public function testCorrectIdGeneration(): void } } -/** @Entity */ +#[Entity] class AssignedSingleIdEntity { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $myId; } -/** @Entity */ +#[Entity] class AssignedCompositeIdEntity { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $myId1; - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $myId2; } diff --git a/tests/Tests/ORM/Id/SequenceGeneratorTest.php b/tests/Tests/ORM/Id/SequenceGeneratorTest.php index 2582e45bf62..6565423ba74 100644 --- a/tests/Tests/ORM/Id/SequenceGeneratorTest.php +++ b/tests/Tests/ORM/Id/SequenceGeneratorTest.php @@ -9,12 +9,9 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\ORM\Id\SequenceGenerator; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; class SequenceGeneratorTest extends OrmTestCase { - use MockBuilderCompatibilityTools; - public function testGeneration(): void { $sequenceGenerator = new SequenceGenerator('seq', 10); @@ -23,7 +20,8 @@ public function testGeneration(): void $platform->method('getSequenceNextValSQL') ->willReturn(''); - $connection = $this->getMockBuilderWithOnlyMethods(Connection::class, ['fetchOne', 'getDatabasePlatform']) + $connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['fetchOne', 'getDatabasePlatform']) ->setConstructorArgs([[], $this->createMock(Driver::class)]) ->getMock(); $connection->method('getDatabasePlatform') @@ -38,7 +36,7 @@ public function testGeneration(): void return $i; }); - $entityManager = $this->getTestEntityManager($connection); + $entityManager = $this->createTestEntityManagerWithConnection($connection); for ($i = 0; $i < 42; ++$i) { $id = $sequenceGenerator->generateId($entityManager, null); diff --git a/tests/Tests/ORM/Internal/HydrationCompleteHandlerTest.php b/tests/Tests/ORM/Internal/HydrationCompleteHandlerTest.php index 13423be458e..2bdeab19498 100644 --- a/tests/Tests/ORM/Internal/HydrationCompleteHandlerTest.php +++ b/tests/Tests/ORM/Internal/HydrationCompleteHandlerTest.php @@ -10,6 +10,8 @@ use Doctrine\ORM\Internal\HydrationCompleteHandler; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Persistence\Event\LifecycleEventArgs; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use stdClass; @@ -19,19 +21,13 @@ /** * Tests for {@see \Doctrine\ORM\Internal\HydrationCompleteHandler} - * - * @covers \Doctrine\ORM\Internal\HydrationCompleteHandler */ +#[CoversClass(HydrationCompleteHandler::class)] class HydrationCompleteHandlerTest extends TestCase { - /** @var ListenersInvoker&MockObject */ - private $listenersInvoker; - - /** @var EntityManagerInterface&MockObject */ - private $entityManager; - - /** @var HydrationCompleteHandler */ - private $handler; + private ListenersInvoker&MockObject $listenersInvoker; + private EntityManagerInterface&MockObject $entityManager; + private HydrationCompleteHandler $handler; protected function setUp(): void { @@ -40,7 +36,7 @@ protected function setUp(): void $this->handler = new HydrationCompleteHandler($this->listenersInvoker, $this->entityManager); } - /** @dataProvider invocationFlagProvider */ + #[DataProvider('invocationFlagProvider')] public function testDefersPostLoadOfEntity(int $listenersFlag): void { $metadata = $this->createMock(ClassMetadata::class); @@ -65,16 +61,14 @@ public function testDefersPostLoadOfEntity(int $listenersFlag): void $metadata, Events::postLoad, $entity, - self::callback(static function (LifecycleEventArgs $args) use ($entityManager, $entity) { - return $entity === $args->getObject() && $entityManager === $args->getObjectManager(); - }), - $listenersFlag + self::callback(static fn (LifecycleEventArgs $args) => $entity === $args->getObject() && $entityManager === $args->getObjectManager()), + $listenersFlag, ); $this->handler->hydrationComplete(); } - /** @dataProvider invocationFlagProvider */ + #[DataProvider('invocationFlagProvider')] public function testDefersPostLoadOfEntityOnlyOnce(int $listenersFlag): void { $metadata = $this->createMock(ClassMetadata::class); @@ -96,7 +90,7 @@ public function testDefersPostLoadOfEntityOnlyOnce(int $listenersFlag): void $this->handler->hydrationComplete(); } - /** @dataProvider invocationFlagProvider */ + #[DataProvider('invocationFlagProvider')] public function testDefersMultiplePostLoadOfEntity(int $listenersFlag): void { $metadata1 = $this->createMock(ClassMetadata::class); @@ -123,11 +117,9 @@ public function testDefersMultiplePostLoadOfEntity(int $listenersFlag): void self::logicalOr($metadata1, $metadata2), Events::postLoad, self::logicalOr($entity1, $entity2), - self::callback(static function (LifecycleEventArgs $args) use ($entityManager, $entity1, $entity2) { - return in_array($args->getObject(), [$entity1, $entity2], true) - && $entityManager === $args->getObjectManager(); - }), - $listenersFlag + self::callback(static fn (LifecycleEventArgs $args) => in_array($args->getObject(), [$entity1, $entity2], true) + && $entityManager === $args->getObjectManager()), + $listenersFlag, ); $this->handler->hydrationComplete(); diff --git a/tests/Tests/ORM/Internal/StronglyConnectedComponentsTest.php b/tests/Tests/ORM/Internal/StronglyConnectedComponentsTest.php index a2adee9dc22..f4bc91ebcc9 100644 --- a/tests/Tests/ORM/Internal/StronglyConnectedComponentsTest.php +++ b/tests/Tests/ORM/Internal/StronglyConnectedComponentsTest.php @@ -99,7 +99,7 @@ private function assertNodesAreInSameComponent(string $first, string $second): v { self::assertSame( $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$first]), - $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]) + $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]), ); } @@ -107,7 +107,7 @@ private function assertNodesAreNotInSameComponent(string $first, string $second) { self::assertNotSame( $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$first]), - $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]) + $this->stronglyConnectedComponents->getNodeRepresentingStronglyConnectedComponent($this->nodes[$second]), ); } } diff --git a/tests/Tests/ORM/Internal/TopologicalSortTest.php b/tests/Tests/ORM/Internal/TopologicalSortTest.php index e1a93b684ef..05a1be05534 100644 --- a/tests/Tests/ORM/Internal/TopologicalSortTest.php +++ b/tests/Tests/ORM/Internal/TopologicalSortTest.php @@ -258,7 +258,7 @@ public function testDetectLargerCycleNotIncludingStartNode(): void } catch (CycleDetectedException $exception) { self::assertEquals( [$this->nodes['B'], $this->nodes['C'], $this->nodes['D'], $this->nodes['B']], - $exception->getCycle() + $exception->getCycle(), ); } } @@ -277,9 +277,7 @@ private function addEdge(string $from, string $to, bool $optional = false): void $this->topologicalSort->addEdge($this->nodes[$from], $this->nodes[$to], $optional); } - /** - * @return list - */ + /** @return list */ private function computeResult(): array { return array_map(static function (Node $n): string { diff --git a/tests/Tests/ORM/LazyCriteriaCollectionTest.php b/tests/Tests/ORM/LazyCriteriaCollectionTest.php index 6b8776c130d..82733dd643c 100644 --- a/tests/Tests/ORM/LazyCriteriaCollectionTest.php +++ b/tests/Tests/ORM/LazyCriteriaCollectionTest.php @@ -8,21 +8,17 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\LazyCriteriaCollection; use Doctrine\ORM\Persisters\Entity\EntityPersister; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use stdClass; -/** @covers \Doctrine\ORM\LazyCriteriaCollection */ +#[CoversClass(LazyCriteriaCollection::class)] class LazyCriteriaCollectionTest extends TestCase { - /** @var EntityPersister&MockObject */ - private $persister; - - /** @var Criteria */ - private $criteria; - - /** @var LazyCriteriaCollection */ - private $lazyCriteriaCollection; + private EntityPersister&MockObject $persister; + private Criteria $criteria; + private LazyCriteriaCollection $lazyCriteriaCollection; protected function setUp(): void { diff --git a/tests/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Tests/ORM/Mapping/AnnotationDriverTest.php deleted file mode 100644 index ae8702e9156..00000000000 --- a/tests/Tests/ORM/Mapping/AnnotationDriverTest.php +++ /dev/null @@ -1,478 +0,0 @@ -initializeReflection(new RuntimeReflectionService()); - $reader = new AnnotationReader(); - $annotationDriver = new AnnotationDriver($reader); - - $this->expectException(MappingException::class); - $annotationDriver->loadMetadataForClass('stdClass', $cm); - } - - public function testFailingSecondLevelCacheAssociation(): void - { - $this->expectException(CacheException::class); - $this->expectExceptionMessage('Entity association field "Doctrine\Tests\ORM\Mapping\AnnotationSLC#foo" not configured as part of the second-level cache.'); - $mappingDriver = $this->loadDriver(); - - $class = new ClassMetadata(AnnotationSLC::class); - $mappingDriver->loadMetadataForClass(AnnotationSLC::class, $class); - } - - /** @group DDC-268 */ - public function testColumnWithMissingTypeDefaultsToString(): void - { - $cm = new ClassMetadata(ColumnWithoutType::class); - $cm->initializeReflection(new RuntimeReflectionService()); - $annotationDriver = $this->loadDriver(); - - $annotationDriver->loadMetadataForClass(Mapping\InvalidColumn::class, $cm); - self::assertEquals('string', $cm->fieldMappings['id']['type']); - } - - /** @group DDC-318 */ - public function testGetAllClassNamesIsIdempotent(): void - { - $annotationDriver = $this->loadDriverForCMSModels(); - $original = $annotationDriver->getAllClassNames(); - - $annotationDriver = $this->loadDriverForCMSModels(); - $afterTestReset = $annotationDriver->getAllClassNames(); - - self::assertEquals($original, $afterTestReset); - } - - /** @group DDC-318 */ - public function testGetAllClassNamesIsIdempotentEvenWithDifferentDriverInstances(): void - { - $annotationDriver = $this->loadDriverForCMSModels(); - $original = $annotationDriver->getAllClassNames(); - - $annotationDriver = $this->loadDriverForCMSModels(); - $afterTestReset = $annotationDriver->getAllClassNames(); - - self::assertEquals($original, $afterTestReset); - } - - /** @group DDC-318 */ - public function testGetAllClassNamesReturnsAlreadyLoadedClassesIfAppropriate(): void - { - $this->ensureIsLoaded(CmsUser::class); - - $annotationDriver = $this->loadDriverForCMSModels(); - $classes = $annotationDriver->getAllClassNames(); - - self::assertContains(CmsUser::class, $classes); - } - - /** @group DDC-318 */ - public function testGetClassNamesReturnsOnlyTheAppropriateClasses(): void - { - $this->ensureIsLoaded(ECommerceCart::class); - - $annotationDriver = $this->loadDriverForCMSModels(); - $classes = $annotationDriver->getAllClassNames(); - - self::assertNotContains(ECommerceCart::class, $classes); - } - - protected function loadDriverForCMSModels(): AnnotationDriver - { - $annotationDriver = $this->loadDriver(); - $annotationDriver->addPaths([__DIR__ . '/../../Models/CMS/']); - - return $annotationDriver; - } - - /** @return AnnotationDriver */ - protected function loadDriver(): MappingDriver - { - return $this->createAnnotationDriver(); - } - - /** @var class-string $entityClassName */ - protected function ensureIsLoaded(string $entityClassName): void - { - new $entityClassName(); - } - - /** - * @group DDC-671 - * - * Entities for this test are in AbstractMappingDriverTest - */ - public function testJoinTablesWithMappedSuperclassForAnnotationDriver(): void - { - $annotationDriver = $this->loadDriver(); - $annotationDriver->addPaths([__DIR__ . '/../../Models/DirectoryTree/']); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $classPage = $factory->getMetadataFor(File::class); - self::assertEquals(File::class, $classPage->associationMappings['parentDirectory']['sourceEntity']); - - $classDirectory = $factory->getMetadataFor(Directory::class); - self::assertEquals(Directory::class, $classDirectory->associationMappings['parentDirectory']['sourceEntity']); - } - - /** @group DDC-1050 */ - public function testInvalidMappedSuperClassWithInheritanceInformation(): void - { - $annotationDriver = $this->loadDriver(); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $this->expectException(MappingException::class); - $this->expectExceptionMessage( - 'It is not supported to define inheritance information on a mapped ' . - "superclass '" . MappedSuperClassInheritence::class . "'." - ); - - $usingInvalidMsc = $factory->getMetadataFor(MappedSuperClassInheritence::class); - } - - /** @group DDC-1034 */ - public function testInheritanceSkipsParentLifecycleCallbacks(): void - { - $annotationDriver = $this->loadDriver(); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $cm = $factory->getMetadataFor(AnnotationChild::class); - self::assertEquals(['postLoad' => ['postLoad'], 'preUpdate' => ['preUpdate']], $cm->lifecycleCallbacks); - - $cm = $factory->getMetadataFor(AnnotationParent::class); - self::assertEquals(['postLoad' => ['postLoad'], 'preUpdate' => ['preUpdate']], $cm->lifecycleCallbacks); - } - - /** @group DDC-1156 */ - public function testMappedSuperclassInMiddleOfInheritanceHierarchy(): void - { - $annotationDriver = $this->loadDriver(); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - self::assertInstanceOf(ClassMetadata::class, $factory->getMetadataFor(ChildEntity::class)); - } - - public function testInvalidFetchOptionThrowsException(): void - { - $annotationDriver = $this->loadDriver(); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $this->expectException(MappingException::class); - $this->expectExceptionMessage("Entity 'Doctrine\Tests\ORM\Mapping\InvalidFetchOption' has a mapping with invalid fetch mode 'eager'"); - - $factory->getMetadataFor(InvalidFetchOption::class); - } - - public function testAttributeOverridesMappingWithTrait(): void - { - $factory = $this->createClassMetadataFactory(); - - $metadataWithoutOverride = $factory->getMetadataFor(DDC1872ExampleEntityWithoutOverride::class); - $metadataWithOverride = $factory->getMetadataFor(DDC1872ExampleEntityWithOverride::class); - - self::assertEquals('trait_foo', $metadataWithoutOverride->fieldMappings['foo']['columnName']); - self::assertEquals('foo_overridden', $metadataWithOverride->fieldMappings['foo']['columnName']); - self::assertArrayHasKey('example_trait_bar_id', $metadataWithoutOverride->associationMappings['bar']['joinColumnFieldNames']); - self::assertArrayHasKey('example_entity_overridden_bar_id', $metadataWithOverride->associationMappings['bar']['joinColumnFieldNames']); - } - - /** - * @param class-string $class - * - * @dataProvider provideDiscriminatorColumnTestcases - */ - public function testLengthForDiscriminatorColumn(string $class, int $expectedLength): void - { - $factory = $this->createClassMetadataFactory(); - - $metadata = $factory->getMetadataFor($class); - - self::assertNotNull($metadata->discriminatorColumn); - self::assertArrayHasKey('length', $metadata->discriminatorColumn); - self::assertSame($expectedLength, $metadata->discriminatorColumn['length']); - } - - public static function provideDiscriminatorColumnTestcases(): Generator - { - yield [DiscriminatorColumnWithNullLength::class, 255]; - yield [DiscriminatorColumnWithNoLength::class, 255]; - yield [DiscriminatorColumnWithZeroLength::class, 0]; - yield [DiscriminatorColumnWithNonZeroLength::class, 60]; - } - - public function testLegacyInheritance(): void - { - if (! class_exists(PersistenceAnnotationDriver::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2.'); - } - - self::assertTrue(is_subclass_of(AnnotationDriver::class, PersistenceAnnotationDriver::class)); - } -} - -/** @Entity */ -class ColumnWithoutType -{ - /** - * @var int - * @Id - * @Column - */ - public $id; -} - -/** - * @MappedSuperclass - * @InheritanceType("JOINED") - * @DiscriminatorMap({"test" = "ColumnWithoutType"}) - */ -class MappedSuperClassInheritence -{ -} - -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorMap({"parent" = "AnnotationParent", "child" = "AnnotationChild"}) - * @HasLifecycleCallbacks - */ -class AnnotationParent -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** @PostLoad */ - public function postLoad(): void - { - } - - /** @PreUpdate */ - public function preUpdate(): void - { - } -} - -/** - * @Entity - * @HasLifecycleCallbacks - */ -class AnnotationChild extends AnnotationParent -{ -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"}) - */ -class SuperEntity -{ - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - private $id; -} - -/** @MappedSuperclass */ -class MiddleMappedSuperclass extends SuperEntity -{ - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; -} - -/** @Entity */ -class ChildEntity extends MiddleMappedSuperclass -{ - /** - * @var string - * @Column(type="string", length=255) - */ - private $text; -} - -/** @Entity */ -class InvalidFetchOption -{ - /** - * @var CmsUser - * @OneToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsUser", fetch="eager") - */ - private $collection; -} - -/** - * @Entity - * @Cache - */ -class AnnotationSLC -{ - /** - * @var AnnotationSLCFoo - * @Id - * @ManyToOne(targetEntity="AnnotationSLCFoo") - */ - public $foo; -} -/** @Entity */ -class AnnotationSLCFoo -{ - /** - * @var string - * @Column(type="string", length=255) - */ - public $id; -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn( - * name="type", - * type="string", - * length=0, - * columnDefinition="enum('region','airport','station','poi') NOT NULL", - * ), - * @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"}) - */ -class DiscriminatorColumnWithZeroLength -{ - /** - * @var int - * @Id - * @Column - */ - public $id; -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn( - * name="type", - * type="string", - * ), - * @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"}) - */ -class DiscriminatorColumnWithNoLength -{ - /** - * @var int - * @Id - * @Column - */ - public $id; -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn( - * name="type", - * type="string", - * length=60, - * ), - * @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"}) - */ -class DiscriminatorColumnWithNonZeroLength -{ - /** - * @var int - * @Id - * @Column - */ - public $id; -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn( - * name="type", - * type="string", - * length=null, - * ), - * @DiscriminatorMap({"s"="SuperEntity", "c"="ChildEntity"}) - */ -class DiscriminatorColumnWithNullLength -{ - /** - * @var int - * @Id - * @Column - */ - public $id; -} diff --git a/tests/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php b/tests/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php index fd73817934d..522b0fcf634 100644 --- a/tests/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php +++ b/tests/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php @@ -13,18 +13,15 @@ use Doctrine\Tests\Models\DDC117\DDC117Article; use Doctrine\Tests\Models\DDC117\DDC117ArticleDetails; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** - * @group DDC-1845 - * @group DDC-2459 - */ +#[Group('DDC-1845')] +#[Group('DDC-2459')] class AnsiQuoteStrategyTest extends OrmTestCase { - /** @var AnsiQuoteStrategy */ - private $strategy; + private AnsiQuoteStrategy $strategy; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; protected function setUp(): void { @@ -71,7 +68,7 @@ public function testJoinTableName(): void 'targetEntity' => 'CmsUser', 'inversedBy' => 'users', 'joinTable' => ['name' => 'cmsaddress_cmsuser'], - ] + ], ); self::assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName($class->associationMappings['user'], $class, $this->platform)); @@ -86,7 +83,7 @@ public function testIdentifierColumnNames(): void 'id' => true, 'fieldName' => 'id', 'columnName' => 'id', - ] + ], ); self::assertEquals(['id'], $this->strategy->getIdentifierColumnNames($class, $this->platform)); @@ -107,12 +104,12 @@ public function testJoinColumnName(): void 'fieldName' => 'article', 'targetEntity' => DDC117Article::class, 'joinColumns' => [ - ['name' => 'article'], + ['name' => 'article', 'referencedColumnName' => 'id'], ], - ] + ], ); - $joinColumn = $class->associationMappings['article']['joinColumns'][0]; + $joinColumn = $class->associationMappings['article']->joinColumns[0]; self::assertEquals('article', $this->strategy->getJoinColumnName($joinColumn, $class, $this->platform)); } @@ -126,12 +123,12 @@ public function testReferencedJoinColumnName(): void 'fieldName' => 'article', 'targetEntity' => DDC117Article::class, 'joinColumns' => [ - ['name' => 'article'], + ['name' => 'article', 'referencedColumnName' => 'id'], ], - ] + ], ); - $joinColumn = $cm->associationMappings['article']['joinColumns'][0]; + $joinColumn = $cm->associationMappings['article']->joinColumns[0]; self::assertEquals('id', $this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform)); } diff --git a/tests/Tests/ORM/Mapping/AssociationMappingTest.php b/tests/Tests/ORM/Mapping/AssociationMappingTest.php new file mode 100644 index 00000000000..22c329392bf --- /dev/null +++ b/tests/Tests/ORM/Mapping/AssociationMappingTest.php @@ -0,0 +1,96 @@ +cascade = ['persist']; + $mapping->fetch = ClassMetadata::FETCH_EAGER; + $mapping->inherited = self::class; + $mapping->declared = self::class; + $mapping->cache = ['usage' => ClassMetadata::CACHE_USAGE_READ_ONLY]; + $mapping->id = true; + $mapping->isOnDeleteCascade = true; + $mapping->originalClass = self::class; + $mapping->originalField = 'foo'; + $mapping->orphanRemoval = true; + $mapping->unique = true; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof AssociationMapping); + + self::assertSame(['persist'], $resurrectedMapping->cascade); + self::assertSame(ClassMetadata::FETCH_EAGER, $resurrectedMapping->fetch); + self::assertSame(self::class, $resurrectedMapping->inherited); + self::assertSame(self::class, $resurrectedMapping->declared); + self::assertSame(['usage' => ClassMetadata::CACHE_USAGE_READ_ONLY], $resurrectedMapping->cache); + self::assertTrue($resurrectedMapping->id); + self::assertTrue($resurrectedMapping->isOnDeleteCascade); + self::assertSame(self::class, $resurrectedMapping->originalClass); + self::assertSame('foo', $resurrectedMapping->originalField); + self::assertTrue($resurrectedMapping->orphanRemoval); + self::assertTrue($resurrectedMapping->unique); + } + + public function testItThrowsWhenAccessingUnknownProperty(): void + { + $mapping = new MyAssociationMapping( + fieldName: 'foo', + sourceEntity: self::class, + targetEntity: self::class, + ); + + $this->expectException(OutOfRangeException::class); + + $mapping['foo']; + } + + public function testItThrowsWhenSettingUnknownProperty(): void + { + $mapping = new MyAssociationMapping( + fieldName: 'foo', + sourceEntity: self::class, + targetEntity: self::class, + ); + + $this->expectException(OutOfRangeException::class); + + $mapping['foo'] = 'bar'; + } + + public function testItThrowsWhenUnsettingUnknownProperty(): void + { + $mapping = new MyAssociationMapping( + fieldName: 'foo', + sourceEntity: self::class, + targetEntity: self::class, + ); + + $this->expectException(OutOfRangeException::class); + + unset($mapping['foo']); + } +} + +class MyAssociationMapping extends AssociationMapping +{ +} diff --git a/tests/Tests/ORM/Mapping/AttributeDriverTest.php b/tests/Tests/ORM/Mapping/AttributeDriverTest.php index 43a9942fb3a..b16ec788d33 100644 --- a/tests/Tests/ORM/Mapping/AttributeDriverTest.php +++ b/tests/Tests/ORM/Mapping/AttributeDriverTest.php @@ -8,27 +8,15 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Driver\AttributeDriver; +use Doctrine\ORM\Mapping\JoinColumnMapping; use Doctrine\ORM\Mapping\MappingAttribute; -use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as PersistenceAnnotationDriver; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Tests\ORM\Mapping\Fixtures\AttributeEntityWithNestedJoinColumns; +use InvalidArgumentException; use stdClass; -use function class_exists; -use function is_subclass_of; - -use const PHP_VERSION_ID; - class AttributeDriverTest extends MappingDriverTestCase { - /** @before */ - public function requiresPhp8Assertion(): void - { - if (PHP_VERSION_ID < 80000) { - self::markTestSkipped('requires PHP 8.0'); - } - } - protected function loadDriver(): MappingDriver { $paths = []; @@ -36,57 +24,6 @@ protected function loadDriver(): MappingDriver return new AttributeDriver($paths, true); } - public function testNamedQuery(): void - { - self::markTestSkipped('AttributeDriver does not support named queries.'); - } - - public function testNamedNativeQuery(): void - { - self::markTestSkipped('AttributeDriver does not support named native queries.'); - } - - public function testSqlResultSetMapping(): void - { - self::markTestSkipped('AttributeDriver does not support named sql resultset mapping.'); - } - - public function testAssociationOverridesMapping(): void - { - if (PHP_VERSION_ID < 80100) { - self::markTestSkipped('AttributeDriver does not support association overrides.'); - } else { - parent::testAssociationOverridesMapping(); - } - } - - public function testInversedByOverrideMapping(): void - { - if (PHP_VERSION_ID < 80100) { - self::markTestSkipped('AttributeDriver does not support association overrides.'); - } else { - parent::testInversedByOverrideMapping(); - } - } - - public function testFetchOverrideMapping(): void - { - if (PHP_VERSION_ID < 80100) { - self::markTestSkipped('AttributeDriver does not support association overrides.'); - } else { - parent::testFetchOverrideMapping(); - } - } - - public function testAttributeOverridesMapping(): void - { - if (PHP_VERSION_ID < 80100) { - self::markTestSkipped('AttributeDriver does not support association overrides.'); - } else { - parent::testAttributeOverridesMapping(); - } - } - public function testOriginallyNestedAttributesDeclaredWithoutOriginalParent(): void { $factory = $this->createClassMetadataFactory(); @@ -99,9 +36,9 @@ public function testOriginallyNestedAttributesDeclaredWithoutOriginalParent(): v 'uniqueConstraints' => ['foo' => ['columns' => ['id']]], 'indexes' => ['bar' => ['columns' => ['id']]], ], - $metadata->table + $metadata->table, ); - self::assertEquals(['assoz_id', 'assoz_id'], $metadata->associationMappings['assoc']['joinTableColumns']); + self::assertEquals(['assoz_id', 'assoz_id'], $metadata->associationMappings['assoc']->joinTableColumns); } public function testIsTransient(): void @@ -117,18 +54,6 @@ public function testIsTransient(): void self::assertFalse($driver->isTransient(AttributeEntityStartingWithRepeatableAttributes::class)); } - public function testLegacyInheritance(): void - { - if (! class_exists(PersistenceAnnotationDriver::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2.'); - } - - self::assertTrue(is_subclass_of(AttributeDriver::class, PersistenceAnnotationDriver::class)); - } - - /** - * @requires PHP 8.1 - */ public function testManyToManyAssociationWithNestedJoinColumns(): void { $factory = $this->createClassMetadataFactory(); @@ -137,32 +62,39 @@ public function testManyToManyAssociationWithNestedJoinColumns(): void self::assertEquals( [ - [ + JoinColumnMapping::fromMappingArray([ 'name' => 'assoz_id', 'referencedColumnName' => 'assoz_id', 'unique' => false, 'nullable' => true, 'onDelete' => null, 'columnDefinition' => null, - ], + ]), ], - $metadata->associationMappings['assoc']['joinTable']['joinColumns'] + $metadata->associationMappings['assoc']->joinTable->joinColumns, ); self::assertEquals( [ - [ + JoinColumnMapping::fromMappingArray([ 'name' => 'inverse_assoz_id', 'referencedColumnName' => 'inverse_assoz_id', 'unique' => false, 'nullable' => true, 'onDelete' => null, 'columnDefinition' => null, - ], + ]), ], - $metadata->associationMappings['assoc']['joinTable']['inverseJoinColumns'] + $metadata->associationMappings['assoc']->joinTable->inverseJoinColumns, ); } + + public function testItThrowsWhenSettingReportFieldsWhereDeclaredToFalse(): void + { + $this->expectException(InvalidArgumentException::class); + + new AttributeDriver([], false); + } } #[ORM\Entity] @@ -191,11 +123,11 @@ class AttributeEntityStartingWithRepeatableAttributes } #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)] -class AttributeTransientAnnotation implements MappingAttribute +class AttributeTransientAttribute implements MappingAttribute { } -#[AttributeTransientAnnotation] +#[AttributeTransientAttribute] class AttributeTransientClass { } diff --git a/tests/Tests/ORM/Mapping/AttributeReaderTest.php b/tests/Tests/ORM/Mapping/AttributeReaderTest.php index 0faaa943e78..eec12397586 100644 --- a/tests/Tests/ORM/Mapping/AttributeReaderTest.php +++ b/tests/Tests/ORM/Mapping/AttributeReaderTest.php @@ -22,9 +22,6 @@ use ReflectionClass; use ReflectionProperty; -/** - * @requires PHP 8.0 - */ class AttributeReaderTest extends TestCase { public function testItThrowsWhenGettingRepeatableAttributeWithTheWrongMethod(): void @@ -33,7 +30,7 @@ public function testItThrowsWhenGettingRepeatableAttributeWithTheWrongMethod(): $property = new ReflectionProperty(TestEntity::class, 'id'); $this->expectException(LogicException::class); $this->expectExceptionMessage( - 'The attribute "Doctrine\ORM\Mapping\Index" is repeatable. Call getPropertyAttributeCollection() instead.' + 'The attribute "Doctrine\ORM\Mapping\Index" is repeatable. Call getPropertyAttributeCollection() instead.', ); $reader->getPropertyAttribute($property, ORM\Index::class); } @@ -44,7 +41,7 @@ public function testItThrowsWhenGettingNonRepeatableAttributeWithTheWrongMethod( $property = new ReflectionProperty(TestEntity::class, 'id'); $this->expectException(LogicException::class); $this->expectExceptionMessage( - 'The attribute "Doctrine\ORM\Mapping\Id" is not repeatable. Call getPropertyAttribute() instead.' + 'The attribute "Doctrine\ORM\Mapping\Id" is not repeatable. Call getPropertyAttribute() instead.', ); $reader->getPropertyAttributeCollection($property, ORM\Id::class); } diff --git a/tests/Tests/ORM/Mapping/BasicInheritanceMappingTest.php b/tests/Tests/ORM/Mapping/BasicInheritanceMappingTest.php index a5e65643b34..28642118e01 100644 --- a/tests/Tests/ORM/Mapping/BasicInheritanceMappingTest.php +++ b/tests/Tests/ORM/Mapping/BasicInheritanceMappingTest.php @@ -4,7 +4,6 @@ namespace Doctrine\Tests\ORM\Mapping; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Id\SequenceGenerator as IdSequenceGenerator; use Doctrine\ORM\Mapping\ClassMetadata; @@ -32,17 +31,17 @@ use Doctrine\Tests\Models\DDC869\DDC869PaymentRepository; use Doctrine\Tests\OrmTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function assert; use function serialize; +use function sprintf; use function unserialize; class BasicInheritanceMappingTest extends OrmTestCase { - use VerifyDeprecations; - - /** @var ClassMetadataFactory */ - private $cmf; + private ClassMetadataFactory $cmf; protected function setUp(): void { @@ -80,14 +79,14 @@ public function testGetMetadataForSubclassWithMappedSuperclass(): void self::assertArrayHasKey('id', $class->fieldMappings); self::assertArrayHasKey('name', $class->fieldMappings); - self::assertArrayNotHasKey('inherited', $class->fieldMappings['mapped1']); - self::assertArrayNotHasKey('inherited', $class->fieldMappings['mapped2']); + self::assertNull($class->fieldMappings['mapped1']->inherited); + self::assertNull($class->fieldMappings['mapped2']->inherited); self::assertArrayNotHasKey('transient', $class->fieldMappings); self::assertArrayHasKey('mappedRelated1', $class->associationMappings); } - /** @group DDC-869 */ + #[Group('DDC-869')] public function testGetMetadataForSubclassWithMappedSuperclassWithRepository(): void { $class = $this->cmf->getMetadataFor(DDC869CreditCardPayment::class); @@ -112,7 +111,7 @@ public function testGetMetadataForSubclassWithMappedSuperclassWithRepository(): self::assertEquals($class->customRepositoryClassName, EntityRepository::class); } - /** @group DDC-388 */ + #[Group('DDC-388')] public function testSerializationWithPrivateFieldsFromMappedSuperclass(): void { $class = $this->cmf->getMetadataFor(EntitySubClass2::class); @@ -125,7 +124,7 @@ public function testSerializationWithPrivateFieldsFromMappedSuperclass(): void self::assertArrayHasKey('mappedRelated1', $class2->reflFields); } - /** @group DDC-1203 */ + #[Group('DDC-1203')] public function testUnmappedSuperclassInHierarchy(): void { $class = $this->cmf->getMetadataFor(HierarchyD::class); @@ -135,7 +134,7 @@ public function testUnmappedSuperclassInHierarchy(): void self::assertArrayHasKey('d', $class->fieldMappings); } - /** @group DDC-1204 */ + #[Group('DDC-1204')] public function testUnmappedEntityInHierarchy(): void { $this->expectException(MappingException::class); @@ -143,16 +142,14 @@ public function testUnmappedEntityInHierarchy(): void 'Entity \'Doctrine\Tests\ORM\Mapping\HierarchyBEntity\' has to be part of the discriminator map' . ' of \'Doctrine\Tests\ORM\Mapping\HierarchyBase\' to be properly mapped in the inheritance hierarchy.' . ' Alternatively you can make \'Doctrine\Tests\ORM\Mapping\HierarchyBEntity\' an abstract class to' - . ' avoid this exception from occurring.' + . ' avoid this exception from occurring.', ); $this->cmf->getMetadataFor(HierarchyE::class); } - /** - * @group DDC-1204 - * @group DDC-1203 - */ + #[Group('DDC-1204')] + #[Group('DDC-1203')] public function testMappedSuperclassWithId(): void { $class = $this->cmf->getMetadataFor(SuperclassEntity::class); @@ -160,11 +157,9 @@ public function testMappedSuperclassWithId(): void self::assertArrayHasKey('id', $class->fieldMappings); } - /** - * @group DDC-1156 - * @group DDC-1218 - * @group GH-10927 - */ + #[Group('DDC-1156')] + #[Group('DDC-1218')] + #[Group('GH-10927')] public function testSequenceDefinitionInHierarchyWithSandwichMappedSuperclass(): void { $class = $this->cmf->getMetadataFor(HierarchyD::class); @@ -173,15 +168,14 @@ public function testSequenceDefinitionInHierarchyWithSandwichMappedSuperclass(): self::assertInstanceOf(IdSequenceGenerator::class, $class->idGenerator); self::assertEquals( ['allocationSize' => 1, 'initialValue' => 10, 'sequenceName' => 'foo'], - $class->sequenceGeneratorDefinition + $class->sequenceGeneratorDefinition, ); } /** * Ensure indexes are inherited from the mapped superclass. - * - * @group DDC-3418 */ + #[Group('DDC-3418')] public function testMappedSuperclassIndex(): void { $class = $this->cmf->getMetadataFor(EntityIndexSubClass::class); @@ -193,12 +187,15 @@ public function testMappedSuperclassIndex(): void self::assertArrayHasKey('IDX_MAPPED2_INDEX', $class->table['indexes']); } - /** - * @dataProvider invalidHierarchyDeclarationClasses - */ + #[DataProvider('invalidHierarchyDeclarationClasses')] public function testUndeclaredHierarchyRejection(string $rootEntity, string $childClass): void { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10431'); + $this->expectException(MappingException::class); + $this->expectExceptionMessage(sprintf( + "Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared.", + $childClass, + $rootEntity, + )); $this->cmf->getMetadataFor($childClass); } @@ -221,22 +218,23 @@ public static function invalidHierarchyDeclarationClasses(): Generator => [InvalidComplexRoot::class, InvalidComplexEntity::class]; } - /** @group DDC-964 */ + #[Group('DDC-964')] public function testInvalidOverrideFieldInheritedFromEntity(): void { $cm = $this->cmf->getMetadataFor(CompanyFixContract::class); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10470'); + $this->expectException(MappingException::class); + $this->expectExceptionMessageMatches('/Overrides are only allowed for fields or associations declared in mapped superclasses or traits./'); $cm->setAttributeOverride('completed', ['name' => 'other_column_name']); } - /** @group DDC-964 */ public function testInvalidOverrideAssociationInheritedFromEntity(): void { $cm = $this->cmf->getMetadataFor(CompanyFixContract::class); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10470'); + $this->expectException(MappingException::class); + $this->expectExceptionMessage('Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for Doctrine\Tests\Models\Company\CompanyFixContract::salesPerson, which was inherited from Doctrine\Tests\Models\Company\CompanyContract.'); $cm->setAssociationOverride('salesPerson', ['inversedBy' => 'other_inversed_by_name']); } @@ -251,285 +249,211 @@ class TransientBaseClass private $transient2; } -/** @Entity */ +#[Entity] class EntitySubClass extends TransientBaseClass { - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; + #[Column(type: 'string', length: 255)] + private string $name; } -/** @MappedSuperclass */ +#[MappedSuperclass] class MappedSuperclassBase { - /** - * @var int - * @Column(type="integer") - */ - private $mapped1; + #[Column(type: 'integer')] + private int $mapped1; - /** - * @var string - * @Column(type="string", length=255) - */ - private $mapped2; + #[Column(type: 'string', length: 255)] + private string $mapped2; - /** - * @var MappedSuperclassRelated1 - * @OneToOne(targetEntity="MappedSuperclassRelated1") - * @JoinColumn(name="related1_id", referencedColumnName="id") - */ - private $mappedRelated1; + #[OneToOne(targetEntity: 'MappedSuperclassRelated1')] + #[JoinColumn(name: 'related1_id', referencedColumnName: 'id')] + private MappedSuperclassRelated1 $mappedRelated1; /** @var mixed */ private $transient; } +#[Entity] class MappedSuperclassRelated1 { } -/** @Entity */ +#[Entity] class EntitySubClass2 extends MappedSuperclassBase { - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; + #[Column(type: 'string', length: 255)] + private string $name; } -/** - * @MappedSuperclass - * @Table( - * uniqueConstraints={@UniqueConstraint(name="IDX_MAPPED1_INDEX",columns={"mapped1"})}, - * indexes={@Index(name="IDX_MAPPED2_INDEX", columns={"mapped2"})} - * ) - */ +#[Table] +#[Index(name: 'IDX_MAPPED2_INDEX', columns: ['mapped2'])] +#[UniqueConstraint(name: 'IDX_MAPPED1_INDEX', columns: ['mapped1'])] +#[MappedSuperclass] class MappedSuperclassBaseIndex { - /** - * @var string - * @Column(type="string", length=255) - */ - private $mapped1; - /** - * @var string - * @Column(type="string", length=255) - */ - private $mapped2; + #[Column(type: 'string', length: 255)] + private string $mapped1; + #[Column(type: 'string', length: 255)] + private string $mapped2; } -/** - * @Entity - * @Table(uniqueConstraints={@UniqueConstraint(name="IDX_NAME_INDEX",columns={"name"})}) - */ +#[Table] +#[UniqueConstraint(name: 'IDX_NAME_INDEX', columns: ['name'])] +#[Entity] class EntityIndexSubClass extends MappedSuperclassBaseIndex { - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; + #[Column(type: 'string', length: 255)] + private string $name; } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string", length=20) - * @DiscriminatorMap({ - * "c" = "HierarchyC", - * "d" = "HierarchyD", - * "e" = "HierarchyE" - * }) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorColumn(name: 'type', type: 'string', length: 20)] +#[DiscriminatorMap(['c' => 'HierarchyC', 'd' => 'HierarchyD', 'e' => 'HierarchyE'])] abstract class HierarchyBase { - /** - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="SEQUENCE") - * @SequenceGenerator(sequenceName="foo", initialValue=10) - * @var int - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'SEQUENCE')] + #[SequenceGenerator(sequenceName: 'foo', initialValue: 10)] public $id; } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class HierarchyASuperclass extends HierarchyBase { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $a; } -/** @Entity */ +#[Entity] class HierarchyBEntity extends HierarchyBase { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $b; } -/** @Entity */ +#[Entity] class HierarchyC extends HierarchyBase { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $c; } -/** @Entity */ +#[Entity] class HierarchyD extends HierarchyASuperclass { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $d; } -/** @Entity */ +#[Entity] class HierarchyE extends HierarchyBEntity { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $e; } -/** @Entity */ +#[Entity] class SuperclassEntity extends SuperclassBase { } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class SuperclassBase { - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="SEQUENCE") - * @SequenceGenerator(sequenceName="foo", initialValue=10) - */ + /** @var int */ + #[Column(type: 'integer')] + #[Id] + #[GeneratedValue(strategy: 'SEQUENCE')] + #[SequenceGenerator(sequenceName: 'foo', initialValue: 10)] public $id; } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class MediumSuperclassBase extends SuperclassBase { } -/** @Entity */ +#[Entity] class MediumSuperclassEntity extends MediumSuperclassBase { } -/** @Entity(repositoryClass = "Doctrine\ORM\EntityRepository") */ +#[Entity(repositoryClass: 'Doctrine\ORM\EntityRepository')] class SubclassWithRepository extends DDC869Payment { } -/** - * @Entity - * - * This class misses the DiscriminatorMap declaration - */ +/** This class misses the DiscriminatorMap declaration */ +#[Entity] class InvalidEntityRoot { - /** - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - * @var int - */ - public $id; + #[Column] + #[Id] + #[GeneratedValue] + public int|null $id = null; } -/** @Entity */ +#[Entity] class InvalidEntityRootChild extends InvalidEntityRoot { } -/** @Entity */ +#[Entity] abstract class InvalidEntityRootAbstractChild extends InvalidEntityRoot { } -/** - * @Entity - * - * This class misses the DiscriminatorMap declaration - */ +/** This class misses the DiscriminatorMap declaration */ +#[Entity] class InvalidAbstractEntityRoot { - /** - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - * @var int - */ - public $id; + #[Column] + #[Id] + #[GeneratedValue] + public int|null $id = null; } -/** @Entity */ +#[Entity] class InvalidAbstractEntityRootChild extends InvalidAbstractEntityRoot { } -/** @Entity */ +#[Entity] abstract class InvalidAbstractEntityRootAbstractChild extends InvalidAbstractEntityRoot { } -/** - * @Entity - * - * This class misses the DiscriminatorMap declaration - */ +/** This class misses the DiscriminatorMap declaration */ +#[Entity] class InvalidComplexRoot { - /** - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - * @var int - */ - public $id; + #[Column] + #[Id] + #[GeneratedValue] + public int|null $id = null; } -/** @MappedSuperclass */ +#[MappedSuperclass] class InvalidComplexMappedSuperclass extends InvalidComplexRoot { } @@ -538,7 +462,7 @@ class InvalidComplexTransientClass extends InvalidComplexMappedSuperclass { } -/** @Entity */ +#[Entity] class InvalidComplexEntity extends InvalidComplexTransientClass { } diff --git a/tests/Tests/ORM/Mapping/ClassMetadataBuilderTest.php b/tests/Tests/ORM/Mapping/ClassMetadataBuilderTest.php index 23f664c9205..58ea2202ef1 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataBuilderTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataBuilderTest.php @@ -8,20 +8,26 @@ use Doctrine\ORM\Mapping\Builder\EmbeddedBuilder; use Doctrine\ORM\Mapping\Builder\FieldBuilder; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\DiscriminatorColumnMapping; +use Doctrine\ORM\Mapping\EmbeddedClassMapping; +use Doctrine\ORM\Mapping\FieldMapping; +use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping; +use Doctrine\ORM\Mapping\ManyToOneAssociationMapping; use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; +use Doctrine\ORM\Mapping\OneToOneOwningSideMapping; use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\ValueObjects\Name; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-659 */ +#[Group('DDC-659')] class ClassMetadataBuilderTest extends OrmTestCase { - /** @var ClassMetadata */ - private $cm; - /** @var ClassMetadataBuilder */ - private $builder; + private ClassMetadata $cm; + private ClassMetadataBuilder $builder; protected function setUp(): void { @@ -50,14 +56,14 @@ public function testAddEmbeddedWithOnlyRequiredParams(): void self::assertEquals( [ - 'name' => [ + 'name' => EmbeddedClassMapping::fromMappingArray([ 'class' => Name::class, 'columnPrefix' => null, 'declaredField' => null, 'originalField' => null, - ], + ]), ], - $this->cm->embeddedClasses + $this->cm->embeddedClasses, ); } @@ -67,20 +73,20 @@ public function testAddEmbeddedWithPrefix(): void $this->builder->addEmbedded( 'name', Name::class, - 'nm_' - ) + 'nm_', + ), ); self::assertEquals( [ - 'name' => [ + 'name' => EmbeddedClassMapping::fromMappingArray([ 'class' => Name::class, 'columnPrefix' => 'nm_', 'declaredField' => null, 'originalField' => null, - ], + ]), ], - $this->cm->embeddedClasses + $this->cm->embeddedClasses, ); } @@ -93,13 +99,13 @@ public function testCreateEmbeddedWithoutExtraParams(): void $this->assertIsFluent($embeddedBuilder->build()); self::assertEquals( - [ + EmbeddedClassMapping::fromMappingArray([ 'class' => Name::class, 'columnPrefix' => null, 'declaredField' => null, 'originalField' => null, - ], - $this->cm->embeddedClasses['name'] + ]), + $this->cm->embeddedClasses['name'], ); } @@ -112,13 +118,13 @@ public function testCreateEmbeddedWithColumnPrefix(): void $this->assertIsFluent($embeddedBuilder->build()); self::assertEquals( - [ + EmbeddedClassMapping::fromMappingArray([ 'class' => Name::class, 'columnPrefix' => 'nm_', 'declaredField' => null, 'originalField' => null, - ], - $this->cm->embeddedClasses['name'] + ]), + $this->cm->embeddedClasses['name'], ); } @@ -164,7 +170,7 @@ public function testSetPrimaryTableRelated(): void 'indexes' => ['users_idx' => ['columns' => ['username', 'name']]], 'uniqueConstraints' => ['users_idx' => ['columns' => ['username', 'name']]], ], - $this->cm->table + $this->cm->table, ); } @@ -182,8 +188,16 @@ public function testSetInheritanceSingleTable(): void public function testSetDiscriminatorColumn(): void { - $this->assertIsFluent($this->builder->setDiscriminatorColumn('discr', 'string', '124', null, null)); - self::assertEquals(['fieldName' => 'discr', 'name' => 'discr', 'type' => 'string', 'length' => '124', 'columnDefinition' => null, 'enumType' => null, 'options' => []], $this->cm->discriminatorColumn); + $this->assertIsFluent($this->builder->setDiscriminatorColumn('discr', 'string', 124, null, null)); + self::assertEquals(DiscriminatorColumnMapping::fromMappingArray([ + 'fieldName' => 'discr', + 'name' => 'discr', + 'type' => 'string', + 'length' => 124, + 'columnDefinition' => null, + 'enumType' => null, + 'options' => [], + ]), $this->cm->discriminatorColumn); } public function testAddDiscriminatorMapClass(): void @@ -193,7 +207,7 @@ public function testAddDiscriminatorMapClass(): void self::assertEquals( ['test' => CmsUser::class, 'test2' => CmsGroup::class], - $this->cm->discriminatorMap + $this->cm->discriminatorMap, ); self::assertEquals('test', $this->cm->discriminatorValue); } @@ -204,16 +218,13 @@ public function testChangeTrackingPolicyExplicit(): void self::assertEquals(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT, $this->cm->changeTrackingPolicy); } - public function testChangeTrackingPolicyNotify(): void - { - $this->assertIsFluent($this->builder->setChangeTrackingPolicyNotify()); - self::assertEquals(ClassMetadata::CHANGETRACKING_NOTIFY, $this->cm->changeTrackingPolicy); - } - public function testAddField(): void { $this->assertIsFluent($this->builder->addField('name', 'string')); - self::assertEquals(['columnName' => 'name', 'fieldName' => 'name', 'type' => 'string'], $this->cm->fieldMappings['name']); + $mapping = $this->cm->getFieldMapping('name'); + self::assertSame('name', $mapping->fieldName); + self::assertSame('name', $mapping->columnName); + self::assertSame('string', $mapping->type); } public function testCreateField(): void @@ -223,14 +234,17 @@ public function testCreateField(): void self::assertFalse(isset($this->cm->fieldMappings['name'])); $this->assertIsFluent($fieldBuilder->build()); - self::assertEquals(['columnName' => 'name', 'fieldName' => 'name', 'type' => 'string'], $this->cm->fieldMappings['name']); + $mapping = $this->cm->getFieldMapping('name'); + self::assertSame('name', $mapping->fieldName); + self::assertSame('name', $mapping->columnName); + self::assertSame('string', $mapping->type); } public function testCreateVersionedField(): void { $this->builder->createField('name', 'integer')->columnName('username')->length(124)->nullable()->columnDefinition('foobar')->unique()->isVersionField()->build(); self::assertEquals( - [ + FieldMapping::fromMappingArray([ 'columnDefinition' => 'foobar', 'columnName' => 'username', 'default' => 1, @@ -239,8 +253,8 @@ public function testCreateVersionedField(): void 'type' => 'integer', 'nullable' => true, 'unique' => true, - ], - $this->cm->fieldMappings['name'] + ]), + $this->cm->fieldMappings['name'], ); } @@ -249,17 +263,18 @@ public function testCreatePrimaryField(): void $this->builder->createField('id', 'integer')->makePrimaryKey()->generatedValue()->build(); self::assertEquals(['id'], $this->cm->identifier); - self::assertEquals(['columnName' => 'id', 'fieldName' => 'id', 'id' => true, 'type' => 'integer'], $this->cm->fieldMappings['id']); + self::assertEquals(FieldMapping::fromMappingArray( + ['columnName' => 'id', 'fieldName' => 'id', 'id' => true, 'type' => 'integer'], + ), $this->cm->fieldMappings['id']); } public function testCreateUnsignedOptionField(): void { $this->builder->createField('state', 'integer')->option('unsigned', true)->build(); - self::assertEquals( + self::assertEquals(FieldMapping::fromMappingArray( ['fieldName' => 'state', 'type' => 'integer', 'options' => ['unsigned' => true], 'columnName' => 'state'], - $this->cm->fieldMappings['state'] - ); + ), $this->cm->fieldMappings['state']); } public function testAddLifecycleEvent(): void @@ -276,20 +291,19 @@ public function testCreateManyToOne(): void ->addJoinColumn('group_id', 'id', true, false, 'CASCADE') ->cascadeAll() ->fetchExtraLazy() - ->build() + ->build(), ); self::assertEquals( [ - 'groups' => [ + 'groups' => ManyToOneAssociationMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [ 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'fetch' => 4, 'joinColumns' => [ @@ -304,15 +318,9 @@ public function testCreateManyToOne(): void ], ], 'type' => 2, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => true, - 'isCascadeRefresh' => true, - 'isCascadeMerge' => true, - 'isCascadeDetach' => true, 'sourceToTargetKeyColumns' => ['group_id' => 'id'], 'joinColumnFieldNames' => @@ -320,9 +328,9 @@ public function testCreateManyToOne(): void 'targetToSourceKeyColumns' => ['id' => 'group_id'], 'orphanRemoval' => false, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -336,20 +344,19 @@ public function testCreateManyToOneWithIdentity(): void ->cascadeAll() ->fetchExtraLazy() ->makePrimaryKey() - ->build() + ->build(), ); self::assertEquals( [ - 'groups' => [ + 'groups' => ManyToOneAssociationMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [ 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'fetch' => 4, 'joinColumns' => [ @@ -364,15 +371,9 @@ public function testCreateManyToOneWithIdentity(): void ], ], 'type' => 2, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => true, - 'isCascadeRefresh' => true, - 'isCascadeMerge' => true, - 'isCascadeDetach' => true, 'sourceToTargetKeyColumns' => ['group_id' => 'id'], 'joinColumnFieldNames' => @@ -381,9 +382,9 @@ public function testCreateManyToOneWithIdentity(): void ['id' => 'group_id'], 'orphanRemoval' => false, 'id' => true, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -394,20 +395,19 @@ public function testCreateOneToOne(): void ->addJoinColumn('group_id', 'id', true, false, 'CASCADE') ->cascadeAll() ->fetchExtraLazy() - ->build() + ->build(), ); self::assertEquals( [ - 'groups' => [ + 'groups' => OneToOneOwningSideMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [ 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'fetch' => 4, 'joinColumns' => [ @@ -422,15 +422,9 @@ public function testCreateOneToOne(): void ], ], 'type' => 1, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => true, - 'isCascadeRefresh' => true, - 'isCascadeMerge' => true, - 'isCascadeDetach' => true, 'sourceToTargetKeyColumns' => ['group_id' => 'id'], 'joinColumnFieldNames' => @@ -438,9 +432,9 @@ public function testCreateOneToOne(): void 'targetToSourceKeyColumns' => ['id' => 'group_id'], 'orphanRemoval' => false, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -454,20 +448,19 @@ public function testCreateOneToOneWithIdentity(): void ->cascadeAll() ->fetchExtraLazy() ->makePrimaryKey() - ->build() + ->build(), ); self::assertEquals( [ - 'groups' => [ + 'groups' => OneToOneOwningSideMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [ 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'fetch' => 4, 'id' => true, @@ -483,15 +476,9 @@ public function testCreateOneToOneWithIdentity(): void ], ], 'type' => 1, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => true, - 'isCascadeRefresh' => true, - 'isCascadeMerge' => true, - 'isCascadeDetach' => true, 'sourceToTargetKeyColumns' => ['group_id' => 'id'], 'joinColumnFieldNames' => @@ -499,9 +486,9 @@ public function testCreateOneToOneWithIdentity(): void 'targetToSourceKeyColumns' => ['id' => 'group_id'], 'orphanRemoval' => false, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -527,13 +514,13 @@ public function testCreateManyToMany(): void ->addInverseJoinColumn('user_id', 'id') ->cascadeAll() ->fetchExtraLazy() - ->build() + ->build(), ); self::assertEquals( [ 'groups' => - [ + ManyToManyOwningSideMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => @@ -541,8 +528,7 @@ public function testCreateManyToMany(): void 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'fetch' => 4, 'joinTable' => @@ -574,16 +560,10 @@ public function testCreateManyToMany(): void 'name' => 'groups_users', ], 'type' => 8, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, - 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => true, - 'isCascadeRefresh' => true, - 'isCascadeMerge' => true, - 'isCascadeDetach' => true, 'isOnDeleteCascade' => true, + 'sourceEntity' => CmsUser::class, 'relationToSourceKeyColumns' => ['group_id' => 'id'], 'joinTableColumns' => @@ -594,9 +574,9 @@ public function testCreateManyToMany(): void 'relationToTargetKeyColumns' => ['user_id' => 'id'], 'orphanRemoval' => false, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -621,35 +601,27 @@ public function testCreateOneToMany(): void ->mappedBy('test') ->setOrderBy(['test']) ->setIndexBy('test') - ->build() + ->build(), ); self::assertEquals( [ 'groups' => - [ + OneToManyAssociationMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'mappedBy' => 'test', - 'orderBy' => - [0 => 'test'], + 'orderBy' => [0 => 'test'], 'indexBy' => 'test', 'type' => 4, - 'inversedBy' => null, 'isOwningSide' => false, 'sourceEntity' => CmsUser::class, 'fetch' => 2, - 'cascade' => - [], - 'isCascadeRemove' => false, - 'isCascadePersist' => false, - 'isCascadeRefresh' => false, - 'isCascadeMerge' => false, - 'isCascadeDetach' => false, + 'cascade' => [], 'orphanRemoval' => false, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -672,12 +644,12 @@ public function testOrphanRemovalOnCreateOneToOne(): void ->createOneToOne('groups', CmsGroup::class) ->addJoinColumn('group_id', 'id', true, false, 'CASCADE') ->orphanRemoval() - ->build() + ->build(), ); self::assertEquals( [ - 'groups' => [ + 'groups' => OneToOneOwningSideMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [], @@ -694,15 +666,9 @@ public function testOrphanRemovalOnCreateOneToOne(): void ], ], 'type' => 1, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => true, - 'isCascadePersist' => false, - 'isCascadeRefresh' => false, - 'isCascadeMerge' => false, - 'isCascadeDetach' => false, 'sourceToTargetKeyColumns' => ['group_id' => 'id'], 'joinColumnFieldNames' => @@ -710,9 +676,9 @@ public function testOrphanRemovalOnCreateOneToOne(): void 'targetToSourceKeyColumns' => ['id' => 'group_id'], 'orphanRemoval' => true, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -723,31 +689,25 @@ public function testOrphanRemovalOnCreateOneToMany(): void ->createOneToMany('groups', CmsGroup::class) ->mappedBy('test') ->orphanRemoval() - ->build() + ->build(), ); self::assertEquals( [ 'groups' => - [ + OneToManyAssociationMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'mappedBy' => 'test', 'type' => 4, - 'inversedBy' => null, 'isOwningSide' => false, 'sourceEntity' => CmsUser::class, 'fetch' => 2, 'cascade' => [], - 'isCascadeRemove' => true, - 'isCascadePersist' => false, - 'isCascadeRefresh' => false, - 'isCascadeMerge' => false, - 'isCascadeDetach' => false, 'orphanRemoval' => true, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } @@ -772,7 +732,7 @@ public function testOrphanRemovalOnManyToMany(): void self::assertEquals( [ - 'groups' => [ + 'groups' => ManyToManyOwningSideMapping::fromMappingArray([ 'fieldName' => 'groups', 'targetEntity' => CmsGroup::class, 'cascade' => [], @@ -798,15 +758,9 @@ public function testOrphanRemovalOnManyToMany(): void 'name' => 'cmsuser_cmsgroup', ], 'type' => 8, - 'mappedBy' => null, 'inversedBy' => null, 'isOwningSide' => true, 'sourceEntity' => CmsUser::class, - 'isCascadeRemove' => false, - 'isCascadePersist' => false, - 'isCascadeRefresh' => false, - 'isCascadeMerge' => false, - 'isCascadeDetach' => false, 'isOnDeleteCascade' => true, 'relationToSourceKeyColumns' => ['group_id' => 'id'], 'joinTableColumns' => [ @@ -815,9 +769,9 @@ public function testOrphanRemovalOnManyToMany(): void ], 'relationToTargetKeyColumns' => ['cmsgroup_id' => 'id'], 'orphanRemoval' => true, - ], + ]), ], - $this->cm->associationMappings + $this->cm->associationMappings, ); } diff --git a/tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index bea5e2578da..540ab47dde8 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -5,13 +5,10 @@ namespace Doctrine\Tests\ORM\Mapping; use Doctrine\Common\EventManager; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; @@ -24,7 +21,6 @@ use Doctrine\ORM\Mapping\DiscriminatorColumn; use Doctrine\ORM\Mapping\DiscriminatorMap; use Doctrine\ORM\Mapping\Entity; -use Doctrine\ORM\Mapping\Exception\CannotGenerateIds; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; @@ -39,9 +35,11 @@ use Doctrine\Tests\Models\JoinedInheritanceType\AnotherChildClass; use Doctrine\Tests\Models\JoinedInheritanceType\ChildClass; use Doctrine\Tests\Models\JoinedInheritanceType\RootClass; -use Doctrine\Tests\Models\Quote; +use Doctrine\Tests\Models\Quote\Address; +use Doctrine\Tests\Models\Quote\Group; +use Doctrine\Tests\Models\Quote\Phone; +use Doctrine\Tests\Models\Quote\User; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; use Doctrine\Tests\TestUtil; use Exception; use InvalidArgumentException; @@ -50,15 +48,12 @@ use function array_search; use function assert; -use function class_exists; use function count; +use function method_exists; use function sprintf; class ClassMetadataFactoryTest extends OrmTestCase { - use MockBuilderCompatibilityTools; - use VerifyDeprecations; - public function testGetMetadataForSingleClass(): void { $platform = $this->createMock(AbstractPlatform::class); @@ -99,46 +94,6 @@ public function testGetMetadataForSingleClass(): void self::assertTrue($cmMap1->hasField('name')); } - public function testUsingIdentityWithAPlatformThatDoesNotSupportIdentityColumnsIsDeprecated(): void - { - $cm = $this->createValidClassMetadata(); - $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); - $cmf = new ClassMetadataFactoryTestSubject(); - $driver = $this->createMock(Driver::class); - $platform = $this->createStub(AbstractPlatform::class); - $platform->method('usesSequenceEmulatedIdentityColumns')->willReturn(true); - $platform->method('getIdentitySequenceName')->willReturn('whatever'); - $driver->method('getDatabasePlatform')->willReturn($platform); - $entityManager = $this->createEntityManager( - new MetadataDriverMock(), - new Connection([], $driver) - ); - $cmf->setEntityManager($entityManager); - $cmf->setMetadataForClass($cm->name, $cm); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8850'); - $cmf->getMetadataFor($cm->name); - } - - public function testItThrowsWhenUsingAutoWithIncompatiblePlatform(): void - { - $cm1 = $this->createValidClassMetadata(); - $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - - $driver = $this->createMock(Driver::class); - $driver->method('getDatabasePlatform') - ->willReturn($this->createMock(AbstractPlatform::class)); - - $connection = new Connection([], $driver); - $entityManager = $this->createEntityManager(new MetadataDriverMock(), $connection); - $cmf = new ClassMetadataFactoryTestSubject(); - $cmf->setEntityManager($entityManager); - $cmf->setMetadataForClass($cm1->name, $cm1); - $this->expectException(CannotGenerateIds::class); - - $actual = $cmf->getMetadataFor($cm1->name); - } - public function testGetMetadataForReturnsLoadedCustomIdGenerator(): void { $cm1 = $this->createValidClassMetadata(); @@ -162,7 +117,7 @@ private function setUpCmfForPlatform(AbstractPlatform $platform, array $preferen ->willReturn($platform); $entityManager = $this->createEntityManager( new MetadataDriverMock(), - new Connection([], $driver) + new Connection([], $driver), ); $cmf->setEntityManager($entityManager); $entityManager->getConfiguration()->setIdentityGenerationPreferences($preferences); @@ -170,41 +125,36 @@ private function setUpCmfForPlatform(AbstractPlatform $platform, array $preferen return $cmf; } - public function testRelyingOnLegacyIdGenerationDefaultsIsOKIfItResultsInTheCurrentlyRecommendedStrategyBeingUsed(): void + public function testPostgresSticksWithSequencesWhenDbal3IsUsed(): void { - $cm = $this->createValidClassMetadata(); - $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - $cmf = $this->setUpCmfForPlatform(new OraclePlatform()); - $cmf->setMetadataForClass($cm->name, $cm); - - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8893'); - $cmf->getMetadataFor($cm->name); - } + if (! method_exists(AbstractPlatform::class, 'getIdentitySequenceName')) { + self::markTestSkipped('This test requires DBAL 3'); + } - public function testRelyingOnLegacyIdGenerationDefaultsIsDeprecatedIfItResultsInADefaultThatWillChange(): void - { $cm = $this->createValidClassMetadata(); $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - $cmf = $this->setUpCmfForPlatform(new PostgreSQLPlatform()); $cmf->setMetadataForClass($cm->name, $cm); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8893'); - $cmf->getMetadataFor($cm->name); + $metadata = $cmf->getMetadataFor($cm->name); + + self::assertSame(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $metadata->generatorType); } - public function testSpecifyingIdGenerationStrategyThroughConfigurationFixesTheDeprecation(): void + public function testPostgresSwitchesToIdentityColumnsWhenDbal4IsUsed(): void { + if (method_exists(AbstractPlatform::class, 'getIdentitySequenceName')) { + self::markTestSkipped('This test requires DBAL 4'); + } + $cm = $this->createValidClassMetadata(); $cm->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - - $cmf = $this->setUpCmfForPlatform(new PostgreSQLPlatform(), [ - PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE, - ]); + $cmf = $this->setUpCmfForPlatform(new PostgreSQLPlatform()); $cmf->setMetadataForClass($cm->name, $cm); - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8893'); - $cmf->getMetadataFor($cm->name); + $metadata = $cmf->getMetadataFor($cm->name); + + self::assertSame(ClassMetadata::GENERATOR_TYPE_IDENTITY, $metadata->generatorType); } public function testGetMetadataForThrowsExceptionOnUnknownCustomGeneratorClass(): void @@ -230,7 +180,7 @@ public function testGetMetadataForThrowsExceptionOnMissingCustomGeneratorDefinit $actual = $cmf->getMetadataFor($cm1->name); } - /** @group DDC-1512 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1512')] public function testIsTransient(): void { $cmf = new ClassMetadataFactory(); @@ -248,43 +198,12 @@ public function testIsTransient(): void self::assertFalse($em->getMetadataFactory()->isTransient(CmsArticle::class)); } - /** @group DDC-1512 */ - public function testIsTransientEntityNamespace(): void - { - if (! class_exists(PersistentObject::class)) { - $this->markTestSkipped('This test requires doctrine/persistence 2'); - } - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8818'); - - $cmf = new ClassMetadataFactory(); - $driver = $this->createMock(MappingDriver::class); - $driver->expects(self::exactly(2)) - ->method('isTransient') - ->withConsecutive( - [CmsUser::class], - [CmsArticle::class] - ) - ->willReturnMap( - [ - [CmsUser::class, true], - [CmsArticle::class, false], - ] - ); - - $em = $this->createEntityManager($driver); - $em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS'); - - self::assertTrue($em->getMetadataFactory()->isTransient('CMS:CmsUser')); - self::assertFalse($em->getMetadataFactory()->isTransient('CMS:CmsArticle')); - } - public function testAddDefaultDiscriminatorMap(): void { self::markTestSkipped('This test is just incorrect and must be fixed'); $cmf = new ClassMetadataFactory(); - $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/JoinedInheritanceType/']); + $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/JoinedInheritanceType/']); $em = $this->createEntityManager($driver); $cmf->setEntityManager($em); @@ -314,7 +233,13 @@ public function testAddDefaultDiscriminatorMap(): void public function testGetAllMetadataWorksWithBadConnection(): void { // DDC-3551 - $conn = $this->createMock(Connection::class); + $conn = $this->createMock(Connection::class); + + if (method_exists($conn, 'getEventManager')) { + $conn->method('getEventManager') + ->willReturn(new EventManager()); + } + $mockDriver = new MetadataDriverMock(); $em = $this->createEntityManager($mockDriver, $conn); @@ -380,7 +305,7 @@ protected function createValidClassMetadata(): ClassMetadata ['name' => 'other_id', 'referencedColumnName' => 'id'], ]; $cm1->mapOneToOne( - ['fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns] + ['fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns], ); // and an id generator type $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -388,76 +313,74 @@ protected function createValidClassMetadata(): ClassMetadata return $cm1; } - /** @group DDC-1845 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1845')] public function testQuoteMetadata(): void { $cmf = new ClassMetadataFactory(); - $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/Quote/']); + $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/Quote/']); $em = $this->createEntityManager($driver); $cmf->setEntityManager($em); - $userMetadata = $cmf->getMetadataFor(Quote\User::class); - $phoneMetadata = $cmf->getMetadataFor(Quote\Phone::class); - $groupMetadata = $cmf->getMetadataFor(Quote\Group::class); - $addressMetadata = $cmf->getMetadataFor(Quote\Address::class); + $userMetadata = $cmf->getMetadataFor(User::class); + $phoneMetadata = $cmf->getMetadataFor(Phone::class); + $groupMetadata = $cmf->getMetadataFor(Group::class); + $addressMetadata = $cmf->getMetadataFor(Address::class); // Phone Class Metadata - self::assertTrue($phoneMetadata->fieldMappings['number']['quoted']); - self::assertEquals('phone-number', $phoneMetadata->fieldMappings['number']['columnName']); + self::assertTrue($phoneMetadata->fieldMappings['number']->quoted); + self::assertEquals('phone-number', $phoneMetadata->fieldMappings['number']->columnName); $user = $phoneMetadata->associationMappings['user']; - self::assertTrue($user['joinColumns'][0]['quoted']); - self::assertEquals('user-id', $user['joinColumns'][0]['name']); - self::assertEquals('user-id', $user['joinColumns'][0]['referencedColumnName']); + self::assertTrue($user->joinColumns[0]->quoted); + self::assertEquals('user-id', $user->joinColumns[0]->name); + self::assertEquals('user-id', $user->joinColumns[0]->referencedColumnName); // User Group Metadata - self::assertTrue($groupMetadata->fieldMappings['id']['quoted']); - self::assertTrue($groupMetadata->fieldMappings['name']['quoted']); + self::assertTrue($groupMetadata->fieldMappings['id']->quoted); + self::assertTrue($groupMetadata->fieldMappings['name']->quoted); - self::assertEquals('user-id', $userMetadata->fieldMappings['id']['columnName']); - self::assertEquals('user-name', $userMetadata->fieldMappings['name']['columnName']); + self::assertEquals('user-id', $userMetadata->fieldMappings['id']->columnName); + self::assertEquals('user-name', $userMetadata->fieldMappings['name']->columnName); $user = $groupMetadata->associationMappings['parent']; - self::assertTrue($user['joinColumns'][0]['quoted']); - self::assertEquals('parent-id', $user['joinColumns'][0]['name']); - self::assertEquals('group-id', $user['joinColumns'][0]['referencedColumnName']); + self::assertTrue($user->joinColumns[0]->quoted); + self::assertEquals('parent-id', $user->joinColumns[0]->name); + self::assertEquals('group-id', $user->joinColumns[0]->referencedColumnName); // Address Class Metadata - self::assertTrue($addressMetadata->fieldMappings['id']['quoted']); - self::assertTrue($addressMetadata->fieldMappings['zip']['quoted']); + self::assertTrue($addressMetadata->fieldMappings['id']->quoted); + self::assertTrue($addressMetadata->fieldMappings['zip']->quoted); - self::assertEquals('address-id', $addressMetadata->fieldMappings['id']['columnName']); - self::assertEquals('address-zip', $addressMetadata->fieldMappings['zip']['columnName']); + self::assertEquals('address-id', $addressMetadata->fieldMappings['id']->columnName); + self::assertEquals('address-zip', $addressMetadata->fieldMappings['zip']->columnName); $user = $addressMetadata->associationMappings['user']; - self::assertTrue($user['joinColumns'][0]['quoted']); - self::assertEquals('user-id', $user['joinColumns'][0]['name']); - self::assertEquals('user-id', $user['joinColumns'][0]['referencedColumnName']); + self::assertTrue($user->joinColumns[0]->quoted); + self::assertEquals('user-id', $user->joinColumns[0]->name); + self::assertEquals('user-id', $user->joinColumns[0]->referencedColumnName); // User Class Metadata - self::assertTrue($userMetadata->fieldMappings['id']['quoted']); - self::assertTrue($userMetadata->fieldMappings['name']['quoted']); + self::assertTrue($userMetadata->fieldMappings['id']->quoted); + self::assertTrue($userMetadata->fieldMappings['name']->quoted); - self::assertEquals('user-id', $userMetadata->fieldMappings['id']['columnName']); - self::assertEquals('user-name', $userMetadata->fieldMappings['name']['columnName']); + self::assertEquals('user-id', $userMetadata->fieldMappings['id']->columnName); + self::assertEquals('user-name', $userMetadata->fieldMappings['name']->columnName); $groups = $userMetadata->associationMappings['groups']; - self::assertTrue($groups['joinTable']['quoted']); - self::assertTrue($groups['joinTable']['joinColumns'][0]['quoted']); - self::assertEquals('quote-users-groups', $groups['joinTable']['name']); - self::assertEquals('user-id', $groups['joinTable']['joinColumns'][0]['name']); - self::assertEquals('user-id', $groups['joinTable']['joinColumns'][0]['referencedColumnName']); - - self::assertTrue($groups['joinTable']['inverseJoinColumns'][0]['quoted']); - self::assertEquals('group-id', $groups['joinTable']['inverseJoinColumns'][0]['name']); - self::assertEquals('group-id', $groups['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); + self::assertTrue($groups->joinTable->quoted); + self::assertTrue($groups->joinTable->joinColumns[0]->quoted); + self::assertEquals('quote-users-groups', $groups->joinTable->name); + self::assertEquals('user-id', $groups->joinTable->joinColumns[0]->name); + self::assertEquals('user-id', $groups->joinTable->joinColumns[0]->referencedColumnName); + + self::assertTrue($groups->joinTable->inverseJoinColumns[0]->quoted); + self::assertEquals('group-id', $groups->joinTable->inverseJoinColumns[0]->name); + self::assertEquals('group-id', $groups->joinTable->inverseJoinColumns[0]->referencedColumnName); } - /** - * @group DDC-3385 - * @group 1181 - * @group 385 - */ + #[\PHPUnit\Framework\Attributes\Group('DDC-3385')] + #[\PHPUnit\Framework\Attributes\Group('1181')] + #[\PHPUnit\Framework\Attributes\Group('385')] public function testFallbackLoadingCausesEventTriggeringThatCanModifyFetchedMetadata(): void { $metadata = $this->createMock(ClassMetadata::class); @@ -494,7 +417,7 @@ public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args): self::assertSame($metadata, $cmf->getMetadataFor('Foo')); } - /** @group DDC-3427 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-3427')] public function testAcceptsEntityManagerInterfaceInstances(): void { $classMetadataFactory = new ClassMetadataFactory(); @@ -505,15 +428,14 @@ public function testAcceptsEntityManagerInterfaceInstances(): void // not really the cleanest way to check it, but we won't add a getter to the CMF just for the sake of testing. $class = new ReflectionClass(ClassMetadataFactory::class); $property = $class->getProperty('em'); - $property->setAccessible(true); self::assertSame($entityManager, $property->getValue($classMetadataFactory)); } - /** @group DDC-4006 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-4006')] public function testInheritsIdGeneratorMappingFromEmbeddable(): void { $cmf = new ClassMetadataFactory(); - $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/DDC4006/']); + $driver = $this->createAttributeDriver([__DIR__ . '/../../Models/DDC4006/']); $em = $this->createEntityManager($driver); $cmf->setEntityManager($em); @@ -528,7 +450,7 @@ public function testInvalidSubClassCase(): void $this->expectExceptionMessage('Entity class \'Doctrine\Tests\ORM\Mapping\cube\' used in the discriminator map of class \'Doctrine\Tests\ORM\Mapping\Shape\' does not exist.'); $cmf = new ClassMetadataFactory(); - $driver = $this->createAnnotationDriver([__DIR__]); + $driver = $this->createAttributeDriver([__DIR__]); $em = $this->createEntityManager($driver); $cmf->setEntityManager($em); @@ -536,24 +458,20 @@ public function testInvalidSubClassCase(): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"cube" = cube::class}) - * @DiscriminatorColumn(name="discr", length=32, type="string") - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['cube' => cube::class])] +#[DiscriminatorColumn(name: 'discr', length: 32, type: 'string')] abstract class Shape { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="AUTO") - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** @Entity */ +#[Entity] final class Cube extends Shape { } @@ -562,19 +480,14 @@ final class Cube extends Shape class ClassMetadataFactoryTestSubject extends ClassMetadataFactory { /** @var array, ClassMetadata> */ - private $mockMetadata = []; - - /** @var list> */ - private $requestedClasses = []; + private array $mockMetadata = []; - /** @param class-string $className */ - protected function newClassMetadataInstance($className): ClassMetadata + protected function newClassMetadataInstance(string $className): ClassMetadata { - $this->requestedClasses[] = $className; if (! isset($this->mockMetadata[$className])) { throw new InvalidArgumentException(sprintf( 'No mock metadata found for class %s.', - $className + $className, )); } @@ -586,21 +499,13 @@ public function setMetadataForClass(string $className, ClassMetadata $metadata): { $this->mockMetadata[$className] = $metadata; } - - /** @return list> */ - public function getRequestedClasses(): array - { - return $this->requestedClasses; - } } class TestEntity1 { - /** @var int */ - private $id; + private int $id; - /** @var string */ - private $name; + private string $name; /** @var mixed */ private $other; @@ -614,10 +519,7 @@ class TestEntity1 class CustomIdGenerator extends AbstractIdGenerator { - /** - * {@inheritDoc} - */ - public function generateId(EntityManagerInterface $em, $entity): string + public function generateId(EntityManagerInterface $em, object|null $entity): string { return 'foo'; } diff --git a/tests/Tests/ORM/Mapping/ClassMetadataLoadEventTest.php b/tests/Tests/ORM/Mapping/ClassMetadataLoadEventTest.php index ae25cdaddf2..294ac3d53b7 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataLoadEventTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataLoadEventTest.php @@ -12,10 +12,12 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; +use ReflectionProperty; class ClassMetadataLoadEventTest extends OrmTestCase { - /** @group DDC-1610 */ + #[Group('DDC-1610')] public function testEvent(): void { $em = $this->getTestEntityManager(); @@ -25,7 +27,7 @@ public function testEvent(): void $classMetadata = $metadataFactory->getMetadataFor(LoadEventTestEntity::class); self::assertTrue($classMetadata->hasField('about')); self::assertArrayHasKey('about', $classMetadata->reflFields); - self::assertInstanceOf('ReflectionProperty', $classMetadata->reflFields['about']); + self::assertInstanceOf(ReflectionProperty::class, $classMetadata->reflFields['about']); } public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void @@ -40,25 +42,17 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void } } -/** - * @Entity - * @Table(name="load_event_test_entity") - */ +#[Table(name: 'load_event_test_entity')] +#[Entity] class LoadEventTestEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; - /** - * @var string - * @Column(type="string", length=255) - */ - private $name; + #[Column(type: 'string', length: 255)] + private string $name; /** @var mixed */ private $about; diff --git a/tests/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Tests/ORM/Mapping/ClassMetadataTest.php index fcd4cffc35f..52e5853c8a1 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataTest.php @@ -13,18 +13,27 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\DefaultNamingStrategy; use Doctrine\ORM\Mapping\DefaultTypedFieldMapper; +use Doctrine\ORM\Mapping\DiscriminatorColumnMapping; use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\Mapping\JoinTableMapping; use Doctrine\ORM\Mapping\MappedSuperclass; use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\ORM\Mapping\UnderscoreNamingStrategy; use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Persistence\Mapping\StaticReflectionService; use Doctrine\Tests\DbalTypes\CustomIdObject; use Doctrine\Tests\DbalTypes\CustomIdObjectType; use Doctrine\Tests\DbalTypes\CustomIntType; -use Doctrine\Tests\Models\CMS; +use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\CMS\One; +use Doctrine\Tests\Models\CMS\Three; +use Doctrine\Tests\Models\CMS\Two; +use Doctrine\Tests\Models\CMS\UserRepository; use Doctrine\Tests\Models\Company\CompanyContract; +use Doctrine\Tests\Models\Company\CompanyContractListener; use Doctrine\Tests\Models\Customer\CustomerType; use Doctrine\Tests\Models\CustomType\CustomTypeParent; use Doctrine\Tests\Models\DDC117\DDC117Article; @@ -35,13 +44,20 @@ use Doctrine\Tests\Models\DirectoryTree\AbstractContentItem; use Doctrine\Tests\Models\DirectoryTree\Directory; use Doctrine\Tests\Models\Routing\RoutingLeg; -use Doctrine\Tests\Models\TypedProperties; +use Doctrine\Tests\Models\TypedProperties\Contact; +use Doctrine\Tests\Models\TypedProperties\UserTyped; +use Doctrine\Tests\Models\TypedProperties\UserTypedWithCustomTypedField; use Doctrine\Tests\ORM\Mapping\TypedFieldMapper\CustomIntAsStringTypedFieldMapper; use Doctrine\Tests\OrmTestCase; use DoctrineGlobalArticle; +use LogicException; +use PHPUnit\Framework\Attributes\Group as TestGroup; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; use ReflectionClass; +use stdClass; use function assert; +use function class_exists; use function count; use function serialize; use function str_contains; @@ -51,7 +67,6 @@ use function unserialize; use const CASE_UPPER; -use const PHP_VERSION_ID; require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php'; @@ -61,14 +76,14 @@ class ClassMetadataTest extends OrmTestCase public function testClassMetadataInstanceSerialization(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); // Test initial state self::assertTrue(count($cm->getReflectionProperties()) === 0); - self::assertInstanceOf('ReflectionClass', $cm->reflClass); - self::assertEquals(CMS\CmsUser::class, $cm->name); - self::assertEquals(CMS\CmsUser::class, $cm->rootEntityName); + self::assertInstanceOf(ReflectionClass::class, $cm->reflClass); + self::assertEquals(CmsUser::class, $cm->name); + self::assertEquals(CmsUser::class, $cm->rootEntityName); self::assertEquals([], $cm->subClasses); self::assertEquals([], $cm->parentClasses); self::assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType); @@ -82,7 +97,6 @@ public function testClassMetadataInstanceSerialization(): void $cm->mapOneToOne(['fieldName' => 'phonenumbers', 'targetEntity' => 'CmsAddress', 'mappedBy' => 'foo']); $cm->markReadOnly(); $cm->mapField(['fieldName' => 'status', 'notInsertable' => true, 'generated' => ClassMetadata::GENERATED_ALWAYS]); - $cm->addNamedQuery(['name' => 'dql', 'query' => 'foo']); self::assertTrue($cm->requiresFetchAfterChange); self::assertEquals(1, count($cm->associationMappings)); @@ -95,26 +109,29 @@ public function testClassMetadataInstanceSerialization(): void self::assertTrue(count($cm->getReflectionProperties()) > 0); self::assertEquals('Doctrine\Tests\Models\CMS', $cm->namespace); self::assertInstanceOf(ReflectionClass::class, $cm->reflClass); - self::assertEquals(CMS\CmsUser::class, $cm->name); + self::assertEquals(CmsUser::class, $cm->name); self::assertEquals('UserParent', $cm->rootEntityName); - self::assertEquals([CMS\One::class, CMS\Two::class, CMS\Three::class], $cm->subClasses); + self::assertEquals([One::class, Two::class, Three::class], $cm->subClasses); self::assertEquals(['UserParent'], $cm->parentClasses); - self::assertEquals(CMS\UserRepository::class, $cm->customRepositoryClassName); - self::assertEquals(['name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'], $cm->discriminatorColumn); - self::assertTrue($cm->associationMappings['phonenumbers']['type'] === ClassMetadata::ONE_TO_ONE); + self::assertEquals(UserRepository::class, $cm->customRepositoryClassName); + self::assertEquals(DiscriminatorColumnMapping::fromMappingArray([ + 'name' => 'disc', + 'type' => 'integer', + 'fieldName' => 'disc', + ]), $cm->discriminatorColumn); + self::assertTrue($cm->associationMappings['phonenumbers']->isOneToOne()); self::assertEquals(1, count($cm->associationMappings)); $oneOneMapping = $cm->getAssociationMapping('phonenumbers'); - self::assertTrue($oneOneMapping['fetch'] === ClassMetadata::FETCH_LAZY); - self::assertEquals('phonenumbers', $oneOneMapping['fieldName']); - self::assertEquals(CMS\CmsAddress::class, $oneOneMapping['targetEntity']); + self::assertTrue($oneOneMapping->fetch === ClassMetadata::FETCH_LAZY); + self::assertEquals('phonenumbers', $oneOneMapping->fieldName); + self::assertEquals(CmsAddress::class, $oneOneMapping->targetEntity); self::assertTrue($cm->isReadOnly); - self::assertEquals(['dql' => ['name' => 'dql', 'query' => 'foo', 'dql' => 'foo']], $cm->namedQueries); self::assertTrue($cm->requiresFetchAfterChange); } public function testFieldIsNullable(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); // Explicit Nullable @@ -132,30 +149,22 @@ public function testFieldIsNullable(): void public function testFieldIsNullableByType(): void { - if (PHP_VERSION_ID < 70400) { - self::markTestSkipped('requies PHP 7.4'); - } - - $cm = new ClassMetadata(TypedProperties\UserTyped::class); + $cm = new ClassMetadata(UserTyped::class); $cm->initializeReflection(new RuntimeReflectionService()); - $cm->mapOneToOne(['fieldName' => 'email', 'joinColumns' => [[]]]); - self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('email')['targetEntity']); + $cm->mapOneToOne(['fieldName' => 'email', 'joinColumns' => [['name' => 'email_id', 'referencedColumnName' => 'id']]]); + self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('email')->targetEntity); $cm->mapManyToOne(['fieldName' => 'mainEmail']); - self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('mainEmail')['targetEntity']); + self::assertEquals(CmsEmail::class, $cm->getAssociationMapping('mainEmail')->targetEntity); $cm->mapEmbedded(['fieldName' => 'contact']); - self::assertEquals(TypedProperties\Contact::class, $cm->embeddedClasses['contact']['class']); + self::assertEquals(Contact::class, $cm->embeddedClasses['contact']->class); } public function testFieldTypeFromReflection(): void { - if (PHP_VERSION_ID < 70400) { - self::markTestSkipped('requies PHP 7.4'); - } - - $cm = new ClassMetadata(TypedProperties\UserTyped::class); + $cm = new ClassMetadata(UserTyped::class); $cm->initializeReflection(new RuntimeReflectionService()); // Integer @@ -191,21 +200,18 @@ public function testFieldTypeFromReflection(): void self::assertEquals('float', $cm->getTypeOfField('float')); } - /** - * @group GH10313 - * @requires PHP 7.4 - */ + #[TestGroup('GH10313')] public function testFieldTypeFromReflectionDefaultTypedFieldMapper(): void { $cm = new ClassMetadata( - TypedProperties\UserTypedWithCustomTypedField::class, + UserTypedWithCustomTypedField::class, null, new DefaultTypedFieldMapper( [ CustomIdObject::class => CustomIdObjectType::class, 'int' => CustomIntType::class, - ] - ) + ], + ), ); $cm->initializeReflection(new RuntimeReflectionService()); @@ -215,19 +221,16 @@ public function testFieldTypeFromReflectionDefaultTypedFieldMapper(): void self::assertEquals(CustomIntType::class, $cm->getTypeOfField('customIntTypedField')); } - /** - * @group GH10313 - * @requires PHP 7.4 - */ + #[TestGroup('GH10313')] public function testFieldTypeFromReflectionChainTypedFieldMapper(): void { $cm = new ClassMetadata( - TypedProperties\UserTyped::class, + UserTyped::class, null, new ChainTypedFieldMapper( new CustomIntAsStringTypedFieldMapper(), - new DefaultTypedFieldMapper() - ) + new DefaultTypedFieldMapper(), + ), ); $cm->initializeReflection(new RuntimeReflectionService()); @@ -237,7 +240,7 @@ public function testFieldTypeFromReflectionChainTypedFieldMapper(): void self::assertEquals(Types::STRING, $cm->getTypeOfField('username')); } - /** @group DDC-115 */ + #[TestGroup('DDC-115')] public function testMapAssociationInGlobalNamespace(): void { require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php'; @@ -253,53 +256,53 @@ public function testMapAssociationInGlobalNamespace(): void 'joinColumns' => [['name' => 'bar_id', 'referencedColumnName' => 'id']], 'inverseJoinColumns' => [['name' => 'baz_id', 'referencedColumnName' => 'id']], ], - ] + ], ); - self::assertEquals('DoctrineGlobalUser', $cm->associationMappings['author']['targetEntity']); + self::assertEquals('DoctrineGlobalUser', $cm->associationMappings['author']->targetEntity); } public function testMapManyToManyJoinTableDefaults(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapManyToMany( [ 'fieldName' => 'groups', 'targetEntity' => 'CmsGroup', - ] + ], ); $assoc = $cm->associationMappings['groups']; self::assertEquals( - [ + JoinTableMapping::fromMappingArray([ 'name' => 'cmsuser_cmsgroup', 'joinColumns' => [['name' => 'cmsuser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']], 'inverseJoinColumns' => [['name' => 'cmsgroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']], - ], - $assoc['joinTable'] + ]), + $assoc->joinTable, ); - self::assertTrue($assoc['isOnDeleteCascade']); + self::assertTrue($assoc->isOnDeleteCascade); } public function testSerializeManyToManyJoinTableCascade(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapManyToMany( [ 'fieldName' => 'groups', 'targetEntity' => 'CmsGroup', - ] + ], ); $assoc = $cm->associationMappings['groups']; $assoc = unserialize(serialize($assoc)); - self::assertTrue($assoc['isOnDeleteCascade']); + self::assertTrue($assoc->isOnDeleteCascade); } - /** @group DDC-115 */ + #[TestGroup('DDC-115')] public function testSetDiscriminatorMapInGlobalNamespace(): void { require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php'; @@ -312,7 +315,7 @@ public function testSetDiscriminatorMapInGlobalNamespace(): void self::assertEquals('DoctrineGlobalUser', $cm->discriminatorMap['foo']); } - /** @group DDC-115 */ + #[TestGroup('DDC-115')] public function testSetSubClassesInGlobalNamespace(): void { require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php'; @@ -324,23 +327,40 @@ public function testSetSubClassesInGlobalNamespace(): void self::assertEquals('DoctrineGlobalArticle', $cm->subClasses[0]); } - /** @group DDC-268 */ + #[TestGroup('DDC-268')] public function testSetInvalidVersionMappingThrowsException(): void { $field = []; $field['fieldName'] = 'foo'; $field['type'] = 'string'; - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $this->expectException(MappingException::class); $cm->setVersionMapping($field); } + public function testItThrowsOnJoinColumnForInverseOneToOne(): void + { + $cm = new ClassMetadata(CmsUser::class); + $cm->initializeReflection(new RuntimeReflectionService()); + + $this->expectException(MappingException::class); + $this->expectExceptionMessage( + 'Doctrine\Tests\Models\CMS\CmsUser#address is a OneToOne inverse side, which does not allow join columns.', + ); + $cm->mapOneToOne([ + 'fieldName' => 'address', + 'targetEntity' => CmsAddress::class, + 'mappedBy' => 'user', + 'joinColumns' => ['non', 'empty', 'list'], + ]); + } + public function testGetSingleIdentifierFieldNameMultipleIdentifierEntityThrowsException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->isIdentifierComposite = true; @@ -359,11 +379,21 @@ public function testGetSingleIdentifierFieldNameNoIdEntityThrowsException(): voi public function testDuplicateAssociationMappingException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); - $a1 = ['fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo']; - $a2 = ['fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo']; + $a1 = OneToManyAssociationMapping::fromMappingArray([ + 'fieldName' => 'foo', + 'sourceEntity' => stdClass::class, + 'targetEntity' => stdClass::class, + 'mappedBy' => 'foo', + ]); + $a2 = OneToManyAssociationMapping::fromMappingArray([ + 'fieldName' => 'foo', + 'sourceEntity' => stdClass::class, + 'targetEntity' => stdClass::class, + 'mappedBy' => 'foo', + ]); $cm->addInheritedAssociationMapping($a1); $this->expectException(MappingException::class); @@ -372,7 +402,7 @@ public function testDuplicateAssociationMappingException(): void public function testDuplicateColumnNameThrowsMappingException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']); @@ -383,7 +413,7 @@ public function testDuplicateColumnNameThrowsMappingException(): void public function testDuplicateColumnNameDiscriminatorColumnThrowsMappingException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']); @@ -394,7 +424,7 @@ public function testDuplicateColumnNameDiscriminatorColumnThrowsMappingException public function testDuplicateColumnNameDiscriminatorColumn2ThrowsMappingException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->setDiscriminatorColumn(['name' => 'name']); @@ -405,7 +435,7 @@ public function testDuplicateColumnNameDiscriminatorColumn2ThrowsMappingExceptio public function testDuplicateFieldAndAssociationMapping1ThrowsException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']); @@ -416,7 +446,7 @@ public function testDuplicateFieldAndAssociationMapping1ThrowsException(): void public function testDuplicateFieldAndAssociationMapping2ThrowsException(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapOneToOne(['fieldName' => 'name', 'targetEntity' => 'CmsUser']); @@ -425,10 +455,10 @@ public function testDuplicateFieldAndAssociationMapping2ThrowsException(): void $cm->mapField(['fieldName' => 'name', 'columnName' => 'name']); } - /** @group DDC-1224 */ + #[TestGroup('DDC-1224')] public function testGetTemporaryTableNameSchema(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->setTableName('foo.bar'); @@ -438,7 +468,7 @@ public function testGetTemporaryTableNameSchema(): void public function testDefaultTableName(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); // When table's name is not given @@ -448,7 +478,7 @@ public function testDefaultTableName(): void self::assertEquals('CmsUser', $cm->getTableName()); self::assertEquals('CmsUser', $cm->table['name']); - $cm = new ClassMetadata(CMS\CmsAddress::class); + $cm = new ClassMetadata(CmsAddress::class); $cm->initializeReflection(new RuntimeReflectionService()); // When joinTable's name is not given $cm->mapManyToMany( @@ -460,14 +490,14 @@ public function testDefaultTableName(): void 'joinColumns' => [['referencedColumnName' => 'id']], 'inverseJoinColumns' => [['referencedColumnName' => 'id']], ], - ] + ], ); - self::assertEquals('cmsaddress_cmsuser', $cm->associationMappings['user']['joinTable']['name']); + self::assertEquals('cmsaddress_cmsuser', $cm->associationMappings['user']->joinTable->name); } public function testDefaultJoinColumnName(): void { - $cm = new ClassMetadata(CMS\CmsAddress::class); + $cm = new ClassMetadata(CmsAddress::class); $cm->initializeReflection(new RuntimeReflectionService()); // this is really dirty, but it's the simplest way to test whether @@ -477,11 +507,11 @@ public function testDefaultJoinColumnName(): void 'fieldName' => 'user', 'targetEntity' => 'CmsUser', 'joinColumns' => [['referencedColumnName' => 'id']], - ] + ], ); - self::assertEquals('user_id', $cm->associationMappings['user']['joinColumns'][0]['name']); + self::assertEquals('user_id', $cm->associationMappings['user']->joinColumns[0]->name); - $cm = new ClassMetadata(CMS\CmsAddress::class); + $cm = new ClassMetadata(CmsAddress::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapManyToMany( [ @@ -493,61 +523,61 @@ public function testDefaultJoinColumnName(): void 'joinColumns' => [['referencedColumnName' => 'id']], 'inverseJoinColumns' => [['referencedColumnName' => 'id']], ], - ] + ], ); - self::assertEquals('cmsaddress_id', $cm->associationMappings['user']['joinTable']['joinColumns'][0]['name']); - self::assertEquals('cmsuser_id', $cm->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['name']); + self::assertEquals('cmsaddress_id', $cm->associationMappings['user']->joinTable->joinColumns[0]->name); + self::assertEquals('cmsuser_id', $cm->associationMappings['user']->joinTable->inverseJoinColumns[0]->name); } - /** @group DDC-559 */ + #[TestGroup('DDC-559')] public function testUnderscoreNamingStrategyDefaults(): void { $namingStrategy = new UnderscoreNamingStrategy(CASE_UPPER); - $oneToOneMetadata = new ClassMetadata(CMS\CmsAddress::class, $namingStrategy); - $manyToManyMetadata = new ClassMetadata(CMS\CmsAddress::class, $namingStrategy); + $oneToOneMetadata = new ClassMetadata(CmsAddress::class, $namingStrategy); + $manyToManyMetadata = new ClassMetadata(CmsAddress::class, $namingStrategy); $oneToOneMetadata->mapOneToOne( [ 'fieldName' => 'user', 'targetEntity' => 'CmsUser', - ] + ], ); $manyToManyMetadata->mapManyToMany( [ 'fieldName' => 'user', 'targetEntity' => 'CmsUser', - ] + ], ); - self::assertEquals(['USER_ID' => 'ID'], $oneToOneMetadata->associationMappings['user']['sourceToTargetKeyColumns']); - self::assertEquals(['USER_ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']['joinColumnFieldNames']); - self::assertEquals(['ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']['targetToSourceKeyColumns']); + self::assertEquals(['USER_ID' => 'ID'], $oneToOneMetadata->associationMappings['user']->sourceToTargetKeyColumns); + self::assertEquals(['USER_ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']->joinColumnFieldNames); + self::assertEquals(['ID' => 'USER_ID'], $oneToOneMetadata->associationMappings['user']->targetToSourceKeyColumns); - self::assertEquals('USER_ID', $oneToOneMetadata->associationMappings['user']['joinColumns'][0]['name']); - self::assertEquals('ID', $oneToOneMetadata->associationMappings['user']['joinColumns'][0]['referencedColumnName']); + self::assertEquals('USER_ID', $oneToOneMetadata->associationMappings['user']->joinColumns[0]->name); + self::assertEquals('ID', $oneToOneMetadata->associationMappings['user']->joinColumns[0]->referencedColumnName); - self::assertEquals('CMS_ADDRESS_CMS_USER', $manyToManyMetadata->associationMappings['user']['joinTable']['name']); + self::assertEquals('CMS_ADDRESS_CMS_USER', $manyToManyMetadata->associationMappings['user']->joinTable->name); - self::assertEquals(['CMS_ADDRESS_ID', 'CMS_USER_ID'], $manyToManyMetadata->associationMappings['user']['joinTableColumns']); - self::assertEquals(['CMS_ADDRESS_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']['relationToSourceKeyColumns']); - self::assertEquals(['CMS_USER_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']['relationToTargetKeyColumns']); + self::assertEquals(['CMS_ADDRESS_ID', 'CMS_USER_ID'], $manyToManyMetadata->associationMappings['user']->joinTableColumns); + self::assertEquals(['CMS_ADDRESS_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']->relationToSourceKeyColumns); + self::assertEquals(['CMS_USER_ID' => 'ID'], $manyToManyMetadata->associationMappings['user']->relationToTargetKeyColumns); - self::assertEquals('CMS_ADDRESS_ID', $manyToManyMetadata->associationMappings['user']['joinTable']['joinColumns'][0]['name']); - self::assertEquals('CMS_USER_ID', $manyToManyMetadata->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['name']); + self::assertEquals('CMS_ADDRESS_ID', $manyToManyMetadata->associationMappings['user']->joinTable->joinColumns[0]->name); + self::assertEquals('CMS_USER_ID', $manyToManyMetadata->associationMappings['user']->joinTable->inverseJoinColumns[0]->name); - self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']['joinTable']['joinColumns'][0]['referencedColumnName']); - self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); + self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']->joinTable->joinColumns[0]->referencedColumnName); + self::assertEquals('ID', $manyToManyMetadata->associationMappings['user']->joinTable->inverseJoinColumns[0]->referencedColumnName); $cm = new ClassMetadata('DoctrineGlobalArticle', $namingStrategy); - $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CMS\CmsUser::class]); - self::assertEquals('DOCTRINE_GLOBAL_ARTICLE_CMS_USER', $cm->associationMappings['author']['joinTable']['name']); + $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CmsUser::class]); + self::assertEquals('DOCTRINE_GLOBAL_ARTICLE_CMS_USER', $cm->associationMappings['author']->joinTable->name); } - /** @group DDC-886 */ + #[TestGroup('DDC-886')] public function testSetMultipleIdentifierSetsComposite(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapField(['fieldName' => 'name']); @@ -557,30 +587,30 @@ public function testSetMultipleIdentifierSetsComposite(): void self::assertTrue($cm->isIdentifierComposite); } - /** @group DDC-944 */ + #[TestGroup('DDC-944')] public function testMappingNotFound(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $this->expectException(MappingException::class); - $this->expectExceptionMessage("No mapping found for field 'foo' on class '" . CMS\CmsUser::class . "'."); + $this->expectExceptionMessage("No mapping found for field 'foo' on class '" . CmsUser::class . "'."); $cm->getFieldMapping('foo'); } - /** @group DDC-961 */ + #[TestGroup('DDC-961')] public function testJoinTableMappingDefaults(): void { $cm = new ClassMetadata('DoctrineGlobalArticle'); $cm->initializeReflection(new RuntimeReflectionService()); - $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CMS\CmsUser::class]); + $cm->mapManyToMany(['fieldName' => 'author', 'targetEntity' => CmsUser::class]); - self::assertEquals('doctrineglobalarticle_cmsuser', $cm->associationMappings['author']['joinTable']['name']); + self::assertEquals('doctrineglobalarticle_cmsuser', $cm->associationMappings['author']->joinTable->name); } - /** @group DDC-117 */ + #[TestGroup('DDC-117')] public function testMapIdentifierAssociation(): void { $cm = new ClassMetadata(DDC117ArticleDetails::class); @@ -592,14 +622,14 @@ public function testMapIdentifierAssociation(): void 'id' => true, 'targetEntity' => DDC117Article::class, 'joinColumns' => [], - ] + ], ); self::assertTrue($cm->containsForeignIdentifier, "Identifier Association should set 'containsForeignIdentifier' boolean flag."); self::assertEquals(['article'], $cm->identifier); } - /** @group DDC-117 */ + #[TestGroup('DDC-117')] public function testOrphanRemovalIdentifierAssociation(): void { $cm = new ClassMetadata(DDC117ArticleDetails::class); @@ -615,11 +645,11 @@ public function testOrphanRemovalIdentifierAssociation(): void 'targetEntity' => DDC117Article::class, 'orphanRemoval' => true, 'joinColumns' => [], - ] + ], ); } - /** @group DDC-117 */ + #[TestGroup('DDC-117')] public function testInverseIdentifierAssociation(): void { $cm = new ClassMetadata(DDC117ArticleDetails::class); @@ -635,11 +665,11 @@ public function testInverseIdentifierAssociation(): void 'mappedBy' => 'details', // INVERSE! 'targetEntity' => DDC117Article::class, 'joinColumns' => [], - ] + ], ); } - /** @group DDC-117 */ + #[TestGroup('DDC-117')] public function testIdentifierAssociationManyToMany(): void { $cm = new ClassMetadata(DDC117ArticleDetails::class); @@ -654,244 +684,23 @@ public function testIdentifierAssociationManyToMany(): void 'id' => true, 'targetEntity' => DDC117Article::class, 'joinColumns' => [], - ] + ], ); } - /** @group DDC-996 */ + #[TestGroup('DDC-996')] public function testEmptyFieldNameThrowsException(): void { $this->expectException(MappingException::class); - $this->expectExceptionMessage("The field or association mapping misses the 'fieldName' attribute in entity '" . CMS\CmsUser::class . "'."); + $this->expectExceptionMessage("The field or association mapping misses the 'fieldName' attribute in entity '" . CmsUser::class . "'."); - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapField(['fieldName' => '']); } - public function testRetrievalOfNamedQueries(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - self::assertCount(0, $cm->getNamedQueries()); - - $cm->addNamedQuery( - [ - 'name' => 'userById', - 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1', - ] - ); - - self::assertCount(1, $cm->getNamedQueries()); - } - - /** @group DDC-1663 */ - public function testRetrievalOfResultSetMappings(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - self::assertCount(0, $cm->getSqlResultSetMappings()); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => CMS\CmsUser::class, - ], - ], - ] - ); - - self::assertCount(1, $cm->getSqlResultSetMappings()); - } - - public function testExistanceOfNamedQuery(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedQuery( - [ - 'name' => 'all', - 'query' => 'SELECT u FROM __CLASS__ u', - ] - ); - - self::assertTrue($cm->hasNamedQuery('all')); - self::assertFalse($cm->hasNamedQuery('userById')); - } - - /** @group DDC-1663 */ - public function testRetrieveOfNamedNativeQuery(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT * FROM cms_users', - 'resultSetMapping' => 'result-mapping-name', - 'resultClass' => CMS\CmsUser::class, - ] - ); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-by-id', - 'query' => 'SELECT * FROM cms_users WHERE id = ?', - 'resultClass' => '__CLASS__', - 'resultSetMapping' => 'result-mapping-name', - ] - ); - - $mapping = $cm->getNamedNativeQuery('find-all'); - self::assertEquals('SELECT * FROM cms_users', $mapping['query']); - self::assertEquals('result-mapping-name', $mapping['resultSetMapping']); - self::assertEquals(CMS\CmsUser::class, $mapping['resultClass']); - - $mapping = $cm->getNamedNativeQuery('find-by-id'); - self::assertEquals('SELECT * FROM cms_users WHERE id = ?', $mapping['query']); - self::assertEquals('result-mapping-name', $mapping['resultSetMapping']); - self::assertEquals(CMS\CmsUser::class, $mapping['resultClass']); - } - - /** @group DDC-1663 */ - public function testRetrieveOfSqlResultSetMapping(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => '__CLASS__', - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - ], - ], - [ - 'entityClass' => CMS\CmsEmail::class, - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'email', - 'column' => 'email', - ], - ], - ], - ], - 'columns' => [ - ['name' => 'scalarColumn'], - ], - ] - ); - - $mapping = $cm->getSqlResultSetMapping('find-all'); - - self::assertEquals(CMS\CmsUser::class, $mapping['entities'][0]['entityClass']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'name'], $mapping['entities'][0]['fields'][1]); - - self::assertEquals(CMS\CmsEmail::class, $mapping['entities'][1]['entityClass']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][1]['fields'][0]); - self::assertEquals(['name' => 'email', 'column' => 'email'], $mapping['entities'][1]['fields'][1]); - - self::assertEquals('scalarColumn', $mapping['columns'][0]['name']); - } - - /** @group DDC-1663 */ - public function testExistanceOfSqlResultSetMapping(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => CMS\CmsUser::class, - ], - ], - ] - ); - - self::assertTrue($cm->hasSqlResultSetMapping('find-all')); - self::assertFalse($cm->hasSqlResultSetMapping('find-by-id')); - } - - /** @group DDC-1663 */ - public function testExistanceOfNamedNativeQuery(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT * FROM cms_users', - 'resultClass' => CMS\CmsUser::class, - 'resultSetMapping' => 'result-mapping-name', - ] - ); - - self::assertTrue($cm->hasNamedNativeQuery('find-all')); - self::assertFalse($cm->hasNamedNativeQuery('find-by-id')); - } - - public function testRetrieveOfNamedQuery(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedQuery( - [ - 'name' => 'userById', - 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1', - ] - ); - - self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', $cm->getNamedQuery('userById')); - } - - /** @group DDC-1663 */ - public function testRetrievalOfNamedNativeQueries(): void - { - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - self::assertCount(0, $cm->getNamedNativeQueries()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT * FROM cms_users', - 'resultClass' => CMS\CmsUser::class, - 'resultSetMapping' => 'result-mapping-name', - ] - ); - - self::assertCount(1, $cm->getNamedNativeQueries()); - } - - /** @group DDC-2451 */ + #[TestGroup('DDC-2451')] public function testSerializeEntityListeners(): void { $metadata = new ClassMetadata(CompanyContract::class); @@ -906,187 +715,58 @@ public function testSerializeEntityListeners(): void self::assertEquals($metadata->entityListeners, $unserialize->entityListeners); } - public function testNamingCollisionNamedQueryShouldThrowException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Query named "userById" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedQuery( - [ - 'name' => 'userById', - 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1', - ] - ); - - $cm->addNamedQuery( - [ - 'name' => 'userById', - 'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1', - ] - ); - } - - /** @group DDC-1663 */ - public function testNamingCollisionNamedNativeQueryShouldThrowException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Query named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT * FROM cms_users', - 'resultClass' => CMS\CmsUser::class, - 'resultSetMapping' => 'result-mapping-name', - ] - ); - - $cm->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT * FROM cms_users', - 'resultClass' => CMS\CmsUser::class, - 'resultSetMapping' => 'result-mapping-name', - ] - ); - } - - /** @group DDC-1663 */ - public function testNamingCollisionSqlResultSetMappingShouldThrowException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Result set mapping named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => CMS\CmsUser::class, - ], - ], - ] - ); - - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'entityClass' => CMS\CmsUser::class, - ], - ], - ] - ); - } - - /** @group DDC-1068 */ + #[TestGroup('DDC-1068')] public function testClassCaseSensitivity(): void { - $user = new CMS\CmsUser(); - $cm = new ClassMetadata(strtoupper(CMS\CmsUser::class)); + $user = new CmsUser(); + $cm = new ClassMetadata(strtoupper(CmsUser::class)); $cm->initializeReflection(new RuntimeReflectionService()); - self::assertEquals(CMS\CmsUser::class, $cm->name); + self::assertEquals(CmsUser::class, $cm->name); } - /** @group DDC-659 */ + #[TestGroup('DDC-659')] public function testLifecycleCallbackNotFound(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->addLifecycleCallback('notfound', 'postLoad'); $this->expectException(MappingException::class); - $this->expectExceptionMessage("Entity '" . CMS\CmsUser::class . "' has no method 'notfound' to be registered as lifecycle callback."); + $this->expectExceptionMessage("Entity '" . CmsUser::class . "' has no method 'notfound' to be registered as lifecycle callback."); $cm->validateLifecycleCallbacks(new RuntimeReflectionService()); } - /** @group ImproveErrorMessages */ + #[TestGroup('ImproveErrorMessages')] public function testTargetEntityNotFound(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->mapManyToOne(['fieldName' => 'address', 'targetEntity' => 'UnknownClass']); $this->expectException(MappingException::class); - $this->expectExceptionMessage("The target-entity Doctrine\\Tests\\Models\\CMS\\UnknownClass cannot be found in '" . CMS\CmsUser::class . "#address'."); + $this->expectExceptionMessage("The target-entity Doctrine\\Tests\\Models\\CMS\\UnknownClass cannot be found in '" . CmsUser::class . "#address'."); $cm->validateAssociations(); } - /** @group DDC-1663 */ - public function testNameIsMandatoryForNamedQueryMappingException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Query name on entity class \'Doctrine\Tests\Models\CMS\CmsUser\' is not defined.'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - $cm->addNamedQuery( - ['query' => 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'] - ); - } - - /** @group DDC-1663 */ - public function testNameIsMandatoryForNameNativeQueryMappingException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Query name on entity class \'Doctrine\Tests\Models\CMS\CmsUser\' is not defined.'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - $cm->addNamedQuery( - [ - 'query' => 'SELECT * FROM cms_users', - 'resultClass' => CMS\CmsUser::class, - 'resultSetMapping' => 'result-mapping-name', - ] - ); - } - - /** @group DDC-1663 */ - public function testNameIsMandatoryForEntityNameSqlResultSetMappingException(): void - { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Result set mapping named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser requires a entity class name.'); - $cm = new ClassMetadata(CMS\CmsUser::class); - $cm->initializeReflection(new RuntimeReflectionService()); - $cm->addSqlResultSetMapping( - [ - 'name' => 'find-all', - 'entities' => [ - [ - 'fields' => [], - ], - ], - ] - ); - } - public function testNameIsMandatoryForDiscriminatorColumnsMappingException(): void { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('Discriminator column name on entity class \'Doctrine\Tests\Models\CMS\CmsUser\' is not defined.'); - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->setDiscriminatorColumn([]); } - /** - * @group DDC-984 - * @group DDC-559 - * @group DDC-1575 - */ + #[TestGroup('DDC-984')] + #[TestGroup('DDC-559')] + #[TestGroup('DDC-1575')] public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategy(): void { $namingStrategy = new MyNamespacedNamingStrategy(); - $addressMetadata = new ClassMetadata(CMS\CmsAddress::class, $namingStrategy); + $addressMetadata = new ClassMetadata(CmsAddress::class, $namingStrategy); $articleMetadata = new ClassMetadata(DoctrineGlobalArticle::class, $namingStrategy); $routingMetadata = new ClassMetadata(RoutingLeg::class, $namingStrategy); @@ -1098,29 +778,27 @@ public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategy(): void [ 'fieldName' => 'user', 'targetEntity' => 'CmsUser', - ] + ], ); $articleMetadata->mapManyToMany( [ 'fieldName' => 'author', - 'targetEntity' => CMS\CmsUser::class, - ] + 'targetEntity' => CmsUser::class, + ], ); self::assertEquals('routing_routingleg', $routingMetadata->table['name']); - self::assertEquals('cms_cmsaddress_cms_cmsuser', $addressMetadata->associationMappings['user']['joinTable']['name']); - self::assertEquals('doctrineglobalarticle_cms_cmsuser', $articleMetadata->associationMappings['author']['joinTable']['name']); + self::assertEquals('cms_cmsaddress_cms_cmsuser', $addressMetadata->associationMappings['user']->joinTable->name); + self::assertEquals('doctrineglobalarticle_cms_cmsuser', $articleMetadata->associationMappings['author']->joinTable->name); } - /** - * @group DDC-984 - * @group DDC-559 - */ + #[TestGroup('DDC-984')] + #[TestGroup('DDC-559')] public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategyPropertyToColumnName(): void { $namingStrategy = new MyPrefixNamingStrategy(); - $metadata = new ClassMetadata(CMS\CmsAddress::class, $namingStrategy); + $metadata = new ClassMetadata(CmsAddress::class, $namingStrategy); $metadata->initializeReflection(new RuntimeReflectionService()); @@ -1133,26 +811,22 @@ public function testFullyQualifiedClassNameShouldBeGivenToNamingStrategyProperty ]); } - /** @group DDC-1746 */ + #[TestGroup('DDC-1746')] public function testInvalidCascade(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $this->expectException(MappingException::class); - $this->expectExceptionMessage('You have specified invalid cascade options for ' . CMS\CmsUser::class . "::\$address: 'invalid'; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'"); + $this->expectExceptionMessage('You have specified invalid cascade options for ' . CmsUser::class . "::\$address: 'invalid'; available options: 'remove', 'persist', 'refresh', and 'detach'"); $cm->mapManyToOne(['fieldName' => 'address', 'targetEntity' => 'UnknownClass', 'cascade' => ['invalid']]); } - /** @group DDC-964 */ + #[TestGroup('DDC-964')] public function testInvalidPropertyAssociationOverrideNameException(): void { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('Invalid field override named \'invalidPropertyName\' for class \'Doctrine\Tests\Models\DDC964\DDC964Admin'); $cm = new ClassMetadata(DDC964Admin::class); $cm->initializeReflection(new RuntimeReflectionService()); @@ -1161,14 +835,10 @@ public function testInvalidPropertyAssociationOverrideNameException(): void $cm->setAssociationOverride('invalidPropertyName', []); } - /** @group DDC-964 */ + #[TestGroup('DDC-964')] public function testInvalidPropertyAttributeOverrideNameException(): void { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('Invalid field override named \'invalidPropertyName\' for class \'Doctrine\Tests\Models\DDC964\DDC964Guest\'.'); $cm = new ClassMetadata(DDC964Guest::class); $cm->initializeReflection(new RuntimeReflectionService()); @@ -1177,14 +847,10 @@ public function testInvalidPropertyAttributeOverrideNameException(): void $cm->setAttributeOverride('invalidPropertyName', []); } - /** @group DDC-964 */ + #[TestGroup('DDC-964')] public function testInvalidOverrideAttributeFieldTypeException(): void { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('The column type of attribute \'name\' on class \'Doctrine\Tests\Models\DDC964\DDC964Guest\' could not be changed.'); $cm = new ClassMetadata(DDC964Guest::class); $cm->initializeReflection(new RuntimeReflectionService()); @@ -1201,42 +867,50 @@ public function testAttributeOverrideKeepsDeclaringClass(): void $mapping = $cm->getFieldMapping('id'); - self::assertArrayHasKey('declared', $mapping); - self::assertSame(AbstractContentItem::class, $mapping['declared']); + self::assertSame(AbstractContentItem::class, $mapping->declared); } public function testAssociationOverrideKeepsDeclaringClass(): void { $cm = new ClassMetadata(Directory::class); $cm->mapManyToOne(['fieldName' => 'parentDirectory', 'targetEntity' => Directory::class, 'cascade' => ['remove'], 'declared' => Directory::class]); - $cm->setAssociationOverride('parentDirectory', ['cascade' => '']); + $cm->setAssociationOverride('parentDirectory', ['cascade' => ['remove']]); $mapping = $cm->getAssociationMapping('parentDirectory'); - self::assertArrayHasKey('declared', $mapping); - self::assertSame(Directory::class, $mapping['declared']); + self::assertSame(Directory::class, $mapping->declared); + } + + public function testAssociationOverrideCanOverrideCascade(): void + { + $cm = new ClassMetadata(Directory::class); + $cm->mapManyToOne(['fieldName' => 'parentDirectory', 'targetEntity' => Directory::class, 'cascade' => ['remove'], 'declared' => Directory::class]); + $cm->setAssociationOverride('parentDirectory', ['cascade' => ['all']]); + + $mapping = $cm->getAssociationMapping('parentDirectory'); + self::assertSame(['remove', 'persist', 'refresh', 'detach'], $mapping->cascade); } - /** @group DDC-1955 */ + #[TestGroup('DDC-1955')] public function testInvalidEntityListenerClassException(): void { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('Entity Listener "\InvalidClassName" declared on "Doctrine\Tests\Models\CMS\CmsUser" not found.'); - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->addEntityListener(Events::postLoad, '\InvalidClassName', 'postLoadHandler'); } - /** @group DDC-1955 */ + #[TestGroup('DDC-1955')] public function testInvalidEntityListenerMethodException(): void { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); - $this->expectExceptionMessage('Entity Listener "\Doctrine\Tests\Models\Company\CompanyContractListener" declared on "Doctrine\Tests\Models\CMS\CmsUser" has no method "invalidMethod".'); - $cm = new ClassMetadata(CMS\CmsUser::class); + $this->expectException(MappingException::class); + $this->expectExceptionMessage('Entity Listener "Doctrine\Tests\Models\Company\CompanyContractListener" declared on "Doctrine\Tests\Models\CMS\CmsUser" has no method "invalidMethod".'); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); - $cm->addEntityListener(Events::postLoad, '\Doctrine\Tests\Models\Company\CompanyContractListener', 'invalidMethod'); + $cm->addEntityListener(Events::postLoad, CompanyContractListener::class, 'invalidMethod'); } public function testManyToManySelfReferencingNamingStrategyDefaults(): void @@ -1247,50 +921,48 @@ public function testManyToManySelfReferencingNamingStrategyDefaults(): void [ 'fieldName' => 'friendsWithMe', 'targetEntity' => 'CustomTypeParent', - ] + ], ); self::assertEquals( - [ + JoinTableMapping::fromMappingArray([ 'name' => 'customtypeparent_customtypeparent', 'joinColumns' => [['name' => 'customtypeparent_source', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']], 'inverseJoinColumns' => [['name' => 'customtypeparent_target', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE']], - ], - $cm->associationMappings['friendsWithMe']['joinTable'] + ]), + $cm->associationMappings['friendsWithMe']->joinTable, ); - self::assertEquals(['customtypeparent_source', 'customtypeparent_target'], $cm->associationMappings['friendsWithMe']['joinTableColumns']); - self::assertEquals(['customtypeparent_source' => 'id'], $cm->associationMappings['friendsWithMe']['relationToSourceKeyColumns']); - self::assertEquals(['customtypeparent_target' => 'id'], $cm->associationMappings['friendsWithMe']['relationToTargetKeyColumns']); + self::assertEquals(['customtypeparent_source', 'customtypeparent_target'], $cm->associationMappings['friendsWithMe']->joinTableColumns); + self::assertEquals(['customtypeparent_source' => 'id'], $cm->associationMappings['friendsWithMe']->relationToSourceKeyColumns); + self::assertEquals(['customtypeparent_target' => 'id'], $cm->associationMappings['friendsWithMe']->relationToTargetKeyColumns); } - /** @group DDC-2608 */ + #[TestGroup('DDC-2608')] public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $this->expectException(MappingException::class); $cm->setSequenceGeneratorDefinition([]); } - /** - * @group DDC-2662 - * @group 6682 - */ + #[TestGroup('DDC-2662')] + #[TestGroup('6682')] public function testQuotedSequenceName(): void { - $cm = new ClassMetadata(CMS\CmsUser::class); + $cm = new ClassMetadata(CmsUser::class); $cm->initializeReflection(new RuntimeReflectionService()); $cm->setSequenceGeneratorDefinition(['sequenceName' => '`foo`']); self::assertSame( ['sequenceName' => 'foo', 'quoted' => true, 'allocationSize' => '1', 'initialValue' => '1'], - $cm->sequenceGeneratorDefinition + $cm->sequenceGeneratorDefinition, ); } - /** @group DDC-2700 */ + #[TestGroup('DDC-2700')] public function testIsIdentifierMappedSuperClass(): void { $class = new ClassMetadata(DDC2700MappedSuperClass::class); @@ -1298,7 +970,7 @@ public function testIsIdentifierMappedSuperClass(): void self::assertFalse($class->isIdentifier('foo')); } - /** @group DDC-3120 */ + #[TestGroup('DDC-3120')] public function testCanInstantiateInternalPhpClassSubclass(): void { $classMetadata = new ClassMetadata(MyArrayObjectEntity::class); @@ -1306,7 +978,7 @@ public function testCanInstantiateInternalPhpClassSubclass(): void self::assertInstanceOf(MyArrayObjectEntity::class, $classMetadata->newInstance()); } - /** @group DDC-3120 */ + #[TestGroup('DDC-3120')] public function testCanInstantiateInternalPhpClassSubclassFromUnserializedMetadata(): void { $classMetadata = unserialize(serialize(new ClassMetadata(MyArrayObjectEntity::class))); @@ -1319,6 +991,10 @@ public function testCanInstantiateInternalPhpClassSubclassFromUnserializedMetada public function testWakeupReflectionWithEmbeddableAndStaticReflectionService(): void { + if (! class_exists(StaticReflectionService::class)) { + self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version'); + } + $classMetadata = new ClassMetadata(TestEntity1::class); $classMetadata->mapEmbedded( @@ -1326,7 +1002,7 @@ public function testWakeupReflectionWithEmbeddableAndStaticReflectionService(): 'fieldName' => 'test', 'class' => TestEntity1::class, 'columnPrefix' => false, - ] + ], ); $field = [ @@ -1345,7 +1021,7 @@ public function testWakeupReflectionWithEmbeddableAndStaticReflectionService(): public function testGetColumnNamesWithGivenFieldNames(): void { - $metadata = new ClassMetadata(CMS\CmsUser::class); + $metadata = new ClassMetadata(CmsUser::class); $metadata->initializeReflection(new RuntimeReflectionService()); $metadata->mapField(['fieldName' => 'status', 'type' => 'string', 'columnName' => 'foo']); @@ -1355,7 +1031,7 @@ public function testGetColumnNamesWithGivenFieldNames(): void self::assertSame(['foo', 'baz'], $metadata->getColumnNames(['status', 'name'])); } - /** @group DDC-6460 */ + #[TestGroup('DDC-6460')] public function testInlineEmbeddable(): void { $classMetadata = new ClassMetadata(TestEntity1::class); @@ -1365,13 +1041,13 @@ public function testInlineEmbeddable(): void 'fieldName' => 'test', 'class' => TestEntity1::class, 'columnPrefix' => false, - ] + ], ); self::assertTrue($classMetadata->hasField('test')); } - /** @group DDC-3305 */ + #[TestGroup('DDC-3305')] public function testRejectsEmbeddableWithoutValidClassName(): void { $metadata = new ClassMetadata(TestEntity1::class); @@ -1385,12 +1061,41 @@ public function testRejectsEmbeddableWithoutValidClassName(): void ]); } - public function testInvalidCallToGetAssociationMappedByTargetFieldIsDeprecated(): void + public function testItAddingLifecycleCallbackOnEmbeddedClassIsIllegal(): void + { + $metadata = new ClassMetadata(self::class); + $metadata->isEmbeddedClass = true; + + $this->expectException(MappingException::class); + $this->expectExceptionMessage(<<<'EXCEPTION' + Context: Attempt to register lifecycle callback "foo" on embedded class "Doctrine\Tests\ORM\Mapping\ClassMetadataTest". + Problem: Registering lifecycle callbacks on embedded classes is not allowed. + EXCEPTION); + + $metadata->addLifecycleCallback('foo', 'bar'); + } + + #[WithoutErrorHandler] + public function testGettingAnFQCNForNullIsDeprecated(): void + { + $metadata = new ClassMetadata(self::class); + + $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11294'); + + $metadata->fullyQualifiedClassName(null); + } + + public function testItThrowsOnInvalidCallToGetAssociationMappedByTargetField(): void { $metadata = new ClassMetadata(self::class); $metadata->mapOneToOne(['fieldName' => 'foo', 'targetEntity' => 'bar']); - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11309'); + $this->expectException(LogicException::class); + $this->expectExceptionMessage(<<<'EXCEPTION' + Context: Calling Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField() with "foo", which is the owning side of an association. + Problem: The owning side of an association has no "mappedBy" field. + Solution: Call Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide() to check first. + EXCEPTION); $metadata->getAssociationMappedByTargetField('foo'); } @@ -1400,32 +1105,35 @@ public function testClassNameMappingDiscriminatorValue(): void $driver = new XmlDriver( __DIR__ . '/xml', XmlDriver::DEFAULT_FILE_EXTENSION, - true + true, ); $xmlElement = $driver->getElement(CustomerType::class); self::assertEquals( 'Doctrine\Tests\Models\Customer\InternalCustomer', - $xmlElement->children()->{'discriminator-map'}->{'discriminator-mapping'}[0]->attributes()['value'] + $xmlElement->children()->{'discriminator-map'}->{'discriminator-mapping'}[0]->attributes()['value'], ); } + + #[WithoutErrorHandler] + public function testDiscriminatorMapWithSameClassMultipleTimesDeprecated(): void + { + $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/3519'); + + $cm = new ClassMetadata(CMS\CmsUser::class); + $cm->setDiscriminatorMap(['foo' => CMS\CmsUser::class, 'bar' => CMS\CmsUser::class]); + } } -/** @MappedSuperclass */ +#[MappedSuperclass] class DDC2700MappedSuperClass { - /** - * @var mixed - * @Column - */ - private $foo; + #[Column] + private mixed $foo; } class MyNamespacedNamingStrategy extends DefaultNamingStrategy { - /** - * {@inheritDoc} - */ - public function classToTableName($className) + public function classToTableName(string $className): string { if (str_contains($className, '\\')) { $className = str_replace('\\', '_', str_replace('Doctrine\Tests\Models\\', '', $className)); @@ -1437,10 +1145,7 @@ public function classToTableName($className) class MyPrefixNamingStrategy extends DefaultNamingStrategy { - /** - * {@inheritDoc} - */ - public function propertyToColumnName($propertyName, $className = null) + public function propertyToColumnName(string $propertyName, string $className): string { return strtolower($this->classToTableName($className)) . '_' . $propertyName; } diff --git a/tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php b/tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php index f42857f3cc6..949e668154e 100644 --- a/tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php +++ b/tests/Tests/ORM/Mapping/DefaultQuoteStrategyTest.php @@ -8,6 +8,7 @@ use Doctrine\ORM\Mapping\DefaultQuoteStrategy; use Doctrine\Tests\Models\NonPublicSchemaJoins\User as NonPublicSchemaUser; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use function assert; @@ -16,10 +17,8 @@ */ class DefaultQuoteStrategyTest extends OrmTestCase { - /** - * @group DDC-3590 - * @group 1316 - */ + #[Group('DDC-3590')] + #[Group('DDC-1316')] public function testGetJoinTableName(): void { $em = $this->getTestEntityManager(); @@ -30,7 +29,7 @@ public function testGetJoinTableName(): void self::assertSame( 'readers.author_reader', - $strategy->getJoinTableName($metadata->associationMappings['readers'], $metadata, $platform) + $strategy->getJoinTableName($metadata->associationMappings['readers'], $metadata, $platform), ); } } diff --git a/tests/Tests/ORM/Mapping/DiscriminatorColumnMappingTest.php b/tests/Tests/ORM/Mapping/DiscriminatorColumnMappingTest.php new file mode 100644 index 00000000000..904fdab2b3a --- /dev/null +++ b/tests/Tests/ORM/Mapping/DiscriminatorColumnMappingTest.php @@ -0,0 +1,37 @@ +length = 255; + $mapping->columnDefinition = 'VARCHAR(255)'; + $mapping->enumType = 'MyEnum'; + $mapping->options = ['foo' => 'bar']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof DiscriminatorColumnMapping); + + self::assertSame(255, $resurrectedMapping->length); + self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition); + self::assertSame('MyEnum', $resurrectedMapping->enumType); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->options); + } +} diff --git a/tests/Tests/ORM/Mapping/EmbeddedClassMappingTest.php b/tests/Tests/ORM/Mapping/EmbeddedClassMappingTest.php new file mode 100644 index 00000000000..386cc94533b --- /dev/null +++ b/tests/Tests/ORM/Mapping/EmbeddedClassMappingTest.php @@ -0,0 +1,34 @@ +columnPrefix = 'these'; + $mapping->declaredField = 'values'; + $mapping->originalField = 'make'; + $mapping->inherited = self::class; // no + $mapping->declared = self::class; // sense + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof EmbeddedClassMapping); + + self::assertSame('these', $resurrectedMapping->columnPrefix); + self::assertSame('values', $resurrectedMapping->declaredField); + self::assertSame('make', $resurrectedMapping->originalField); + self::assertSame(self::class, $resurrectedMapping->inherited); + self::assertSame(self::class, $resurrectedMapping->declared); + } +} diff --git a/tests/Tests/ORM/Mapping/EntityListenerResolverTest.php b/tests/Tests/ORM/Mapping/EntityListenerResolverTest.php index 31f7d32056b..83be2f0368d 100644 --- a/tests/Tests/ORM/Mapping/EntityListenerResolverTest.php +++ b/tests/Tests/ORM/Mapping/EntityListenerResolverTest.php @@ -5,13 +5,15 @@ namespace Doctrine\Tests\ORM\Mapping; use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; +use Doctrine\Tests\Models\Company\CompanyContractListener; +use Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1955 */ +#[Group('DDC-1955')] class EntityListenerResolverTest extends OrmTestCase { - /** @var DefaultEntityListenerResolver */ - private $resolver; + private DefaultEntityListenerResolver $resolver; protected function setUp(): void { @@ -22,7 +24,7 @@ protected function setUp(): void public function testResolve(): void { - $className = '\Doctrine\Tests\Models\Company\CompanyContractListener'; + $className = CompanyContractListener::class; $object = $this->resolver->resolve($className); self::assertInstanceOf($className, $object); @@ -31,7 +33,7 @@ public function testResolve(): void public function testRegisterAndResolve(): void { - $className = '\Doctrine\Tests\Models\Company\CompanyContractListener'; + $className = CompanyContractListener::class; $object = new $className(); $this->resolver->register($object); @@ -41,8 +43,8 @@ public function testRegisterAndResolve(): void public function testClearOne(): void { - $className1 = '\Doctrine\Tests\Models\Company\CompanyContractListener'; - $className2 = '\Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener'; + $className1 = CompanyContractListener::class; + $className2 = CompanyFlexUltraContractListener::class; $obj1 = $this->resolver->resolve($className1); $obj2 = $this->resolver->resolve($className2); @@ -64,8 +66,8 @@ public function testClearOne(): void public function testClearAll(): void { - $className1 = '\Doctrine\Tests\Models\Company\CompanyContractListener'; - $className2 = '\Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener'; + $className1 = CompanyContractListener::class; + $className2 = CompanyFlexUltraContractListener::class; $obj1 = $this->resolver->resolve($className1); $obj2 = $this->resolver->resolve($className2); @@ -84,11 +86,4 @@ public function testClearAll(): void self::assertNotSame($obj1, $this->resolver->resolve($className1)); self::assertNotSame($obj2, $this->resolver->resolve($className2)); } - - public function testRegisterStringException(): void - { - $this->expectException('InvalidArgumentException'); - $this->expectExceptionMessage('An object was expected, but got "string".'); - $this->resolver->register('CompanyContractListener'); - } } diff --git a/tests/Tests/ORM/Mapping/FieldBuilderTest.php b/tests/Tests/ORM/Mapping/FieldBuilderTest.php index cef9bbc723e..5d47f6b3c36 100644 --- a/tests/Tests/ORM/Mapping/FieldBuilderTest.php +++ b/tests/Tests/ORM/Mapping/FieldBuilderTest.php @@ -8,6 +8,7 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmTestCase; +use stdClass; class FieldBuilderTest extends OrmTestCase { @@ -18,11 +19,11 @@ public function testCustomIdGeneratorCanBeSet(): void $fieldBuilder = $cmBuilder->createField('aField', 'string'); $fieldBuilder->generatedValue('CUSTOM'); - $fieldBuilder->setCustomIdGenerator('stdClass'); + $fieldBuilder->setCustomIdGenerator(stdClass::class); $fieldBuilder->build(); self::assertEquals(ClassMetadata::GENERATOR_TYPE_CUSTOM, $cmBuilder->getClassMetadata()->generatorType); - self::assertEquals(['class' => 'stdClass'], $cmBuilder->getClassMetadata()->customGeneratorDefinition); + self::assertEquals(['class' => stdClass::class], $cmBuilder->getClassMetadata()->customGeneratorDefinition); } } diff --git a/tests/Tests/ORM/Mapping/FieldMappingTest.php b/tests/Tests/ORM/Mapping/FieldMappingTest.php new file mode 100644 index 00000000000..cfbd89b8ca5 --- /dev/null +++ b/tests/Tests/ORM/Mapping/FieldMappingTest.php @@ -0,0 +1,70 @@ +length = 255; + $mapping->id = true; + $mapping->nullable = true; + $mapping->notInsertable = true; + $mapping->notUpdatable = true; + $mapping->columnDefinition = 'VARCHAR(255)'; + $mapping->generated = ClassMetadata::GENERATOR_TYPE_AUTO; + $mapping->enumType = 'MyEnum'; + $mapping->precision = 10; + $mapping->scale = 2; + $mapping->unique = true; + $mapping->inherited = self::class; + $mapping->originalClass = self::class; + $mapping->originalField = 'id'; + $mapping->quoted = true; + $mapping->declared = self::class; + $mapping->declaredField = 'id'; + $mapping->options = ['foo' => 'bar']; + $mapping->version = true; + $mapping->default = 'foo'; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof FieldMapping); + + self::assertSame(255, $resurrectedMapping->length); + self::assertTrue($resurrectedMapping->id); + self::assertTrue($resurrectedMapping->nullable); + self::assertTrue($resurrectedMapping->notInsertable); + self::assertTrue($resurrectedMapping->notUpdatable); + self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition); + self::assertSame(ClassMetadata::GENERATOR_TYPE_AUTO, $resurrectedMapping->generated); + self::assertSame('MyEnum', $resurrectedMapping->enumType); + self::assertSame(10, $resurrectedMapping->precision); + self::assertSame(2, $resurrectedMapping->scale); + self::assertTrue($resurrectedMapping->unique); + self::assertSame(self::class, $resurrectedMapping->inherited); + self::assertSame(self::class, $resurrectedMapping->originalClass); + self::assertSame('id', $resurrectedMapping->originalField); + self::assertTrue($resurrectedMapping->quoted); + self::assertSame(self::class, $resurrectedMapping->declared); + self::assertSame('id', $resurrectedMapping->declaredField); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->options); + self::assertTrue($resurrectedMapping->version); + self::assertSame('foo', $resurrectedMapping->default); + } +} diff --git a/tests/Tests/ORM/Mapping/Fixtures/AttributeEntityWithNestedJoinColumns.php b/tests/Tests/ORM/Mapping/Fixtures/AttributeEntityWithNestedJoinColumns.php index bcfb9255d70..8c283454f0c 100644 --- a/tests/Tests/ORM/Mapping/Fixtures/AttributeEntityWithNestedJoinColumns.php +++ b/tests/Tests/ORM/Mapping/Fixtures/AttributeEntityWithNestedJoinColumns.php @@ -21,7 +21,7 @@ class AttributeEntityWithNestedJoinColumns #[ORM\JoinTable( name: 'assoc_table', joinColumns: new ORM\JoinColumn(name: 'assoz_id', referencedColumnName: 'assoz_id'), - inverseJoinColumns: new ORM\JoinColumn(name: 'inverse_assoz_id', referencedColumnName: 'inverse_assoz_id') + inverseJoinColumns: new ORM\JoinColumn(name: 'inverse_assoz_id', referencedColumnName: 'inverse_assoz_id'), )] public $assoc; } diff --git a/tests/Tests/ORM/Mapping/InverseSideMappingTest.php b/tests/Tests/ORM/Mapping/InverseSideMappingTest.php new file mode 100644 index 00000000000..b5a7cd74127 --- /dev/null +++ b/tests/Tests/ORM/Mapping/InverseSideMappingTest.php @@ -0,0 +1,35 @@ +mappedBy = 'bar'; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof InverseSideMapping); + + self::assertSame('bar', $resurrectedMapping->mappedBy); + } +} + +class MyInverseAssociationMapping extends InverseSideMapping +{ +} diff --git a/tests/Tests/ORM/Mapping/JoinColumnMappingTest.php b/tests/Tests/ORM/Mapping/JoinColumnMappingTest.php new file mode 100644 index 00000000000..b5459db7f76 --- /dev/null +++ b/tests/Tests/ORM/Mapping/JoinColumnMappingTest.php @@ -0,0 +1,42 @@ +unique = true; + $mapping->quoted = true; + $mapping->fieldName = 'bar'; + $mapping->onDelete = 'CASCADE'; + $mapping->columnDefinition = 'VARCHAR(255)'; + $mapping->nullable = true; + $mapping->referencedColumnName = 'baz'; + $mapping->options = ['foo' => 'bar']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof JoinColumnMapping); + + self::assertSame('foo', $resurrectedMapping->name); + self::assertTrue($resurrectedMapping->unique); + self::assertTrue($resurrectedMapping->quoted); + self::assertSame('bar', $resurrectedMapping->fieldName); + self::assertSame('CASCADE', $resurrectedMapping->onDelete); + self::assertSame('VARCHAR(255)', $resurrectedMapping->columnDefinition); + self::assertTrue($resurrectedMapping->nullable); + self::assertSame('baz', $resurrectedMapping->referencedColumnName); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->options); + } +} diff --git a/tests/Tests/ORM/Mapping/JoinTableMappingTest.php b/tests/Tests/ORM/Mapping/JoinTableMappingTest.php new file mode 100644 index 00000000000..65c00ed0fe6 --- /dev/null +++ b/tests/Tests/ORM/Mapping/JoinTableMappingTest.php @@ -0,0 +1,37 @@ +quoted = true; + $mapping->joinColumns = [new JoinColumnMapping('foo_id', 'id')]; + $mapping->inverseJoinColumns = [new JoinColumnMapping('bar_id', 'id')]; + $mapping->schema = 'foo'; + $mapping->options = ['foo' => 'bar']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof JoinTableMapping); + + self::assertTrue($resurrectedMapping->quoted); + self::assertCount(1, $resurrectedMapping->joinColumns); + self::assertCount(1, $resurrectedMapping->inverseJoinColumns); + self::assertSame('foo', $resurrectedMapping->schema); + self::assertSame('bar', $resurrectedMapping->name); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->options); + } +} diff --git a/tests/Tests/ORM/Mapping/ManyToManyOwningSideMappingTest.php b/tests/Tests/ORM/Mapping/ManyToManyOwningSideMappingTest.php new file mode 100644 index 00000000000..cc2e2e74cab --- /dev/null +++ b/tests/Tests/ORM/Mapping/ManyToManyOwningSideMappingTest.php @@ -0,0 +1,38 @@ +joinTable = new JoinTableMapping('bar'); + $mapping->joinTableColumns = ['foo', 'bar']; + $mapping->relationToSourceKeyColumns = ['foo' => 'bar']; + $mapping->relationToTargetKeyColumns = ['bar' => 'baz']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof ManyToManyOwningSideMapping); + + self::assertSame($resurrectedMapping->joinTable->name, 'bar'); + self::assertSame(['foo', 'bar'], $resurrectedMapping->joinTableColumns); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->relationToSourceKeyColumns); + self::assertSame(['bar' => 'baz'], $resurrectedMapping->relationToTargetKeyColumns); + } +} diff --git a/tests/Tests/ORM/Mapping/ManyToOneAssociationMappingTest.php b/tests/Tests/ORM/Mapping/ManyToOneAssociationMappingTest.php new file mode 100644 index 00000000000..3833e51ef53 --- /dev/null +++ b/tests/Tests/ORM/Mapping/ManyToOneAssociationMappingTest.php @@ -0,0 +1,38 @@ +joinColumns = [new JoinColumnMapping('foo_id', 'id')]; + $mapping->joinColumnFieldNames = ['foo' => 'bar']; + $mapping->sourceToTargetKeyColumns = ['foo' => 'bar']; + $mapping->targetToSourceKeyColumns = ['bar' => 'foo']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof ManyToOneAssociationMapping); + + self::assertCount(1, $resurrectedMapping->joinColumns); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->joinColumnFieldNames); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->sourceToTargetKeyColumns); + self::assertSame(['bar' => 'foo'], $resurrectedMapping->targetToSourceKeyColumns); + } +} diff --git a/tests/Tests/ORM/Mapping/MappingDriverTestCase.php b/tests/Tests/ORM/Mapping/MappingDriverTestCase.php index f7aa97e6a91..10d9d4ba965 100644 --- a/tests/Tests/ORM/Mapping/MappingDriverTestCase.php +++ b/tests/Tests/ORM/Mapping/MappingDriverTestCase.php @@ -11,35 +11,22 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\Column; -use Doctrine\ORM\Mapping\CustomIdGenerator; use Doctrine\ORM\Mapping\DefaultNamingStrategy; use Doctrine\ORM\Mapping\DefaultTypedFieldMapper; use Doctrine\ORM\Mapping\DiscriminatorColumn; +use Doctrine\ORM\Mapping\DiscriminatorColumnMapping; use Doctrine\ORM\Mapping\DiscriminatorMap; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; -use Doctrine\ORM\Mapping\HasLifecycleCallbacks; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Index; use Doctrine\ORM\Mapping\InheritanceType; -use Doctrine\ORM\Mapping\JoinColumn; -use Doctrine\ORM\Mapping\JoinTable; -use Doctrine\ORM\Mapping\ManyToMany; use Doctrine\ORM\Mapping\MappingException; -use Doctrine\ORM\Mapping\NamedQueries; -use Doctrine\ORM\Mapping\NamedQuery; use Doctrine\ORM\Mapping\NamingStrategy; -use Doctrine\ORM\Mapping\OneToMany; -use Doctrine\ORM\Mapping\OneToOne; -use Doctrine\ORM\Mapping\OrderBy; -use Doctrine\ORM\Mapping\PostPersist; -use Doctrine\ORM\Mapping\PrePersist; -use Doctrine\ORM\Mapping\SequenceGenerator; use Doctrine\ORM\Mapping\Table; use Doctrine\ORM\Mapping\TypedFieldMapper; use Doctrine\ORM\Mapping\UnderscoreNamingStrategy; use Doctrine\ORM\Mapping\UniqueConstraint; -use Doctrine\ORM\Mapping\Version; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Tests\DbalTypes\CustomIdObject; @@ -49,14 +36,12 @@ use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsAddressListener; use Doctrine\Tests\Models\CMS\CmsEmail; -use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Company\CompanyContract; use Doctrine\Tests\Models\Company\CompanyContractListener; use Doctrine\Tests\Models\Company\CompanyFixContract; use Doctrine\Tests\Models\Company\CompanyFlexContract; use Doctrine\Tests\Models\Company\CompanyFlexUltraContract; use Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener; -use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType; use Doctrine\Tests\Models\DDC2825\ExplicitSchemaAndTable; use Doctrine\Tests\Models\DDC2825\SchemaAndTableInTableName; @@ -78,28 +63,25 @@ use Doctrine\Tests\Models\Upsertable\Insertable; use Doctrine\Tests\Models\Upsertable\Updatable; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Depends; +use stdClass; use function assert; use function count; -use function get_debug_type; -use function sprintf; use function str_contains; use function strtolower; use const CASE_UPPER; -use const PHP_VERSION_ID; abstract class MappingDriverTestCase extends OrmTestCase { abstract protected function loadDriver(): MappingDriver; - /** - * @param class-string $entityClassName - */ + /** @param class-string $entityClassName */ public function createClassMetadata( string $entityClassName, - ?NamingStrategy $namingStrategy = null, - ?TypedFieldMapper $typedFieldMapper = null + NamingStrategy|null $namingStrategy = null, + TypedFieldMapper|null $typedFieldMapper = null, ): ClassMetadata { $mappingDriver = $this->loadDriver(); @@ -110,10 +92,10 @@ public function createClassMetadata( return $class; } - protected function createClassMetadataFactory(?EntityManagerInterface $em = null): ClassMetadataFactory + protected function createClassMetadataFactory(EntityManagerInterface|null $em = null): ClassMetadataFactory { $driver = $this->loadDriver(); - $em = $em ?? $this->getTestEntityManager(); + $em ??= $this->getTestEntityManager(); $factory = new ClassMetadataFactory(); $em->getConfiguration()->setMetadataDriverImpl($driver); $factory->setEntityManager($em); @@ -131,7 +113,7 @@ public function testEntityTableNameAndInheritance(): ClassMetadata return $class; } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testEntityIndexes(ClassMetadata $class): ClassMetadata { self::assertArrayHasKey('indexes', $class->table, 'ClassMetadata should have indexes key in table property.'); @@ -141,7 +123,7 @@ public function testEntityIndexes(ClassMetadata $class): ClassMetadata 0 => ['columns' => ['user_email']], 'fields' => ['fields' => ['name', 'email']], ], - $class->table['indexes'] + $class->table['indexes'], ); return $class; @@ -165,17 +147,17 @@ public function testEntityIndexFlagsAndPartialIndexes(): void 'options' => ['where' => 'content IS NOT NULL'], ], ], - $class->table['indexes'] + $class->table['indexes'], ); } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testEntityUniqueConstraints(ClassMetadata $class): ClassMetadata { self::assertArrayHasKey( 'uniqueConstraints', $class->table, - 'ClassMetadata should have uniqueConstraints key in table property when Unique Constraints are set.' + 'ClassMetadata should have uniqueConstraints key in table property when Unique Constraints are set.', ); self::assertEquals( @@ -183,7 +165,7 @@ public function testEntityUniqueConstraints(ClassMetadata $class): ClassMetadata 'search_idx' => ['columns' => ['name', 'user_email'], 'options' => ['where' => 'name IS NOT NULL']], 'phone_idx' => ['fields' => ['name', 'phone']], ], - $class->table['uniqueConstraints'] + $class->table['uniqueConstraints'], ); return $class; @@ -195,7 +177,7 @@ public function testEntityIncorrectUniqueContraint(): void $this->createClassMetadata(UserIncorrectUniqueConstraint::class); } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testEntityOptions(ClassMetadata $class): ClassMetadata { self::assertArrayHasKey('options', $class->table, 'ClassMetadata should have options key in table property.'); @@ -205,13 +187,13 @@ public function testEntityOptions(ClassMetadata $class): ClassMetadata 'foo' => 'bar', 'baz' => ['key' => 'val'], ], - $class->table['options'] + $class->table['options'], ); return $class; } - /** @depends testEntityOptions */ + #[Depends('testEntityOptions')] public function testEntitySequence(ClassMetadata $class): void { self::assertIsArray($class->sequenceGeneratorDefinition, 'No Sequence Definition set on this driver.'); @@ -221,7 +203,7 @@ public function testEntitySequence(ClassMetadata $class): void 'allocationSize' => 100, 'initialValue' => 1, ], - $class->sequenceGeneratorDefinition + $class->sequenceGeneratorDefinition, ); } @@ -232,16 +214,16 @@ public function testEntityCustomGenerator(): void self::assertEquals( ClassMetadata::GENERATOR_TYPE_CUSTOM, $class->generatorType, - 'Generator Type' + 'Generator Type', ); self::assertEquals( - ['class' => 'stdClass'], + ['class' => stdClass::class], $class->customGeneratorDefinition, - 'Custom Generator Definition' + 'Custom Generator Definition', ); } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testFieldMappings(ClassMetadata $class): ClassMetadata { self::assertEquals(4, count($class->fieldMappings)); @@ -253,39 +235,36 @@ public function testFieldMappings(ClassMetadata $class): ClassMetadata return $class; } - /** @depends testFieldMappings */ + #[Depends('testFieldMappings')] public function testVersionedField(ClassMetadata $class): void { self::assertTrue($class->isVersioned); self::assertEquals('version', $class->versionField); - self::assertFalse(isset($class->fieldMappings['version']['version'])); + self::assertFalse(isset($class->fieldMappings['version']->version)); } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testFieldMappingsColumnNames(ClassMetadata $class): ClassMetadata { - self::assertEquals('id', $class->fieldMappings['id']['columnName']); - self::assertEquals('name', $class->fieldMappings['name']['columnName']); - self::assertEquals('user_email', $class->fieldMappings['email']['columnName']); + self::assertEquals('id', $class->fieldMappings['id']->columnName); + self::assertEquals('name', $class->fieldMappings['name']->columnName); + self::assertEquals('user_email', $class->fieldMappings['email']->columnName); return $class; } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testStringFieldMappings(ClassMetadata $class): ClassMetadata { - self::assertEquals('string', $class->fieldMappings['name']['type']); - self::assertEquals(50, $class->fieldMappings['name']['length']); - self::assertTrue($class->fieldMappings['name']['nullable']); - self::assertTrue($class->fieldMappings['name']['unique']); + self::assertEquals('string', $class->fieldMappings['name']->type); + self::assertEquals(50, $class->fieldMappings['name']->length); + self::assertTrue($class->fieldMappings['name']->nullable); + self::assertTrue($class->fieldMappings['name']->unique); return $class; } - /** - * @requires PHP 7.4 - */ public function testFieldTypeFromReflection(): void { $class = $this->createClassMetadata(UserTyped::class); @@ -299,15 +278,12 @@ public function testFieldTypeFromReflection(): void self::assertEquals('boolean', $class->getTypeOfField('boolean')); self::assertEquals('float', $class->getTypeOfField('float')); - self::assertEquals(CmsEmail::class, $class->getAssociationMapping('email')['targetEntity']); - self::assertEquals(CmsEmail::class, $class->getAssociationMapping('mainEmail')['targetEntity']); - self::assertEquals(Contact::class, $class->embeddedClasses['contact']['class']); + self::assertEquals(CmsEmail::class, $class->getAssociationMapping('email')->targetEntity); + self::assertEquals(CmsEmail::class, $class->getAssociationMapping('mainEmail')->targetEntity); + self::assertEquals(Contact::class, $class->embeddedClasses['contact']->class); } - /** - * @group GH10313 - * @requires PHP 7.4 - */ + #[\PHPUnit\Framework\Attributes\Group('GH10313')] public function testCustomFieldTypeFromReflection(): void { $class = $this->createClassMetadata( @@ -317,56 +293,56 @@ public function testCustomFieldTypeFromReflection(): void [ CustomIdObject::class => CustomIdObjectType::class, 'int' => CustomIntType::class, - ] - ) + ], + ), ); self::assertEquals(CustomIdObjectType::class, $class->getTypeOfField('customId')); self::assertEquals(CustomIntType::class, $class->getTypeOfField('customIntTypedField')); } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testFieldOptions(ClassMetadata $class): ClassMetadata { $expected = ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false]; - self::assertEquals($expected, $class->fieldMappings['name']['options']); + self::assertEquals($expected, $class->fieldMappings['name']->options); return $class; } - /** @depends testEntityTableNameAndInheritance */ + #[Depends('testEntityTableNameAndInheritance')] public function testIdFieldOptions(ClassMetadata $class): ClassMetadata { - self::assertEquals(['foo' => 'bar', 'unsigned' => false], $class->fieldMappings['id']['options']); + self::assertEquals(['foo' => 'bar', 'unsigned' => false], $class->fieldMappings['id']->options); return $class; } - /** @depends testFieldMappings */ + #[Depends('testFieldMappings')] public function testIdentifier(ClassMetadata $class): ClassMetadata { self::assertEquals(['id'], $class->identifier); - self::assertEquals('integer', $class->fieldMappings['id']['type']); + self::assertEquals('integer', $class->fieldMappings['id']->type); self::assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $class->generatorType, 'ID-Generator is not ClassMetadata::GENERATOR_TYPE_AUTO'); return $class; } - /** @group #6129 */ + #[\PHPUnit\Framework\Attributes\Group('#6129')] public function testBooleanValuesForOptionIsSetCorrectly(): ClassMetadata { $class = $this->createClassMetadata(User::class); - self::assertIsBool($class->fieldMappings['id']['options']['unsigned']); - self::assertFalse($class->fieldMappings['id']['options']['unsigned']); + self::assertIsBool($class->fieldMappings['id']->options['unsigned']); + self::assertFalse($class->fieldMappings['id']->options['unsigned']); - self::assertIsBool($class->fieldMappings['name']['options']['fixed']); - self::assertFalse($class->fieldMappings['name']['options']['fixed']); + self::assertIsBool($class->fieldMappings['name']->options['fixed']); + self::assertFalse($class->fieldMappings['name']->options['fixed']); return $class; } - /** @depends testIdentifier */ + #[Depends('testIdentifier')] public function testAssociations(ClassMetadata $class): ClassMetadata { self::assertEquals(3, count($class->associationMappings)); @@ -374,58 +350,55 @@ public function testAssociations(ClassMetadata $class): ClassMetadata return $class; } - /** @depends testAssociations */ + #[Depends('testAssociations')] public function testOwningOneToOneAssociation(ClassMetadata $class): ClassMetadata { self::assertTrue(isset($class->associationMappings['address'])); - self::assertTrue($class->associationMappings['address']['isOwningSide']); - self::assertEquals('user', $class->associationMappings['address']['inversedBy']); + self::assertTrue($class->associationMappings['address']->isOwningSide()); + self::assertEquals('user', $class->associationMappings['address']->inversedBy); // Check cascading - self::assertTrue($class->associationMappings['address']['isCascadeRemove']); - self::assertFalse($class->associationMappings['address']['isCascadePersist']); - self::assertFalse($class->associationMappings['address']['isCascadeRefresh']); - self::assertFalse($class->associationMappings['address']['isCascadeDetach']); - self::assertFalse($class->associationMappings['address']['isCascadeMerge']); + self::assertTrue($class->associationMappings['address']->isCascadeRemove()); + self::assertFalse($class->associationMappings['address']->isCascadePersist()); + self::assertFalse($class->associationMappings['address']->isCascadeRefresh()); + self::assertFalse($class->associationMappings['address']->isCascadeDetach()); return $class; } - /** @depends testOwningOneToOneAssociation */ + #[Depends('testOwningOneToOneAssociation')] public function testInverseOneToManyAssociation(ClassMetadata $class): ClassMetadata { self::assertTrue(isset($class->associationMappings['phonenumbers'])); - self::assertFalse($class->associationMappings['phonenumbers']['isOwningSide']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadeRemove']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']); - self::assertTrue($class->associationMappings['phonenumbers']['orphanRemoval']); + self::assertFalse($class->associationMappings['phonenumbers']->isOwningSide()); + self::assertTrue($class->associationMappings['phonenumbers']->isCascadePersist()); + self::assertTrue($class->associationMappings['phonenumbers']->isCascadeRemove()); + self::assertFalse($class->associationMappings['phonenumbers']->isCascadeRefresh()); + self::assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach()); + self::assertTrue($class->associationMappings['phonenumbers']->orphanRemoval); // Test Order By - self::assertEquals(['number' => 'ASC'], $class->associationMappings['phonenumbers']['orderBy']); + self::assertEquals(['number' => 'ASC'], $class->associationMappings['phonenumbers']->orderBy); return $class; } - /** @depends testInverseOneToManyAssociation */ + #[Depends('testInverseOneToManyAssociation')] public function testManyToManyAssociationWithCascadeAll(ClassMetadata $class): ClassMetadata { self::assertTrue(isset($class->associationMappings['groups'])); - self::assertTrue($class->associationMappings['groups']['isOwningSide']); + self::assertTrue($class->associationMappings['groups']->isOwningSide()); // Make sure that cascade-all works as expected - self::assertTrue($class->associationMappings['groups']['isCascadeRemove']); - self::assertTrue($class->associationMappings['groups']['isCascadePersist']); - self::assertTrue($class->associationMappings['groups']['isCascadeRefresh']); - self::assertTrue($class->associationMappings['groups']['isCascadeDetach']); - self::assertTrue($class->associationMappings['groups']['isCascadeMerge']); + self::assertTrue($class->associationMappings['groups']->isCascadeRemove()); + self::assertTrue($class->associationMappings['groups']->isCascadePersist()); + self::assertTrue($class->associationMappings['groups']->isCascadeRefresh()); + self::assertTrue($class->associationMappings['groups']->isCascadeDetach()); - self::assertFalse(isset($class->associationMappings['groups']['orderBy'])); + self::assertFalse($class->associationMappings['groups']->isOrdered()); return $class; } - /** @depends testManyToManyAssociationWithCascadeAll */ + #[Depends('testManyToManyAssociationWithCascadeAll')] public function testLifecycleCallbacks(ClassMetadata $class): ClassMetadata { self::assertCount(2, $class->lifecycleCallbacks); @@ -435,7 +408,7 @@ public function testLifecycleCallbacks(ClassMetadata $class): ClassMetadata return $class; } - /** @depends testManyToManyAssociationWithCascadeAll */ + #[Depends('testManyToManyAssociationWithCascadeAll')] public function testLifecycleCallbacksSupportMultipleMethodNames(ClassMetadata $class): ClassMetadata { self::assertCount(2, $class->lifecycleCallbacks['prePersist']); @@ -444,34 +417,34 @@ public function testLifecycleCallbacksSupportMultipleMethodNames(ClassMetadata $ return $class; } - /** @depends testLifecycleCallbacksSupportMultipleMethodNames */ + #[Depends('testLifecycleCallbacksSupportMultipleMethodNames')] public function testJoinColumnUniqueAndNullable(ClassMetadata $class): ClassMetadata { // Non-Nullability of Join Column - self::assertFalse($class->associationMappings['groups']['joinTable']['joinColumns'][0]['nullable']); - self::assertFalse($class->associationMappings['groups']['joinTable']['joinColumns'][0]['unique']); + self::assertFalse($class->associationMappings['groups']->joinTable->joinColumns[0]->nullable); + self::assertFalse($class->associationMappings['groups']->joinTable->joinColumns[0]->unique); return $class; } - /** @depends testJoinColumnUniqueAndNullable */ + #[Depends('testJoinColumnUniqueAndNullable')] public function testColumnDefinition(ClassMetadata $class): ClassMetadata { - self::assertEquals('CHAR(32) NOT NULL', $class->fieldMappings['email']['columnDefinition']); - self::assertEquals('INT NULL', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['columnDefinition']); + self::assertEquals('CHAR(32) NOT NULL', $class->fieldMappings['email']->columnDefinition); + self::assertEquals('INT NULL', $class->associationMappings['groups']->joinTable->inverseJoinColumns[0]->columnDefinition); return $class; } - /** @depends testColumnDefinition */ + #[Depends('testColumnDefinition')] public function testJoinColumnOnDelete(ClassMetadata $class): ClassMetadata { - self::assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onDelete']); + self::assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]->onDelete); return $class; } - /** @group DDC-514 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-514')] public function testDiscriminatorColumnDefaults(): void { if (str_contains(static::class, 'PHPMappingDriver')) { @@ -481,12 +454,19 @@ public function testDiscriminatorColumnDefaults(): void $class = $this->createClassMetadata(Animal::class); self::assertEquals( - ['name' => 'discr', 'type' => 'string', 'length' => 32, 'fieldName' => 'discr', 'columnDefinition' => null, 'enumType' => null], - $class->discriminatorColumn + DiscriminatorColumnMapping::fromMappingArray([ + 'name' => 'discr', + 'type' => 'string', + 'length' => 32, + 'fieldName' => 'discr', + 'columnDefinition' => null, + 'enumType' => null, + ]), + $class->discriminatorColumn, ); } - /** @group DDC-869 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-869')] public function testMappedSuperclassWithRepository(): void { $em = $this->getTestEntityManager(); @@ -511,7 +491,7 @@ public function testMappedSuperclassWithRepository(): void self::assertTrue($em->getRepository(DDC869ChequePayment::class)->isTrue()); } - /** @group DDC-1476 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1476')] public function testDefaultFieldType(): void { $factory = $this->createClassMetadataFactory(); @@ -520,28 +500,19 @@ public function testDefaultFieldType(): void self::assertArrayHasKey('id', $class->fieldMappings); self::assertArrayHasKey('name', $class->fieldMappings); - self::assertArrayHasKey('type', $class->fieldMappings['id']); - self::assertArrayHasKey('type', $class->fieldMappings['name']); - - self::assertEquals('string', $class->fieldMappings['id']['type']); - self::assertEquals('string', $class->fieldMappings['name']['type']); + self::assertEquals('string', $class->fieldMappings['id']->type); + self::assertEquals('string', $class->fieldMappings['name']->type); - self::assertArrayHasKey('fieldName', $class->fieldMappings['id']); - self::assertArrayHasKey('fieldName', $class->fieldMappings['name']); + self::assertEquals('id', $class->fieldMappings['id']->fieldName); + self::assertEquals('name', $class->fieldMappings['name']->fieldName); - self::assertEquals('id', $class->fieldMappings['id']['fieldName']); - self::assertEquals('name', $class->fieldMappings['name']['fieldName']); - - self::assertArrayHasKey('columnName', $class->fieldMappings['id']); - self::assertArrayHasKey('columnName', $class->fieldMappings['name']); - - self::assertEquals('id', $class->fieldMappings['id']['columnName']); - self::assertEquals('name', $class->fieldMappings['name']['columnName']); + self::assertEquals('id', $class->fieldMappings['id']->columnName); + self::assertEquals('name', $class->fieldMappings['name']->columnName); self::assertEquals(ClassMetadata::GENERATOR_TYPE_NONE, $class->generatorType); } - /** @group DDC-1170 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1170')] public function testIdentifierColumnDefinition(): void { $class = $this->createClassMetadata(DDC1170Entity::class); @@ -549,14 +520,11 @@ public function testIdentifierColumnDefinition(): void self::assertArrayHasKey('id', $class->fieldMappings); self::assertArrayHasKey('value', $class->fieldMappings); - self::assertArrayHasKey('columnDefinition', $class->fieldMappings['id']); - self::assertArrayHasKey('columnDefinition', $class->fieldMappings['value']); - - self::assertEquals('int unsigned not null', strtolower($class->fieldMappings['id']['columnDefinition'])); - self::assertEquals('varchar(255) not null', strtolower($class->fieldMappings['value']['columnDefinition'])); + self::assertEquals('int unsigned not null', strtolower($class->fieldMappings['id']->columnDefinition)); + self::assertEquals('varchar(255) not null', strtolower($class->fieldMappings['value']->columnDefinition)); } - /** @group DDC-559 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-559')] public function testNamingStrategy(): void { $em = $this->getTestEntityManager(); @@ -570,39 +538,29 @@ public function testNamingStrategy(): void self::assertEquals('ID', $class->getColumnName('id')); self::assertEquals('NAME', $class->getColumnName('name')); - self::assertEquals('DDC1476ENTITY_WITH_DEFAULT_FIELD_TYPE', $class->table['name']); + self::assertEquals('DDC1476_ENTITY_WITH_DEFAULT_FIELD_TYPE', $class->table['name']); } - /** - * @group DDC-807 - * @group DDC-553 - */ + #[\PHPUnit\Framework\Attributes\Group('DDC-807')] + #[\PHPUnit\Framework\Attributes\Group('DDC-553')] public function testDiscriminatorColumnDefinition(): void { $class = $this->createClassMetadata(DDC807Entity::class); - self::assertArrayHasKey('columnDefinition', $class->discriminatorColumn); - self::assertArrayHasKey('name', $class->discriminatorColumn); - - self::assertEquals("ENUM('ONE','TWO')", $class->discriminatorColumn['columnDefinition']); - self::assertEquals('dtype', $class->discriminatorColumn['name']); + self::assertEquals("ENUM('ONE','TWO')", $class->discriminatorColumn->columnDefinition); + self::assertEquals('dtype', $class->discriminatorColumn->name); } - /** - * @group GH10288 - */ + #[\PHPUnit\Framework\Attributes\Group('GH10288')] public function testDiscriminatorColumnEnumTypeDefinition(): void { $class = $this->createClassMetadata(GH10288EnumTypePerson::class); - self::assertArrayHasKey('enumType', $class->discriminatorColumn); - self::assertArrayHasKey('name', $class->discriminatorColumn); - - self::assertEquals(GH10288People::class, $class->discriminatorColumn['enumType']); - self::assertEquals('discr', $class->discriminatorColumn['name']); + self::assertEquals(GH10288People::class, $class->discriminatorColumn->enumType); + self::assertEquals('discr', $class->discriminatorColumn->name); } - /** @group DDC-889 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-889')] public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void { $this->expectException(MappingException::class); @@ -611,7 +569,7 @@ public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): $this->createClassMetadata(DDC889Class::class); } - /** @group DDC-889 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-889')] public function testIdentifierRequiredShouldMentionParentClasses(): void { $factory = $this->createClassMetadataFactory(); @@ -622,136 +580,33 @@ public function testIdentifierRequiredShouldMentionParentClasses(): void $factory->getMetadataFor(DDC889Entity::class); } - public function testNamedQuery(): void - { - $driver = $this->loadDriver(); - $class = $this->createClassMetadata(User::class); - - self::assertCount(1, $class->getNamedQueries(), sprintf('Named queries not processed correctly by driver %s', get_debug_type($driver))); - } - - /** @group DDC-1663 */ - public function testNamedNativeQuery(): void - { - $class = $this->createClassMetadata(CmsAddress::class); - - //named native query - self::assertCount(3, $class->namedNativeQueries); - self::assertArrayHasKey('find-all', $class->namedNativeQueries); - self::assertArrayHasKey('find-by-id', $class->namedNativeQueries); - - $findAllQuery = $class->getNamedNativeQuery('find-all'); - self::assertEquals('find-all', $findAllQuery['name']); - self::assertEquals('mapping-find-all', $findAllQuery['resultSetMapping']); - self::assertEquals('SELECT id, country, city FROM cms_addresses', $findAllQuery['query']); - - $findByIdQuery = $class->getNamedNativeQuery('find-by-id'); - self::assertEquals('find-by-id', $findByIdQuery['name']); - self::assertEquals(CmsAddress::class, $findByIdQuery['resultClass']); - self::assertEquals('SELECT * FROM cms_addresses WHERE id = ?', $findByIdQuery['query']); - - $countQuery = $class->getNamedNativeQuery('count'); - self::assertEquals('count', $countQuery['name']); - self::assertEquals('mapping-count', $countQuery['resultSetMapping']); - self::assertEquals('SELECT COUNT(*) AS count FROM cms_addresses', $countQuery['query']); - - // result set mapping - self::assertCount(3, $class->sqlResultSetMappings); - self::assertArrayHasKey('mapping-count', $class->sqlResultSetMappings); - self::assertArrayHasKey('mapping-find-all', $class->sqlResultSetMappings); - self::assertArrayHasKey('mapping-without-fields', $class->sqlResultSetMappings); - - $findAllMapping = $class->getSqlResultSetMapping('mapping-find-all'); - self::assertEquals('mapping-find-all', $findAllMapping['name']); - self::assertEquals(CmsAddress::class, $findAllMapping['entities'][0]['entityClass']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $findAllMapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'city', 'column' => 'city'], $findAllMapping['entities'][0]['fields'][1]); - self::assertEquals(['name' => 'country', 'column' => 'country'], $findAllMapping['entities'][0]['fields'][2]); - - $withoutFieldsMapping = $class->getSqlResultSetMapping('mapping-without-fields'); - self::assertEquals('mapping-without-fields', $withoutFieldsMapping['name']); - self::assertEquals(CmsAddress::class, $withoutFieldsMapping['entities'][0]['entityClass']); - self::assertEquals([], $withoutFieldsMapping['entities'][0]['fields']); - - $countMapping = $class->getSqlResultSetMapping('mapping-count'); - self::assertEquals('mapping-count', $countMapping['name']); - self::assertEquals(['name' => 'count'], $countMapping['columns'][0]); - } - - /** @group DDC-1663 */ - public function testSqlResultSetMapping(): void - { - $userMetadata = $this->createClassMetadata(CmsUser::class); - $personMetadata = $this->createClassMetadata(CompanyPerson::class); - - // user asserts - self::assertCount(4, $userMetadata->getSqlResultSetMappings()); - - $mapping = $userMetadata->getSqlResultSetMapping('mappingJoinedAddress'); - self::assertEquals([], $mapping['columns']); - self::assertEquals('mappingJoinedAddress', $mapping['name']); - self::assertNull($mapping['entities'][0]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'name'], $mapping['entities'][0]['fields'][1]); - self::assertEquals(['name' => 'status', 'column' => 'status'], $mapping['entities'][0]['fields'][2]); - self::assertEquals(['name' => 'address.zip', 'column' => 'zip'], $mapping['entities'][0]['fields'][3]); - self::assertEquals(['name' => 'address.city', 'column' => 'city'], $mapping['entities'][0]['fields'][4]); - self::assertEquals(['name' => 'address.country', 'column' => 'country'], $mapping['entities'][0]['fields'][5]); - self::assertEquals(['name' => 'address.id', 'column' => 'a_id'], $mapping['entities'][0]['fields'][6]); - self::assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); - - $mapping = $userMetadata->getSqlResultSetMapping('mappingJoinedPhonenumber'); - self::assertEquals([], $mapping['columns']); - self::assertEquals('mappingJoinedPhonenumber', $mapping['name']); - self::assertNull($mapping['entities'][0]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'name'], $mapping['entities'][0]['fields'][1]); - self::assertEquals(['name' => 'status', 'column' => 'status'], $mapping['entities'][0]['fields'][2]); - self::assertEquals(['name' => 'phonenumbers.phonenumber', 'column' => 'number'], $mapping['entities'][0]['fields'][3]); - self::assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); - - $mapping = $userMetadata->getSqlResultSetMapping('mappingUserPhonenumberCount'); - self::assertEquals(['name' => 'numphones'], $mapping['columns'][0]); - self::assertEquals('mappingUserPhonenumberCount', $mapping['name']); - self::assertNull($mapping['entities'][0]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'name'], $mapping['entities'][0]['fields'][1]); - self::assertEquals(['name' => 'status', 'column' => 'status'], $mapping['entities'][0]['fields'][2]); - self::assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); - - $mapping = $userMetadata->getSqlResultSetMapping('mappingMultipleJoinsEntityResults'); - self::assertEquals(['name' => 'numphones'], $mapping['columns'][0]); - self::assertEquals('mappingMultipleJoinsEntityResults', $mapping['name']); - self::assertNull($mapping['entities'][0]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'u_id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'u_name'], $mapping['entities'][0]['fields'][1]); - self::assertEquals(['name' => 'status', 'column' => 'u_status'], $mapping['entities'][0]['fields'][2]); - self::assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); - self::assertNull($mapping['entities'][1]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'a_id'], $mapping['entities'][1]['fields'][0]); - self::assertEquals(['name' => 'zip', 'column' => 'a_zip'], $mapping['entities'][1]['fields'][1]); - self::assertEquals(['name' => 'country', 'column' => 'a_country'], $mapping['entities'][1]['fields'][2]); - self::assertEquals(CmsAddress::class, $mapping['entities'][1]['entityClass']); - - //person asserts - self::assertCount(1, $personMetadata->getSqlResultSetMappings()); - - $mapping = $personMetadata->getSqlResultSetMapping('mappingFetchAll'); - self::assertEquals([], $mapping['columns']); - self::assertEquals('mappingFetchAll', $mapping['name']); - self::assertEquals('discriminator', $mapping['entities'][0]['discriminatorColumn']); - self::assertEquals(['name' => 'id', 'column' => 'id'], $mapping['entities'][0]['fields'][0]); - self::assertEquals(['name' => 'name', 'column' => 'name'], $mapping['entities'][0]['fields'][1]); - self::assertEquals($personMetadata->name, $mapping['entities'][0]['entityClass']); - } - - /** @group DDC-964 */ - public function testAssociationOverridesMapping(): void + #[\PHPUnit\Framework\Attributes\Group('DDC-3579')] + public function testInversedByOverrideMapping(): void { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } + $factory = $this->createClassMetadataFactory(); + $adminMetadata = $factory->getMetadataFor(DDC3579Admin::class); + + // assert groups association mappings + self::assertArrayHasKey('groups', $adminMetadata->associationMappings); + $adminGroups = $adminMetadata->associationMappings['groups']; + + // assert override + self::assertEquals('admins', $adminGroups->inversedBy); + } + #[\PHPUnit\Framework\Attributes\Group('DDC-5934')] + public function testFetchOverrideMapping(): void + { + // check override metadata + $contractMetadata = $this->createClassMetadataFactory()->getMetadataFor(DDC5934Contract::class); + + self::assertArrayHasKey('members', $contractMetadata->associationMappings); + self::assertSame(ClassMetadata::FETCH_EXTRA_LAZY, $contractMetadata->associationMappings['members']->fetch); + } + + #[\PHPUnit\Framework\Attributes\Group('DDC-964')] + public function testAssociationOverridesMapping(): void + { $factory = $this->createClassMetadataFactory(); $adminMetadata = $factory->getMetadataFor(DDC964Admin::class); $guestMetadata = $factory->getMetadataFor(DDC964Guest::class); @@ -764,34 +619,32 @@ public function testAssociationOverridesMapping(): void $adminGroups = $adminMetadata->associationMappings['groups']; // assert not override attributes - self::assertEquals($guestGroups['fieldName'], $adminGroups['fieldName']); - self::assertEquals($guestGroups['type'], $adminGroups['type']); - self::assertEquals($guestGroups['mappedBy'], $adminGroups['mappedBy']); - self::assertEquals($guestGroups['inversedBy'], $adminGroups['inversedBy']); - self::assertEquals($guestGroups['isOwningSide'], $adminGroups['isOwningSide']); - self::assertEquals($guestGroups['fetch'], $adminGroups['fetch']); - self::assertEquals($guestGroups['isCascadeRemove'], $adminGroups['isCascadeRemove']); - self::assertEquals($guestGroups['isCascadePersist'], $adminGroups['isCascadePersist']); - self::assertEquals($guestGroups['isCascadeRefresh'], $adminGroups['isCascadeRefresh']); - self::assertEquals($guestGroups['isCascadeMerge'], $adminGroups['isCascadeMerge']); - self::assertEquals($guestGroups['isCascadeDetach'], $adminGroups['isCascadeDetach']); - - // assert not override attributes - self::assertEquals('ddc964_users_groups', $guestGroups['joinTable']['name']); - self::assertEquals('user_id', $guestGroups['joinTable']['joinColumns'][0]['name']); - self::assertEquals('group_id', $guestGroups['joinTable']['inverseJoinColumns'][0]['name']); - - self::assertEquals(['user_id' => 'id'], $guestGroups['relationToSourceKeyColumns']); - self::assertEquals(['group_id' => 'id'], $guestGroups['relationToTargetKeyColumns']); - self::assertEquals(['user_id', 'group_id'], $guestGroups['joinTableColumns']); - - self::assertEquals('ddc964_users_admingroups', $adminGroups['joinTable']['name']); - self::assertEquals('adminuser_id', $adminGroups['joinTable']['joinColumns'][0]['name']); - self::assertEquals('admingroup_id', $adminGroups['joinTable']['inverseJoinColumns'][0]['name']); - - self::assertEquals(['adminuser_id' => 'id'], $adminGroups['relationToSourceKeyColumns']); - self::assertEquals(['admingroup_id' => 'id'], $adminGroups['relationToTargetKeyColumns']); - self::assertEquals(['adminuser_id', 'admingroup_id'], $adminGroups['joinTableColumns']); + self::assertEquals($guestGroups->fieldName, $adminGroups->fieldName); + self::assertEquals($guestGroups->type(), $adminGroups->type()); + self::assertEquals($guestGroups->inversedBy, $adminGroups->inversedBy); + self::assertEquals($guestGroups->isOwningSide(), $adminGroups->isOwningSide()); + self::assertEquals($guestGroups->fetch, $adminGroups->fetch); + self::assertEquals($guestGroups->isCascadeRemove(), $adminGroups->isCascadeRemove()); + self::assertEquals($guestGroups->isCascadePersist(), $adminGroups->isCascadePersist()); + self::assertEquals($guestGroups->isCascadeRefresh(), $adminGroups->isCascadeRefresh()); + self::assertEquals($guestGroups->isCascadeDetach(), $adminGroups->isCascadeDetach()); + + // assert not override attributes + self::assertEquals('ddc964_users_groups', $guestGroups->joinTable->name); + self::assertEquals('user_id', $guestGroups->joinTable->joinColumns[0]->name); + self::assertEquals('group_id', $guestGroups->joinTable->inverseJoinColumns[0]->name); + + self::assertEquals(['user_id' => 'id'], $guestGroups->relationToSourceKeyColumns); + self::assertEquals(['group_id' => 'id'], $guestGroups->relationToTargetKeyColumns); + self::assertEquals(['user_id', 'group_id'], $guestGroups->joinTableColumns); + + self::assertEquals('ddc964_users_admingroups', $adminGroups->joinTable->name); + self::assertEquals('adminuser_id', $adminGroups->joinTable->joinColumns[0]->name); + self::assertEquals('admingroup_id', $adminGroups->joinTable->inverseJoinColumns[0]->name); + + self::assertEquals(['adminuser_id' => 'id'], $adminGroups->relationToSourceKeyColumns); + self::assertEquals(['admingroup_id' => 'id'], $adminGroups->relationToTargetKeyColumns); + self::assertEquals(['adminuser_id', 'admingroup_id'], $adminGroups->joinTableColumns); // assert address association mappings self::assertArrayHasKey('address', $guestMetadata->associationMappings); @@ -801,101 +654,63 @@ public function testAssociationOverridesMapping(): void $adminAddress = $adminMetadata->associationMappings['address']; // assert not override attributes - self::assertEquals($guestAddress['fieldName'], $adminAddress['fieldName']); - self::assertEquals($guestAddress['type'], $adminAddress['type']); - self::assertEquals($guestAddress['mappedBy'], $adminAddress['mappedBy']); - self::assertEquals($guestAddress['inversedBy'], $adminAddress['inversedBy']); - self::assertEquals($guestAddress['isOwningSide'], $adminAddress['isOwningSide']); - self::assertEquals($guestAddress['fetch'], $adminAddress['fetch']); - self::assertEquals($guestAddress['isCascadeRemove'], $adminAddress['isCascadeRemove']); - self::assertEquals($guestAddress['isCascadePersist'], $adminAddress['isCascadePersist']); - self::assertEquals($guestAddress['isCascadeRefresh'], $adminAddress['isCascadeRefresh']); - self::assertEquals($guestAddress['isCascadeMerge'], $adminAddress['isCascadeMerge']); - self::assertEquals($guestAddress['isCascadeDetach'], $adminAddress['isCascadeDetach']); - - // assert override - self::assertEquals('address_id', $guestAddress['joinColumns'][0]['name']); - self::assertEquals(['address_id' => 'id'], $guestAddress['sourceToTargetKeyColumns']); - self::assertEquals(['address_id' => 'address_id'], $guestAddress['joinColumnFieldNames']); - self::assertEquals(['id' => 'address_id'], $guestAddress['targetToSourceKeyColumns']); - - self::assertEquals('adminaddress_id', $adminAddress['joinColumns'][0]['name']); - self::assertEquals(['adminaddress_id' => 'id'], $adminAddress['sourceToTargetKeyColumns']); - self::assertEquals(['adminaddress_id' => 'adminaddress_id'], $adminAddress['joinColumnFieldNames']); - self::assertEquals(['id' => 'adminaddress_id'], $adminAddress['targetToSourceKeyColumns']); - } - - /** @group DDC-3579 */ - public function testInversedByOverrideMapping(): void - { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - - $factory = $this->createClassMetadataFactory(); - $adminMetadata = $factory->getMetadataFor(DDC3579Admin::class); - - // assert groups association mappings - self::assertArrayHasKey('groups', $adminMetadata->associationMappings); - $adminGroups = $adminMetadata->associationMappings['groups']; + self::assertEquals($guestAddress->fieldName, $adminAddress->fieldName); + self::assertEquals($guestAddress->type(), $adminAddress->type()); + self::assertEquals($guestAddress->inversedBy, $adminAddress->inversedBy); + self::assertEquals($guestAddress->isOwningSide(), $adminAddress->isOwningSide()); + self::assertEquals($guestAddress->fetch, $adminAddress->fetch); + self::assertEquals($guestAddress->isCascadeRemove(), $adminAddress->isCascadeRemove()); + self::assertEquals($guestAddress->isCascadePersist(), $adminAddress->isCascadePersist()); + self::assertEquals($guestAddress->isCascadeRefresh(), $adminAddress->isCascadeRefresh()); + self::assertEquals($guestAddress->isCascadeDetach(), $adminAddress->isCascadeDetach()); // assert override - self::assertEquals('admins', $adminGroups['inversedBy']); - } - - /** @group DDC-5934 */ - public function testFetchOverrideMapping(): void - { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - - // check override metadata - $contractMetadata = $this->createClassMetadataFactory()->getMetadataFor(DDC5934Contract::class); + self::assertEquals('address_id', $guestAddress->joinColumns[0]->name); + self::assertEquals(['address_id' => 'id'], $guestAddress->sourceToTargetKeyColumns); + self::assertEquals(['address_id' => 'address_id'], $guestAddress->joinColumnFieldNames); + self::assertEquals(['id' => 'address_id'], $guestAddress->targetToSourceKeyColumns); - self::assertArrayHasKey('members', $contractMetadata->associationMappings); - self::assertSame(ClassMetadata::FETCH_EXTRA_LAZY, $contractMetadata->associationMappings['members']['fetch']); + self::assertEquals('adminaddress_id', $adminAddress->joinColumns[0]->name); + self::assertEquals(['adminaddress_id' => 'id'], $adminAddress->sourceToTargetKeyColumns); + self::assertEquals(['adminaddress_id' => 'adminaddress_id'], $adminAddress->joinColumnFieldNames); + self::assertEquals(['id' => 'adminaddress_id'], $adminAddress->targetToSourceKeyColumns); } - /** @group DDC-964 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-964')] public function testAttributeOverridesMapping(): void { - if (PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80100) { - $this->markTestSkipped('Does not work on PHP 8.0.* due to nested attributes missing.'); - } - $factory = $this->createClassMetadataFactory(); $guestMetadata = $factory->getMetadataFor(DDC964Guest::class); $adminMetadata = $factory->getMetadataFor(DDC964Admin::class); - self::assertTrue($adminMetadata->fieldMappings['id']['id']); - self::assertEquals('id', $adminMetadata->fieldMappings['id']['fieldName']); - self::assertEquals('user_id', $adminMetadata->fieldMappings['id']['columnName']); + self::assertTrue($adminMetadata->fieldMappings['id']->id); + self::assertEquals('id', $adminMetadata->fieldMappings['id']->fieldName); + self::assertEquals('user_id', $adminMetadata->fieldMappings['id']->columnName); self::assertEquals(['user_id' => 'id', 'user_name' => 'name'], $adminMetadata->fieldNames); self::assertEquals(['id' => 'user_id', 'name' => 'user_name'], $adminMetadata->columnNames); - self::assertEquals(150, $adminMetadata->fieldMappings['id']['length']); + self::assertEquals(150, $adminMetadata->fieldMappings['id']->length); - self::assertEquals('name', $adminMetadata->fieldMappings['name']['fieldName']); - self::assertEquals('user_name', $adminMetadata->fieldMappings['name']['columnName']); - self::assertEquals(250, $adminMetadata->fieldMappings['name']['length']); - self::assertTrue($adminMetadata->fieldMappings['name']['nullable']); - self::assertFalse($adminMetadata->fieldMappings['name']['unique']); + self::assertEquals('name', $adminMetadata->fieldMappings['name']->fieldName); + self::assertEquals('user_name', $adminMetadata->fieldMappings['name']->columnName); + self::assertEquals(250, $adminMetadata->fieldMappings['name']->length); + self::assertTrue($adminMetadata->fieldMappings['name']->nullable); + self::assertFalse($adminMetadata->fieldMappings['name']->unique); - self::assertTrue($guestMetadata->fieldMappings['id']['id']); - self::assertEquals('guest_id', $guestMetadata->fieldMappings['id']['columnName']); - self::assertEquals('id', $guestMetadata->fieldMappings['id']['fieldName']); + self::assertTrue($guestMetadata->fieldMappings['id']->id); + self::assertEquals('guest_id', $guestMetadata->fieldMappings['id']->columnName); + self::assertEquals('id', $guestMetadata->fieldMappings['id']->fieldName); self::assertEquals(['guest_id' => 'id', 'guest_name' => 'name'], $guestMetadata->fieldNames); self::assertEquals(['id' => 'guest_id', 'name' => 'guest_name'], $guestMetadata->columnNames); - self::assertEquals(140, $guestMetadata->fieldMappings['id']['length']); + self::assertEquals(140, $guestMetadata->fieldMappings['id']->length); - self::assertEquals('name', $guestMetadata->fieldMappings['name']['fieldName']); - self::assertEquals('guest_name', $guestMetadata->fieldMappings['name']['columnName']); - self::assertEquals(240, $guestMetadata->fieldMappings['name']['length']); - self::assertFalse($guestMetadata->fieldMappings['name']['nullable']); - self::assertTrue($guestMetadata->fieldMappings['name']['unique']); + self::assertEquals('name', $guestMetadata->fieldMappings['name']->fieldName); + self::assertEquals('guest_name', $guestMetadata->fieldMappings['name']->columnName); + self::assertEquals(240, $guestMetadata->fieldMappings['name']->length); + self::assertFalse($guestMetadata->fieldMappings['name']->nullable); + self::assertTrue($guestMetadata->fieldMappings['name']->unique); } - /** @group DDC-1955 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1955')] public function testEntityListeners(): void { $em = $this->getTestEntityManager(); @@ -924,7 +739,7 @@ public function testEntityListeners(): void self::assertEquals($flexClass->entityListeners, $superClass->entityListeners); } - /** @group DDC-1955 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1955')] public function testEntityListenersOverride(): void { $em = $this->getTestEntityManager(); @@ -955,7 +770,7 @@ public function testEntityListenersOverride(): void self::assertEquals('prePersistHandler2', $prePersist['method']); } - /** @group DDC-1955 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-1955')] public function testEntityListenersNamingConvention(): void { $em = $this->getTestEntityManager(); @@ -1008,7 +823,7 @@ public function testEntityListenersNamingConvention(): void self::assertEquals(Events::preFlush, $preFlush['method']); } - /** @group DDC-2183 */ + #[\PHPUnit\Framework\Attributes\Group('DDC-2183')] public function testSecondLevelCacheMapping(): void { $em = $this->getTestEntityManager(); @@ -1020,25 +835,23 @@ public function testSecondLevelCacheMapping(): void self::assertEquals('doctrine_tests_models_cache_city', $class->cache['region']); self::assertArrayHasKey('state', $class->associationMappings); - self::assertArrayHasKey('cache', $class->associationMappings['state']); - self::assertArrayHasKey('usage', $class->associationMappings['state']['cache']); - self::assertArrayHasKey('region', $class->associationMappings['state']['cache']); - self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['state']['cache']['usage']); - self::assertEquals('doctrine_tests_models_cache_city__state', $class->associationMappings['state']['cache']['region']); + self::assertNotNull($class->associationMappings['state']->cache); + self::assertArrayHasKey('usage', $class->associationMappings['state']->cache); + self::assertArrayHasKey('region', $class->associationMappings['state']->cache); + self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['state']->cache['usage']); + self::assertEquals('doctrine_tests_models_cache_city__state', $class->associationMappings['state']->cache['region']); self::assertArrayHasKey('attractions', $class->associationMappings); - self::assertArrayHasKey('cache', $class->associationMappings['attractions']); - self::assertArrayHasKey('usage', $class->associationMappings['attractions']['cache']); - self::assertArrayHasKey('region', $class->associationMappings['attractions']['cache']); - self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['attractions']['cache']['usage']); - self::assertEquals('doctrine_tests_models_cache_city__attractions', $class->associationMappings['attractions']['cache']['region']); + self::assertNotNull($class->associationMappings['attractions']->cache); + self::assertArrayHasKey('usage', $class->associationMappings['attractions']->cache); + self::assertArrayHasKey('region', $class->associationMappings['attractions']->cache); + self::assertEquals(ClassMetadata::CACHE_USAGE_READ_ONLY, $class->associationMappings['attractions']->cache['usage']); + self::assertEquals('doctrine_tests_models_cache_city__attractions', $class->associationMappings['attractions']->cache['region']); } - /** - * @group DDC-2825 - * @group 881 - */ - public function testSchemaDefinitionViaExplicitTableSchemaAnnotationProperty(): void + #[\PHPUnit\Framework\Attributes\Group('DDC-2825')] + #[\PHPUnit\Framework\Attributes\Group('881')] + public function testSchemaDefinitionViaExplicitTableSchemaAttributeProperty(): void { $metadata = $this->createClassMetadataFactory()->getMetadataFor(ExplicitSchemaAndTable::class); assert($metadata instanceof ClassMetadata); @@ -1047,11 +860,9 @@ public function testSchemaDefinitionViaExplicitTableSchemaAnnotationProperty(): self::assertSame('explicit_table', $metadata->getTableName()); } - /** - * @group DDC-2825 - * @group 881 - */ - public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAnnotationProperty(): void + #[\PHPUnit\Framework\Attributes\Group('DDC-2825')] + #[\PHPUnit\Framework\Attributes\Group('881')] + public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAttributeProperty(): void { $metadata = $this->createClassMetadataFactory()->getMetadataFor(SchemaAndTableInTableName::class); assert($metadata instanceof ClassMetadata); @@ -1060,10 +871,8 @@ public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAnnotation self::assertSame('implicit_table', $metadata->getTableName()); } - /** - * @group DDC-514 - * @group DDC-1015 - */ + #[\PHPUnit\Framework\Attributes\Group('DDC-514')] + #[\PHPUnit\Framework\Attributes\Group('DDC-1015')] public function testDiscriminatorColumnDefaultLength(): void { if (str_contains(static::class, 'PHPMappingDriver')) { @@ -1071,15 +880,13 @@ public function testDiscriminatorColumnDefaultLength(): void } $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class); - self::assertEquals(255, $class->discriminatorColumn['length']); + self::assertEquals(255, $class->discriminatorColumn->length); $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class); - self::assertEquals(255, $class->discriminatorColumn['length']); + self::assertEquals(255, $class->discriminatorColumn->length); } - /** - * @group DDC-514 - * @group DDC-1015 - */ + #[\PHPUnit\Framework\Attributes\Group('DDC-514')] + #[\PHPUnit\Framework\Attributes\Group('DDC-1015')] public function testDiscriminatorColumnDefaultType(): void { if (str_contains(static::class, 'PHPMappingDriver')) { @@ -1087,15 +894,13 @@ public function testDiscriminatorColumnDefaultType(): void } $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class); - self::assertEquals('string', $class->discriminatorColumn['type']); + self::assertEquals('string', $class->discriminatorColumn->type); $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class); - self::assertEquals('string', $class->discriminatorColumn['type']); + self::assertEquals('string', $class->discriminatorColumn->type); } - /** - * @group DDC-514 - * @group DDC-1015 - */ + #[\PHPUnit\Framework\Attributes\Group('DDC-514')] + #[\PHPUnit\Framework\Attributes\Group('DDC-1015')] public function testDiscriminatorColumnDefaultName(): void { if (str_contains(static::class, 'PHPMappingDriver')) { @@ -1103,16 +908,16 @@ public function testDiscriminatorColumnDefaultName(): void } $class = $this->createClassMetadata(SingleTableEntityNoDiscriminatorColumnMapping::class); - self::assertEquals('dtype', $class->discriminatorColumn['name']); + self::assertEquals('dtype', $class->discriminatorColumn->name); $class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class); - self::assertEquals('dtype', $class->discriminatorColumn['name']); + self::assertEquals('dtype', $class->discriminatorColumn->name); } public function testReservedWordInTableColumn(): void { $metadata = $this->createClassMetadata(ReservedWordInTableColumn::class); - self::assertSame('count', $metadata->getFieldMapping('count')['columnName']); + self::assertSame('count', $metadata->getFieldMapping('count')->columnName); } public function testInsertableColumn(): void @@ -1121,10 +926,8 @@ public function testInsertableColumn(): void $mapping = $metadata->getFieldMapping('nonInsertableContent'); - self::assertArrayHasKey('notInsertable', $mapping); - self::assertArrayHasKey('generated', $mapping); - self::assertSame(ClassMetadata::GENERATED_INSERT, $mapping['generated']); - self::assertArrayNotHasKey('notInsertable', $metadata->getFieldMapping('insertableContent')); + self::assertSame(ClassMetadata::GENERATED_INSERT, $mapping->generated); + self::assertNull($metadata->getFieldMapping('insertableContent')->notInsertable); } public function testUpdatableColumn(): void @@ -1133,34 +936,18 @@ public function testUpdatableColumn(): void $mapping = $metadata->getFieldMapping('nonUpdatableContent'); - self::assertArrayHasKey('notUpdatable', $mapping); - self::assertArrayHasKey('generated', $mapping); - self::assertSame(ClassMetadata::GENERATED_ALWAYS, $mapping['generated']); - self::assertArrayNotHasKey('notUpdatable', $metadata->getFieldMapping('updatableContent')); + self::assertSame(ClassMetadata::GENERATED_ALWAYS, $mapping->generated); + self::assertNull($metadata->getFieldMapping('updatableContent')->notUpdatable); } - /** - * @requires PHP 8.1 - */ public function testEnumType(): void { $metadata = $this->createClassMetadata(Card::class); - self::assertEquals(Suit::class, $metadata->fieldMappings['suit']['enumType']); + self::assertEquals(Suit::class, $metadata->fieldMappings['suit']->enumType); } } -/** - * @Entity - * @HasLifecycleCallbacks - * @Table( - * name="cms_users", - * uniqueConstraints={@UniqueConstraint(name="search_idx", columns={"name", "user_email"}, options={"where": "name IS NOT NULL"}), @UniqueConstraint(name="phone_idx", fields={"name", "phone"})}, - * indexes={@Index(name="name_idx", columns={"name"}), @Index(name="0", columns={"user_email"}), @index(name="fields", fields={"name", "email"})}, - * options={"foo": "bar", "baz": {"key": "val"}} - * ) - * @NamedQueries({@NamedQuery(name="all", query="SELECT u FROM __CLASS__ u")}) - */ #[ORM\Entity()] #[ORM\HasLifecycleCallbacks()] #[ORM\Table(name: 'cms_users', options: ['foo' => 'bar', 'baz' => ['key' => 'val']])] @@ -1171,87 +958,53 @@ public function testEnumType(): void #[ORM\UniqueConstraint(name: 'phone_idx', fields: ['name', 'phone'])] class User { - /** - * @var int - * @Id - * @Column(type="integer", options={"foo": "bar", "unsigned": false}) - * @GeneratedValue(strategy="AUTO") - * @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100) - **/ + /** @var int **/ #[ORM\Id] #[ORM\Column(type: 'integer', options: ['foo' => 'bar', 'unsigned' => false])] #[ORM\GeneratedValue(strategy: 'AUTO')] #[ORM\SequenceGenerator(sequenceName: 'tablename_seq', initialValue: 1, allocationSize: 100)] public $id; - /** - * @var string - * @Column(length=50, nullable=true, unique=true, options={"foo": "bar", "baz": {"key": "val"}, "fixed": false}) - */ + /** @var string */ #[ORM\Column(length: 50, nullable: true, unique: true, options: ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false])] public $name; - /** - * @var string - * @Column(name="user_email", columnDefinition="CHAR(32) NOT NULL") - */ + /** @var string */ #[ORM\Column(name: 'user_email', columnDefinition: 'CHAR(32) NOT NULL')] public $email; - /** - * @var Address - * @OneToOne(targetEntity="Address", cascade={"remove"}, inversedBy="user") - * @JoinColumn(onDelete="CASCADE") - */ + /** @var Address */ #[ORM\OneToOne(targetEntity: 'Address', cascade: ['remove'], inversedBy: 'user')] #[ORM\JoinColumn(onDelete: 'CASCADE')] public $address; - /** - * @var Collection - * @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"}, orphanRemoval=true) - * @OrderBy({"number"="ASC"}) - */ + /** @var Collection */ #[ORM\OneToMany(targetEntity: 'Phonenumber', mappedBy: 'user', cascade: ['persist'], orphanRemoval: true)] #[ORM\OrderBy(['number' => 'ASC'])] public $phonenumbers; - /** - * @var Collection - * @ManyToMany(targetEntity="Group", cascade={"all"}) - * @JoinTable(name="cms_user_groups", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id", nullable=false, unique=false)}, - * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id", columnDefinition="INT NULL")} - * ) - */ + /** @var Collection */ #[ORM\ManyToMany(targetEntity: 'Group', cascade: ['all'])] #[ORM\JoinTable(name: 'cms_user_groups')] #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, unique: false)] #[ORM\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', columnDefinition: 'INT NULL')] public $groups; - /** - * @var int - * @Column(type="integer") - * @Version - */ + /** @var int */ #[ORM\Column(type: 'integer')] #[ORM\Version] public $version; - /** @PrePersist */ #[ORM\PrePersist] public function doStuffOnPrePersist(): void { } - /** @PrePersist */ #[ORM\PrePersist] public function doOtherStuffOnPrePersistToo(): void { } - /** @PostPersist */ #[ORM\PostPersist] public function doStuffOnPostPersist(): void { @@ -1264,7 +1017,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'name' => 'cms_users', 'options' => ['foo' => 'bar', 'baz' => ['key' => 'val']], - ] + ], ); $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); $metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist'); @@ -1277,7 +1030,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'integer', 'columnName' => 'id', 'options' => ['foo' => 'bar', 'unsigned' => false], - ] + ], ); $metadata->mapField( [ @@ -1288,7 +1041,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'nullable' => true, 'columnName' => 'name', 'options' => ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false], - ] + ], ); $metadata->mapField( [ @@ -1296,7 +1049,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'type' => 'string', 'columnName' => 'user_email', 'columnDefinition' => 'CHAR(32) NOT NULL', - ] + ], ); $mapping = ['fieldName' => 'version', 'type' => 'integer']; $metadata->setVersionMapping($mapping); @@ -1320,7 +1073,7 @@ public static function loadMetadata(ClassMetadata $metadata): void ], ], 'orphanRemoval' => false, - ] + ], ); $metadata->mapOneToMany( [ @@ -1332,7 +1085,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'orphanRemoval' => true, 'orderBy' => ['number' => 'ASC'], - ] + ], ); $metadata->mapManyToMany( [ @@ -1343,8 +1096,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 0 => 'remove', 1 => 'persist', 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', + 3 => 'detach', ], 'mappedBy' => null, 'joinTable' => @@ -1370,8 +1122,8 @@ public static function loadMetadata(ClassMetadata $metadata): void ], ], ], - 'orderBy' => null, - ] + 'orderBy' => [], + ], ); $metadata->table['uniqueConstraints'] = [ 'search_idx' => ['columns' => ['name', 'user_email'], 'options' => ['where' => 'name IS NOT NULL']], @@ -1387,43 +1139,28 @@ public static function loadMetadata(ClassMetadata $metadata): void 'sequenceName' => 'tablename_seq', 'allocationSize' => 100, 'initialValue' => 1, - ] - ); - $metadata->addNamedQuery( - [ - 'name' => 'all', - 'query' => 'SELECT u FROM __CLASS__ u', - ] + ], ); } } -/** - * @Entity - * @Table( - * indexes={@Index(name="name_idx", columns={"name"}, fields={"email"})}, - * ) - */ +#[Table] +#[Index(name: 'name_idx', columns: ['name'], fields: ['email'])] +#[Entity] class UserIncorrectIndex { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - **/ + /** @var int **/ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $name; - /** - * @var string - * @Column(name="user_email") - */ + /** @var string */ + #[Column(name: 'user_email')] public $email; public static function loadMetadata(ClassMetadata $metadata): void @@ -1436,20 +1173,20 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'id', 'type' => 'integer', 'columnName' => 'id', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'name', 'type' => 'string', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'email', 'type' => 'string', 'columnName' => 'user_email', - ] + ], ); $metadata->table['indexes'] = [ 'name_idx' => ['columns' => ['name'], 'fields' => ['email']], @@ -1457,32 +1194,23 @@ public static function loadMetadata(ClassMetadata $metadata): void } } -/** - * @Entity - * @Table( - * uniqueConstraints={@UniqueConstraint(name="name_idx", columns={"name"}, fields={"email"})}, - * ) - */ +#[Table] +#[UniqueConstraint(name: 'name_idx', columns: ['name'], fields: ['email'])] +#[Entity] class UserIncorrectUniqueConstraint { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - **/ + /** @var int **/ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $name; - /** - * @var string - * @Column(name="user_email") - */ + /** @var string */ + #[Column(name: 'user_email')] public $email; public static function loadMetadata(ClassMetadata $metadata): void @@ -1495,20 +1223,20 @@ public static function loadMetadata(ClassMetadata $metadata): void 'fieldName' => 'id', 'type' => 'integer', 'columnName' => 'id', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'name', 'type' => 'string', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'email', 'type' => 'string', 'columnName' => 'user_email', - ] + ], ); $metadata->table['uniqueConstraints'] = [ 'name_idx' => ['columns' => ['name'], 'fields' => ['email']], @@ -1516,39 +1244,26 @@ public static function loadMetadata(ClassMetadata $metadata): void } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"}) - * @DiscriminatorColumn(name="discr", length=32, type="string") - */ #[ORM\Entity] #[ORM\InheritanceType('SINGLE_TABLE')] #[ORM\DiscriminatorColumn(name: 'discr', length: 32, type: 'string')] #[ORM\DiscriminatorMap(['cat' => 'Cat', 'dog' => 'Dog'])] abstract class Animal { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="CUSTOM") - * @CustomIdGenerator(class="stdClass") - */ + /** @var string */ #[ORM\Id] #[ORM\Column(type: 'string')] #[ORM\GeneratedValue(strategy: 'CUSTOM')] - #[ORM\CustomIdGenerator(class: 'stdClass')] + #[ORM\CustomIdGenerator(class: stdClass::class)] public $id; public static function loadMetadata(ClassMetadata $metadata): void { $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM); - $metadata->setCustomGeneratorDefinition(['class' => 'stdClass']); + $metadata->setCustomGeneratorDefinition(['class' => stdClass::class]); } } -/** @Entity */ #[ORM\Entity] class Cat extends Animal { @@ -1557,7 +1272,6 @@ public static function loadMetadata(ClassMetadata $metadata): void } } -/** @Entity */ #[ORM\Entity] class Dog extends Animal { @@ -1566,39 +1280,26 @@ public static function loadMetadata(ClassMetadata $metadata): void } } -/** @Entity */ #[ORM\Entity] class DDC1170Entity { - public function __construct(?string $value = null) - { - $this->value = $value; + public function __construct( + #[ORM\Column(columnDefinition: 'VARCHAR(255) NOT NULL')] + private string|null $value = null, + ) { } - /** - * @var int - * @Id - * @GeneratedValue(strategy="NONE") - * @Column(type="integer", columnDefinition = "INT unsigned NOT NULL") - **/ #[ORM\Id] #[ORM\GeneratedValue(strategy: 'NONE')] #[ORM\Column(type: 'integer', columnDefinition: 'INT UNSIGNED NOT NULL')] - private $id; - - /** - * @var string|null - * @Column(columnDefinition = "VARCHAR(255) NOT NULL") - */ - #[ORM\Column(columnDefinition: 'VARCHAR(255) NOT NULL')] - private $value; + private int $id; public function getId(): int { return $this->id; } - public function getValue(): ?string + public function getValue(): string|null { return $this->value; } @@ -1610,38 +1311,27 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'columnDefinition' => 'INT unsigned NOT NULL', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'value', 'columnDefinition' => 'VARCHAR(255) NOT NULL', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"ONE" = "DDC807SubClasse1", "TWO" = "DDC807SubClasse2"}) - * @DiscriminatorColumn(name = "dtype", columnDefinition="ENUM('ONE','TWO')") - */ #[ORM\Entity] #[ORM\InheritanceType('SINGLE_TABLE')] #[ORM\DiscriminatorColumn(name: 'dtype', columnDefinition: "ENUM('ONE','TWO')")] #[ORM\DiscriminatorMap(['ONE' => 'DDC807SubClasse1', 'TWO' => 'DDC807SubClasse2'])] class DDC807Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - **/ + /** @var int **/ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'NONE')] @@ -1653,7 +1343,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setDiscriminatorColumn( @@ -1661,7 +1351,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'name' => 'dtype', 'type' => 'string', 'columnDefinition' => "ENUM('ONE','TWO')", - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); @@ -1685,21 +1375,13 @@ class Group { } -/** - * @Entity - * @Table(indexes={@Index(columns={"content"}, flags={"fulltext"}, options={"where": "content IS NOT NULL"})}) - */ #[ORM\Entity] #[ORM\Table(name: 'Comment')] #[ORM\Index(columns: ['content'], flags: ['fulltext'], options: ['where' => 'content IS NOT NULL'])] class Comment { - /** - * @var string - * @Column(type="text") - */ #[ORM\Column(type: 'text')] - private $content; + private string $content; public static function loadMetadata(ClassMetadata $metadata): void { @@ -1709,7 +1391,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'indexes' => [ ['columns' => ['content'], 'flags' => ['fulltext'], 'options' => ['where' => 'content IS NOT NULL']], ], - ] + ], ); $metadata->mapField( @@ -1722,30 +1404,17 @@ public static function loadMetadata(ClassMetadata $metadata): void 'nullable' => false, 'precision' => 0, 'columnName' => 'content', - ] + ], ); } } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({ - * "ONE" = "SingleTableEntityNoDiscriminatorColumnMappingSub1", - * "TWO" = "SingleTableEntityNoDiscriminatorColumnMappingSub2" - * }) - */ #[ORM\Entity] #[ORM\InheritanceType('SINGLE_TABLE')] #[ORM\DiscriminatorMap(['ONE' => 'SingleTableEntityNoDiscriminatorColumnMappingSub1', 'TWO' => 'SingleTableEntityNoDiscriminatorColumnMappingSub2'])] class SingleTableEntityNoDiscriminatorColumnMapping { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'NONE')] @@ -1757,7 +1426,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); @@ -1771,27 +1440,13 @@ class SingleTableEntityNoDiscriminatorColumnMappingSub2 extends SingleTableEntit { } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({ - * "ONE" = "SingleTableEntityIncompleteDiscriminatorColumnMappingSub1", - * "TWO" = "SingleTableEntityIncompleteDiscriminatorColumnMappingSub2" - * }) - * @DiscriminatorColumn(name="dtype") - */ #[ORM\Entity] #[ORM\InheritanceType('SINGLE_TABLE')] #[ORM\DiscriminatorMap(['ONE' => 'SingleTableEntityNoDiscriminatorColumnMappingSub1', 'TWO' => 'SingleTableEntityNoDiscriminatorColumnMappingSub2'])] #[ORM\DiscriminatorColumn(name: 'dtype')] class SingleTableEntityIncompleteDiscriminatorColumnMapping { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'NONE')] @@ -1803,7 +1458,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); @@ -1817,25 +1472,16 @@ class SingleTableEntityIncompleteDiscriminatorColumnMappingSub2 extends SingleTa { } -/** @Entity */ #[ORM\Entity] class ReservedWordInTableColumn { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ + /** @var int */ #[ORM\Id] #[ORM\Column(type: 'integer')] #[ORM\GeneratedValue(strategy: 'NONE')] public $id; - /** - * @var string|null - * @Column(name="`count`", type="integer") - */ + /** @var string|null */ #[ORM\Column(name: '`count`', type: 'integer')] public $count; @@ -1846,14 +1492,14 @@ public static function loadMetadata(ClassMetadata $metadata): void 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'count', 'type' => 'integer', 'columnName' => '`count`', - ] + ], ); } } @@ -1866,28 +1512,16 @@ class UserMissingAttributes extends User { } - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", enumType=GH10288People::class) - * @DiscriminatorMap({ - * "boss" = GH10288EnumTypeBoss::class - * }) - */ #[Entity] #[InheritanceType('SINGLE_TABLE')] #[DiscriminatorColumn(name: 'discr', enumType: GH10288People::class)] #[DiscriminatorMap(['boss' => GH10288EnumTypeBoss::class])] abstract class GH10288EnumTypePerson { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - public $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + public int|null $id = null; public static function loadMetadata(ClassMetadata $metadata): void { @@ -1895,21 +1529,20 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setDiscriminatorColumn( [ 'name' => 'discr', 'enumType' => GH10288People::class, - ] + ], ); - $metadata->setIdGeneratorType(ORM\ClassMetadataInfo::GENERATOR_TYPE_NONE); + $metadata->setIdGeneratorType(ORM\ClassMetadata::GENERATOR_TYPE_NONE); } } -/** @Entity */ #[Entity] class GH10288EnumTypeBoss extends GH10288EnumTypePerson { diff --git a/tests/Tests/ORM/Mapping/NamingStrategy/JoinColumnClassNamingStrategy.php b/tests/Tests/ORM/Mapping/NamingStrategy/JoinColumnClassNamingStrategy.php index 0437cafbc7d..e78dee59ffd 100644 --- a/tests/Tests/ORM/Mapping/NamingStrategy/JoinColumnClassNamingStrategy.php +++ b/tests/Tests/ORM/Mapping/NamingStrategy/JoinColumnClassNamingStrategy.php @@ -13,10 +13,7 @@ */ class JoinColumnClassNamingStrategy extends DefaultNamingStrategy { - /** - * {@inheritDoc} - */ - public function joinColumnName($propertyName, $className = null) + public function joinColumnName(string $propertyName, string|null $className = null): string { return strtolower($this->classToTableName($className)) . '_' . $propertyName diff --git a/tests/Tests/ORM/Mapping/NamingStrategyTest.php b/tests/Tests/ORM/Mapping/NamingStrategyTest.php index ba9b03c0fe1..ecade647392 100644 --- a/tests/Tests/ORM/Mapping/NamingStrategyTest.php +++ b/tests/Tests/ORM/Mapping/NamingStrategyTest.php @@ -9,11 +9,13 @@ use Doctrine\ORM\Mapping\UnderscoreNamingStrategy; use Doctrine\Tests\ORM\Mapping\NamingStrategy\JoinColumnClassNamingStrategy; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use const CASE_LOWER; use const CASE_UPPER; -/** @group DDC-559 */ +#[Group('DDC-559')] class NamingStrategyTest extends OrmTestCase { private static function defaultNaming(): DefaultNamingStrategy @@ -31,16 +33,6 @@ private static function underscoreNamingUpper(): UnderscoreNamingStrategy return new UnderscoreNamingStrategy(CASE_UPPER); } - private static function numberAwareUnderscoreNamingLower(): UnderscoreNamingStrategy - { - return new UnderscoreNamingStrategy(CASE_LOWER, true); - } - - private static function numberAwareUnderscoreNamingUpper(): UnderscoreNamingStrategy - { - return new UnderscoreNamingStrategy(CASE_UPPER, true); - } - /** * Data Provider for NamingStrategy#classToTableName * @@ -58,24 +50,16 @@ public static function dataClassToTableName(): array // UnderscoreNamingStrategy [self::underscoreNamingLower(), 'some_class_name', '\Name\Space\SomeClassName'], [self::underscoreNamingLower(), 'name', '\Some\Class\Name'], - [self::underscoreNamingLower(), 'name2test', '\Some\Class\Name2Test'], + [self::underscoreNamingLower(), 'name2_test', '\Some\Class\Name2Test'], + [self::underscoreNamingLower(), 'name2test', '\Some\Class\Name2test'], [self::underscoreNamingUpper(), 'SOME_CLASS_NAME', '\Name\Space\SomeClassName'], [self::underscoreNamingUpper(), 'NAME', '\Some\Class\Name'], - [self::underscoreNamingUpper(), 'NAME2TEST', '\Some\Class\Name2Test'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'some_class_name', '\Name\Space\SomeClassName'], - [self::numberAwareUnderscoreNamingLower(), 'name', '\Some\Class\Name'], - [self::numberAwareUnderscoreNamingLower(), 'name2_test', '\Some\Class\Name2Test'], - [self::numberAwareUnderscoreNamingLower(), 'name2test', '\Some\Class\Name2test'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_CLASS_NAME', '\Name\Space\SomeClassName'], - [self::numberAwareUnderscoreNamingUpper(), 'NAME', '\Some\Class\Name'], - [self::numberAwareUnderscoreNamingUpper(), 'NAME2_TEST', '\Some\Class\Name2Test'], - [self::numberAwareUnderscoreNamingUpper(), 'NAME2TEST', '\Some\Class\Name2test'], + [self::underscoreNamingUpper(), 'NAME2_TEST', '\Some\Class\Name2Test'], + [self::underscoreNamingUpper(), 'NAME2TEST', '\Some\Class\Name2test'], ]; } - /** @dataProvider dataClassToTableName */ + #[DataProvider('dataClassToTableName')] public function testClassToTableName(NamingStrategy $strategy, string $expected, string $className): void { self::assertSame($expected, $strategy->classToTableName($className)); @@ -98,32 +82,22 @@ public static function dataPropertyToColumnName(): array // UnderscoreNamingStrategy [self::underscoreNamingLower(), 'some_property', 'someProperty', 'Some\Class'], - [self::underscoreNamingLower(), 'base64encoded', 'base64Encoded', 'Some\Class'], + [self::underscoreNamingLower(), 'base64_encoded', 'base64Encoded', 'Some\Class'], [self::underscoreNamingLower(), 'base64encoded', 'base64encoded', 'Some\Class'], [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'someProperty', 'Some\Class'], [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'some_property', 'Some\Class'], [self::underscoreNamingUpper(), 'SOME_PROPERTY', 'SOME_PROPERTY', 'Some\Class'], - [self::underscoreNamingUpper(), 'BASE64ENCODED', 'base64Encoded', 'Some\Class'], + [self::underscoreNamingUpper(), 'BASE64_ENCODED', 'base64Encoded', 'Some\Class'], [self::underscoreNamingUpper(), 'BASE64ENCODED', 'base64encoded', 'Some\Class'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'some_property', 'someProperty', 'Some\Class'], - [self::numberAwareUnderscoreNamingLower(), 'base64_encoded', 'base64Encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingLower(), 'base64encoded', 'base64encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_PROPERTY', 'someProperty', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_PROPERTY', 'some_property', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_PROPERTY', 'SOME_PROPERTY', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'BASE64_ENCODED', 'base64Encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'BASE64ENCODED', 'base64encoded', 'Some\Class'], ]; } - /** @dataProvider dataPropertyToColumnName */ + #[DataProvider('dataPropertyToColumnName')] public function testPropertyToColumnName( NamingStrategy $strategy, string $expected, string $propertyName, - string $className + string $className, ): void { self::assertSame($expected, $strategy->propertyToColumnName($propertyName, $className)); } @@ -142,14 +116,10 @@ public static function dataReferenceColumnName(): array // UnderscoreNamingStrategy [self::underscoreNamingLower(), 'id'], [self::underscoreNamingUpper(), 'ID'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'id'], - [self::numberAwareUnderscoreNamingUpper(), 'ID'], ]; } - /** @dataProvider dataReferenceColumnName */ + #[DataProvider('dataReferenceColumnName')] public function testReferenceColumnName(NamingStrategy $strategy, string $expected): void { self::assertSame($expected, $strategy->referenceColumnName()); @@ -171,17 +141,11 @@ public static function dataJoinColumnName(): array // UnderscoreNamingStrategy [self::underscoreNamingLower(), 'some_column_id', 'someColumn', 'Some\Class'], - [self::underscoreNamingLower(), 'base64encoded_id', 'base64Encoded', 'Some\Class'], + [self::underscoreNamingLower(), 'base64_encoded_id', 'base64Encoded', 'Some\Class'], + [self::underscoreNamingLower(), 'base64encoded_id', 'base64encoded', 'Some\Class'], [self::underscoreNamingUpper(), 'SOME_COLUMN_ID', 'someColumn', 'Some\Class'], - [self::underscoreNamingUpper(), 'BASE64ENCODED_ID', 'base64Encoded', 'Some\Class'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'some_column_id', 'someColumn', 'Some\Class'], - [self::numberAwareUnderscoreNamingLower(), 'base64_encoded_id', 'base64Encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingLower(), 'base64encoded_id', 'base64encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_COLUMN_ID', 'someColumn', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'BASE64_ENCODED_ID', 'base64Encoded', 'Some\Class'], - [self::numberAwareUnderscoreNamingUpper(), 'BASE64ENCODED_ID', 'base64encoded', 'Some\Class'], + [self::underscoreNamingUpper(), 'BASE64_ENCODED_ID', 'base64Encoded', 'Some\Class'], + [self::underscoreNamingUpper(), 'BASE64ENCODED_ID', 'base64encoded', 'Some\Class'], // JoinColumnClassNamingStrategy [new JoinColumnClassNamingStrategy(), 'classname_someColumn_id', 'someColumn', 'Some\ClassName'], @@ -189,16 +153,12 @@ public static function dataJoinColumnName(): array ]; } - /** - * @param UnderscoreNamingStrategy|DefaultNamingStrategy $strategy - * - * @dataProvider dataJoinColumnName - */ + #[DataProvider('dataJoinColumnName')] public function testJoinColumnName( - NamingStrategy $strategy, + UnderscoreNamingStrategy|DefaultNamingStrategy $strategy, string $expected, string $propertyName, - ?string $className = null + string|null $className = null, ): void { self::assertSame($expected, $strategy->joinColumnName($propertyName, $className)); } @@ -206,7 +166,7 @@ public function testJoinColumnName( /** * Data Provider for NamingStrategy#joinTableName * - * @return array + * @return array */ public static function dataJoinTableName(): array { @@ -218,33 +178,23 @@ public static function dataJoinTableName(): array // UnderscoreNamingStrategy [self::underscoreNamingLower(), 'some_class_name_class_name', 'SomeClassName', 'Some\ClassName', 'some_property'], - [self::underscoreNamingLower(), 'class1test_class2test', 'Class1Test', 'Some\Class2Test', 'some_property'], + [self::underscoreNamingLower(), 'class1_test_class2_test', 'Class1Test', 'Some\Class2Test', 'some_property'], [self::underscoreNamingLower(), 'some_class_name_class_name', '\SomeClassName', 'ClassName', 'some_property'], [self::underscoreNamingLower(), 'name_class_name', '\Some\Class\Name', 'ClassName', 'some_property'], [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', 'SomeClassName', 'Some\ClassName', 'some_property'], - [self::underscoreNamingUpper(), 'CLASS1TEST_CLASS2TEST', 'Class1Test', 'Some\Class2Test', 'some_property'], + [self::underscoreNamingUpper(), 'CLASS1_TEST_CLASS2_TEST', 'Class1Test', 'Some\Class2Test', 'some_property'], [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', '\SomeClassName', 'ClassName', 'some_property'], [self::underscoreNamingUpper(), 'NAME_CLASS_NAME', '\Some\Class\Name', 'ClassName', 'some_property'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'some_class_name_class_name', 'SomeClassName', 'Some\ClassName', 'some_property'], - [self::numberAwareUnderscoreNamingLower(), 'class1_test_class2_test', 'Class1Test', 'Some\Class2Test', 'some_property'], - [self::numberAwareUnderscoreNamingLower(), 'some_class_name_class_name', '\SomeClassName', 'ClassName', 'some_property'], - [self::numberAwareUnderscoreNamingLower(), 'name_class_name', '\Some\Class\Name', 'ClassName', 'some_property'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', 'SomeClassName', 'Some\ClassName', 'some_property'], - [self::numberAwareUnderscoreNamingUpper(), 'CLASS1_TEST_CLASS2_TEST', 'Class1Test', 'Some\Class2Test', 'some_property'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_CLASS_NAME_CLASS_NAME', '\SomeClassName', 'ClassName', 'some_property'], - [self::numberAwareUnderscoreNamingUpper(), 'NAME_CLASS_NAME', '\Some\Class\Name', 'ClassName', 'some_property'], ]; } - /** @dataProvider dataJoinTableName */ + #[DataProvider('dataJoinTableName')] public function testJoinTableName( NamingStrategy $strategy, string $expected, string $ownerEntity, string $associatedEntity, - string $propertyName + string $propertyName, ): void { self::assertSame($expected, $strategy->joinTableName($ownerEntity, $associatedEntity, $propertyName)); } @@ -262,33 +212,23 @@ public static function dataJoinKeyColumnName(): array [self::defaultNaming(), 'name_identifier', '\Some\Class\Name', 'identifier'], // UnderscoreNamingStrategy - [self::underscoreNamingLower(), 'some_class_name2test_id', 'SomeClassName2Test', null], - [self::underscoreNamingLower(), 'some_class_name_id', 'SomeClassName', null], + [self::underscoreNamingLower(), 'some_class_name2_test_id', 'SomeClassName2Test', null], + [self::underscoreNamingLower(), 'some_class_name_id', 'SomeClassName', null, null], [self::underscoreNamingLower(), 'class_name_identifier', '\Some\Class\ClassName', 'identifier'], - [self::underscoreNamingLower(), 'name2test_identifier', '\Some\Class\Name2Test', 'identifier'], + [self::underscoreNamingLower(), 'name2_test_identifier', '\Some\Class\Name2Test', 'identifier'], [self::underscoreNamingUpper(), 'SOME_CLASS_NAME_ID', 'SomeClassName', null], - [self::underscoreNamingUpper(), 'SOME_CLASS_NAME2TEST_ID', 'SomeClassName2Test', null], + [self::underscoreNamingUpper(), 'SOME_CLASS_NAME2_TEST_ID', 'SomeClassName2Test', null], [self::underscoreNamingUpper(), 'CLASS_NAME_IDENTIFIER', '\Some\Class\ClassName', 'IDENTIFIER'], - [self::underscoreNamingUpper(), 'NAME2TEST_IDENTIFIER', '\Some\Class\Name2Test', 'IDENTIFIER'], - - // NumberAwareUnderscoreNamingStrategy - [self::numberAwareUnderscoreNamingLower(), 'some_class_name2_test_id', 'SomeClassName2Test', null], - [self::numberAwareUnderscoreNamingLower(), 'some_class_name_id', 'SomeClassName', null], - [self::numberAwareUnderscoreNamingLower(), 'class_name_identifier', '\Some\Class\ClassName', 'identifier'], - [self::numberAwareUnderscoreNamingLower(), 'name2_test_identifier', '\Some\Class\Name2Test', 'identifier'], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_CLASS_NAME_ID', 'SomeClassName', null], - [self::numberAwareUnderscoreNamingUpper(), 'SOME_CLASS_NAME2_TEST_ID', 'SomeClassName2Test', null], - [self::numberAwareUnderscoreNamingUpper(), 'CLASS_NAME_IDENTIFIER', '\Some\Class\ClassName', 'IDENTIFIER'], - [self::numberAwareUnderscoreNamingUpper(), 'NAME2_TEST_IDENTIFIER', '\Some\Class\Name2Test', 'IDENTIFIER'], + [self::underscoreNamingUpper(), 'NAME2_TEST_IDENTIFIER', '\Some\Class\Name2Test', 'IDENTIFIER'], ]; } - /** @dataProvider dataJoinKeyColumnName */ + #[DataProvider('dataJoinKeyColumnName')] public function testJoinKeyColumnName( NamingStrategy $strategy, string $expected, string $propertyEntityName, - ?string $referencedColumnName = null + string|null $referencedColumnName = null, ): void { self::assertSame($expected, $strategy->joinKeyColumnName($propertyEntityName, $referencedColumnName)); } diff --git a/tests/Tests/ORM/Mapping/OneToOneOwningSideMappingTest.php b/tests/Tests/ORM/Mapping/OneToOneOwningSideMappingTest.php new file mode 100644 index 00000000000..73875653da2 --- /dev/null +++ b/tests/Tests/ORM/Mapping/OneToOneOwningSideMappingTest.php @@ -0,0 +1,38 @@ +joinColumns = [new JoinColumnMapping('foo_id', 'id')]; + $mapping->joinColumnFieldNames = ['foo' => 'bar']; + $mapping->sourceToTargetKeyColumns = ['foo' => 'bar']; + $mapping->targetToSourceKeyColumns = ['bar' => 'foo']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof OneToOneOwningSideMapping); + + self::assertCount(1, $resurrectedMapping->joinColumns); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->joinColumnFieldNames); + self::assertSame(['foo' => 'bar'], $resurrectedMapping->sourceToTargetKeyColumns); + self::assertSame(['bar' => 'foo'], $resurrectedMapping->targetToSourceKeyColumns); + } +} diff --git a/tests/Tests/ORM/Mapping/OwningSideMappingTest.php b/tests/Tests/ORM/Mapping/OwningSideMappingTest.php new file mode 100644 index 00000000000..21289db949a --- /dev/null +++ b/tests/Tests/ORM/Mapping/OwningSideMappingTest.php @@ -0,0 +1,35 @@ +inversedBy = 'bar'; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof OwningSideMapping); + + self::assertSame('bar', $resurrectedMapping->inversedBy); + } +} + +class MyOwningAssociationMapping extends OwningSideMapping +{ +} diff --git a/tests/Tests/ORM/Mapping/PHPMappingDriverTest.php b/tests/Tests/ORM/Mapping/PHPMappingDriverTest.php deleted file mode 100644 index e3218d352d2..00000000000 --- a/tests/Tests/ORM/Mapping/PHPMappingDriverTest.php +++ /dev/null @@ -1,63 +0,0 @@ -createAnnotationDriver(); -// $driver->loadMetadataForClass("Doctrine\Tests\ORM\Mapping\Animal", $meta); -// $exporter = $cme->getExporter('php', $path); -// echo $exporter->exportClassMetadata($meta); - - return new PHPDriver($path); - } - - /** - * All class are entitier for php driver - * - * @group DDC-889 - */ - public function testinvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void - { - self::assertInstanceOf(ClassMetadata::class, $this->createClassMetadata(DDC889Class::class)); - } - - public function testFailingSecondLevelCacheAssociation(): void - { - $this->expectException(CacheException::class); - $this->expectExceptionMessage('Entity association field "Doctrine\Tests\ORM\Mapping\PHPSLC#foo" not configured as part of the second-level cache.'); - $mappingDriver = $this->loadDriver(); - - $class = new ClassMetadata(Mapping\PHPSLC::class); - $mappingDriver->loadMetadataForClass(Mapping\PHPSLC::class, $class); - } - - public function testEntityIncorrectIndexes(): void - { - self::markTestSkipped('PHP driver does not ensure index correctness'); - } - - public function testEntityIncorrectUniqueContraint(): void - { - self::markTestSkipped('PHP driver does not ensure index correctness'); - } -} diff --git a/tests/Tests/ORM/Mapping/QuoteStrategyTest.php b/tests/Tests/ORM/Mapping/QuoteStrategyTest.php index 51dd5c54da6..8a8eec6095a 100644 --- a/tests/Tests/ORM/Mapping/QuoteStrategyTest.php +++ b/tests/Tests/ORM/Mapping/QuoteStrategyTest.php @@ -14,15 +14,14 @@ use Doctrine\Tests\Models\DDC117\DDC117Article; use Doctrine\Tests\Models\DDC117\DDC117ArticleDetails; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1845 */ +#[Group('DDC-1845')] class QuoteStrategyTest extends OrmTestCase { - /** @var DefaultQuoteStrategy */ - private $strategy; + private DefaultQuoteStrategy $strategy; - /** @var AbstractPlatform */ - private $platform; + private AbstractPlatform $platform; protected function setUp(): void { @@ -88,7 +87,7 @@ public function testJoinTableName(): void 'targetEntity' => 'CmsUser', 'inversedBy' => 'users', 'joinTable' => ['name' => '`cmsaddress_cmsuser`'], - ] + ], ); $cm2->mapManyToMany( @@ -97,7 +96,7 @@ public function testJoinTableName(): void 'targetEntity' => 'CmsUser', 'inversedBy' => 'users', 'joinTable' => ['name' => 'cmsaddress_cmsuser'], - ] + ], ); self::assertEquals('"cmsaddress_cmsuser"', $this->strategy->getJoinTableName($cm1->associationMappings['user'], $cm1, $this->platform)); @@ -114,7 +113,7 @@ public function testIdentifierColumnNames(): void 'id' => true, 'fieldName' => 'id', 'columnName' => '`id`', - ] + ], ); $cm2->mapField( @@ -122,7 +121,7 @@ public function testIdentifierColumnNames(): void 'id' => true, 'fieldName' => 'id', 'columnName' => 'id', - ] + ], ); self::assertEquals(['"id"'], $this->strategy->getIdentifierColumnNames($cm1, $this->platform)); @@ -148,9 +147,9 @@ public function testQuoteIdentifierJoinColumns(): void 'fieldName' => 'article', 'targetEntity' => DDC117Article::class, 'joinColumns' => [ - ['name' => '`article`'], + ['name' => '`article`', 'referencedColumnName' => 'article'], ], - ] + ], ); self::assertEquals(['"article"'], $this->strategy->getIdentifierColumnNames($cm, $this->platform)); @@ -166,12 +165,12 @@ public function testJoinColumnName(): void 'fieldName' => 'article', 'targetEntity' => DDC117Article::class, 'joinColumns' => [ - ['name' => '`article`'], + ['name' => '`article`', 'referencedColumnName' => 'article'], ], - ] + ], ); - $joinColumn = $cm->associationMappings['article']['joinColumns'][0]; + $joinColumn = $cm->associationMappings['article']->joinColumns[0]; self::assertEquals('"article"', $this->strategy->getJoinColumnName($joinColumn, $cm, $this->platform)); } @@ -185,12 +184,12 @@ public function testReferencedJoinColumnName(): void 'fieldName' => 'article', 'targetEntity' => DDC117Article::class, 'joinColumns' => [ - ['name' => '`article`'], + ['name' => '`article`', 'referencedColumnName' => 'id'], ], - ] + ], ); - $joinColumn = $cm->associationMappings['article']['joinColumns'][0]; + $joinColumn = $cm->associationMappings['article']->joinColumns[0]; self::assertEquals('"id"', $this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform)); } } diff --git a/tests/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php b/tests/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php deleted file mode 100644 index 9d5271add83..00000000000 --- a/tests/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php +++ /dev/null @@ -1,134 +0,0 @@ -getProperties(ClassWithMixedProperties::class); - - self::assertCount(5, $properties); - - foreach ($properties as $property) { - self::assertInstanceOf('ReflectionProperty', $property); - } - } - - public function testRetrievedInstancesAreNotStatic(): void - { - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - foreach ($properties as $property) { - self::assertFalse($property->isStatic()); - } - } - - public function testExpectedKeys(): void - { - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - self::assertArrayHasKey( - "\0" . ClassWithMixedProperties::class . "\0" . 'privateProperty', - $properties - ); - self::assertArrayHasKey( - "\0" . ClassWithMixedProperties::class . "\0" . 'privatePropertyOverride', - $properties - ); - self::assertArrayHasKey( - "\0" . ParentClass::class . "\0" . 'privatePropertyOverride', - $properties - ); - self::assertArrayHasKey( - "\0*\0protectedProperty", - $properties - ); - self::assertArrayHasKey( - 'publicProperty', - $properties - ); - } - - public function testPropertiesAreAccessible(): void - { - $object = new ClassWithMixedProperties(); - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - foreach ($properties as $property) { - self::assertSame($property->getName(), $property->getValue($object)); - } - } - - public function testPropertyGetterIsIdempotent(): void - { - $getter = (new ReflectionPropertiesGetter(new RuntimeReflectionService())); - - self::assertSame( - $getter->getProperties(ClassWithMixedProperties::class), - $getter->getProperties(ClassWithMixedProperties::class) - ); - } - - public function testPropertyGetterWillSkipPropertiesNotRetrievedByTheRuntimeReflectionService(): void - { - $reflectionService = $this->createMock(ReflectionService::class); - assert($reflectionService instanceof ReflectionService || $reflectionService instanceof MockObject); - - $reflectionService - ->expects(self::exactly(2)) - ->method('getClass') - ->with(self::logicalOr(ClassWithMixedProperties::class, ParentClass::class)) - ->willReturnMap([ - [ClassWithMixedProperties::class, new ReflectionClass(ClassWithMixedProperties::class)], - [ParentClass::class, new ReflectionClass(ParentClass::class)], - ]); - - $reflectionService - ->expects(self::atLeastOnce()) - ->method('getAccessibleProperty'); - - $getter = (new ReflectionPropertiesGetter($reflectionService)); - - self::assertEmpty($getter->getProperties(ClassWithMixedProperties::class)); - } - - public function testPropertyGetterWillSkipClassesNotRetrievedByTheRuntimeReflectionService(): void - { - $reflectionService = $this->createMock(ReflectionService::class); - assert($reflectionService instanceof ReflectionService || $reflectionService instanceof MockObject); - - $reflectionService - ->expects(self::once()) - ->method('getClass') - ->with(ClassWithMixedProperties::class); - - $reflectionService->expects(self::never())->method('getAccessibleProperty'); - - $getter = (new ReflectionPropertiesGetter($reflectionService)); - - self::assertEmpty($getter->getProperties(ClassWithMixedProperties::class)); - } -} diff --git a/tests/Tests/ORM/Mapping/ReflectionEmbeddedPropertyTest.php b/tests/Tests/ORM/Mapping/ReflectionEmbeddedPropertyTest.php index b5582b3c96a..471f7a529f8 100644 --- a/tests/Tests/ORM/Mapping/ReflectionEmbeddedPropertyTest.php +++ b/tests/Tests/ORM/Mapping/ReflectionEmbeddedPropertyTest.php @@ -10,27 +10,27 @@ use Doctrine\Tests\Models\Reflection\AbstractEmbeddable; use Doctrine\Tests\Models\Reflection\ArrayObjectExtendingClass; use Doctrine\Tests\Models\Reflection\ConcreteEmbeddable; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionProperty; /** * Tests for {@see \Doctrine\ORM\Mapping\ReflectionEmbeddedProperty} - * - * @covers \Doctrine\ORM\Mapping\ReflectionEmbeddedProperty */ +#[CoversClass(ReflectionEmbeddedProperty::class)] class ReflectionEmbeddedPropertyTest extends TestCase { /** * @param ReflectionProperty $parentProperty property of the embeddable/entity where to write the embeddable to * @param ReflectionProperty $childProperty property of the embeddable class where to write values to * @param string $embeddableClass name of the embeddable class to be instantiated - * - * @dataProvider getTestedReflectionProperties */ + #[DataProvider('getTestedReflectionProperties')] public function testCanSetAndGetEmbeddedProperty( ReflectionProperty $parentProperty, ReflectionProperty $childProperty, - string $embeddableClass + string $embeddableClass, ): void { $embeddedPropertyReflection = new ReflectionEmbeddedProperty($parentProperty, $childProperty, $embeddableClass); @@ -51,70 +51,58 @@ public function testCanSetAndGetEmbeddedProperty( * @param ReflectionProperty $parentProperty property of the embeddable/entity where to write the embeddable to * @param ReflectionProperty $childProperty property of the embeddable class where to write values to * @param string $embeddableClass name of the embeddable class to be instantiated - * - * @dataProvider getTestedReflectionProperties */ + #[DataProvider('getTestedReflectionProperties')] public function testWillSkipReadingPropertiesFromNullEmbeddable( ReflectionProperty $parentProperty, ReflectionProperty $childProperty, - string $embeddableClass + string $embeddableClass, ): void { $embeddedPropertyReflection = new ReflectionEmbeddedProperty($parentProperty, $childProperty, $embeddableClass); $instantiator = new Instantiator(); self::assertNull($embeddedPropertyReflection->getValue( - $instantiator->instantiate($parentProperty->getDeclaringClass()->getName()) + $instantiator->instantiate($parentProperty->getDeclaringClass()->getName()), )); } - /** - * @return ReflectionProperty[][]|string[][] - */ + /** @return ReflectionProperty[][]|string[][] */ public static function getTestedReflectionProperties(): array { return [ [ - self::getReflectionProperty(BooleanModel::class, 'id'), - self::getReflectionProperty(BooleanModel::class, 'id'), + new ReflectionProperty(BooleanModel::class, 'id'), + new ReflectionProperty(BooleanModel::class, 'id'), BooleanModel::class, ], // reflection on embeddables that have properties defined in abstract ancestors: [ - self::getReflectionProperty(BooleanModel::class, 'id'), - self::getReflectionProperty(AbstractEmbeddable::class, 'propertyInAbstractClass'), + new ReflectionProperty(BooleanModel::class, 'id'), + new ReflectionProperty(AbstractEmbeddable::class, 'propertyInAbstractClass'), ConcreteEmbeddable::class, ], [ - self::getReflectionProperty(BooleanModel::class, 'id'), - self::getReflectionProperty(ConcreteEmbeddable::class, 'propertyInConcreteClass'), + new ReflectionProperty(BooleanModel::class, 'id'), + new ReflectionProperty(ConcreteEmbeddable::class, 'propertyInConcreteClass'), ConcreteEmbeddable::class, ], // reflection on classes extending internal PHP classes: [ - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'privateProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'privateProperty'), ArrayObjectExtendingClass::class, ], [ - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'protectedProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'protectedProperty'), ArrayObjectExtendingClass::class, ], [ - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), - self::getReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), + new ReflectionProperty(ArrayObjectExtendingClass::class, 'publicProperty'), ArrayObjectExtendingClass::class, ], ]; } - - private static function getReflectionProperty(string $className, string $propertyName): ReflectionProperty - { - $reflectionProperty = new ReflectionProperty($className, $propertyName); - - $reflectionProperty->setAccessible(true); - - return $reflectionProperty; - } } diff --git a/tests/Tests/ORM/Mapping/ReflectionReadonlyPropertyTest.php b/tests/Tests/ORM/Mapping/ReflectionReadonlyPropertyTest.php index aa3e77444de..54e2a74534e 100644 --- a/tests/Tests/ORM/Mapping/ReflectionReadonlyPropertyTest.php +++ b/tests/Tests/ORM/Mapping/ReflectionReadonlyPropertyTest.php @@ -12,9 +12,6 @@ use PHPUnit\Framework\TestCase; use ReflectionProperty; -/** - * @requires PHP 8.1 - */ class ReflectionReadonlyPropertyTest extends TestCase { public function testSecondWriteWithSameValue(): void diff --git a/tests/Tests/ORM/Mapping/StaticPHPMappingDriverTest.php b/tests/Tests/ORM/Mapping/StaticPHPMappingDriverTest.php index 2ebba696dcc..981fe141564 100644 --- a/tests/Tests/ORM/Mapping/StaticPHPMappingDriverTest.php +++ b/tests/Tests/ORM/Mapping/StaticPHPMappingDriverTest.php @@ -8,6 +8,7 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver; use Doctrine\Tests\Models\DDC889\DDC889Class; +use PHPUnit\Framework\Attributes\Group; use const DIRECTORY_SEPARATOR; @@ -20,28 +21,23 @@ protected function loadDriver(): MappingDriver /** * All class with static::loadMetadata are entities for php driver - * - * @group DDC-889 */ + #[Group('DDC-889')] public function testinvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void { self::assertInstanceOf(ClassMetadata::class, $this->createClassMetadata(DDC889Class::class)); } - /** - * @group DDC-2825 - * @group 881 - */ - public function testSchemaDefinitionViaExplicitTableSchemaAnnotationProperty(): void + #[Group('DDC-2825')] + #[Group('881')] + public function testSchemaDefinitionViaExplicitTableSchemaAttributeProperty(): void { self::markTestIncomplete(); } - /** - * @group DDC-2825 - * @group 881 - */ - public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAnnotationProperty(): void + #[Group('DDC-2825')] + #[Group('881')] + public function testSchemaDefinitionViaSchemaDefinedInTableNameInTableAttributeProperty(): void { self::markTestIncomplete(); } diff --git a/tests/Tests/ORM/Mapping/Symfony/DriverTestCase.php b/tests/Tests/ORM/Mapping/Symfony/DriverTestCase.php index 95f548ac58a..065c207be1d 100644 --- a/tests/Tests/ORM/Mapping/Symfony/DriverTestCase.php +++ b/tests/Tests/ORM/Mapping/Symfony/DriverTestCase.php @@ -6,6 +6,7 @@ use Doctrine\Persistence\Mapping\Driver\FileDriver; use Doctrine\Persistence\Mapping\MappingException; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; @@ -16,11 +17,10 @@ use function touch; use function unlink; -/** @group DDC-1418 */ +#[Group('DDC-1418')] abstract class DriverTestCase extends TestCase { - /** @var string */ - private $dir; + private string $dir; public function testFindMappingFile(): void { @@ -28,7 +28,7 @@ public function testFindMappingFile(): void [ 'MyNamespace\MySubnamespace\EntityFoo' => 'foo', 'MyNamespace\MySubnamespace\Entity' => $this->dir, - ] + ], ); touch($filename = $this->dir . '/Foo' . $this->getFileExtension()); @@ -40,7 +40,7 @@ public function testFindMappingFileInSubnamespace(): void $driver = $this->getDriver( [ 'MyNamespace\MySubnamespace\Entity' => $this->dir, - ] + ], ); touch($filename = $this->dir . '/Foo.Bar' . $this->getFileExtension()); @@ -55,7 +55,7 @@ public function testFindMappingFileNamespacedFoundFileNotFound(): void $driver = $this->getDriver( [ 'MyNamespace\MySubnamespace\Entity' => $this->dir, - ] + ], ); $driver->getLocator()->findMappingFile('MyNamespace\MySubnamespace\Entity\Foo'); @@ -69,7 +69,7 @@ public function testFindMappingNamespaceNotFound(): void $driver = $this->getDriver( [ 'MyNamespace\MySubnamespace\Entity' => $this->dir, - ] + ], ); $driver->getLocator()->findMappingFile('MyOtherNamespace\MySubnamespace\Entity\Foo'); diff --git a/tests/Tests/ORM/Mapping/Symfony/XmlDriverTest.php b/tests/Tests/ORM/Mapping/Symfony/XmlDriverTest.php index 93c07f58378..1ff559eb5dd 100644 --- a/tests/Tests/ORM/Mapping/Symfony/XmlDriverTest.php +++ b/tests/Tests/ORM/Mapping/Symfony/XmlDriverTest.php @@ -6,10 +6,11 @@ use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; use Doctrine\Persistence\Mapping\Driver\FileDriver; +use PHPUnit\Framework\Attributes\Group; use function array_flip; -/** @group DDC-1418 */ +#[Group('DDC-1418')] class XmlDriverTest extends DriverTestCase { protected function getFileExtension(): string diff --git a/tests/Tests/ORM/Mapping/Symfony/YamlDriverTest.php b/tests/Tests/ORM/Mapping/Symfony/YamlDriverTest.php deleted file mode 100644 index 88831b558e7..00000000000 --- a/tests/Tests/ORM/Mapping/Symfony/YamlDriverTest.php +++ /dev/null @@ -1,24 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357'); + + new Table(indexes: []); + } + + #[WithoutErrorHandler] + public function testDeprecationOnUniqueConstraintsPropertyIsTriggered(): void + { + $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11357'); + + new Table(uniqueConstraints: []); + } +} diff --git a/tests/Tests/ORM/Mapping/ToManyAssociationMappingTest.php b/tests/Tests/ORM/Mapping/ToManyAssociationMappingTest.php new file mode 100644 index 00000000000..e435a944b7f --- /dev/null +++ b/tests/Tests/ORM/Mapping/ToManyAssociationMappingTest.php @@ -0,0 +1,40 @@ +indexBy = 'foo'; + $mapping->orderBy = ['foo' => 'asc']; + + $resurrectedMapping = unserialize(serialize($mapping)); + assert($resurrectedMapping instanceof ToManyAssociationMapping); + + self::assertSame('foo', $resurrectedMapping->fieldName); + self::assertSame(['foo' => 'asc'], $resurrectedMapping->orderBy); + } +} + +class MyToManyAssociationMapping extends AssociationMapping implements ToManyAssociationMapping +{ + use ToManyAssociationMappingImplementation; +} diff --git a/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php b/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php index c72625eddff..3954cff86a3 100644 --- a/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php +++ b/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php @@ -10,9 +10,6 @@ use Doctrine\Tests\OrmTestCase; use ReflectionClass; -/** - * @requires PHP >= 8.1 - */ class TypedEnumFieldMapperTest extends OrmTestCase { private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper @@ -26,7 +23,7 @@ public function testNotBackedEnumThrows(): void $this->expectException(MappingException::class); $this->expectExceptionMessage( - 'Attempting to map a non-backed enum type Doctrine\Tests\Models\Enums\SwitchStatus in entity Doctrine\Tests\Models\Enums\FaultySwitch::$status. Please use backed enums only' + 'Attempting to map a non-backed enum type Doctrine\Tests\Models\Enums\SwitchStatus in entity Doctrine\Tests\Models\Enums\FaultySwitch::$status. Please use backed enums only', ); self::defaultTypedFieldMapper()->validateAndComplete(['fieldName' => 'status'], $reflectionClass->getProperty('status')); diff --git a/tests/Tests/ORM/Mapping/TypedFieldMapperTest.php b/tests/Tests/ORM/Mapping/TypedFieldMapperTest.php index 8960a530d40..3f8c0863ca0 100644 --- a/tests/Tests/ORM/Mapping/TypedFieldMapperTest.php +++ b/tests/Tests/ORM/Mapping/TypedFieldMapperTest.php @@ -11,12 +11,11 @@ use Doctrine\Tests\Models\TypedProperties\UserTyped; use Doctrine\Tests\ORM\Mapping\TypedFieldMapper\CustomIntAsStringTypedFieldMapper; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use ReflectionClass; -/** - * @group GH10313 - * @requires PHP 7.4 - */ +#[Group('GH10313')] class TypedFieldMapperTest extends OrmTestCase { private static function defaultTypedFieldMapper(): DefaultTypedFieldMapper @@ -72,14 +71,13 @@ public static function dataFieldToMappedField(): array /** * @param array{fieldName: string, enumType?: string, type?: mixed} $mapping * @param array{fieldName: string, enumType?: string, type?: mixed} $finalMapping - * - * @dataProvider dataFieldToMappedField */ + #[DataProvider('dataFieldToMappedField')] public function testValidateAndComplete( TypedFieldMapper $typedFieldMapper, ReflectionClass $reflectionClass, array $mapping, - array $finalMapping + array $finalMapping, ): void { self::assertSame($finalMapping, $typedFieldMapper->validateAndComplete($mapping, $reflectionClass->getProperty($mapping['fieldName']))); } diff --git a/tests/Tests/ORM/Mapping/UnderscoreNamingStrategyTest.php b/tests/Tests/ORM/Mapping/UnderscoreNamingStrategyTest.php deleted file mode 100644 index 0c3f30af730..00000000000 --- a/tests/Tests/ORM/Mapping/UnderscoreNamingStrategyTest.php +++ /dev/null @@ -1,35 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/7908'); - - new UnderscoreNamingStrategy(CASE_LOWER, false); - } - - public function testNoDeprecationMessageWhenNumberAwareEnabled(): void - { - $before = Deprecation::getTriggeredDeprecations()['https://github.com/doctrine/orm/pull/7908'] ?? 0; - - new UnderscoreNamingStrategy(CASE_LOWER, true); - - $after = Deprecation::getTriggeredDeprecations()['https://github.com/doctrine/orm/pull/7908'] ?? 0; - - self::assertSame($before, $after); - } -} diff --git a/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php index 69fe443d1c7..474c623f352 100644 --- a/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\Mapping\EmbeddedClassMapping; use Doctrine\ORM\Mapping\MappingException; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException; @@ -22,12 +23,15 @@ use Doctrine\Tests\Models\Generic\BooleanModel; use Doctrine\Tests\Models\GH7141\GH7141Article; use Doctrine\Tests\Models\GH7316\GH7316Article; +use Doctrine\Tests\Models\InvalidXml; use Doctrine\Tests\Models\Project\Project; use Doctrine\Tests\Models\Project\ProjectId; use Doctrine\Tests\Models\Project\ProjectInvalidMapping; use Doctrine\Tests\Models\Project\ProjectName; use Doctrine\Tests\Models\ValueObjects\Name; use Doctrine\Tests\Models\ValueObjects\Person; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function substr_count; @@ -40,7 +44,7 @@ protected function loadDriver(): MappingDriver return new XmlDriver( __DIR__ . DIRECTORY_SEPARATOR . 'xml', XmlDriver::DEFAULT_FILE_EXTENSION, - true + true, ); } @@ -86,8 +90,7 @@ public function testIdentifierWithAssociationKey(): void self::assertEquals(['language', 'article'], $class->identifier); self::assertArrayHasKey('article', $class->associationMappings); - self::assertArrayHasKey('id', $class->associationMappings['article']); - self::assertTrue($class->associationMappings['article']['id']); + self::assertTrue($class->associationMappings['article']->id); } public function testEmbeddableMapping(): void @@ -97,11 +100,9 @@ public function testEmbeddableMapping(): void self::assertTrue($class->isEmbeddedClass); } - /** - * @group DDC-3293 - * @group DDC-3477 - * @group 1238 - */ + #[Group('DDC-3293')] + #[Group('DDC-3477')] + #[Group('1238')] public function testEmbeddedMappingsWithUseColumnPrefix(): void { $factory = new ClassMetadataFactory(); @@ -113,15 +114,13 @@ public function testEmbeddedMappingsWithUseColumnPrefix(): void self::assertEquals( '__prefix__', $factory->getMetadataFor(DDC3293UserPrefixed::class) - ->embeddedClasses['address']['columnPrefix'] + ->embeddedClasses['address']->columnPrefix, ); } - /** - * @group DDC-3293 - * @group DDC-3477 - * @group 1238 - */ + #[Group('DDC-3293')] + #[Group('DDC-3477')] + #[Group('1238')] public function testEmbeddedMappingsWithFalseUseColumnPrefix(): void { $factory = new ClassMetadataFactory(); @@ -132,7 +131,7 @@ public function testEmbeddedMappingsWithFalseUseColumnPrefix(): void self::assertFalse( $factory->getMetadataFor(DDC3293User::class) - ->embeddedClasses['address']['columnPrefix'] + ->embeddedClasses['address']->columnPrefix, ); } @@ -142,18 +141,18 @@ public function testEmbeddedMapping(): void self::assertEquals( [ - 'name' => [ + 'name' => EmbeddedClassMapping::fromMappingArray([ 'class' => Name::class, 'columnPrefix' => 'nm_', 'declaredField' => null, 'originalField' => null, - ], + ]), ], - $class->embeddedClasses + $class->embeddedClasses, ); } - /** @group DDC-1468 */ + #[Group('DDC-1468')] public function testItMentionsFilenameAndEntityNameOnInvalidMapping(): void { $this->expectException(PersistenceMappingException::class); @@ -161,15 +160,13 @@ public function testItMentionsFilenameAndEntityNameOnInvalidMapping(): void $this->createClassMetadata(BooleanModel::class); } - /** - * @dataProvider dataValidSchema - * @group DDC-2429 - */ + #[Group('DDC-2429')] + #[DataProvider('dataValidSchema')] public function testValidateXmlSchema( string $class, string $tableName, array $fieldNames, - array $associationNames + array $associationNames, ): void { $metadata = $this->createClassMetadata($class); @@ -207,9 +204,8 @@ public static function dataValidSchema(): array /** * @param class-string $class * @param non-empty-array $expectedExceptionOccurrences - * - * @dataProvider dataInvalidSchema */ + #[DataProvider('dataInvalidSchema')] public function testValidateIncorrectXmlSchema(string $class, array $expectedExceptionOccurrences): void { try { @@ -250,7 +246,7 @@ public static function dataInvalidSchema(): array ]; } - /** @group GH-7141 */ + #[Group('GH-7141')] public function testOneToManyDefaultOrderByAsc(): void { $driver = $this->loadDriver(); @@ -261,7 +257,7 @@ public function testOneToManyDefaultOrderByAsc(): void self::assertEquals( Criteria::ASC, - $class->getMetadataValue('associationMappings')['tags']['orderBy']['position'] + $class->getMetadataValue('associationMappings')['tags']->orderBy['position'], ); } @@ -275,11 +271,11 @@ public function testManyToManyDefaultOrderByAsc(): void self::assertEquals( Criteria::ASC, - $class->getMetadataValue('associationMappings')['tags']['orderBy']['position'] + $class->getMetadataValue('associationMappings')['tags']->orderBy['position'], ); } - /** @group DDC-889 */ + #[Group('DDC-889')] public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): void { $this->expectException(MappingException::class); @@ -301,8 +297,52 @@ public function testClassNameInFieldOrId(): void /** @var array{type: string} $name */ $name = $class->getFieldMapping('name'); - self::assertEquals(ProjectId::class, $id['type']); - self::assertEquals(ProjectName::class, $name['type']); + self::assertEquals(ProjectId::class, $id->type); + self::assertEquals(ProjectName::class, $name->type); + } + + public function testDisablingXmlValidationIsPossible(): void + { + $this->expectNotToPerformAssertions(); + + new XmlDriver( + __DIR__ . DIRECTORY_SEPARATOR . 'xml', + XmlDriver::DEFAULT_FILE_EXTENSION, + false, + ); + } + + public function testXmlValidationEnabled(): void + { + $driver = new XmlDriver( + __DIR__ . DIRECTORY_SEPARATOR . 'invalid_xml', + XmlDriver::DEFAULT_FILE_EXTENSION, + true, + ); + + $class = new ClassMetadata(InvalidXml::class); + $class->initializeReflection(new RuntimeReflectionService()); + + self::expectException(MappingException::class); + self::expectExceptionMessage("libxml error: Element '{http://doctrine-project.org/schemas/orm/doctrine-mapping}field', attribute 'invalid': The attribute 'invalid' is not allowed."); + + $driver->loadMetadataForClass(InvalidXml::class, $class); + } + + public function testXmlValidationDisabled(): void + { + $driver = new XmlDriver( + __DIR__ . DIRECTORY_SEPARATOR . 'invalid_xml', + XmlDriver::DEFAULT_FILE_EXTENSION, + false, + ); + + $class = new ClassMetadata(InvalidXml::class); + $class->initializeReflection(new RuntimeReflectionService()); + + $driver->loadMetadataForClass(InvalidXml::class, $class); + + self::assertCount(1, $class->fieldMappings); } } diff --git a/tests/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Tests/ORM/Mapping/YamlMappingDriverTest.php deleted file mode 100644 index 4ce9bf4f0b9..00000000000 --- a/tests/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ /dev/null @@ -1,112 +0,0 @@ -loadDriver(); - $yamlDriver->getLocator()->addPaths([__DIR__ . DIRECTORY_SEPARATOR . 'yaml']); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($yamlDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $classPage = new ClassMetadata(File::class); - $classPage = $factory->getMetadataFor(File::class); - self::assertEquals(File::class, $classPage->associationMappings['parentDirectory']['sourceEntity']); - - $classDirectory = new ClassMetadata(Directory::class); - $classDirectory = $factory->getMetadataFor(Directory::class); - self::assertEquals(Directory::class, $classDirectory->associationMappings['parentDirectory']['sourceEntity']); - } - - /** @group DDC-1468 */ - public function testItMentionsFilenameAndEntityNameOnInvalidMapping(): void - { - $this->expectException(MappingException::class); - $this->expectExceptionMessage('Invalid mapping file \'Doctrine.Tests.Models.Generic.BooleanModel.dcm.yml\' for class \'Doctrine\Tests\Models\Generic\BooleanModel\'.'); - $this->createClassMetadata(BooleanModel::class); - } - - /** @group DDC-2069 */ - public function testSpacesShouldBeIgnoredWhenUseExplode(): void - { - $metadata = $this->createClassMetadata(DDC2069Entity::class); - $unique = $metadata->table['uniqueConstraints'][0]['columns']; - $indexes = $metadata->table['indexes'][0]['columns']; - - $nameField = $metadata->fieldMappings['name']; - $valueField = $metadata->fieldMappings['value']; - - self::assertEquals('name', $unique[0]); - self::assertEquals('value', $unique[1]); - - self::assertEquals('value', $indexes[0]); - self::assertEquals('name', $indexes[1]); - - self::assertEquals(255, $nameField['length']); - self::assertEquals(255, $valueField['length']); - } - - public function testCompositeKeyForJoinTableInManyToManyCreation(): void - { - $yamlDriver = $this->loadDriver(); - - $em = $this->getTestEntityManager(); - $em->getConfiguration()->setMetadataDriverImpl($yamlDriver); - $factory = new ClassMetadataFactory(); - $factory->setEntityManager($em); - - $entityA = new ClassMetadata(DDC3711EntityA::class); - $entityA = $factory->getMetadataFor(DDC3711EntityA::class); - - self::assertEquals(['link_a_id1' => 'id1', 'link_a_id2' => 'id2'], $entityA->associationMappings['entityB']['relationToSourceKeyColumns']); - self::assertEquals(['link_b_id1' => 'id1', 'link_b_id2' => 'id2'], $entityA->associationMappings['entityB']['relationToTargetKeyColumns']); - } -} - -class DDC2069Entity -{ - /** @var int */ - public $id; - - /** @var string */ - public $name; - - /** @var mixed */ - public $value; -} diff --git a/tests/Tests/ORM/Mapping/invalid_xml/Doctrine.Tests.Models.InvalidXml.dcm.xml b/tests/Tests/ORM/Mapping/invalid_xml/Doctrine.Tests.Models.InvalidXml.dcm.xml new file mode 100644 index 00000000000..859bf4a70d1 --- /dev/null +++ b/tests/Tests/ORM/Mapping/invalid_xml/Doctrine.Tests.Models.InvalidXml.dcm.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php deleted file mode 100644 index 1907de9d866..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php +++ /dev/null @@ -1,125 +0,0 @@ -setPrimaryTable( - ['name' => 'company_person'] -); - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'zip', - 'length' => 50, - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'city', - 'length' => 50, - ] -); - -$metadata->mapOneToOne( - [ - 'fieldName' => 'user', - 'targetEntity' => 'CmsUser', - 'joinColumns' => [['referencedColumnName' => 'id']], - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'find-all', - 'query' => 'SELECT id, country, city FROM cms_addresses', - 'resultSetMapping' => 'mapping-find-all', - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'find-by-id', - 'query' => 'SELECT * FROM cms_addresses WHERE id = ?', - 'resultClass' => CmsAddress::class, - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'count', - 'query' => 'SELECT COUNT(*) AS count FROM cms_addresses', - 'resultSetMapping' => 'mapping-count', - ] -); - - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-find-all', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'city', - 'column' => 'city', - ], - [ - 'name' => 'country', - 'column' => 'country', - ], - ], - 'entityClass' => CmsAddress::class, - ], - ], - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-without-fields', - 'columns' => [], - 'entities' => [ - [ - 'entityClass' => CmsAddress::class, - 'fields' => [], - ], - ], - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mapping-count', - 'columns' => [ - ['name' => 'count'], - ], - ] -); - -$metadata->addEntityListener(Events::postPersist, 'CmsAddressListener', 'postPersist'); -$metadata->addEntityListener(Events::prePersist, 'CmsAddressListener', 'prePersist'); - -$metadata->addEntityListener(Events::postUpdate, 'CmsAddressListener', 'postUpdate'); -$metadata->addEntityListener(Events::preUpdate, 'CmsAddressListener', 'preUpdate'); - -$metadata->addEntityListener(Events::postRemove, 'CmsAddressListener', 'postRemove'); -$metadata->addEntityListener(Events::preRemove, 'CmsAddressListener', 'preRemove'); - -$metadata->addEntityListener(Events::preFlush, 'CmsAddressListener', 'preFlush'); -$metadata->addEntityListener(Events::postLoad, 'CmsAddressListener', 'postLoad'); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php deleted file mode 100644 index 7e4eb9852c9..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php +++ /dev/null @@ -1,209 +0,0 @@ -setPrimaryTable( - ['name' => 'cms_users'] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchIdAndUsernameWithResultClass', - 'query' => 'SELECT id, username FROM cms_users WHERE username = ?', - 'resultClass' => CmsUser::class, - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllColumns', - 'query' => 'SELECT * FROM cms_users WHERE username = ?', - 'resultClass' => CmsUser::class, - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchJoinedAddress', - 'query' => 'SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', - 'resultSetMapping' => 'mappingJoinedAddress', - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchJoinedPhonenumber', - 'query' => 'SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', - 'resultSetMapping' => 'mappingJoinedPhonenumber', - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchUserPhonenumberCount', - 'query' => 'SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username', - 'resultSetMapping' => 'mappingUserPhonenumberCount', - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchMultipleJoinsEntityResults', - 'resultSetMapping' => 'mappingMultipleJoinsEntityResults', - 'query' => 'SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username', - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingJoinedAddress', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - [ - 'name' => 'address.zip', - 'column' => 'zip', - ], - [ - 'name' => 'address.city', - 'column' => 'city', - ], - [ - 'name' => 'address.country', - 'column' => 'country', - ], - [ - 'name' => 'address.id', - 'column' => 'a_id', - ], - ], - 'entityClass' => CmsUser::class, - 'discriminatorColumn' => null, - ], - ], - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingJoinedPhonenumber', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - [ - 'name' => 'phonenumbers.phonenumber', - 'column' => 'number', - ], - ], - 'entityClass' => CmsUser::class, - 'discriminatorColumn' => null, - ], - ], - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingUserPhonenumberCount', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - [ - 'name' => 'status', - 'column' => 'status', - ], - ], - 'entityClass' => CmsUser::class, - 'discriminatorColumn' => null, - ], - ], - 'columns' => [ - ['name' => 'numphones'], - ], - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingMultipleJoinsEntityResults', - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'u_id', - ], - [ - 'name' => 'name', - 'column' => 'u_name', - ], - [ - 'name' => 'status', - 'column' => 'u_status', - ], - ], - 'entityClass' => CmsUser::class, - 'discriminatorColumn' => null, - ], - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'a_id', - ], - [ - 'name' => 'zip', - 'column' => 'a_zip', - ], - [ - 'name' => 'country', - 'column' => 'a_country', - ], - ], - 'entityClass' => CmsAddress::class, - 'discriminatorColumn' => null, - ], - ], - 'columns' => [ - ['name' => 'numphones'], - ], - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Cache.City.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Cache.City.php deleted file mode 100644 index f6c221aebd6..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Cache.City.php +++ /dev/null @@ -1,73 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); -$metadata->setPrimaryTable(['name' => 'cache_city']); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); -$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); - -$metadata->enableCache( - [ - 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'id', - 'type' => 'integer', - 'id' => true, - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - ] -); - - -$metadata->mapOneToOne( - [ - 'fieldName' => 'state', - 'targetEntity' => State::class, - 'inversedBy' => 'cities', - 'joinColumns' => - [ - [ - 'name' => 'state_id', - 'referencedColumnName' => 'id', - ], - ], - ] -); -$metadata->enableAssociationCache('state', [ - 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, -]); - -$metadata->mapManyToMany( - [ - 'fieldName' => 'travels', - 'targetEntity' => Travel::class, - 'mappedBy' => 'visitedCities', - ] -); - -$metadata->mapOneToMany( - [ - 'fieldName' => 'attractions', - 'targetEntity' => Attraction::class, - 'mappedBy' => 'city', - 'orderBy' => ['name' => 'ASC'], - ] -); -$metadata->enableAssociationCache('attractions', [ - 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, -]); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php deleted file mode 100644 index 05fb17f6bca..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php +++ /dev/null @@ -1,51 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); -$metadata->setTableName('company_contracts'); -$metadata->setDiscriminatorColumn( - [ - 'name' => 'discr', - 'type' => 'string', - ] -); - -$metadata->mapField( - [ - 'id' => true, - 'name' => 'id', - 'fieldName' => 'id', - ] -); - -$metadata->mapField( - [ - 'type' => 'boolean', - 'name' => 'completed', - 'fieldName' => 'completed', - ] -); - -$metadata->setDiscriminatorMap( - [ - 'fix' => 'CompanyFixContract', - 'flexible' => 'CompanyFlexContract', - 'flexultra' => 'CompanyFlexUltraContract', - ] -); - -$metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler'); -$metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler'); - -$metadata->addEntityListener(Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler'); -$metadata->addEntityListener(Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler'); - -$metadata->addEntityListener(Events::postRemove, 'CompanyContractListener', 'postRemoveHandler'); -$metadata->addEntityListener(Events::preRemove, 'CompanyContractListener', 'preRemoveHandler'); - -$metadata->addEntityListener(Events::preFlush, 'CompanyContractListener', 'preFlushHandler'); -$metadata->addEntityListener(Events::postLoad, 'CompanyContractListener', 'postLoadHandler'); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php deleted file mode 100644 index 45cb4d631e2..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php +++ /dev/null @@ -1,11 +0,0 @@ -mapField( - [ - 'type' => 'integer', - 'name' => 'fixPrice', - 'fieldName' => 'fixPrice', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php deleted file mode 100644 index 76a7e05f420..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php +++ /dev/null @@ -1,19 +0,0 @@ -mapField( - [ - 'type' => 'integer', - 'name' => 'hoursWorked', - 'fieldName' => 'hoursWorked', - ] -); - -$metadata->mapField( - [ - 'type' => 'integer', - 'name' => 'pricePerHour', - 'fieldName' => 'pricePerHour', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php deleted file mode 100644 index 0d9a1b53c4e..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php +++ /dev/null @@ -1,27 +0,0 @@ -mapField( - [ - 'type' => 'integer', - 'name' => 'maxPrice', - 'fieldName' => 'maxPrice', - ] -); -$metadata->addEntityListener(Events::postPersist, 'CompanyContractListener', 'postPersistHandler'); -$metadata->addEntityListener(Events::prePersist, 'CompanyContractListener', 'prePersistHandler'); - -$metadata->addEntityListener(Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler'); -$metadata->addEntityListener(Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler'); - -$metadata->addEntityListener(Events::postRemove, 'CompanyContractListener', 'postRemoveHandler'); -$metadata->addEntityListener(Events::preRemove, 'CompanyContractListener', 'preRemoveHandler'); - -$metadata->addEntityListener(Events::preFlush, 'CompanyContractListener', 'preFlushHandler'); -$metadata->addEntityListener(Events::postLoad, 'CompanyContractListener', 'postLoadHandler'); - -$metadata->addEntityListener(Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler1'); -$metadata->addEntityListener(Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler2'); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php deleted file mode 100644 index f7656ccd732..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php +++ /dev/null @@ -1,48 +0,0 @@ -setPrimaryTable( - ['name' => 'company_person'] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllWithResultClass', - 'query' => 'SELECT id, name, discr FROM company_persons ORDER BY name', - 'resultClass' => CompanyPerson::class, - ] -); - -$metadata->addNamedNativeQuery( - [ - 'name' => 'fetchAllWithSqlResultSetMapping', - 'query' => 'SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name', - 'resultSetMapping' => 'mappingFetchAll', - ] -); - -$metadata->addSqlResultSetMapping( - [ - 'name' => 'mappingFetchAll', - 'columns' => [], - 'entities' => [ - [ - 'fields' => [ - [ - 'name' => 'id', - 'column' => 'id', - ], - [ - 'name' => 'name', - 'column' => 'name', - ], - ], - 'entityClass' => CompanyPerson::class, - 'discriminatorColumn' => 'discriminator', - ], - ], - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.php deleted file mode 100644 index b5cb3c585ae..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.php +++ /dev/null @@ -1,16 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - ] -); -$metadata->mapField( - ['fieldName' => 'name'] -); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.php deleted file mode 100644 index 46d2a25a2db..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.php +++ /dev/null @@ -1,22 +0,0 @@ -setPrimaryTable( - [ - 'name' => 'explicit_table', - 'schema' => 'explicit_schema', - ] -); - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.php deleted file mode 100644 index ff318fd2079..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.php +++ /dev/null @@ -1,19 +0,0 @@ -setPrimaryTable( - ['name' => 'implicit_schema.implicit_table'] -); - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579Admin.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579Admin.php deleted file mode 100644 index 94ed4de9755..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579Admin.php +++ /dev/null @@ -1,5 +0,0 @@ -setAssociationOverride('groups', ['inversedBy' => 'admins']); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579User.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579User.php deleted file mode 100644 index de61bdbdfcf..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC3579.DDC3579User.php +++ /dev/null @@ -1,37 +0,0 @@ -isMappedSuperclass = true; - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'user_id', - 'length' => 150, - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - 'columnName' => 'user_name', - 'nullable' => true, - 'unique' => false, - 'length' => 250, - ] -); - -$metadata->mapManyToMany( - [ - 'fieldName' => 'groups', - 'targetEntity' => 'DDC3579Group', - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.php deleted file mode 100644 index 0fda161ca2e..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.php +++ /dev/null @@ -1,21 +0,0 @@ -isMappedSuperclass = true; - -$metadata->mapField([ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', -]); - -$metadata->mapManyToMany([ - 'fieldName' => 'members', - 'targetEntity' => 'DDC5934Member', -]); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934Contract.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934Contract.php deleted file mode 100644 index e46a32f26f1..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC5934.DDC5934Contract.php +++ /dev/null @@ -1,9 +0,0 @@ -setAssociationOverride('members', [ - 'fetch' => ClassMetadata::FETCH_EXTRA_LAZY, -]); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.php deleted file mode 100644 index c7e5cae4ae4..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.php +++ /dev/null @@ -1,10 +0,0 @@ -mapField( - [ - 'fieldName' => 'serialNumber', - 'type' => 'string', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.php deleted file mode 100644 index a9698997d59..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.php +++ /dev/null @@ -1,10 +0,0 @@ -mapField( - [ - 'fieldName' => 'creditCardNumber', - 'type' => 'string', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869Payment.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869Payment.php deleted file mode 100644 index d394e02df35..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC869.DDC869Payment.php +++ /dev/null @@ -1,24 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', - ] -); -$metadata->mapField( - [ - 'fieldName' => 'value', - 'type' => 'float', - ] -); -$metadata->isMappedSuperclass = true; -$metadata->setCustomRepositoryClass(DDC869PaymentRepository::class); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Class.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Class.php deleted file mode 100644 index 06be2ffbadf..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Class.php +++ /dev/null @@ -1,14 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', - ] -); - -//$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Entity.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Entity.php deleted file mode 100644 index 174d7fd709f..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC889.DDC889Entity.php +++ /dev/null @@ -1,3 +0,0 @@ -mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - ] -); -$metadata->isMappedSuperclass = true; -$metadata->setCustomRepositoryClass(DDC889SuperClass::class); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php deleted file mode 100644 index b76d5222907..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php +++ /dev/null @@ -1,31 +0,0 @@ -setAssociationOverride( - 'address', - [ - 'joinColumns' => [ - [ - 'name' => 'adminaddress_id', - 'referencedColumnName' => 'id', - ], - ], - ] -); - -$metadata->setAssociationOverride( - 'groups', - [ - 'joinTable' => [ - 'name' => 'ddc964_users_admingroups', - 'joinColumns' => [ - ['name' => 'adminuser_id'], - ], - - 'inverseJoinColumns' => [ - ['name' => 'admingroup_id'], - ], - ], - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php deleted file mode 100644 index af20773f8ca..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php +++ /dev/null @@ -1,19 +0,0 @@ -setAttributeOverride('id', [ - 'columnName' => 'guest_id', - 'type' => 'integer', - 'length' => 140, -]); - -$metadata->setAttributeOverride( - 'name', - [ - 'columnName' => 'guest_name', - 'nullable' => false, - 'unique' => true, - 'length' => 240, - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php deleted file mode 100644 index 0bd4cffc280..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php +++ /dev/null @@ -1,62 +0,0 @@ -isMappedSuperclass = true; - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'user_id', - 'length' => 150, - ] -); -$metadata->mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - 'columnName' => 'user_name', - 'nullable' => true, - 'unique' => false, - 'length' => 250, - ] -); - -$metadata->mapManyToOne( - [ - 'fieldName' => 'address', - 'targetEntity' => 'DDC964Address', - 'cascade' => ['persist','merge'], - 'joinColumn' => ['name' => 'address_id', 'referencedColumnMame' => 'id'], - ] -); - -$metadata->mapManyToMany( - [ - 'fieldName' => 'groups', - 'targetEntity' => 'DDC964Group', - 'inversedBy' => 'users', - 'cascade' => ['persist','merge','detach'], - 'joinTable' => [ - 'name' => 'ddc964_users_groups', - 'joinColumns' => [ - [ - 'name' => 'user_id', - 'referencedColumnName' => 'id', - ], - ], - 'inverseJoinColumns' => [ - [ - 'name' => 'group_id', - 'referencedColumnName' => 'id', - ], - ], - ], - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Enums.Card.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Enums.Card.php index fd0eca82a85..0fad421c746 100644 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Enums.Card.php +++ b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Enums.Card.php @@ -9,12 +9,12 @@ 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - ] + ], ); $metadata->mapField( [ 'fieldName' => 'suit', 'type' => 'string', 'enumType' => Suit::class, - ] + ], ); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTyped.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTyped.php deleted file mode 100644 index fbdf9a3f54b..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTyped.php +++ /dev/null @@ -1,65 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); -$metadata->setPrimaryTable( - ['name' => 'cms_users_typed'] -); - -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - ] -); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - -$metadata->mapField( - [ - 'fieldName' => 'status', - 'length' => 50, - ] -); -$metadata->mapField( - [ - 'fieldName' => 'username', - 'length' => 255, - 'unique' => true, - ] -); -$metadata->mapField( - ['fieldName' => 'dateInterval'] -); -$metadata->mapField( - ['fieldName' => 'dateTime'] -); -$metadata->mapField( - ['fieldName' => 'dateTimeImmutable'] -); -$metadata->mapField( - ['fieldName' => 'array'] -); -$metadata->mapField( - ['fieldName' => 'boolean'] -); -$metadata->mapField( - ['fieldName' => 'float'] -); - -$metadata->mapOneToOne( - [ - 'fieldName' => 'email', - 'cascade' => [0 => 'persist'], - 'joinColumns' => [[]], - 'orphanRemoval' => true, - ] -); - -$metadata->mapManyToOne( - ['fieldName' => 'mainEmail'] -); - -$metadata->mapEmbedded(['fieldName' => 'contact']); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.php index 91e07b82da5..f23dbf31715 100644 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.php +++ b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.TypedProperties.UserTypedWithCustomTypedField.php @@ -6,22 +6,22 @@ $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); $metadata->setPrimaryTable( - ['name' => 'cms_users_typed_with_custom_typed_field'] + ['name' => 'cms_users_typed_with_custom_typed_field'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); $metadata->mapField( - ['fieldName' => 'customId'] + ['fieldName' => 'customId'], ); $metadata->mapField( - ['fieldName' => 'customIntTypedField'] + ['fieldName' => 'customIntTypedField'], ); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Insertable.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Insertable.php index 42be971a2de..c45c91afcc2 100644 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Insertable.php +++ b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Insertable.php @@ -5,14 +5,14 @@ use Doctrine\ORM\Mapping\ClassMetadata; $metadata->setPrimaryTable( - ['name' => 'insertable_column'] + ['name' => 'insertable_column'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -22,8 +22,8 @@ 'notInsertable' => true, 'options' => ['default' => '1234'], 'generated' => ClassMetadata::GENERATED_INSERT, - ] + ], ); $metadata->mapField( - ['fieldName' => 'insertableContent'] + ['fieldName' => 'insertableContent'], ); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Updatable.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Updatable.php index 7a5746abda4..75ecb8efb44 100644 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Updatable.php +++ b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Upsertable.Updatable.php @@ -5,14 +5,14 @@ use Doctrine\ORM\Mapping\ClassMetadata; $metadata->setPrimaryTable( - ['name' => 'updatable_column'] + ['name' => 'updatable_column'], ); $metadata->mapField( [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); @@ -21,8 +21,8 @@ 'fieldName' => 'nonUpdatableContent', 'notUpdatable' => true, 'generated' => ClassMetadata::GENERATED_ALWAYS, - ] + ], ); $metadata->mapField( - ['fieldName' => 'updatableContent'] + ['fieldName' => 'updatableContent'], ); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php deleted file mode 100644 index bdff936e09f..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php +++ /dev/null @@ -1,40 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); -$metadata->setDiscriminatorColumn( - [ - 'name' => 'dtype', - 'type' => 'string', - 'length' => 255, - 'fieldName' => 'dtype', - ] -); -$metadata->setDiscriminatorMap( - [ - 'cat' => Cat::class, - 'dog' => Dog::class, - ] -); -$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); -$metadata->mapField( - [ - 'fieldName' => 'id', - 'type' => 'string', - 'length' => null, - 'precision' => 0, - 'scale' => 0, - 'nullable' => false, - 'unique' => false, - 'id' => true, - 'columnName' => 'id', - ] -); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM); -$metadata->setCustomGeneratorDefinition(['class' => 'stdClass']); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Comment.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Comment.php deleted file mode 100644 index 130f8953706..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Comment.php +++ /dev/null @@ -1,27 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); -$metadata->setPrimaryTable( - [ - 'indexes' => [ - ['columns' => ['content'], 'flags' => ['fulltext'], 'options' => ['where' => 'content IS NOT NULL']], - ], - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'content', - 'type' => 'text', - 'scale' => 0, - 'length' => null, - 'unique' => false, - 'nullable' => false, - 'precision' => 0, - 'columnName' => 'content', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC1170Entity.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC1170Entity.php deleted file mode 100644 index 213198f39cd..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC1170Entity.php +++ /dev/null @@ -1,22 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'columnDefinition' => 'INT unsigned NOT NULL', - ] -); - -$metadata->mapField( - [ - 'fieldName' => 'value', - 'columnDefinition' => 'VARCHAR(255) NOT NULL', - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC807Entity.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC807Entity.php deleted file mode 100644 index 6d4aead41e4..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.DDC807Entity.php +++ /dev/null @@ -1,21 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - ] -); - -$metadata->setDiscriminatorColumn( - [ - 'name' => 'dtype', - 'columnDefinition' => "ENUM('ONE','TWO')", - ] -); - -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.php index 820ffe5e0df..39fffad3c73 100644 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.php +++ b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.GH10288EnumTypePerson.php @@ -9,14 +9,14 @@ [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->setDiscriminatorColumn( [ 'name' => 'discr', 'enumType' => GH10288People::class, - ] + ], ); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php deleted file mode 100644 index 52ecf622f37..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.PHPSLC.php +++ /dev/null @@ -1,18 +0,0 @@ -enableCache( - [ - 'usage' => ClassMetadata::CACHE_USAGE_READ_ONLY, - ] -); -$metadata->mapManyToOne( - [ - 'fieldName' => 'foo', - 'id' => true, - 'targetEntity' => 'PHPSLCFoo', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php deleted file mode 100644 index 03f5a86eb61..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php +++ /dev/null @@ -1,18 +0,0 @@ -mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - ] -); -$metadata->mapField( - [ - 'fieldName' => 'count', - 'type' => 'integer', - 'columnName' => '`count`', - ] -); diff --git a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php b/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php deleted file mode 100644 index 72ece4e6ac2..00000000000 --- a/tests/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php +++ /dev/null @@ -1,146 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); -$metadata->setPrimaryTable( - ['name' => 'cms_users'] -); -$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); -$metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist'); -$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist'); -$metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist'); -$metadata->addNamedQuery( - [ - 'name' => 'all', - 'query' => 'SELECT u FROM __CLASS__ u', - ] -); -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', - 'options' => ['foo' => 'bar', 'unsigned' => false], - ] -); -$metadata->mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - 'length' => 50, - 'unique' => true, - 'nullable' => true, - 'columnName' => 'name', - 'options' => ['foo' => 'bar', 'baz' => ['key' => 'val'], 'fixed' => false], - ] -); -$metadata->mapField( - [ - 'fieldName' => 'email', - 'type' => 'string', - 'columnName' => 'user_email', - 'columnDefinition' => 'CHAR(32) NOT NULL', - ] -); -$mapping = ['fieldName' => 'version', 'type' => 'integer']; -$metadata->setVersionMapping($mapping); -$metadata->mapField($mapping); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); -$metadata->mapOneToOne( - [ - 'fieldName' => 'address', - 'targetEntity' => Address::class, - 'cascade' => - [0 => 'remove'], - 'mappedBy' => null, - 'inversedBy' => 'user', - 'joinColumns' => - [ - 0 => - [ - 'name' => 'address_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE', - ], - ], - 'orphanRemoval' => false, - ] -); -$metadata->mapOneToMany( - [ - 'fieldName' => 'phonenumbers', - 'targetEntity' => Phonenumber::class, - 'cascade' => - [1 => 'persist'], - 'mappedBy' => 'user', - 'orphanRemoval' => true, - 'orderBy' => - ['number' => 'ASC'], - ] -); -$metadata->mapManyToMany( - [ - 'fieldName' => 'groups', - 'targetEntity' => Group::class, - 'cascade' => - [ - 0 => 'remove', - 1 => 'persist', - 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', - ], - 'mappedBy' => null, - 'joinTable' => - [ - 'name' => 'cms_users_groups', - 'joinColumns' => - [ - 0 => - [ - 'name' => 'user_id', - 'referencedColumnName' => 'id', - 'unique' => false, - 'nullable' => false, - ], - ], - 'inverseJoinColumns' => - [ - 0 => - [ - 'name' => 'group_id', - 'referencedColumnName' => 'id', - 'columnDefinition' => 'INT NULL', - ], - ], - ], - 'orderBy' => null, - ] -); -$metadata->table['options'] = [ - 'foo' => 'bar', - 'baz' => ['key' => 'val'], -]; -$metadata->table['uniqueConstraints'] = [ - 'search_idx' => ['columns' => ['name', 'user_email'], 'options' => ['where' => 'name IS NOT NULL']], - 'phone_idx' => ['fields' => ['name', 'phone']], -]; -$metadata->table['indexes'] = [ - 'name_idx' => ['columns' => ['name']], - 0 => ['columns' => ['user_email']], - 'fields' => ['fields' => ['name', 'email']], -]; -$metadata->setSequenceGeneratorDefinition( - [ - 'sequenceName' => 'tablename_seq', - 'allocationSize' => 100, - 'initialValue' => 1, - ] -); diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml index fb8bdb40491..4960b173785 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml @@ -4,43 +4,11 @@ https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - + - - - SELECT id, country, city FROM cms_addresses - - - - SELECT * FROM cms_addresses WHERE id = ? - - - - SELECT COUNT(*) AS count FROM cms_addresses - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml index f91ed311512..104ef15a567 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml @@ -6,87 +6,10 @@ https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - - - - - - - - SELECT id, username FROM cms_users WHERE username = ? - - - - SELECT * FROM cms_users WHERE username = ? - - - - SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ? - - - - SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ? - - - - SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username - - - - SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -108,7 +31,6 @@ - @@ -117,11 +39,10 @@ - + - diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml index daa94cd2cea..d4aa6dbbf7c 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml @@ -13,35 +13,16 @@ - - - SELECT id, name, discr FROM company_persons ORDER BY name - - - - SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name - - - - - - - - - - - - - + - + - + diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml index 3e6a91f39e1..cc4764b1b57 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml @@ -14,7 +14,6 @@ - @@ -22,7 +21,6 @@ - diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml index 5343d040e16..6abbf8c52b6 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml @@ -34,10 +34,6 @@ - - - - diff --git a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectAttributes.dcm.xml b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectAttributes.dcm.xml index f486dd9aedd..31d6bafa728 100644 --- a/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectAttributes.dcm.xml +++ b/tests/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.UserIncorrectAttributes.dcm.xml @@ -21,10 +21,6 @@ - - - - diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml deleted file mode 100644 index 712f089da99..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml +++ /dev/null @@ -1,64 +0,0 @@ -Doctrine\Tests\Models\CMS\CmsAddress: - type: entity - table: cms_address - entityListeners: - CmsAddressListener: ~ - namedNativeQueries: - find-all: - resultSetMapping: mapping-find-all - query: SELECT id, country, city FROM cms_addresses - find-by-id: - name: find-by-id - resultClass: CmsAddress - query: SELECT * FROM cms_addresses WHERE id = ? - count: - name: count - resultSetMapping: mapping-count - query: SELECT COUNT(*) AS count FROM cms_addresses - - sqlResultSetMappings: - mapping-find-all: - entityResult: - address: - entityClass: CmsAddress - fieldResult: - 0: - name: id - column: id - 1: - name: city - column: city - 2: - name: country - column: country - mapping-without-fields: - name: mapping-without-fields - entityResult: - address: - entityClass: CmsAddress - mapping-count: - name: mapping-count - columnResult: - count: - name: count - id: - id: - type: integer - generator: - strategy: AUTO - fields: - country: - type: string - length: 50 - city: - type: string - length: 50 - zip: - type: string - length: 50 - oneToOne: - user: - targetEntity: CmsUser - inversedBy: address - joinColumn: - referencedColumnName: id diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml deleted file mode 100644 index 329e3d061dc..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml +++ /dev/null @@ -1,157 +0,0 @@ -Doctrine\Tests\Models\CMS\CmsUser: - type: entity - table: cms_users - namedQueries: - all: SELECT u FROM __CLASS__ u - namedNativeQueries: - fetchIdAndUsernameWithResultClass: - resultClass: CmsUser - query: SELECT id, username FROM cms_users WHERE username = ? - fetchAllColumns: - name: fetchAllColumns - resultClass: CmsUser - query: SELECT * FROM cms_users WHERE username = ? - fetchJoinedAddress: - name: fetchJoinedAddress - resultSetMapping: mappingJoinedAddress - query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ? - fetchJoinedPhonenumber: - name: fetchJoinedPhonenumber - resultSetMapping: mappingJoinedPhonenumber - query: SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ? - fetchUserPhonenumberCount: - name: fetchUserPhonenumberCount - resultSetMapping: mappingUserPhonenumberCount - query: SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username - fetchMultipleJoinsEntityResults: - name: fetchMultipleJoinsEntityResults - resultSetMapping: mappingMultipleJoinsEntityResults - query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username - - sqlResultSetMappings: - mappingJoinedAddress: - entityResult: - 0: - entityClass: __CLASS__ - fieldResult: - 0: - name: id - 1: - name: name - 2: - name: status - 3: - name: address.zip - 4: - name: address.city - 5: - name: address.country - 6: - name: address.id - column: a_id - mappingJoinedPhonenumber: - name: mappingJoinedPhonenumber - entityResult: - user: - entityClass: CmsUser - fieldResult: - 0: - name: id - 1: - name: name - 2: - name: status - 3: - name: phonenumbers.phonenumber - column: number - mappingUserPhonenumberCount: - name: mappingUserPhonenumberCount - columnResult: - 0: - name: numphones - entityResult: - user_0: - entityClass: CmsUser - fieldResult: - 0: - name: id - 1: - name: name - 2: - name: status - mappingMultipleJoinsEntityResults: - name: mappingMultipleJoinsEntityResults - columnResult: - 0: - name: numphones - entityResult: - 0: - entityClass: __CLASS__ - fieldResult: - 0: - name: id - column: u_id - 1: - name: name - column: u_name - 2: - name: status - column: u_status - 1: - entityClass: CmsAddress - fieldResult: - 0: - name: id - column: a_id - 1: - name: zip - column: a_zip - 2: - name: country - column: a_country - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - length: 255 - username: - type: string - length: 255 - unique: true - status: - type: string - length: 50 - unique: true - oneToOne: - address: - targetEntity: CmsAddress - orphanRemoval: true - inversedBy: user - joinColumn: - name: address_id - referencedColumnName: id - cascade: [ persist ] - email: - targetEntity: CmsEmail - orphanRemoval: true - inversedBy: user - joinColumn: - nullable: true - referencedColumnName: id - cascade: [ persist ] - manyToMany: - groups: - targetEntity: CmsGroup - joinTable: - name: cms_users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - cascade: [ persist , detach, merge] diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Cache.City.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Cache.City.dcm.yml deleted file mode 100644 index 7bc5e7db171..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Cache.City.dcm.yml +++ /dev/null @@ -1,36 +0,0 @@ -Doctrine\Tests\Models\Cache\City: - type: entity - table: cache_city - cache: - usage : READ_ONLY - id: - id: - type: integer - id: true - generator: - strategy: IDENTITY - fields: - name: - type: string - manyToOne: - state: - targetEntity: Doctrine\Tests\Models\Cache\State - inversedBy: cities - joinColumns: - state_id: - referencedColumnName: id - cache: - usage : READ_ONLY - manyToMany: - travels: - targetEntity: Doctrine\Tests\Models\Cache\Travel - mappedBy: visitedCities - - oneToMany: - attractions: - targetEntity: Doctrine\Tests\Models\Cache\Attraction - mappedBy: city - cache: - usage : READ_ONLY - orderBy: - name: ASC diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml deleted file mode 100644 index 34ea8a4611c..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml +++ /dev/null @@ -1,32 +0,0 @@ -Doctrine\Tests\Models\Company\CompanyContract: - type: entity - table: company_contracts - inheritanceType: SINGLE_TABLE - discriminatorMap: - fix: CompanyFixContract - flexible: CompanyFlexContract - flexultra: CompanyFlexUltraContract - - entityListeners: - CompanyContractListener: - - preFlush: [preFlushHandler] - postLoad: [postLoadHandler] - - postPersist: [postPersistHandler] - prePersist: [prePersistHandler] - - postUpdate: [postUpdateHandler] - preUpdate: [preUpdateHandler] - - postRemove: [postRemoveHandler] - preRemove: [preRemoveHandler] - - id: - id: - type: integer - generator: - strategy: AUTO - fields: - completed: - type: boolean diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml deleted file mode 100644 index 99dc79d0501..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\Company\CompanyFixContract: - type: entity - fields: - fixPrice: - type: integer diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml deleted file mode 100644 index ada071a83ed..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml +++ /dev/null @@ -1,7 +0,0 @@ -Doctrine\Tests\Models\Company\CompanyFlexContract: - type: entity - fields: - hoursWorked: - type: integer - pricePerHour: - type: integer diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml deleted file mode 100644 index aecdafe5008..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml +++ /dev/null @@ -1,25 +0,0 @@ -Doctrine\Tests\Models\Company\CompanyFlexUltraContract: - type: entity - - entityListeners: - CompanyContractListener: - - preFlush: [preFlushHandler] - postLoad: [postLoadHandler] - - postPersist: [postPersistHandler] - prePersist: [prePersistHandler] - - postUpdate: [postUpdateHandler] - preUpdate: [preUpdateHandler] - - postRemove: [postRemoveHandler] - preRemove: [preRemoveHandler] - - CompanyFlexUltraContractListener: - - prePersist: [prePersistHandler1, prePersistHandler2] - - fields: - maxPrice: - type: integer diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml deleted file mode 100644 index 28eec43e391..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml +++ /dev/null @@ -1,74 +0,0 @@ -Doctrine\Tests\Models\Company\CompanyPerson: - type: entity - table: company_persons - inheritanceType: JOINED - discriminatorMap: - person: CompanyPerson - manager: CompanyManager - employee: CompanyEmployee - namedNativeQueries: - fetchAllWithResultClass: - resultClass: __CLASS__ - query: SELECT id, name, discr FROM company_persons ORDER BY name - fetchAllWithSqlResultSetMapping: - name: fetchAllWithSqlResultSetMapping - resultSetMapping: mappingFetchAll - query: SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name - - sqlResultSetMappings: - mappingFetchAll: - entityResult: - 0: - entityClass: __CLASS__ - discriminatorColumn: discriminator - fieldResult: - 0: - name: id - 1: - name: name - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - length: 255 - username: - type: string - length: 255 - unique: true - status: - type: string - length: 50 - unique: true - oneToOne: - address: - targetEntity: CmsAddress - orphanRemoval: true - inversedBy: user - joinColumn: - name: address_id - referencedColumnName: id - cascade: [ persist ] - email: - targetEntity: CmsEmail - orphanRemoval: true - inversedBy: user - joinColumn: - nullable: true - referencedColumnName: id - cascade: [ persist ] - manyToMany: - groups: - targetEntity: CmsGroup - joinTable: - name: cms_users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - cascade: [ persist , detach, merge] diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.dcm.yml deleted file mode 100644 index 674328cd526..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC1476.DDC1476EntityWithDefaultFieldType.dcm.yml +++ /dev/null @@ -1,8 +0,0 @@ -Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType: - type: entity - id: - id: - generator: - strategy: NONE - fields: - name: ~ diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.dcm.yml deleted file mode 100644 index f28adbb61f4..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.ExplicitSchemaAndTable.dcm.yml +++ /dev/null @@ -1,8 +0,0 @@ -Doctrine\Tests\Models\DDC2825\ExplicitSchemaAndTable: - type: entity - table: explicit_table - schema: explicit_schema - id: - id: - generator: - strategy: AUTO diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.dcm.yml deleted file mode 100644 index bf072816cd7..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC2825.SchemaAndTableInTableName.dcm.yml +++ /dev/null @@ -1,7 +0,0 @@ -Doctrine\Tests\Models\DDC2825\SchemaAndTableInTableName: - type: entity - table: implicit_schema.implicit_table - id: - id: - generator: - strategy: AUTO diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579Admin.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579Admin.dcm.yml deleted file mode 100644 index 7420b14e66d..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579Admin.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\DDC3579\DDC3579Admin: - type: entity - associationOverride: - groups: - inversedBy: admins diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579User.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579User.dcm.yml deleted file mode 100644 index 63d095035f3..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3579.DDC3579User.dcm.yml +++ /dev/null @@ -1,19 +0,0 @@ -Doctrine\Tests\Models\DDC3579\DDC3579User: - type: mappedSuperclass - id: - id: - type: integer - column: user_id - length: 150 - generator: - strategy: AUTO - fields: - name: - type: string - column: user_name - length: 250 - nullable: true - unique: false - manyToMany: - groups: - targetEntity: DDC3579Group diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityA.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityA.dcm.yml deleted file mode 100644 index 1907bcc112a..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityA.dcm.yml +++ /dev/null @@ -1,23 +0,0 @@ -Doctrine\Tests\Models\DDC3711\DDC3711EntityA: - type: entity - table: ddc3711.entityA - id: - id1: - type: int - id2: - type: int - manyToMany: - entityB: - targetEntity: Doctrine\Tests\Models\DDC3711\DDC3711EntityB - joinTable: - name: link - joinColumns: - link_a_id1: - referencedColumnName: id1 - link_a_id2: - referencedColumnName: id2 - inverseJoinColumns: - link_b_id1: - referencedColumnName: id1 - link_b_id2: - referencedColumnName: id2 diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityB.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityB.dcm.yml deleted file mode 100644 index 24ec96932c6..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC3711.DDC3711EntityB.dcm.yml +++ /dev/null @@ -1,8 +0,0 @@ -Doctrine\Tests\Models\DDC3711\DDC3711EntityB: - type: entity - table: ddc3711.entityB - id: - id1: - type: int - id2: - type: int diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.dcm.yml deleted file mode 100644 index 2044c9be527..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934BaseContract.dcm.yml +++ /dev/null @@ -1,12 +0,0 @@ -Doctrine\Tests\Models\DDC5934\DDC5934BaseContract: - type: mappedSuperclass - id: - id: - type: integer - column: id - generator: - strategy: AUTO - manyToMany: - members: - targetEntity: DDC5934Member - inversedBy: contract diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934Contract.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934Contract.dcm.yml deleted file mode 100644 index 45ba145853e..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC5934.DDC5934Contract.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\DDC5934\DDC5934Contract: - type: entity - associationOverride: - members: - fetch: EXTRA_LAZY diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.dcm.yml deleted file mode 100644 index 04eca657401..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869ChequePayment.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\DDC869\DDC869ChequePayment: - type: entity - fields: - serialNumber: - type: string diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.dcm.yml deleted file mode 100644 index 3da249e5c92..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869CreditCardPayment.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\DDC869\DDC869CreditCardPayment: - type: entity - fields: - creditCardNumber: - type: string diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869Payment.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869Payment.dcm.yml deleted file mode 100644 index fb219451b3e..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC869.DDC869Payment.dcm.yml +++ /dev/null @@ -1,12 +0,0 @@ -Doctrine\Tests\Models\DDC869\DDC869Payment: - type: mappedSuperclass - repositoryClass : Doctrine\Tests\Models\DDC869\DDC869PaymentRepository - id: - id: - type: integer - unsigned: true - generator: - strategy: AUTO - fields: - value: - type: float diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Class.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Class.dcm.yml deleted file mode 100644 index ed94b14c555..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Class.dcm.yml +++ /dev/null @@ -1,8 +0,0 @@ -Doctrine\Tests\Models\DDC889\DDC889Class: - type: class - id: - id: - type: integer - unsigned: true - generator: - strategy: AUTO diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Entity.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Entity.dcm.yml deleted file mode 100644 index 404d4a538f1..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889Entity.dcm.yml +++ /dev/null @@ -1,2 +0,0 @@ -Doctrine\Tests\Models\DDC889\DDC889Entity: - type: entity diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889SuperClass.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889SuperClass.dcm.yml deleted file mode 100644 index aceada9a98f..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC889.DDC889SuperClass.dcm.yml +++ /dev/null @@ -1,5 +0,0 @@ -Doctrine\Tests\Models\DDC889\DDC889SuperClass: - type: mappedSuperclass - fields: - name: - type: string diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml deleted file mode 100644 index 77823f29224..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\Models\DDC964\DDC964Admin: - type: entity - associationOverride: - address: - joinColumn: - adminaddress_id: - name: adminaddress_id - referencedColumnName: id - groups: - joinTable: - name: ddc964_users_admingroups - joinColumns: - adminuser_id: - referencedColumnName: id - inverseJoinColumns: - admingroup_id: - referencedColumnName: id diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml deleted file mode 100644 index 5c4130e6e59..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml +++ /dev/null @@ -1,13 +0,0 @@ -Doctrine\Tests\Models\DDC964\DDC964Guest: - type: entity - attributeOverride: - id: - column: guest_id - type: integer - length: 140 - name: - column: guest_name - type: string - length: 240 - nullable: false - unique: true diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml deleted file mode 100644 index 459b642928c..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml +++ /dev/null @@ -1,35 +0,0 @@ -Doctrine\Tests\Models\DDC964\DDC964User: - type: mappedSuperclass - id: - id: - type: integer - column: user_id - length: 150 - generator: - strategy: AUTO - fields: - name: - type: string - column: user_name - length: 250 - nullable: true - unique: false - manyToOne: - address: - targetEntity: DDC964Address - joinColumn: - name: address_id - referencedColumnName: id - cascade: [ persist, merge ] - manyToMany: - groups: - targetEntity: DDC964Group - joinTable: - name: ddc964_users_groups - joinColumns: - user_id: - referencedColumnName: id - inverseJoinColumns: - group_id: - referencedColumnName: id - cascade: [ persist, merge, detach ] diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml deleted file mode 100644 index 9c573a561d2..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.AbstractContentItem.dcm.yml +++ /dev/null @@ -1,14 +0,0 @@ -Doctrine\Tests\Models\DirectoryTree\AbstractContentItem: - type: mappedSuperclass - id: - id: - type: integer - unsigned: true - generator: - strategy: AUTO - fields: - name: - type: string - manyToOne: - parentDirectory: - targetEntity: Doctrine\Tests\Models\DirectoryTree\Directory diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml deleted file mode 100644 index d2b93d4901c..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.Directory.dcm.yml +++ /dev/null @@ -1,6 +0,0 @@ -Doctrine\Tests\Models\DirectoryTree\Directory: - type: entity - fields: - path: - type: string - length: 255 diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml deleted file mode 100644 index cbc8edfec4f..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DirectoryTree.File.dcm.yml +++ /dev/null @@ -1,6 +0,0 @@ -Doctrine\Tests\Models\DirectoryTree\File: - type: entity - fields: - extension: - type: string - length: 10 diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Enums.Card.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Enums.Card.dcm.yml deleted file mode 100644 index 1694c72070f..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Enums.Card.dcm.yml +++ /dev/null @@ -1,11 +0,0 @@ -Doctrine\Tests\Models\Enums\Card: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - suit: - type: string - enumType: Doctrine\Tests\Models\Enums\Suit \ No newline at end of file diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Generic.BooleanModel.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Generic.BooleanModel.dcm.yml deleted file mode 100644 index 5b1ec64b874..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Generic.BooleanModel.dcm.yml +++ /dev/null @@ -1,11 +0,0 @@ -\stdClass: - type: entity - id: - id: - type: integer - unsigned: true - generator: - strategy: AUTO - fields: - booleanField: - type: boolean diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.TypedProperties.UserTyped.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.TypedProperties.UserTyped.dcm.yml deleted file mode 100644 index 7bc39f55804..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.TypedProperties.UserTyped.dcm.yml +++ /dev/null @@ -1,28 +0,0 @@ -Doctrine\Tests\Models\TypedProperties\UserTyped: - type: entity - table: cms_users_typed - id: - id: - generator: - strategy: AUTO - fields: - status: - length: 50 - username: - length: 255 - unique: true - dateInterval: ~ - dateTime: ~ - dateTimeImmutable: ~ - array: ~ - boolean: ~ - float: ~ - oneToOne: - email: - cascade: [ persist ] - orphanRemoval: true - joinColumn: [] - manyToOne: - mainEmail: [] - embedded: - contact: ~ diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Insertable.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Insertable.dcm.yml deleted file mode 100644 index 4e5e8531841..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Insertable.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\Models\Upsertable\Insertable: - type: entity - table: insertable_column - id: - id: - generator: - strategy: AUTO - fields: - nonInsertableContent: - type: string - insertable: false - generated: INSERT - options: - default: 1234 - insertableContent: - type: string - insertable: true \ No newline at end of file diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Updatable.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Updatable.dcm.yml deleted file mode 100644 index 386ae77209b..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Upsertable.Updatable.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\Models\Upsertable\Updatable: - type: entity - table: updatable_column - id: - id: - generator: - strategy: AUTO - fields: - nonUpdatableContent: - type: string - updatable: false - generated: ALWAYS - options: - default: 1234 - updatableContent: - type: string - updatable: true diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml deleted file mode 100644 index 7bdad824037..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\ORM\Mapping\Animal: - type: entity - inheritanceType: SINGLE_TABLE - discriminatorMap: - cat: Cat - dog: Dog - discriminatorColumn: - type: string - name: discr - length: 32 - id: - id: - type: integer - generator: - strategy: CUSTOM - customIdGenerator: - class: stdClass diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Comment.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Comment.dcm.yml deleted file mode 100644 index f37bfdcc67b..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Comment.dcm.yml +++ /dev/null @@ -1,11 +0,0 @@ -Doctrine\Tests\ORM\Mapping\Comment: - type: entity - fields: - content: - type: text - indexes: - 0: - columns: content - flags: fulltext - options: - where: "content IS NOT NULL" diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC1170Entity.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC1170Entity.dcm.yml deleted file mode 100644 index e42305d4fdf..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC1170Entity.dcm.yml +++ /dev/null @@ -1,10 +0,0 @@ -Doctrine\Tests\ORM\Mapping\DDC1170Entity: - type: entity - id: - id: - columnDefinition: INT unsigned NOT NULL - generator: - strategy: NONE - fields: - value: - columnDefinition: VARCHAR(255) NOT NULL diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC2069Entity.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC2069Entity.dcm.yml deleted file mode 100644 index 5b16c12bc94..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC2069Entity.dcm.yml +++ /dev/null @@ -1,15 +0,0 @@ -Doctrine\Tests\ORM\Mapping\DDC2069Entity: - type: entity - id: - id: ~ - fields: - name: - type: string ( 255 ) - value: - type: string ( 255 ) - uniqueConstraints: - 0: - columns: name, value - indexes: - 0: - columns: value, name diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.yml deleted file mode 100644 index 624fb54a9bf..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.yml +++ /dev/null @@ -1,13 +0,0 @@ -Doctrine\Tests\ORM\Mapping\DDC807Entity: - type: entity - inheritanceType: SINGLE_TABLE - discriminatorMap: - ONE: DDC807SubClasse1 - TWO: DDC807SubClasse2 - discriminatorColumn: - name: dtype - columnDefinition: ENUM('ONE','TWO') - id: - id: - generator: - strategy: NONE diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml deleted file mode 100644 index a315310e4d1..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml +++ /dev/null @@ -1,10 +0,0 @@ -Doctrine\Tests\ORM\Mapping\ReservedWordInTableColumn: - type: entity - id: - id: - generator: - strategy: NONE - fields: - count: - type: integer - column: '`count`' diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityIncompleteDiscriminatorColumnMapping.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityIncompleteDiscriminatorColumnMapping.dcm.yml deleted file mode 100644 index d81bc7a6ae8..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityIncompleteDiscriminatorColumnMapping.dcm.yml +++ /dev/null @@ -1,12 +0,0 @@ -Doctrine\Tests\ORM\Mapping\SingleTableEntityIncompleteDiscriminatorColumnMapping: - type: entity - inheritanceType: SINGLE_TABLE - discriminatorMap: - ONE: SingleTableEntityIncompleteDiscriminatorColumnMappingSub1 - TWO: SingleTableEntityIncompleteDiscriminatorColumnMappingSub2 - discriminatorColumn: - name: dtype - id: - id: - generator: - strategy: NONE diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityNoDiscriminatorColumnMapping.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityNoDiscriminatorColumnMapping.dcm.yml deleted file mode 100644 index 25ec79eb9dc..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.SingleTableEntityNoDiscriminatorColumnMapping.dcm.yml +++ /dev/null @@ -1,10 +0,0 @@ -Doctrine\Tests\ORM\Mapping\SingleTableEntityNoDiscriminatorColumnMapping: - type: entity - inheritanceType: SINGLE_TABLE - discriminatorMap: - ONE: SingleTableEntityNoDiscriminatorColumnMappingSub1 - TWO: SingleTableEntityNoDiscriminatorColumnMappingSub2 - id: - id: - generator: - strategy: NONE diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml deleted file mode 100644 index d3504ffbaa0..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml +++ /dev/null @@ -1,89 +0,0 @@ -Doctrine\Tests\ORM\Mapping\User: - type: entity - table: cms_users - options: - foo: bar - baz: - key: val - namedQueries: - all: SELECT u FROM __CLASS__ u - id: - id: - type: integer - generator: - strategy: AUTO - sequenceGenerator: - sequenceName: tablename_seq - allocationSize: 100 - initialValue: 1 - options: - foo: bar - unsigned: false - fields: - name: - type: string - length: 50 - nullable: true - unique: true - options: - foo: bar - baz: - key: val - fixed: false - email: - type: string - column: user_email - columnDefinition: CHAR(32) NOT NULL - version: - type: integer - version: true - oneToOne: - address: - targetEntity: Address - inversedBy: user - joinColumn: - name: address_id - referencedColumnName: id - onDelete: CASCADE - cascade: [ remove ] - oneToMany: - phonenumbers: - targetEntity: Phonenumber - orphanRemoval: true - mappedBy: user - orderBy: - number: ASC - cascade: [ persist ] - manyToMany: - groups: - targetEntity: Group - joinTable: - name: cms_users_groups - joinColumns: - user_id: - referencedColumnName: id - nullable: false - unique: false - inverseJoinColumns: - group_id: - referencedColumnName: id - columnDefinition: INT NULL - cascade: - - all - lifecycleCallbacks: - prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] - postPersist: [ doStuffOnPostPersist ] - uniqueConstraints: - search_idx: - columns: name,user_email - options: - where: name IS NOT NULL - phone_idx: - fields: name,phone - indexes: - name_idx: - columns: name - 0: - columns: user_email - fields: - fields: name,email diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectIndex.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectIndex.dcm.yml deleted file mode 100644 index 06855341204..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectIndex.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\ORM\Mapping\UserIncorrectIndex: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - email: - type: string - column: user_email - indexes: - name_idx: - columns: name - fields: email diff --git a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectUniqueConstraint.dcm.yml b/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectUniqueConstraint.dcm.yml deleted file mode 100644 index 82f7e72a778..00000000000 --- a/tests/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.UserIncorrectUniqueConstraint.dcm.yml +++ /dev/null @@ -1,17 +0,0 @@ -Doctrine\Tests\ORM\Mapping\UserIncorrectUniqueConstraint: - type: entity - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - email: - type: string - column: user_email - uniqueConstraints: - name_idx: - columns: name - fields: email diff --git a/tests/Tests/ORM/ORMInvalidArgumentExceptionTest.php b/tests/Tests/ORM/ORMInvalidArgumentExceptionTest.php index e878bc067b6..00536a046b1 100644 --- a/tests/Tests/ORM/ORMInvalidArgumentExceptionTest.php +++ b/tests/Tests/ORM/ORMInvalidArgumentExceptionTest.php @@ -4,28 +4,18 @@ namespace Doctrine\Tests\ORM; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\ORM\ORMInvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use stdClass; use function spl_object_id; -/** @covers \Doctrine\ORM\ORMInvalidArgumentException */ +#[CoversClass(ORMInvalidArgumentException::class)] class ORMInvalidArgumentExceptionTest extends TestCase { - /** - * @param mixed $value - * - * @dataProvider invalidEntityNames - */ - public function testInvalidEntityName($value, string $expectedMessage): void - { - $exception = ORMInvalidArgumentException::invalidEntityName($value); - - self::assertInstanceOf(ORMInvalidArgumentException::class, $exception); - self::assertSame($expectedMessage, $exception->getMessage()); - } - /** @phpstan-return list */ public static function invalidEntityNames(): array { @@ -38,7 +28,7 @@ public static function invalidEntityNames(): array ]; } - /** @dataProvider newEntitiesFoundThroughRelationshipsErrorMessages */ + #[DataProvider('newEntitiesFoundThroughRelationshipsErrorMessages')] public function testNewEntitiesFoundThroughRelationships(array $newEntities, string $expectedMessage): void { $exception = ORMInvalidArgumentException::newEntitiesFoundThroughRelationships($newEntities); @@ -57,21 +47,21 @@ public function __toString(): string return 'ThisIsAStringRepresentationOfEntity3'; } }; - $association1 = [ + $association1 = OneToManyAssociationMapping::fromMappingArray([ 'sourceEntity' => 'foo1', 'fieldName' => 'bar1', 'targetEntity' => 'baz1', - ]; - $association2 = [ + ]); + $association2 = OneToManyAssociationMapping::fromMappingArray([ 'sourceEntity' => 'foo2', 'fieldName' => 'bar2', 'targetEntity' => 'baz2', - ]; - $association3 = [ + ]); + $association3 = OneToManyAssociationMapping::fromMappingArray([ 'sourceEntity' => 'foo3', 'fieldName' => 'bar3', 'targetEntity' => 'baz3', - ]; + ]); return [ 'one entity found' => [ diff --git a/tests/Tests/ORM/ORMSetupTest.php b/tests/Tests/ORM/ORMSetupTest.php index 2048346051e..e2139504f71 100644 --- a/tests/Tests/ORM/ORMSetupTest.php +++ b/tests/Tests/ORM/ORMSetupTest.php @@ -4,17 +4,15 @@ namespace Doctrine\Tests\ORM; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\Configuration; -use Doctrine\ORM\Mapping as AnnotationNamespace; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping as MappingNamespace; use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; -use Doctrine\ORM\Mapping\Driver\YamlDriver; use Doctrine\ORM\ORMSetup; -use LogicException; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresPhpExtension; +use PHPUnit\Framework\Attributes\RequiresSetting; use PHPUnit\Framework\TestCase; -use ReflectionClass; use ReflectionProperty; use Symfony\Component\Cache\Adapter\AbstractAdapter; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -24,35 +22,6 @@ class ORMSetupTest extends TestCase { - use VerifyDeprecations; - - public function testAnnotationConfiguration(): void - { - $config = ORMSetup::createAnnotationMetadataConfiguration([], true); - - self::assertInstanceOf(Configuration::class, $config); - self::assertEquals(sys_get_temp_dir(), $config->getProxyDir()); - self::assertEquals('DoctrineProxies', $config->getProxyNamespace()); - self::assertInstanceOf(AnnotationDriver::class, $config->getMetadataDriverImpl()); - } - - public function testNewDefaultAnnotationDriver(): void - { - $paths = [__DIR__]; - $reflectionClass = new ReflectionClass(AnnotatedDummy::class); - - $annotationDriver = ORMSetup::createDefaultAnnotationDriver($paths); - $reader = $annotationDriver->getReader(); - $annotation = $reader->getMethodAnnotation( - $reflectionClass->getMethod('namespacedAnnotationMethod'), - AnnotationNamespace\PrePersist::class - ); - self::assertInstanceOf(AnnotationNamespace\PrePersist::class, $annotation); - } - - /** - * @requires PHP 8.0 - */ public function testAttributeConfiguration(): void { $config = ORMSetup::createAttributeMetadataConfiguration([], true); @@ -63,19 +32,6 @@ public function testAttributeConfiguration(): void self::assertInstanceOf(AttributeDriver::class, $config->getMetadataDriverImpl()); } - /** - * @requires PHP < 8 - */ - public function testAttributeConfigurationFailsOnPHP7(): void - { - self::expectException(LogicException::class); - self::expectExceptionMessage( - 'The attribute metadata driver cannot be enabled on PHP 7. Please upgrade to PHP 8 or choose a different metadata driver.' - ); - - ORMSetup::createAttributeMetadataConfiguration([], true); - } - public function testXMLConfiguration(): void { $config = ORMSetup::createXMLMetadataConfiguration([], true); @@ -84,72 +40,61 @@ public function testXMLConfiguration(): void self::assertInstanceOf(XmlDriver::class, $config->getMetadataDriverImpl()); } - public function testYAMLConfiguration(): void + public function testDisablingXmlValidationIsPossible(): void { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8465'); - $config = ORMSetup::createYAMLMetadataConfiguration([], true); + $this->expectNotToPerformAssertions(); - self::assertInstanceOf(Configuration::class, $config); - self::assertInstanceOf(YamlDriver::class, $config->getMetadataDriverImpl()); + ORMSetup::createXMLMetadataConfiguration(paths: [], isXsdValidationEnabled: false); } - /** - * @requires extension apcu - * @requires setting apc.enable_cli 1 - * @requires setting apc.enabled 1 - */ + #[RequiresPhpExtension('apcu')] + #[RequiresSetting('apc.enable_cli', '1')] + #[RequiresSetting('apc.enabled', '1')] public function testCacheNamespaceShouldBeGeneratedForApcu(): void { $config = ORMSetup::createConfiguration(false, '/foo'); $cache = $config->getMetadataCache(); $namespaceProperty = new ReflectionProperty(AbstractAdapter::class, 'namespace'); - $namespaceProperty->setAccessible(true); self::assertInstanceOf(ApcuAdapter::class, $cache); self::assertSame('dc2_1effb2475fcfba4f9e8b8a1dbc8f3caf:', $namespaceProperty->getValue($cache)); } - /** @group DDC-1350 */ + #[Group('DDC-1350')] public function testConfigureProxyDir(): void { - $config = ORMSetup::createAnnotationMetadataConfiguration([], true, '/foo'); + $config = ORMSetup::createAttributeMetadataConfiguration([], true, '/foo'); self::assertEquals('/foo', $config->getProxyDir()); } - /** @group DDC-1350 */ + #[Group('DDC-1350')] public function testConfigureCache(): void { $cache = new ArrayAdapter(); - $config = ORMSetup::createAnnotationMetadataConfiguration([], true, null, $cache); + $config = ORMSetup::createAttributeMetadataConfiguration([], true, null, $cache); self::assertSame($cache, $config->getResultCache()); - self::assertSame($cache, $config->getResultCacheImpl()->getPool()); self::assertSame($cache, $config->getQueryCache()); - self::assertSame($cache, $config->getQueryCacheImpl()->getPool()); self::assertSame($cache, $config->getMetadataCache()); - self::assertSame($cache, $config->getMetadataCacheImpl()->getPool()); } - /** @group DDC-3190 */ + #[Group('DDC-3190')] public function testConfigureCacheCustomInstance(): void { $cache = new ArrayAdapter(); $config = ORMSetup::createConfiguration(true, null, $cache); self::assertSame($cache, $config->getResultCache()); - self::assertSame($cache, $config->getResultCacheImpl()->getPool()); self::assertSame($cache, $config->getQueryCache()); - self::assertSame($cache, $config->getQueryCacheImpl()->getPool()); self::assertSame($cache, $config->getMetadataCache()); - self::assertSame($cache, $config->getMetadataCacheImpl()->getPool()); } } class AnnotatedDummy { - /** @AnnotationNamespace\PrePersist */ - public function namespacedAnnotationMethod(): void + #[MappingNamespace\PrePersist] + public function namespacedAttributeMethod(): void { } } diff --git a/tests/Tests/ORM/Performance/SecondLevelCacheTest.php b/tests/Tests/ORM/Performance/SecondLevelCacheTest.php index 00a621cfa5a..b8cd1dca2ba 100644 --- a/tests/Tests/ORM/Performance/SecondLevelCacheTest.php +++ b/tests/Tests/ORM/Performance/SecondLevelCacheTest.php @@ -9,6 +9,7 @@ use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use function count; use function microtime; @@ -19,10 +20,8 @@ use const PHP_EOL; -/** - * @group DDC-2183 - * @group performance - */ +#[Group('DDC-2183')] +#[Group('performance')] class SecondLevelCacheTest extends OrmFunctionalTestCase { protected function setUp(): void diff --git a/tests/Tests/ORM/PersistentCollectionTest.php b/tests/Tests/ORM/PersistentCollectionTest.php index ffa7b65f7cc..71a4c62e95a 100644 --- a/tests/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Tests/ORM/PersistentCollectionTest.php @@ -15,6 +15,7 @@ use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use stdClass; @@ -32,8 +33,7 @@ class PersistentCollectionTest extends OrmTestCase /** @var PersistentCollection */ protected $collection; - /** @var EntityManagerMock */ - private $_emMock; + private EntityManagerMock $_emMock; protected function setUp(): void { @@ -43,16 +43,15 @@ protected function setUp(): void $platform->method('supportsIdentityColumns') ->willReturn(true); - if (method_exists($platform, 'getSQLResultCasing')) { - $platform->method('getSQLResultCasing') - ->willReturnArgument(0); - } - $connection = $this->createMock(Connection::class); $connection->method('getDatabasePlatform') ->willReturn($platform); - $connection->method('getEventManager') - ->willReturn(new EventManager()); + + if (method_exists($connection, 'getEventManager')) { + $connection->method('getEventManager') + ->willReturn(new EventManager()); + } + $connection->method('executeQuery') ->willReturn($this->createMock(Result::class)); @@ -107,7 +106,7 @@ public function testNextInitializesCollection(): void self::assertTrue($this->collection->isInitialized()); } - /** @group DDC-3382 */ + #[Group('DDC-3382')] public function testNonObjects(): void { self::assertEmpty($this->collection); @@ -127,7 +126,7 @@ public function testNonObjects(): void self::assertNull($this->collection->get(3)); } - /** @group 6110 */ + #[Group('6110')] public function testRemovingElementsAlsoRemovesKeys(): void { $dummy = new stdClass(); @@ -139,7 +138,7 @@ public function testRemovingElementsAlsoRemovesKeys(): void self::assertEquals([], array_keys($this->collection->toArray())); } - /** @group 6110 */ + #[Group('6110')] public function testClearWillAlsoClearKeys(): void { $this->collection->add(new stdClass()); @@ -147,7 +146,7 @@ public function testClearWillAlsoClearKeys(): void self::assertEquals([], array_keys($this->collection->toArray())); } - /** @group 6110 */ + #[Group('6110')] public function testClearWillAlsoResetKeyPositions(): void { $dummy = new stdClass(); @@ -159,11 +158,9 @@ public function testClearWillAlsoResetKeyPositions(): void self::assertEquals([0], array_keys($this->collection->toArray())); } - /** - * @group 6613 - * @group 6614 - * @group 6616 - */ + #[Group('6613')] + #[Group('6614')] + #[Group('6616')] public function testWillKeepNewItemsInDirtyCollectionAfterInitialization(): void { $unitOfWork = $this->createMock(UnitOfWork::class); @@ -194,11 +191,9 @@ public function testWillKeepNewItemsInDirtyCollectionAfterInitialization(): void self::assertTrue($this->collection->isDirty()); } - /** - * @group 6613 - * @group 6614 - * @group 6616 - */ + #[Group('6613')] + #[Group('6614')] + #[Group('6616')] public function testWillDeDuplicateNewItemsThatWerePreviouslyPersistedInDirtyCollectionAfterInitialization(): void { $unitOfWork = $this->createMock(UnitOfWork::class); @@ -222,7 +217,7 @@ public function testWillDeDuplicateNewItemsThatWerePreviouslyPersistedInDirtyCol ->with($this->collection) ->willReturnCallback(static function (PersistentCollection $persistentCollection) use ( $persistedElement, - $newElementThatIsAlsoPersisted + $newElementThatIsAlsoPersisted, ): void { $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted); $persistentCollection->unwrap()->add($persistedElement); @@ -232,17 +227,15 @@ public function testWillDeDuplicateNewItemsThatWerePreviouslyPersistedInDirtyCol self::assertSame( [$newElementThatIsAlsoPersisted, $persistedElement, $newElement], - $this->collection->toArray() + $this->collection->toArray(), ); self::assertTrue($this->collection->isInitialized()); self::assertTrue($this->collection->isDirty()); } - /** - * @group 6613 - * @group 6614 - * @group 6616 - */ + #[Group('6613')] + #[Group('6614')] + #[Group('6616')] public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsWereAdded(): void { $unitOfWork = $this->createMock(UnitOfWork::class); @@ -264,7 +257,7 @@ public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsW ->with($this->collection) ->willReturnCallback(static function (PersistentCollection $persistentCollection) use ( $persistedElement, - $newElementThatIsAlsoPersisted + $newElementThatIsAlsoPersisted, ): void { $persistentCollection->unwrap()->add($newElementThatIsAlsoPersisted); $persistentCollection->unwrap()->add($persistedElement); @@ -274,7 +267,7 @@ public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsW self::assertSame( [$newElementThatIsAlsoPersisted, $persistedElement], - $this->collection->toArray() + $this->collection->toArray(), ); self::assertTrue($this->collection->isInitialized()); self::assertFalse($this->collection->isDirty()); diff --git a/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeParametersTest.php b/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeParametersTest.php index 39ae6fa5ae3..3f2d0abd8c2 100644 --- a/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeParametersTest.php +++ b/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeParametersTest.php @@ -14,11 +14,8 @@ class BasicEntityPersisterCompositeTypeParametersTest extends OrmTestCase { - /** @var BasicEntityPersister */ - protected $persister; - - /** @var EntityManagerMock */ - protected $entityManager; + protected BasicEntityPersister $persister; + protected EntityManagerMock $entityManager; protected function setUp(): void { diff --git a/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeSqlTest.php b/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeSqlTest.php index c55fc26715c..9ab9e32c86b 100644 --- a/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeSqlTest.php +++ b/tests/Tests/ORM/Persisters/BasicEntityPersisterCompositeTypeSqlTest.php @@ -5,6 +5,8 @@ namespace Doctrine\Tests\ORM\Persisters; use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ManyToOneAssociationMapping; use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; use Doctrine\ORM\Persisters\Exception\CantUseInOperatorOnCompositeKeys; use Doctrine\Tests\Mocks\EntityManagerMock; @@ -13,41 +15,44 @@ class BasicEntityPersisterCompositeTypeSqlTest extends OrmTestCase { - /** @var BasicEntityPersister */ - protected $persister; - - /** @var EntityManagerMock */ - protected $entityManager; + protected BasicEntityPersister $persister; + protected EntityManagerMock $entityManager; + private AssociationMapping $associationMapping; protected function setUp(): void { parent::setUp(); - $this->entityManager = $this->getTestEntityManager(); - $this->persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(Admin1AlternateName::class)); + $this->entityManager = $this->getTestEntityManager(); + $this->persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(Admin1AlternateName::class)); + $this->associationMapping = new ManyToOneAssociationMapping( + fieldName: 'admin1', + sourceEntity: WhoCares::class, + targetEntity: Admin1AlternateName::class, + ); } public function testSelectConditionStatementEq(): void { - $statement = $this->persister->getSelectConditionStatementSQL('admin1', 1, [], Comparison::EQ); + $statement = $this->persister->getSelectConditionStatementSQL('admin1', 1, $this->associationMapping, Comparison::EQ); self::assertEquals('t0.admin1 = ? AND t0.country = ?', $statement); } public function testSelectConditionStatementEqNull(): void { - $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, [], Comparison::IS); + $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, $this->associationMapping, Comparison::IS); self::assertEquals('t0.admin1 IS NULL AND t0.country IS NULL', $statement); } public function testSelectConditionStatementNeqNull(): void { - $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, [], Comparison::NEQ); + $statement = $this->persister->getSelectConditionStatementSQL('admin1', null, $this->associationMapping, Comparison::NEQ); self::assertEquals('t0.admin1 IS NOT NULL AND t0.country IS NOT NULL', $statement); } public function testSelectConditionStatementIn(): void { $this->expectException(CantUseInOperatorOnCompositeKeys::class); - $this->persister->getSelectConditionStatementSQL('admin1', [], [], Comparison::IN); + $this->persister->getSelectConditionStatementSQL('admin1', [], $this->associationMapping, Comparison::IN); } } diff --git a/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php b/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php index 7bb2534b2de..3bfbbeacdd5 100644 --- a/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php +++ b/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php @@ -6,7 +6,11 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type as DBALType; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; use Doctrine\Tests\DbalTypes\NegativeToPositiveType; use Doctrine\Tests\DbalTypes\UpperCaseStringType; @@ -15,17 +19,15 @@ use Doctrine\Tests\Models\CustomType\CustomTypeParent; use Doctrine\Tests\Models\Generic\NonAlphaColumnsEntity; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use ReflectionMethod; -use function array_shift; +use function array_slice; class BasicEntityPersisterTypeValueSqlTest extends OrmTestCase { - /** @var BasicEntityPersister */ - protected $persister; - - /** @var EntityManagerMock */ - protected $entityManager; + protected BasicEntityPersister $persister; + protected EntityManagerMock $entityManager; protected function setUp(): void { @@ -51,89 +53,113 @@ protected function setUp(): void public function testGetInsertSQLUsesTypeValuesSQL(): void { $method = new ReflectionMethod($this->persister, 'getInsertSQL'); - $method->setAccessible(true); - - $sql = $method->invoke($this->persister); + $sql = $method->invoke($this->persister); self::assertEquals('INSERT INTO customtype_parents (customInteger, child_id) VALUES (ABS(?), ?)', $sql); } public function testUpdateUsesTypeValuesSQL(): void { + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($this->createMock(Driver\Connection::class)); + + $platform = $this->getMockBuilder(AbstractPlatform::class) + ->onlyMethods(['supportsIdentityColumns']) + ->getMockForAbstractClass(); + $platform->method('supportsIdentityColumns') + ->willReturn(true); + + $connection = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([[], $driver]) + ->onlyMethods(['executeStatement', 'getDatabasePlatform']) + ->getMock(); + $connection->method('getDatabasePlatform') + ->willReturn($platform); + $child = new CustomTypeChild(); $parent = new CustomTypeParent(); $parent->customInteger = 1; $parent->child = $child; - $this->entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], ['customInteger' => 0, 'child' => null]); - $this->entityManager->getUnitOfWork()->registerManaged($child, ['id' => 1], []); + $entityManager = $this->createTestEntityManagerWithConnection($connection); - $this->entityManager->getUnitOfWork()->propertyChanged($parent, 'customInteger', 0, 1); - $this->entityManager->getUnitOfWork()->propertyChanged($parent, 'child', null, $child); + $entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], ['customInteger' => 0, 'child' => null]); + $entityManager->getUnitOfWork()->registerManaged($child, ['id' => 1], []); - $this->persister->update($parent); + $entityManager->getUnitOfWork()->propertyChanged($parent, 'customInteger', 0, 1); + $entityManager->getUnitOfWork()->propertyChanged($parent, 'child', null, $child); - $executeStatements = $this->entityManager->getConnection()->getExecuteStatements(); + $persister = new BasicEntityPersister($entityManager, $entityManager->getClassMetadata(CustomTypeParent::class)); - self::assertEquals('UPDATE customtype_parents SET customInteger = ABS(?), child_id = ? WHERE id = ?', $executeStatements[0]['sql']); + $connection->expects($this->once()) + ->method('executeStatement') + ->with('UPDATE customtype_parents SET customInteger = ABS(?), child_id = ? WHERE id = ?'); + + $persister->update($parent); } public function testGetSelectConditionSQLUsesTypeValuesSQL(): void { $method = new ReflectionMethod($this->persister, 'getSelectConditionSQL'); - $method->setAccessible(true); - - $sql = $method->invoke($this->persister, ['customInteger' => 1, 'child' => 1]); + $sql = $method->invoke($this->persister, ['customInteger' => 1, 'child' => 1]); self::assertEquals('t0.customInteger = ABS(?) AND t0.child_id = ?', $sql); } - /** @group DDC-1719 */ + #[Group('DDC-1719')] public function testStripNonAlphanumericCharactersFromSelectColumnListSQL(): void { $persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(NonAlphaColumnsEntity::class)); $method = new ReflectionMethod($persister, 'getSelectColumnsSQL'); - $method->setAccessible(true); self::assertEquals('t0."simple-entity-id" AS simpleentityid_1, t0."simple-entity-value" AS simpleentityvalue_2', $method->invoke($persister)); } - /** @group DDC-2073 */ + #[Group('DDC-2073')] public function testSelectConditionStatementIsNull(): void { - $statement = $this->persister->getSelectConditionStatementSQL('test', null, [], Comparison::IS); + $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz'); + $statement = $this->persister->getSelectConditionStatementSQL('test', null, $associationMapping, Comparison::IS); self::assertEquals('test IS NULL', $statement); } public function testSelectConditionStatementEqNull(): void { - $statement = $this->persister->getSelectConditionStatementSQL('test', null, [], Comparison::EQ); + $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz'); + $statement = $this->persister->getSelectConditionStatementSQL('test', null, $associationMapping, Comparison::EQ); self::assertEquals('test IS NULL', $statement); } public function testSelectConditionStatementNeqNull(): void { - $statement = $this->persister->getSelectConditionStatementSQL('test', null, [], Comparison::NEQ); + $associationMapping = new OneToManyAssociationMapping('foo', 'bar', 'baz'); + $statement = $this->persister->getSelectConditionStatementSQL( + 'test', + null, + $associationMapping, + Comparison::NEQ, + ); self::assertEquals('test IS NOT NULL', $statement); } - /** @group DDC-3056 */ + #[Group('DDC-3056')] public function testSelectConditionStatementWithMultipleValuesContainingNull(): void { self::assertEquals( '(t0.id IN (?) OR t0.id IS NULL)', - $this->persister->getSelectConditionStatementSQL('id', [null]) + $this->persister->getSelectConditionStatementSQL('id', [null]), ); self::assertEquals( '(t0.id IN (?) OR t0.id IS NULL)', - $this->persister->getSelectConditionStatementSQL('id', [null, 123]) + $this->persister->getSelectConditionStatementSQL('id', [null, 123]), ); self::assertEquals( '(t0.id IN (?) OR t0.id IS NULL)', - $this->persister->getSelectConditionStatementSQL('id', [123, null]) + $this->persister->getSelectConditionStatementSQL('id', [123, null]), ); } @@ -158,26 +184,46 @@ public function testCountEntities(): void public function testDeleteManyToManyUsesTypeValuesSQL(): void { + $connection = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([[], $this->createMock(Driver::class)]) + ->onlyMethods(['delete', 'getDatabasePlatform']) + ->getMock(); + $connection->method('getDatabasePlatform') + ->willReturn($this->createMock(AbstractPlatform::class)); + + $entityManager = $this->createTestEntityManagerWithConnection($connection); + + $persister = new BasicEntityPersister($entityManager, $this->entityManager->getClassMetadata(CustomTypeParent::class)); + $friend = new CustomTypeParent(); $parent = new CustomTypeParent(); $parent->addMyFriend($friend); - $this->entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], []); - $this->entityManager->getUnitOfWork()->registerManaged($friend, ['id' => 2], []); + $entityManager->getUnitOfWork()->registerManaged($parent, ['id' => 1], []); + $entityManager->getUnitOfWork()->registerManaged($friend, ['id' => 2], []); + + $deleteCalls = []; + + $connection->method('delete') + ->willReturnCallback(static function (...$args) use (&$deleteCalls): int { + $deleteCalls[] = $args; - $this->persister->delete($parent); + return 1; + }); - $deletes = $this->entityManager->getConnection()->getDeletes(); + $persister->delete($parent); - self::assertEquals([ - 'table' => 'customtype_parent_friends', - 'criteria' => ['friend_customtypeparent_id' => 1], - 'types' => ['integer'], - ], array_shift($deletes)); - self::assertEquals([ - 'table' => 'customtype_parent_friends', - 'criteria' => ['customtypeparent_id' => 1], - 'types' => ['integer'], - ], array_shift($deletes)); + self::assertSame([ + [ + 'customtype_parent_friends', + ['friend_customtypeparent_id' => 1], + ['integer'], + ], + [ + 'customtype_parent_friends', + ['customtypeparent_id' => 1], + ['integer'], + ], + ], array_slice($deleteCalls, 0, 2)); } } diff --git a/tests/Tests/ORM/Persisters/ManyToManyPersisterTest.php b/tests/Tests/ORM/Persisters/ManyToManyPersisterTest.php index 4d24d0ad162..c9db932c60f 100644 --- a/tests/Tests/ORM/Persisters/ManyToManyPersisterTest.php +++ b/tests/Tests/ORM/Persisters/ManyToManyPersisterTest.php @@ -4,25 +4,35 @@ namespace Doctrine\Tests\ORM\Persisters; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\ORM\Persisters\Collection\ManyToManyPersister; -use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\Models\ManyToManyPersister\ChildClass; use Doctrine\Tests\Models\ManyToManyPersister\OtherParentClass; use Doctrine\Tests\Models\ManyToManyPersister\ParentClass; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; -use function array_pop; -use function assert; - -/** @covers \Doctrine\ORM\Persisters\Collection\ManyToManyPersister */ +#[CoversClass(ManyToManyPersister::class)] final class ManyToManyPersisterTest extends OrmTestCase { - /** - * @group GH-6991 - * @group ManyToManyPersister - */ + #[Group('GH-6991')] + #[Group('ManyToManyPersister')] public function testDeleteManyToManyCollection(): void { + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($this->createMock(Driver\Connection::class)); + + $connection = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([[], $driver]) + ->onlyMethods(['executeStatement', 'getDatabasePlatform']) + ->getMock(); + $connection->method('getDatabasePlatform') + ->willReturn($this->getMockForAbstractClass(AbstractPlatform::class)); + $parent = new ParentClass(1); $otherParent = new OtherParentClass(42); $child = new ChildClass(1, $otherParent); @@ -30,25 +40,18 @@ public function testDeleteManyToManyCollection(): void $parent->children->add($child); $child->parents->add($parent); - $em = $this->getTestEntityManager(); + $em = $this->createTestEntityManagerWithConnection($connection); $em->persist($parent); $em->flush(); $childReloaded = $em->find(ChildClass::class, ['id1' => 1, 'otherParent' => $otherParent]); - assert($childReloaded instanceof ChildClass || $childReloaded === null); + self::assertInstanceOf(ChildClass::class, $childReloaded); - self::assertNotNull($childReloaded); + $connection->expects($this->once()) + ->method('executeStatement') + ->with('DELETE FROM parent_child WHERE child_id1 = ? AND child_id2 = ?', [1, 42]); $persister = new ManyToManyPersister($em); $persister->delete($childReloaded->parents); - - $conn = $em->getConnection(); - assert($conn instanceof ConnectionMock); - - $updates = $conn->getExecuteStatements(); - $lastStatement = array_pop($updates); - - self::assertEquals('DELETE FROM parent_child WHERE child_id1 = ? AND child_id2 = ?', $lastStatement['sql']); - self::assertEquals([1, 42], $lastStatement['params']); } } diff --git a/tests/Tests/ORM/Proxy/ProxyFactoryTest.php b/tests/Tests/ORM/Proxy/ProxyFactoryTest.php index 1b622f814b7..05b19a4abcf 100644 --- a/tests/Tests/ORM/Proxy/ProxyFactoryTest.php +++ b/tests/Tests/ORM/Proxy/ProxyFactoryTest.php @@ -19,12 +19,12 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\Models\ECommerce\ECommerceFeature; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; -use Exception; +use PHPUnit\Framework\Attributes\Group; use ReflectionProperty; use stdClass; use function assert; +use function method_exists; use function sys_get_temp_dir; /** @@ -32,16 +32,11 @@ */ class ProxyFactoryTest extends OrmTestCase { - use MockBuilderCompatibilityTools; + private UnitOfWorkMock $uowMock; - /** @var UnitOfWorkMock */ - private $uowMock; + private EntityManagerMock $emMock; - /** @var EntityManagerMock */ - private $emMock; - - /** @var ProxyFactory */ - private $proxyFactory; + private ProxyFactory $proxyFactory; protected function setUp(): void { @@ -52,8 +47,11 @@ protected function setUp(): void $connection = $this->createMock(Connection::class); $connection->method('getDatabasePlatform') ->willReturn($platform); - $connection->method('getEventManager') - ->willReturn(new EventManager()); + + if (method_exists($connection, 'getEventManager')) { + $connection->method('getEventManager') + ->willReturn(new EventManager()); + } $this->emMock = new EntityManagerMock($connection); $this->uowMock = new UnitOfWorkMock($this->emMock); @@ -64,7 +62,8 @@ protected function setUp(): void public function testReferenceProxyDelegatesLoadingToThePersister(): void { $identifier = ['id' => 42]; - $persister = $this->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['loadById']) + $persister = $this->getMockBuilder(BasicEntityPersister::class) + ->onlyMethods(['loadById']) ->disableOriginalConstructor() ->getMock(); @@ -89,11 +88,11 @@ public function testSkipMappedSuperClassesOnGeneration(): void self::assertSame( 0, $this->proxyFactory->generateProxyClasses([$cm]), - 'No proxies generated.' + 'No proxies generated.', ); } - /** @group 6625 */ + #[Group('6625')] public function testSkipEmbeddableClassesOnGeneration(): void { $cm = new ClassMetadata(stdClass::class); @@ -102,11 +101,11 @@ public function testSkipEmbeddableClassesOnGeneration(): void self::assertSame( 0, $this->proxyFactory->generateProxyClasses([$cm]), - 'No proxies generated.' + 'No proxies generated.', ); } - /** @group DDC-1771 */ + #[Group('DDC-1771')] public function testSkipAbstractClassesOnGeneration(): void { $cm = new ClassMetadata(AbstractClass::class); @@ -118,11 +117,11 @@ public function testSkipAbstractClassesOnGeneration(): void self::assertEquals(0, $num, 'No proxies generated.'); } - /** @group DDC-2432 */ + #[Group('DDC-2432')] public function testFailedProxyLoadingDoesNotMarkTheProxyAsInitialized(): void { - $persister = $this - ->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['load']) + $persister = $this->getMockBuilder(BasicEntityPersister::class) + ->onlyMethods(['load']) ->disableOriginalConstructor() ->getMock(); $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister); @@ -138,44 +137,17 @@ public function testFailedProxyLoadingDoesNotMarkTheProxyAsInitialized(): void try { $proxy->getDescription(); self::fail('An exception was expected to be raised'); - } catch (EntityNotFoundException $exception) { + } catch (EntityNotFoundException) { } self::assertFalse($proxy->__isInitialized()); } - public function testExceptionOnProxyLoadingDoesNotMarkTheProxyAsInitialized(): void - { - $persister = $this - ->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['load', 'getClassMetadata']) - ->disableOriginalConstructor() - ->getMock(); - $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister); - - $proxy = $this->proxyFactory->getProxy(ECommerceFeature::class, ['id' => 42]); - assert($proxy instanceof Proxy); - - $exception = new Exception('Literally any kind of connection exception'); - - $persister - ->expects(self::atLeastOnce()) - ->method('load') - ->willThrowException($exception); - - try { - $proxy->getDescription(); - self::fail('An exception was expected to be raised'); - } catch (Exception $exception) { - } - - self::assertFalse($proxy->__isInitialized(), 'The proxy should not be initialized'); - } - - /** @group DDC-2432 */ + #[Group('DDC-2432')] public function testFailedProxyCloningDoesNotMarkTheProxyAsInitialized(): void { - $persister = $this - ->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['load']) + $persister = $this->getMockBuilder(BasicEntityPersister::class) + ->onlyMethods(['load', 'getClassMetadata']) ->disableOriginalConstructor() ->getMock(); $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister); @@ -192,7 +164,7 @@ public function testFailedProxyCloningDoesNotMarkTheProxyAsInitialized(): void $cloned = clone $proxy; $cloned->__load(); self::fail('An exception was expected to be raised'); - } catch (EntityNotFoundException $exception) { + } catch (EntityNotFoundException) { } self::assertFalse($proxy->__isInitialized()); @@ -206,14 +178,12 @@ public function testProxyClonesParentFields(): void // Set the id of the CompanyEmployee (which is in the parent CompanyPerson) $property = new ReflectionProperty(CompanyPerson::class, 'id'); - - $property->setAccessible(true); $property->setValue($companyEmployee, 42); $classMetaData = $this->emMock->getClassMetadata(CompanyEmployee::class); - $persister = $this - ->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['loadById', 'getClassMetadata']) + $persister = $this->getMockBuilder(BasicEntityPersister::class) + ->onlyMethods(['loadById', 'getClassMetadata']) ->disableOriginalConstructor() ->getMock(); $this->uowMock->setEntityPersister(CompanyEmployee::class, $persister); diff --git a/tests/Tests/ORM/Query/AST/InExpressionTest.php b/tests/Tests/ORM/Query/AST/InExpressionTest.php deleted file mode 100644 index ee48869a539..00000000000 --- a/tests/Tests/ORM/Query/AST/InExpressionTest.php +++ /dev/null @@ -1,34 +0,0 @@ -expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10267'); - - new InExpression(new ArithmeticExpression()); - } - - public function testNoDeprecations(): void - { - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10267'); - - new InListExpression(new ArithmeticExpression(), [new Literal(Literal::STRING, 'foo')]); - new InSubselectExpression(new ArithmeticExpression(), $this->createMock(Subselect::class)); - } -} diff --git a/tests/Tests/ORM/Query/CustomTreeWalkersJoinTest.php b/tests/Tests/ORM/Query/CustomTreeWalkersJoinTest.php index 75a9d9dec5e..16be0ce6ff3 100644 --- a/tests/Tests/ORM/Query/CustomTreeWalkersJoinTest.php +++ b/tests/Tests/ORM/Query/CustomTreeWalkersJoinTest.php @@ -16,8 +16,7 @@ */ class CustomTreeWalkersJoinTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $em; + private EntityManagerInterface $em; protected function setUp(): void { @@ -38,7 +37,7 @@ public function testAddsJoin(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id', ); } @@ -46,7 +45,7 @@ public function testDoesNotAddJoin(): void { $this->assertSqlGeneration( 'select a from Doctrine\Tests\Models\CMS\CmsAddress a', - 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_' + 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_', ); } } diff --git a/tests/Tests/ORM/Query/CustomTreeWalkersTest.php b/tests/Tests/ORM/Query/CustomTreeWalkersTest.php index eb27b6113a3..83e001fbdcd 100644 --- a/tests/Tests/ORM/Query/CustomTreeWalkersTest.php +++ b/tests/Tests/ORM/Query/CustomTreeWalkersTest.php @@ -6,10 +6,19 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; -use Doctrine\ORM\Query\AST; +use Doctrine\ORM\Query\AST\ComparisonExpression; +use Doctrine\ORM\Query\AST\ConditionalExpression; +use Doctrine\ORM\Query\AST\ConditionalFactor; +use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\ConditionalTerm; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\WhereClause; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Query\SqlOutputWalker; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\TreeWalker; +use Doctrine\ORM\Query\TreeWalkerAdapter; use Doctrine\Tests\Mocks\CustomTreeWalkerJoin; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmTestCase; @@ -24,8 +33,7 @@ */ class CustomTreeWalkersTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; protected function setUp(): void { @@ -36,7 +44,7 @@ protected function setUp(): void * @param list> $treeWalkers * @param class-string|null $outputWalker */ - public function generateSql(string $dqlToBeTested, array $treeWalkers, ?string $outputWalker): string + public function generateSql(string $dqlToBeTested, array $treeWalkers, string|null $outputWalker): string { $query = $this->entityManager->createQuery($dqlToBeTested); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $treeWalkers) @@ -57,7 +65,7 @@ public function assertSqlGeneration( string $dqlToBeTested, string $sqlToBeConfirmed, array $treeWalkers = [], - ?string $outputWalker = null + string|null $outputWalker = null, ): void { self::assertEquals($sqlToBeConfirmed, $this->generateSql($dqlToBeTested, $treeWalkers, $outputWalker)); } @@ -67,7 +75,7 @@ public function testSupportsQueriesWithoutWhere(): void $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u', 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = 1', - [CustomTreeWalker::class] + [CustomTreeWalker::class], ); } @@ -76,7 +84,7 @@ public function testSupportsQueriesWithMultipleConditionalExpressions(): void $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName', 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1', - [CustomTreeWalker::class] + [CustomTreeWalker::class], ); } @@ -85,7 +93,7 @@ public function testSupportsQueriesWithSimpleConditionalExpression(): void $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name', 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1', - [CustomTreeWalker::class] + [CustomTreeWalker::class], ); } @@ -97,7 +105,7 @@ public function testSetUnknownQueryComponentThrowsException(): void $this->generateSql( 'select u from Doctrine\Tests\Models\CMS\CmsUser u', [], - AddUnknownQueryComponentWalker::class + AddUnknownQueryComponentWalker::class, ); } @@ -106,24 +114,24 @@ public function testSupportsSeveralHintsQueries(): void $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u', 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c0_.id = 1', - [CustomTreeWalkerJoin::class, CustomTreeWalker::class] + [CustomTreeWalkerJoin::class, CustomTreeWalker::class], ); } } -class AddUnknownQueryComponentWalker extends Query\SqlOutputWalker +class AddUnknownQueryComponentWalker extends SqlOutputWalker { - protected function createSqlForFinalizer(AST\SelectStatement $AST): string + protected function createSqlForFinalizer(SelectStatement $selectStatement): string { $this->setQueryComponent('x', []); - return parent::createSqlForFinalizer($AST); + return parent::createSqlForFinalizer($selectStatement); } } -class CustomTreeWalker extends Query\TreeWalkerAdapter +class CustomTreeWalker extends TreeWalkerAdapter { - public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): void + public function walkSelectStatement(SelectStatement $selectStatement): void { // Get the DQL aliases of all the classes we want to modify $dqlAliases = []; @@ -139,14 +147,14 @@ public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): // Create our conditions for all involved classes $factors = []; foreach ($dqlAliases as $alias) { - $pathExpr = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, $alias, 'id'); - $pathExpr->type = Query\AST\PathExpression::TYPE_STATE_FIELD; - $comparisonExpr = new Query\AST\ComparisonExpression($pathExpr, '=', 1); + $pathExpr = new PathExpression(PathExpression::TYPE_STATE_FIELD, $alias, 'id'); + $pathExpr->type = PathExpression::TYPE_STATE_FIELD; + $comparisonExpr = new ComparisonExpression($pathExpr, '=', '1'); - $condPrimary = new Query\AST\ConditionalPrimary(); + $condPrimary = new ConditionalPrimary(); $condPrimary->simpleConditionalExpression = $comparisonExpr; - $factor = new Query\AST\ConditionalFactor($condPrimary); + $factor = new ConditionalFactor($condPrimary); $factors[] = $factor; } @@ -156,8 +164,8 @@ public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): $condExpr = $whereClause->conditionalExpression; // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression - if (! ($condExpr instanceof Query\AST\ConditionalExpression)) { - $condExpr = new Query\AST\ConditionalExpression([$condExpr]); + if (! ($condExpr instanceof ConditionalExpression)) { + $condExpr = new ConditionalExpression([$condExpr]); $whereClause->conditionalExpression = $condExpr; } @@ -168,10 +176,10 @@ public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): // More than one term, so we need to wrap all these terms in a single root term // i.e: "WHERE u.name = :foo or u.other = :bar" => "WHERE (u.name = :foo or u.other = :bar) AND " - $primary = new Query\AST\ConditionalPrimary(); - $primary->conditionalExpression = new Query\AST\ConditionalExpression($existingTerms); - $existingFactor = new Query\AST\ConditionalFactor($primary); - $term = new Query\AST\ConditionalTerm(array_merge([$existingFactor], $factors)); + $primary = new ConditionalPrimary(); + $primary->conditionalExpression = new ConditionalExpression($existingTerms); + $existingFactor = new ConditionalFactor($primary); + $term = new ConditionalTerm([...[$existingFactor], ...$factors]); $selectStatement->whereClause->conditionalExpression->conditionalTerms = [$term]; } else { @@ -179,8 +187,8 @@ public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): $singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0]; // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression - if (! ($singleTerm instanceof Query\AST\ConditionalTerm)) { - $singleTerm = new Query\AST\ConditionalTerm([$singleTerm]); + if (! ($singleTerm instanceof ConditionalTerm)) { + $singleTerm = new ConditionalTerm([$singleTerm]); $selectStatement->whereClause->conditionalExpression->conditionalTerms[0] = $singleTerm; } @@ -190,9 +198,9 @@ public function walkSelectStatement(Query\AST\SelectStatement $selectStatement): } } else { // Create a new WHERE clause with our factors - $term = new Query\AST\ConditionalTerm($factors); - $condExpr = new Query\AST\ConditionalExpression([$term]); - $whereClause = new Query\AST\WhereClause($condExpr); + $term = new ConditionalTerm($factors); + $condExpr = new ConditionalExpression([$term]); + $whereClause = new WhereClause($condExpr); $selectStatement->whereClause = $whereClause; } } diff --git a/tests/Tests/ORM/Query/DeleteSqlGenerationTest.php b/tests/Tests/ORM/Query/DeleteSqlGenerationTest.php index 6369759b273..dca376a784f 100644 --- a/tests/Tests/ORM/Query/DeleteSqlGenerationTest.php +++ b/tests/Tests/ORM/Query/DeleteSqlGenerationTest.php @@ -6,6 +6,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Test case for testing the saving and referencing of query identifiers. @@ -18,8 +19,7 @@ */ class DeleteSqlGenerationTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; protected function setUp(): void { @@ -35,12 +35,12 @@ public function assertSqlGeneration(string $dqlToBeTested, string $sqlToBeConfir $query->free(); } - /** @group 6939 */ + #[Group('6939')] public function testSupportsDeleteWithoutWhereAndAlias(): void { $this->assertSqlGeneration( 'DELETE FROM Doctrine\Tests\Models\CMS\CmsUser', - 'DELETE FROM cms_users' + 'DELETE FROM cms_users', ); } @@ -48,7 +48,7 @@ public function testSupportsDeleteWithoutWhereAndFrom(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u', - 'DELETE FROM cms_users' + 'DELETE FROM cms_users', ); } @@ -56,7 +56,7 @@ public function testSupportsDeleteWithoutWhere(): void { $this->assertSqlGeneration( 'DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'DELETE FROM cms_users' + 'DELETE FROM cms_users', ); } @@ -64,7 +64,7 @@ public function testSupportsWhereClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', - 'DELETE FROM cms_users WHERE id = ?' + 'DELETE FROM cms_users WHERE id = ?', ); } @@ -72,7 +72,7 @@ public function testSupportsWhereOrExpressions(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ?1 OR u.name = ?2', - 'DELETE FROM cms_users WHERE username = ? OR name = ?' + 'DELETE FROM cms_users WHERE username = ? OR name = ?', ); } @@ -80,7 +80,7 @@ public function testSupportsWhereNestedConditionalExpressions(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1 OR ( u.username = ?2 OR u.name = ?3)', - 'DELETE FROM cms_users WHERE id = ? OR (username = ? OR name = ?)' + 'DELETE FROM cms_users WHERE id = ? OR (username = ? OR name = ?)', ); //$this->assertSqlGeneration( @@ -93,7 +93,7 @@ public function testIsCaseAgnostic(): void { $this->assertSqlGeneration( 'delete from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1', - 'DELETE FROM cms_users WHERE username = ?' + 'DELETE FROM cms_users WHERE username = ?', ); } @@ -101,7 +101,7 @@ public function testSupportsAndCondition(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ?1 AND u.name = ?2', - 'DELETE FROM cms_users WHERE username = ? AND name = ?' + 'DELETE FROM cms_users WHERE username = ? AND name = ?', ); } @@ -109,7 +109,7 @@ public function testSupportsWhereNot(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT u.id != ?1', - 'DELETE FROM cms_users WHERE NOT id <> ?' + 'DELETE FROM cms_users WHERE NOT id <> ?', ); } @@ -117,7 +117,7 @@ public function testSupportsWhereNotWithParentheses(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT ( u.id != ?1 )', - 'DELETE FROM cms_users WHERE NOT (id <> ?)' + 'DELETE FROM cms_users WHERE NOT (id <> ?)', ); } @@ -125,7 +125,7 @@ public function testSupportsWhereNotWithAndExpression(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT ( u.id != ?1 AND u.username = ?2 )', - 'DELETE FROM cms_users WHERE NOT (id <> ? AND username = ?)' + 'DELETE FROM cms_users WHERE NOT (id <> ? AND username = ?)', ); } @@ -136,7 +136,7 @@ public function testSupportsGreaterThanComparisonClause(): void // id = ? was already tested (see testDeleteWithWhere()) $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ?1', - 'DELETE FROM cms_users WHERE id > ?' + 'DELETE FROM cms_users WHERE id > ?', ); } @@ -144,7 +144,7 @@ public function testSupportsGreaterThanOrEqualToComparisonClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id >= ?1', - 'DELETE FROM cms_users WHERE id >= ?' + 'DELETE FROM cms_users WHERE id >= ?', ); } @@ -152,7 +152,7 @@ public function testSupportsLessThanComparisonClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id < ?1', - 'DELETE FROM cms_users WHERE id < ?' + 'DELETE FROM cms_users WHERE id < ?', ); } @@ -160,7 +160,7 @@ public function testSupportsLessThanOrEqualToComparisonClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id <= ?1', - 'DELETE FROM cms_users WHERE id <= ?' + 'DELETE FROM cms_users WHERE id <= ?', ); } @@ -168,7 +168,7 @@ public function testSupportsNotEqualToComparisonClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id <> ?1', - 'DELETE FROM cms_users WHERE id <> ?' + 'DELETE FROM cms_users WHERE id <> ?', ); } @@ -176,7 +176,7 @@ public function testSupportsNotEqualToComparisonClauseExpressedWithExclamationMa { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id != ?1', - 'DELETE FROM cms_users WHERE id <> ?' + 'DELETE FROM cms_users WHERE id <> ?', ); } @@ -184,7 +184,7 @@ public function testSupportsNotBetweenClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT BETWEEN ?1 AND ?2', - 'DELETE FROM cms_users WHERE id NOT BETWEEN ? AND ?' + 'DELETE FROM cms_users WHERE id NOT BETWEEN ? AND ?', ); } @@ -192,7 +192,7 @@ public function testSupportsBetweenClauseUsedWithAndClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id BETWEEN ?1 AND ?2 AND u.username != ?3', - 'DELETE FROM cms_users WHERE id BETWEEN ? AND ? AND username <> ?' + 'DELETE FROM cms_users WHERE id BETWEEN ? AND ? AND username <> ?', ); } @@ -201,7 +201,7 @@ public function testSupportsNotLikeClause(): void // "WHERE" Expression LikeExpression $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username NOT LIKE ?1', - 'DELETE FROM cms_users WHERE username NOT LIKE ?' + 'DELETE FROM cms_users WHERE username NOT LIKE ?', ); } @@ -209,7 +209,7 @@ public function testSupportsLikeClauseWithEscapeExpression(): void { $this->assertSqlGeneration( "DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username LIKE ?1 ESCAPE '\\'", - "DELETE FROM cms_users WHERE username LIKE ? ESCAPE '\\'" + "DELETE FROM cms_users WHERE username LIKE ? ESCAPE '\\'", ); } @@ -218,7 +218,7 @@ public function testSupportsIsNullClause(): void // "WHERE" Expression NullComparisonExpression $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IS NULL', - 'DELETE FROM cms_users WHERE name IS NULL' + 'DELETE FROM cms_users WHERE name IS NULL', ); } @@ -226,7 +226,7 @@ public function testSupportsIsNotNullClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name IS NOT NULL', - 'DELETE FROM cms_users WHERE name IS NOT NULL' + 'DELETE FROM cms_users WHERE name IS NOT NULL', ); } @@ -234,7 +234,7 @@ public function testSupportsAtomExpressionAsClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE 1 = 1', - 'DELETE FROM cms_users WHERE 1 = 1' + 'DELETE FROM cms_users WHERE 1 = 1', ); } @@ -242,7 +242,7 @@ public function testSupportsParameterizedAtomExpression(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE ?1 = 1', - 'DELETE FROM cms_users WHERE ? = 1' + 'DELETE FROM cms_users WHERE ? = 1', ); } @@ -250,7 +250,7 @@ public function testSupportsInClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN ( ?1, ?2, ?3, ?4 )', - 'DELETE FROM cms_users WHERE id IN (?, ?, ?, ?)' + 'DELETE FROM cms_users WHERE id IN (?, ?, ?, ?)', ); } @@ -258,16 +258,16 @@ public function testSupportsNotInClause(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN ( ?1, ?2 )', - 'DELETE FROM cms_users WHERE id NOT IN (?, ?)' + 'DELETE FROM cms_users WHERE id NOT IN (?, ?)', ); } - /** @group DDC-980 */ + #[Group('DDC-980')] public function testSubselectTableAliasReferencing(): void { $this->assertSqlGeneration( 'DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.groups) = 10', - 'DELETE FROM cms_users WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10' + 'DELETE FROM cms_users WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10', ); } } diff --git a/tests/Tests/ORM/Query/ExprTest.php b/tests/Tests/ORM/Query/ExprTest.php index eed8ae8e0b9..b3ff41f5d38 100644 --- a/tests/Tests/ORM/Query/ExprTest.php +++ b/tests/Tests/ORM/Query/ExprTest.php @@ -6,9 +6,23 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr\Comparison; +use Doctrine\ORM\Query\Expr\From; +use Doctrine\ORM\Query\Expr\Func; +use Doctrine\ORM\Query\Expr\GroupBy; +use Doctrine\ORM\Query\Expr\Join; +use Doctrine\ORM\Query\Expr\Literal; +use Doctrine\ORM\Query\Expr\Math; +use Doctrine\ORM\Query\Expr\OrderBy; +use Doctrine\ORM\Query\Expr\Orx; +use Doctrine\ORM\Query\Expr\Select; use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\OrmTestCase; use Generator; +use InvalidArgumentException; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; /** * Test case for the DQL Expr class used for generating DQL snippets through @@ -18,11 +32,9 @@ */ class ExprTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; - /** @var Expr */ - private $expr; + private Expr $expr; protected function setUp(): void { @@ -109,7 +121,7 @@ public function testIntelligentParenthesisPreventionAndExpr(): void { self::assertEquals( '1 = 1 AND 2 = 2', - (string) $this->expr->andX($this->expr->orX($this->expr->andX($this->expr->eq(1, 1))), (string) $this->expr->eq(2, 2)) + (string) $this->expr->andX($this->expr->orX($this->expr->andX($this->expr->eq(1, 1))), (string) $this->expr->eq(2, 2)), ); } @@ -185,10 +197,8 @@ public function testModExpr(): void self::assertEquals('MOD(10, 1)', (string) $this->expr->mod(10, 1)); } - /** - * @group regression - * @group DDC-612 - */ + #[Group('regression')] + #[Group('DDC-612')] public function testSubstringExprAcceptsTwoArguments(): void { self::assertEquals('SUBSTRING(a.title, 5)', (string) $this->expr->substring('a.title', 5)); @@ -229,10 +239,8 @@ public function testNumericLiteralExpr(): void self::assertEquals(5, (string) $this->expr->literal(5)); } - /** - * @group regression - * @group DDC-610 - */ + #[Group('regression')] + #[Group('DDC-610')] public function testLiteralExprProperlyQuotesStrings(): void { self::assertEquals("'00010001'", (string) $this->expr->literal('00010001')); @@ -298,25 +306,25 @@ public static function provideLiteralIterableValue(): Generator yield 'generator' => [$gen()]; } - /** @dataProvider provideIterableValue */ + #[DataProvider('provideIterableValue')] public function testInExpr(iterable $value): void { self::assertEquals('u.id IN(1, 2, 3)', (string) $this->expr->in('u.id', $value)); } - /** @dataProvider provideLiteralIterableValue */ + #[DataProvider('provideLiteralIterableValue')] public function testInLiteralExpr(iterable $value): void { self::assertEquals("u.type IN('foo', 'bar')", (string) $this->expr->in('u.type', $value)); } - /** @dataProvider provideIterableValue */ + #[DataProvider('provideIterableValue')] public function testNotInExpr(iterable $value): void { self::assertEquals('u.id NOT IN(1, 2, 3)', (string) $this->expr->notIn('u.id', $value)); } - /** @dataProvider provideLiteralIterableValue */ + #[DataProvider('provideLiteralIterableValue')] public function testNotInLiteralExpr(iterable $value): void { self::assertEquals("u.type NOT IN('foo', 'bar')", (string) $this->expr->notIn('u.type', $value)); @@ -366,75 +374,75 @@ public function testOrderByAsc(): void public function testAddThrowsException(): void { - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $orExpr = $this->expr->orX(); $orExpr->add($this->expr->quot(5, 2)); } - /** @group DDC-1683 */ + #[Group('DDC-1683')] public function testBooleanLiteral(): void { self::assertEquals('true', $this->expr->literal(true)); self::assertEquals('false', $this->expr->literal(false)); } - /** @group DDC-1686 */ + #[Group('DDC-1686')] public function testExpressionGetter(): void { // Andx - $andx = new Expr\Andx(['1 = 1', '2 = 2']); + $andx = new Andx(['1 = 1', '2 = 2']); self::assertEquals(['1 = 1', '2 = 2'], $andx->getParts()); // Comparison - $comparison = new Expr\Comparison('foo', Expr\Comparison::EQ, 'bar'); + $comparison = new Comparison('foo', Comparison::EQ, 'bar'); self::assertEquals('foo', $comparison->getLeftExpr()); self::assertEquals('bar', $comparison->getRightExpr()); - self::assertEquals(Expr\Comparison::EQ, $comparison->getOperator()); + self::assertEquals(Comparison::EQ, $comparison->getOperator()); // From - $from = new Expr\From('Foo', 'f', 'f.id'); + $from = new From('Foo', 'f', 'f.id'); self::assertEquals('f', $from->getAlias()); self::assertEquals('Foo', $from->getFrom()); self::assertEquals('f.id', $from->getIndexBy()); // Func - $func = new Expr\Func('MAX', ['f.id']); + $func = new Func('MAX', ['f.id']); self::assertEquals('MAX', $func->getName()); self::assertEquals(['f.id'], $func->getArguments()); // GroupBy - $group = new Expr\GroupBy(['foo DESC', 'bar ASC']); + $group = new GroupBy(['foo DESC', 'bar ASC']); self::assertEquals(['foo DESC', 'bar ASC'], $group->getParts()); // Join - $join = new Expr\Join(Expr\Join::INNER_JOIN, 'f.bar', 'b', Expr\Join::ON, 'b.bar_id = 1', 'b.bar_id'); - self::assertEquals(Expr\Join::INNER_JOIN, $join->getJoinType()); - self::assertEquals(Expr\Join::ON, $join->getConditionType()); + $join = new Join(Join::INNER_JOIN, 'f.bar', 'b', Join::ON, 'b.bar_id = 1', 'b.bar_id'); + self::assertEquals(Join::INNER_JOIN, $join->getJoinType()); + self::assertEquals(Join::ON, $join->getConditionType()); self::assertEquals('b.bar_id = 1', $join->getCondition()); self::assertEquals('b.bar_id', $join->getIndexBy()); self::assertEquals('f.bar', $join->getJoin()); self::assertEquals('b', $join->getAlias()); // Literal - $literal = new Expr\Literal(['foo']); + $literal = new Literal(['foo']); self::assertEquals(['foo'], $literal->getParts()); // Math - $math = new Expr\Math(10, '+', 20); + $math = new Math(10, '+', 20); self::assertEquals(10, $math->getLeftExpr()); self::assertEquals(20, $math->getRightExpr()); self::assertEquals('+', $math->getOperator()); // OrderBy - $order = new Expr\OrderBy('foo', 'DESC'); + $order = new OrderBy('foo', 'DESC'); self::assertEquals(['foo DESC'], $order->getParts()); // Andx - $orx = new Expr\Orx(['foo = 1', 'bar = 2']); + $orx = new Orx(['foo = 1', 'bar = 2']); self::assertEquals(['foo = 1', 'bar = 2'], $orx->getParts()); // Select - $select = new Expr\Select(['foo', 'bar']); + $select = new Select(['foo', 'bar']); self::assertEquals(['foo', 'bar'], $select->getParts()); } diff --git a/tests/Tests/ORM/Query/FilterCollectionTest.php b/tests/Tests/ORM/Query/FilterCollectionTest.php index a4c0f998b47..5efe85a092d 100644 --- a/tests/Tests/ORM/Query/FilterCollectionTest.php +++ b/tests/Tests/ORM/Query/FilterCollectionTest.php @@ -15,8 +15,7 @@ */ class FilterCollectionTest extends OrmTestCase { - /** @var EntityManagerMock */ - private $em; + private EntityManagerMock $em; protected function setUp(): void { @@ -199,10 +198,7 @@ public function testHashing(): void class MyFilter extends SQLFilter { - /** - * {@inheritDoc} - */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string + public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string { // getParameter applies quoting automatically return $targetTableAlias . '.id = ' . $this->getParameter('id'); diff --git a/tests/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Tests/ORM/Query/LanguageRecognitionTest.php index 8212be5fe7a..02b42fb5ba6 100644 --- a/tests/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Tests/ORM/Query/LanguageRecognitionTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Query; +use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; @@ -11,15 +12,19 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Query; +use Doctrine\ORM\Query\AST\Functions\ConcatFunction; +use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\QueryException; use Doctrine\Tests\Mocks\NullSqlWalker; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; class LanguageRecognitionTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; + private int $hydrationMode = AbstractQuery::HYDRATE_OBJECT; protected function setUp(): void { @@ -43,12 +48,13 @@ public function parseDql(string $dql, array $hints = []): ParserResult { $query = $this->entityManager->createQuery($dql); $query->setDQL($dql); + $query->setHydrationMode($this->hydrationMode); foreach ($hints as $key => $value) { $query->setHint($key, $value); } - $parser = new Query\Parser($query); + $parser = new Parser($query); // We do NOT test SQL output here. That only unnecessarily slows down the tests! $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, NullSqlWalker::class); @@ -71,7 +77,7 @@ public function testSelectSingleComponentWithAsterisk(): void $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'); } - /** @dataProvider invalidDQL */ + #[DataProvider('invalidDQL')] public function testRejectsInvalidDQL(string $dql): void { $this->expectException(QueryException::class); @@ -80,7 +86,7 @@ public function testRejectsInvalidDQL(string $dql): void [ 'Unknown' => 'Unknown', 'CMS' => 'Doctrine\Tests\Models\CMS', - ] + ], ); $this->parseDql($dql); @@ -96,8 +102,6 @@ public static function invalidDQL(): array ['SELECT \foo FROM Doctrine\Tests\Models\CMS\CmsUser \foo'], ['SELECT foo\ FROM Doctrine\Tests\Models\CMS\CmsUser foo\\'], ['SELECT foo\bar FROM Doctrine\Tests\Models\CMS\CmsUser foo\bar'], - ['SELECT foo:bar FROM Doctrine\Tests\Models\CMS\CmsUser foo:bar'], - ['SELECT foo: FROM Doctrine\Tests\Models\CMS\CmsUser foo:'], /* Checks for invalid AbstractSchemaName */ ['SELECT u FROM UnknownClass u'], // unknown @@ -105,23 +109,13 @@ public static function invalidDQL(): array ['SELECT u FROM \Unknown\Class u'], // unknown, leading backslash ['SELECT u FROM Unknown\\\\Class u'], // unknown, syntactically bogus (duplicate \\) ['SELECT u FROM Unknown\Class\ u'], // unknown, syntactically bogus (trailing \) - ['SELECT u FROM Unknown:Class u'], // unknown, with namespace alias - ['SELECT u FROM Unknown::Class u'], // unknown, with PAAMAYIM_NEKUDOTAYIM - ['SELECT u FROM Unknown:Class:Name u'], // unknown, with invalid namespace alias - ['SELECT u FROM UnknownClass: u'], // unknown, with invalid namespace alias - ['SELECT u FROM Unknown:Class: u'], // unknown, with invalid namespace alias ['SELECT u FROM Doctrine\Tests\Models\CMS\\\\CmsUser u'], // syntactically bogus (duplicate \\)array('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser\ u'), // syntactically bogus (trailing \) - ['SELECT u FROM CMS::User u'], - ['SELECT u FROM CMS:User: u'], - ['SELECT u FROM CMS:User:Foo u'], /* Checks for invalid AliasResultVariable */ ['SELECT \'foo\' AS \foo FROM Doctrine\Tests\Models\CMS\CmsUser u'], ['SELECT \'foo\' AS \foo\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'], ['SELECT \'foo\' AS foo\ FROM Doctrine\Tests\Models\CMS\CmsUser u'], ['SELECT \'foo\' AS foo\\\\bar FROM Doctrine\Tests\Models\CMS\CmsUser u'], - ['SELECT \'foo\' AS foo: FROM Doctrine\Tests\Models\CMS\CmsUser u'], - ['SELECT \'foo\' AS foo:bar FROM Doctrine\Tests\Models\CMS\CmsUser u'], ['0'], ]; @@ -273,13 +267,13 @@ public function testJoinClassPathUsingWITH(): void $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsArticle a WITH a.user = u.id'); } - /** @group DDC-3701 */ + #[Group('DDC-3701')] public function testJoinClassPathUsingWHERE(): void { $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.user = u.id'); } - /** @group DDC-3701 */ + #[Group('DDC-3701')] public function testDDC3701WHEREIsNotWITH(): void { $this->assertInvalidDQL('SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN Doctrine\Tests\Models\Company\CompanyEmployee e WHERE e.id = c.salesPerson WHERE c.completed = true'); @@ -330,14 +324,14 @@ public function testArithmeticExpressionWithParenthesisInSubselectPart(): void $this->assertValidDQL("SELECT (SELECT (SUM(u.id) / COUNT(u.id)) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } - /** @group DDC-1079 */ + #[Group('DDC-1079')] public function testSelectLiteralInSubselect(): void { $this->assertValidDQL('SELECT (SELECT 1 FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u'); $this->assertValidDQL('SELECT (SELECT 0 FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u'); } - /** @group DDC-1077 */ + #[Group('DDC-1077')] public function testConstantValueInSelect(): void { $this->assertValidDQL("SELECT u.name, 'foo' AS bar FROM Doctrine\Tests\Models\CMS\CmsUser u"); @@ -557,21 +551,21 @@ public function testInputParameterInSelect(): void $this->assertValidDQL('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); } - /** @group DDC-1091 */ + #[Group('DDC-1091')] public function testCustomFunctionsReturningStringInStringPrimary(): void { - $this->entityManager->getConfiguration()->addCustomStringFunction('CC', Query\AST\Functions\ConcatFunction::class); + $this->entityManager->getConfiguration()->addCustomStringFunction('CC', ConcatFunction::class); $this->assertValidDQL("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CC('%', u.name) LIKE '%foo%'"); } - /** @group DDC-505 */ + #[Group('DDC-505')] public function testDQLKeywordInJoinIsAllowed(): void { $this->assertValidDQL('SELECT u FROM ' . __NAMESPACE__ . '\DQLKeywordsModelUser u JOIN u.group g'); } - /** @group DDC-505 */ + #[Group('DDC-505')] public function testDQLKeywordInConditionIsAllowed(): void { $this->assertValidDQL('SELECT g FROM ' . __NAMESPACE__ . '\DQLKeywordsModelGroup g WHERE g.from=0'); @@ -583,43 +577,43 @@ public function testInverseSideSingleValuedAssociationPathNotAllowed(): void $this->assertInvalidDQL('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); } - /** @group DDC-617 */ + #[Group('DDC-617')] public function testSelectOnlyNonRootEntityAlias(): void { $this->assertInvalidDQL('SELECT g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g'); } - /** @group DDC-1108 */ + #[Group('DDC-1108')] public function testInputParameterSingleChar(): void { $this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :q'); } - /** @group DDC-1053 */ + #[Group('DDC-1053')] public function testGroupBy(): void { $this->assertValidDQL('SELECT g.id, count(u.id) FROM Doctrine\Tests\Models\CMS\CmsGroup g JOIN g.users u GROUP BY g.id'); } - /** @group DDC-1053 */ + #[Group('DDC-1053')] public function testGroupByIdentificationVariable(): void { $this->assertValidDQL('SELECT g, count(u.id) FROM Doctrine\Tests\Models\CMS\CmsGroup g JOIN g.users u GROUP BY g'); } - /** @group DDC-1053 */ + #[Group('DDC-1053')] public function testGroupByUnknownIdentificationVariable(): void { $this->assertInvalidDQL('SELECT g, count(u.id) FROM Doctrine\Tests\Models\CMS\CmsGroup g JOIN g.users u GROUP BY m'); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testSizeOfForeignKeyOneToManyPrimaryKeyEntity(): void { $this->assertValidDQL('SELECT a, t FROM Doctrine\Tests\Models\DDC117\DDC117Article a JOIN a.translations t WHERE SIZE(a.translations) > 0'); } - /** @group DDC-117 */ + #[Group('DDC-117')] public function testSizeOfForeignKeyManyToManyPrimaryKeyEntity(): void { $this->assertValidDQL('SELECT e, t FROM Doctrine\Tests\Models\DDC117\DDC117Editor e JOIN e.reviewingTranslations t WHERE SIZE(e.reviewingTranslations) > 0'); @@ -635,31 +629,31 @@ public function testCaseSupportContainingCoalesceExpression(): void $this->assertValidDQL("select COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u"); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportIsNullExpression(): void { $this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL'); } - /** @group DDC-3085 */ + #[Group('DDC-3085')] public function testHavingSupportResultVariableInNullComparisonExpression(): void { $this->assertValidDQL('SELECT u AS user, SUM(a.id) AS score FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN Doctrine\Tests\Models\CMS\CmsAddress a WITH a.user = u GROUP BY u HAVING score IS NOT NULL AND score >= 5'); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportLikeExpression(): void { $this->assertValidDQL("SELECT _u.id, count(_articles) as uuuu FROM Doctrine\Tests\Models\CMS\CmsUser _u LEFT JOIN _u.articles _articles GROUP BY _u HAVING uuuu LIKE '3'"); } - /** @group DDC-3018 */ + #[Group('DDC-3018')] public function testNewLiteralExpression(): void { $this->assertValidDQL('SELECT new ' . __NAMESPACE__ . "\\DummyStruct(u.id, 'foo', 1, true) FROM Doctrine\Tests\Models\CMS\CmsUser u"); } - /** @group DDC-3075 */ + #[Group('DDC-3075')] public function testNewLiteralWithSubselectExpression(): void { $this->assertValidDQL('SELECT new ' . __NAMESPACE__ . "\\DummyStruct(u.id, 'foo', (SELECT 1 FROM Doctrine\Tests\Models\CMS\CmsUser su), true) FROM Doctrine\Tests\Models\CMS\CmsUser u"); @@ -668,45 +662,33 @@ public function testNewLiteralWithSubselectExpression(): void public function testStringPrimaryAcceptsAggregateExpression(): void { $this->assertValidDQL( - 'SELECT CONCAT(a.topic, MAX(a.version)) last FROM Doctrine\Tests\Models\CMS\CmsArticle a GROUP BY a' + 'SELECT CONCAT(a.topic, MAX(a.version)) last FROM Doctrine\Tests\Models\CMS\CmsArticle a GROUP BY a', ); } } -/** @Entity */ +#[Entity] class DQLKeywordsModelUser { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var DQLKeywordsModelGroup - * @OneToOne(targetEntity="DQLKeywordsModelGroup") - */ - private $group; + #[OneToOne(targetEntity: 'DQLKeywordsModelGroup')] + private DQLKeywordsModelGroup $group; } -/** @Entity */ +#[Entity] class DQLKeywordsModelGroup { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; - /** - * @var string - * @Column - */ - private $from; + #[Column] + private string $from; } class DummyStruct diff --git a/tests/Tests/ORM/Query/LexerTest.php b/tests/Tests/ORM/Query/LexerTest.php index 696fac761e4..bfce16ab7b7 100644 --- a/tests/Tests/ORM/Query/LexerTest.php +++ b/tests/Tests/ORM/Query/LexerTest.php @@ -8,10 +8,11 @@ use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\TokenType; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; class LexerTest extends OrmTestCase { - /** @dataProvider provideTokens */ + #[DataProvider('provideTokens')] public function testScannerRecognizesTokens($type, $value): void { $lexer = new Lexer($value); @@ -194,8 +195,6 @@ public static function provideTokens(): array [TokenType::T_IDENTIFIER, '_some_identifier'], // starts with underscore [TokenType::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test) [TokenType::T_FULLY_QUALIFIED_NAME, 'Some\Class'], // DQL class reference - [TokenType::T_ALIASED_NAME, 'Some:Name'], - [TokenType::T_ALIASED_NAME, 'Some:Subclassed\Name'], ]; } } diff --git a/tests/Tests/ORM/Query/ParameterTypeInfererTest.php b/tests/Tests/ORM/Query/ParameterTypeInfererTest.php index 29923cba773..9893979cf59 100644 --- a/tests/Tests/ORM/Query/ParameterTypeInfererTest.php +++ b/tests/Tests/ORM/Query/ParameterTypeInfererTest.php @@ -7,7 +7,7 @@ use DateInterval; use DateTime; use DateTimeImmutable; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Query\ParameterTypeInferer; @@ -15,12 +15,11 @@ use Doctrine\Tests\Models\Enums\UserStatus; use Doctrine\Tests\OrmTestCase; use Generator; - -use const PHP_VERSION_ID; +use PHPUnit\Framework\Attributes\DataProvider; class ParameterTypeInfererTest extends OrmTestCase { - /** @phpstan-return Generator */ + /** @phpstan-return Generator */ public static function providerParameterTypeInferer(): Generator { yield 'integer' => [1, Types::INTEGER]; @@ -29,27 +28,19 @@ public static function providerParameterTypeInferer(): Generator yield 'datetime_object' => [new DateTime(), Types::DATETIME_MUTABLE]; yield 'datetime_immutable_object' => [new DateTimeImmutable(), Types::DATETIME_IMMUTABLE]; yield 'date_interval_object' => [new DateInterval('P1D'), Types::DATEINTERVAL]; - yield 'array_of_int' => [[2], Connection::PARAM_INT_ARRAY]; - yield 'array_of_string' => [['foo'], Connection::PARAM_STR_ARRAY]; - yield 'array_of_numeric_string' => [['1', '2'], Connection::PARAM_STR_ARRAY]; - yield 'empty_array' => [[], Connection::PARAM_STR_ARRAY]; + yield 'array_of_int' => [[2], ArrayParameterType::INTEGER]; + yield 'array_of_string' => [['foo'], ArrayParameterType::STRING]; + yield 'array_of_numeric_string' => [['1', '2'], ArrayParameterType::STRING]; + yield 'empty_array' => [[], ArrayParameterType::STRING]; yield 'boolean' => [true, Types::BOOLEAN]; - - if (PHP_VERSION_ID >= 80100) { - yield 'int_backed_enum' => [AccessLevel::Admin, Types::INTEGER]; - yield 'string_backed_enum' => [UserStatus::Active, Types::STRING]; - yield 'array_of_int_backed_enum' => [[AccessLevel::Admin], Connection::PARAM_INT_ARRAY]; - yield 'array_of_string_backed_enum' => [[UserStatus::Active], Connection::PARAM_STR_ARRAY]; - } + yield 'int_backed_enum' => [AccessLevel::Admin, Types::INTEGER]; + yield 'string_backed_enum' => [UserStatus::Active, Types::STRING]; + yield 'array_of_int_backed_enum' => [[AccessLevel::Admin], ArrayParameterType::INTEGER]; + yield 'array_of_string_backed_enum' => [[UserStatus::Active], ArrayParameterType::STRING]; } - /** - * @param mixed $value - * @param int|string $expected - * - * @dataProvider providerParameterTypeInferer - */ - public function testParameterTypeInferer($value, $expected): void + #[DataProvider('providerParameterTypeInferer')] + public function testParameterTypeInferer(mixed $value, ParameterType|ArrayParameterType|int|string $expected): void { self::assertEquals($expected, ParameterTypeInferer::inferType($value)); } diff --git a/tests/Tests/ORM/Query/ParserResultTest.php b/tests/Tests/ORM/Query/ParserResultTest.php index f1bf6ea8e71..af9831bf77a 100644 --- a/tests/Tests/ORM/Query/ParserResultTest.php +++ b/tests/Tests/ORM/Query/ParserResultTest.php @@ -7,6 +7,7 @@ use Doctrine\ORM\Query\Exec\AbstractSqlExecutor; use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\ResultSetMapping; +use LogicException; use PHPUnit\Framework\TestCase; class ParserResultTest extends TestCase @@ -24,10 +25,18 @@ public function testGetRsm(): void self::assertInstanceOf(ResultSetMapping::class, $this->parserResult->getResultSetMapping()); } - public function testSetGetSqlExecutor(): void + public function testItThrowsWhenAttemptingToAccessTheExecutorBeforeItIsSet(): void { - self::assertNull($this->parserResult->getSqlExecutor()); + $this->expectException(LogicException::class); + $this->expectExceptionMessage( + 'Executor not set yet. Call Doctrine\ORM\Query\ParserResult::setSqlExecutor() first.', + ); + + $this->parserResult->getSqlExecutor(); + } + public function testSetGetSqlExecutor(): void + { $executor = $this->getMockForAbstractClass(AbstractSqlExecutor::class); $this->parserResult->setSqlExecutor($executor); self::assertSame($executor, $this->parserResult->getSqlExecutor()); diff --git a/tests/Tests/ORM/Query/ParserTest.php b/tests/Tests/ORM/Query/ParserTest.php index 464e8595a82..c260455d712 100644 --- a/tests/Tests/ORM/Query/ParserTest.php +++ b/tests/Tests/ORM/Query/ParserTest.php @@ -4,23 +4,20 @@ namespace Doctrine\Tests\ORM\Query; -use Doctrine\Common\Persistence\PersistentObject; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\Query\TokenType; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\Group; use stdClass; -use function class_exists; - class ParserTest extends OrmTestCase { - /** - * @covers \Doctrine\ORM\Query\Parser::AbstractSchemaName - * @group DDC-3715 - */ + #[Group('DDC-3715')] public function testAbstractSchemaNameSupportsFQCN(): void { $parser = $this->createParser(CmsUser::class); @@ -28,10 +25,7 @@ public function testAbstractSchemaNameSupportsFQCN(): void self::assertEquals(CmsUser::class, $parser->AbstractSchemaName()); } - /** - * @covers Doctrine\ORM\Query\Parser::AbstractSchemaName - * @group DDC-3715 - */ + #[Group('DDC-3715')] public function testAbstractSchemaNameSupportsClassnamesWithLeadingBackslash(): void { $parser = $this->createParser('\\' . CmsUser::class); @@ -39,10 +33,7 @@ public function testAbstractSchemaNameSupportsClassnamesWithLeadingBackslash(): self::assertEquals('\\' . CmsUser::class, $parser->AbstractSchemaName()); } - /** - * @covers \Doctrine\ORM\Query\Parser::AbstractSchemaName - * @group DDC-3715 - */ + #[Group('DDC-3715')] public function testAbstractSchemaNameSupportsIdentifier(): void { $parser = $this->createParser(stdClass::class); @@ -50,60 +41,19 @@ public function testAbstractSchemaNameSupportsIdentifier(): void self::assertEquals(stdClass::class, $parser->AbstractSchemaName()); } - /** - * @covers \Doctrine\ORM\Query\Parser::AbstractSchemaName - * @group DDC-3715 - */ - public function testAbstractSchemaNameSupportsNamespaceAlias(): void - { - if (! class_exists(PersistentObject::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2'); - } - - $parser = $this->createParser('CMS:CmsUser'); - - $parser->getEntityManager()->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS'); - - self::assertEquals(CmsUser::class, $parser->AbstractSchemaName()); - } - - /** - * @covers \Doctrine\ORM\Query\Parser::AbstractSchemaName - * @group DDC-3715 - */ - public function testAbstractSchemaNameSupportsNamespaceAliasWithRelativeClassname(): void - { - if (! class_exists(PersistentObject::class)) { - self::markTestSkipped('This test requires doctrine/persistence 2'); - } - - $parser = $this->createParser('Model:CMS\CmsUser'); - - $parser->getEntityManager()->getConfiguration()->addEntityNamespace('Model', 'Doctrine\Tests\Models'); - - self::assertEquals(CmsUser::class, $parser->AbstractSchemaName()); - } - - /** - * @dataProvider validMatches - * @covers Doctrine\ORM\Query\Parser::match - * @group DDC-3701 - */ - public function testMatch(int $expectedToken, string $inputString): void + #[DataProvider('validMatches')] + #[Group('DDC-3701')] + #[DoesNotPerformAssertions] + public function testMatch(TokenType $expectedToken, string $inputString): void { $parser = $this->createParser($inputString); $parser->match($expectedToken); // throws exception if not matched - - $this->addToAssertionCount(1); } - /** - * @dataProvider invalidMatches - * @covers Doctrine\ORM\Query\Parser::match - * @group DDC-3701 - */ - public function testMatchFailure(int $expectedToken, string $inputString): void + #[DataProvider('invalidMatches')] + #[Group('DDC-3701')] + public function testMatchFailure(TokenType $expectedToken, string $inputString): void { $this->expectException(QueryException::class); @@ -145,7 +95,6 @@ public static function invalidMatches(): array // The following are qualified or aliased names and must not be accepted where only an Identifier is expected [TokenType::T_IDENTIFIER, '\\Some\\Class'], [TokenType::T_IDENTIFIER, 'Some\\Class'], - [TokenType::T_IDENTIFIER, 'Some:Name'], ]; } @@ -153,9 +102,8 @@ public static function invalidMatches(): array * PHP 7.4 would fail with Notice: Trying to access array offset on value of type null. * * @see https://github.com/doctrine/orm/pull/7934 - * - * @group GH7934 */ + #[Group('GH7934')] public function testNullLookahead(): void { $query = new Query($this->getTestEntityManager()); diff --git a/tests/Tests/ORM/Query/QueryExpressionVisitorTest.php b/tests/Tests/ORM/Query/QueryExpressionVisitorTest.php index edabff175a9..9ece94b5d2a 100644 --- a/tests/Tests/ORM/Query/QueryExpressionVisitorTest.php +++ b/tests/Tests/ORM/Query/QueryExpressionVisitorTest.php @@ -11,29 +11,23 @@ use Doctrine\ORM\Query\Expr as QueryBuilder; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\QueryExpressionVisitor; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use function method_exists; - /** * Test for QueryExpressionVisitor */ class QueryExpressionVisitorTest extends TestCase { - /** @var QueryExpressionVisitor */ - private $visitor; + private QueryExpressionVisitor $visitor; protected function setUp(): void { $this->visitor = new QueryExpressionVisitor(['o', 'p']); } - /** - * @param QueryBuilder\Comparison|QueryBuilder\Func|string $queryExpr - * - * @dataProvider comparisonData - */ - public function testWalkComparison(CriteriaComparison $criteriaExpr, $queryExpr, ?Parameter $parameter = null): void + #[DataProvider('comparisonData')] + public function testWalkComparison(CriteriaComparison $criteriaExpr, QueryBuilder\Comparison|QueryBuilder\Func|string $queryExpr, Parameter|null $parameter = null): void { self::assertEquals($queryExpr, $this->visitor->walkComparison($criteriaExpr)); if ($parameter) { @@ -89,8 +83,8 @@ public function testWalkAndCompositeExpression(): void $expr = $this->visitor->walkCompositeExpression( $cb->andX( $cb->eq('foo', 1), - $cb->eq('bar', 1) - ) + $cb->eq('bar', 1), + ), ); self::assertInstanceOf(QueryBuilder\Andx::class, $expr); @@ -103,8 +97,8 @@ public function testWalkOrCompositeExpression(): void $expr = $this->visitor->walkCompositeExpression( $cb->orX( $cb->eq('foo', 1), - $cb->eq('bar', 1) - ) + $cb->eq('bar', 1), + ), ); self::assertInstanceOf(QueryBuilder\Orx::class, $expr); @@ -113,17 +107,13 @@ public function testWalkOrCompositeExpression(): void public function testWalkNotCompositeExpression(): void { - if (! method_exists(CriteriaBuilder::class, 'not')) { - self::markTestSkipped('doctrine/collections in version ^2.1 is required for this test to run.'); - } - $qb = new QueryBuilder(); $cb = new CriteriaBuilder(); $expr = $this->visitor->walkCompositeExpression( $cb->not( - $cb->eq('foo', 1) - ) + $cb->eq('foo', 1), + ), ); self::assertInstanceOf(QueryBuilder\Func::class, $expr); diff --git a/tests/Tests/ORM/Query/QueryTest.php b/tests/Tests/ORM/Query/QueryTest.php index 61eb6b8475c..5e0bf0685f9 100644 --- a/tests/Tests/ORM/Query/QueryTest.php +++ b/tests/Tests/ORM/Query/QueryTest.php @@ -6,26 +6,22 @@ use DateTime; use DateTimeImmutable; -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\Psr6\CacheAdapter; -use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\Cache\QueryCacheProfile; 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\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Internal\Hydration\IterableResult; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\UnitOfWork; use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; -use Doctrine\Tests\Mocks\DriverConnectionMock; -use Doctrine\Tests\Mocks\DriverResultMock; +use Doctrine\Tests\Mocks\ArrayResultFactory; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsGroup; @@ -36,22 +32,17 @@ use Doctrine\Tests\Models\Generic\DateTimeModel; use Doctrine\Tests\OrmTestCase; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use function array_map; -use function assert; -use function class_exists; -use function method_exists; - -use const PHP_VERSION_ID; class QueryTest extends OrmTestCase { - use VerifyDeprecations; - - /** @var EntityManagerMock */ - protected $entityManager; + private EntityManagerMock $entityManager; protected function setUp(): void { @@ -91,12 +82,9 @@ public function testSetParameters(): void self::assertEquals($parameters, $query->getParameters()); } - /** - * @phpstan-param LockMode::* $lockMode - * - * @dataProvider provideLockModes - */ - public function testSetLockMode(int $lockMode): void + /** @phpstan-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'); @@ -148,14 +136,12 @@ public function testFluentQueryInterface(): void $q = $this->entityManager->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a'); $q2 = $q->expireQueryCache(true) ->setQueryCacheLifetime(3600) - ->setQueryCacheDriver(null) ->setQueryCache(null) ->expireResultCache(true) ->setHint('foo', 'bar') ->setHint('bar', 'baz') ->setParameter(1, 'bar') ->setParameters(new ArrayCollection([new Parameter(2, 'baz')])) - ->setResultCacheDriver(null) ->setResultCache(null) ->setResultCacheId('foo') ->setDQL('foo') @@ -165,21 +151,7 @@ public function testFluentQueryInterface(): void self::assertSame($q2, $q); } - public function testSettingNullDqlIsDeprecated(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9784'); - $q = $this->entityManager->createQuery(); - $q->setDQL(null); - } - - public function testSettingNullFirstResultIsDeprecated(): void - { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9809'); - $q = $this->entityManager->createQuery(); - $q->setFirstResult(null); - } - - /** @group DDC-968 */ + #[Group('DDC-968')] public function testHints(): void { $q = $this->entityManager->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a'); @@ -192,36 +164,6 @@ public function testHints(): void self::assertFalse($q->hasHint('barFooBaz')); } - /** @group DDC-1588 */ - public function testQueryDefaultResultCache(): void - { - if (! method_exists(QueryCacheProfile::class, 'getResultCache')) { - self::markTestSkipped('This test requires DBAL 3.2 or newer.'); - } - - $this->entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); - $q = $this->entityManager->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a'); - $q->enableResultCache(); - self::assertSame($this->entityManager->getConfiguration()->getResultCache(), $q->getQueryCacheProfile()->getResultCache()); - } - - /** @group DDC-1588 */ - public function testQueryDefaultResultCacheLegacy(): void - { - $this->entityManager->getConfiguration()->setResultCacheImpl(DoctrineProvider::wrap(new ArrayAdapter())); - $q = $this->entityManager->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a'); - $q->enableResultCache(); - self::assertSame($this->entityManager->getConfiguration()->getResultCache(), CacheAdapter::wrap($q->getQueryCacheProfile()->getResultCacheDriver())); - } - - public function testIterateWithNoDistinctAndWrongSelectClause(): void - { - $this->expectException(QueryException::class); - - $q = $this->entityManager->createQuery('select u, a from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a'); - $q->iterate(); - } - public function testToIterableWithNoDistinctAndWrongSelectClause(): void { $this->expectException(QueryException::class); @@ -230,14 +172,6 @@ public function testToIterableWithNoDistinctAndWrongSelectClause(): void $q->toIterable(); } - public function testIterateWithNoDistinctAndWithValidSelectClause(): void - { - $this->expectException(QueryException::class); - - $q = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a'); - $q->iterate(); - } - public function testToIterableWithNoDistinctAndWithValidSelectClause(): void { $this->expectException(QueryException::class); @@ -250,7 +184,7 @@ public function testIterateWithDistinct(): void { $q = $this->entityManager->createQuery('SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a'); - self::assertInstanceOf(IterableResult::class, $q->iterate()); + self::assertInstanceOf(Generator::class, $q->toIterable()); } public function testIterateEmptyResult(): void @@ -264,7 +198,7 @@ public function testIterateEmptyResult(): void self::assertTrue(true); } - /** @group DDC-1697 */ + #[Group('DDC-1697')] public function testCollectionParameters(): void { $cities = [ @@ -284,7 +218,7 @@ public function testCollectionParameters(): void self::assertEquals($cities, $parameter->getValue()); } - /** @group DDC-1697 */ + #[Group('DDC-1697')] public function testExplicitCollectionParameters(): void { $cities = [ @@ -294,8 +228,8 @@ public function testExplicitCollectionParameters(): void ]; $query = $this->entityManager - ->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)') - ->setParameter('cities', $cities, class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY); + ->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)') + ->setParameter('cities', $cities, ArrayParameterType::STRING); $parameters = $query->getParameters(); $parameter = $parameters->first(); @@ -320,13 +254,10 @@ public static function provideProcessParameterValueIterable(): Generator yield 'simple_array' => [$baseArray]; yield 'doctrine_collection' => [new ArrayCollection($baseArray)]; yield 'generator' => [$gen()]; - - if (PHP_VERSION_ID >= 80100) { - yield 'array_of_enum' => [array_map([City::class, 'from'], $baseArray)]; - } + yield 'array_of_enum' => [array_map([City::class, 'from'], $baseArray)]; } - /** @dataProvider provideProcessParameterValueIterable */ + #[DataProvider('provideProcessParameterValueIterable')] public function testProcessParameterValueIterable(iterable $cities): void { $query = $this->entityManager->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)'); @@ -336,7 +267,7 @@ public function testProcessParameterValueIterable(iterable $cities): void 3 => 'Cannes', 9 => 'St Julien', ], - $query->processParameterValue($cities) + $query->processParameterValue($cities), ); } @@ -349,13 +280,13 @@ public function testProcessParameterValueWithIterableEntityShouldNotBeTreatedAsI self::assertEquals(1, $query->processParameterValue($group)); } - /** @group DDC-2224 */ + #[Group('DDC-2224')] public function testProcessParameterValueClassMetadata(): void { $query = $this->entityManager->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)'); self::assertEquals( CmsAddress::class, - $query->processParameterValue($this->entityManager->getClassMetadata(CmsAddress::class)) + $query->processParameterValue($this->entityManager->getClassMetadata(CmsAddress::class)), ); } @@ -367,14 +298,14 @@ public function testProcessParameterValueObject(): void self::assertSame( 12345, - $query->processParameterValue($user) + $query->processParameterValue($user), ); } public function testProcessParameterValueValueObjectWithDriverChain(): void { $driverChain = new MappingDriverChain(); - $driverChain->addDriver($this->createAnnotationDriver(), 'Foo'); + $driverChain->addDriver($this->createAttributeDriver(), 'Foo'); $this->entityManager->getConfiguration()->setMetadataDriverImpl($driverChain); $query = $this->entityManager->createQuery(); @@ -391,9 +322,6 @@ public function testProcessParameterValueNull(): void self::assertNull($query->processParameterValue(null)); } - /** - * @requires PHP 8.1 - */ public function testProcessParameterValueBackedEnum(): void { $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :status'); @@ -402,9 +330,6 @@ public function testProcessParameterValueBackedEnum(): void self::assertSame([2], $query->processParameterValue([AccessLevel::User])); } - /** - * @requires PHP 8.1 - */ public function testProcessParameterValueBackedEnumArray(): void { $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status IN (:status)'); @@ -431,18 +356,22 @@ public function testDefaultQueryHints(): void self::assertSame($config->getDefaultQueryHints(), $q2->getHints()); } - /** @group DDC-3714 */ + #[Group('DDC-3714')] public function testResultCacheCaching(): void { - $this->entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); - $this->entityManager->getConfiguration()->setQueryCache(new ArrayAdapter()); - $driverConnectionMock = $this->entityManager->getConnection()->getWrappedConnection(); - assert($driverConnectionMock instanceof DriverConnectionMock); - $result = new DriverResultMock([ - ['id_0' => 1], - ]); - $driverConnectionMock->setResultMock($result); - $res = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u') + $entityManager = $this->createTestEntityManagerWithConnection( + $this->createConnection( + ArrayResultFactory::createDriverResultFromArray([ + ['id_0' => 1], + ]), + ArrayResultFactory::createDriverResultFromArray([]), + ), + ); + + $entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); + $entityManager->getConfiguration()->setQueryCache(new ArrayAdapter()); + + $res = $entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u') ->useQueryCache(true) ->enableResultCache(60) //let it cache @@ -450,16 +379,14 @@ public function testResultCacheCaching(): void self::assertCount(1, $res); - $driverConnectionMock->setResultMock(null); - - $res = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u') + $res = $entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u') ->useQueryCache(true) ->disableResultCache() ->getResult(); self::assertCount(0, $res); } - /** @group DDC-3741 */ + #[Group('DDC-3741')] public function testSetHydrationCacheProfileNull(): void { $query = $this->entityManager->createQuery(); @@ -467,38 +394,43 @@ public function testSetHydrationCacheProfileNull(): void self::assertNull($query->getHydrationCacheProfile()); } - /** @group 2947 */ + #[Group('2947')] public function testResultCacheEviction(): void { - $this->entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); - - $query = $this->entityManager->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u') - ->enableResultCache(); + $entityManager = $this->createTestEntityManagerWithConnection( + $this->createConnection( + ArrayResultFactory::createDriverResultFromArray([ + ['id_0' => 1], + ]), + ArrayResultFactory::createDriverResultFromArray([ + ['id_0' => 1], + ['id_0' => 2], + ]), + ArrayResultFactory::createDriverResultFromArray([ + ['id_0' => 1], + ]), + ), + ); - $driverConnectionMock = $this->entityManager->getConnection() - ->getWrappedConnection(); - assert($driverConnectionMock instanceof DriverConnectionMock); + $entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); - $driverConnectionMock->setResultMock(new DriverResultMock([['id_0' => 1]])); + $query = $entityManager->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u') + ->enableResultCache(); // Performs the query and sets up the initial cache self::assertCount(1, $query->getResult()); - $driverConnectionMock->setResultMock(new DriverResultMock([['id_0' => 1], ['id_0' => 2]])); - // Retrieves cached data since expire flag is false and we have a cached result set self::assertCount(1, $query->getResult()); // Performs the query and caches the result set since expire flag is true - self::assertCount(2, $query->expireResultCache(true)->getResult()); - - $driverConnectionMock->setResultMock(new DriverResultMock([['id_0' => 1]])); + self::assertCount(2, $query->expireResultCache()->getResult()); // Retrieves cached data since expire flag is false and we have a cached result set self::assertCount(2, $query->expireResultCache(false)->getResult()); } - /** @group #6162 */ + #[Group('#6162')] public function testSelectJoinSubquery(): void { $query = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u JOIN (SELECT )'); @@ -508,7 +440,7 @@ public function testSelectJoinSubquery(): void $query->getSQL(); } - /** @group #6162 */ + #[Group('#6162')] public function testSelectFromSubquery(): void { $query = $this->entityManager->createQuery('select u from (select Doctrine\Tests\Models\CMS\CmsUser c) as u'); @@ -518,7 +450,7 @@ public function testSelectFromSubquery(): void $query->getSQL(); } - /** @group 6699 */ + #[Group('6699')] public function testGetParameterTypeJuggling(): void { $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id = ?0'); @@ -530,7 +462,7 @@ public function testGetParameterTypeJuggling(): void self::assertSame(0, $query->getParameter('0')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithNameZeroIsNotOverridden(): void { $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name'); @@ -543,7 +475,7 @@ public function testSetParameterWithNameZeroIsNotOverridden(): void self::assertSame('Doctrine', $query->getParameter('name')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): void { $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name'); @@ -556,7 +488,7 @@ public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): v self::assertSame('Doctrine', $query->getParameter('name')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithTypeJugglingWorks(): void { $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.id != ?0 and u.username = :name'); @@ -572,19 +504,19 @@ public function testSetParameterWithTypeJugglingWorks(): void self::assertSame('Doctrine', $query->getParameter('name')->getValue()); } - /** @group 6748 */ + #[Group('6748')] public function testResultCacheProfileCanBeRemovedViaSetter(): void { $this->entityManager->getConfiguration()->setResultCache(new ArrayAdapter()); $query = $this->entityManager->createQuery('SELECT u FROM ' . CmsUser::class . ' u'); $query->enableResultCache(); - $query->setResultCacheProfile(); + $query->setResultCacheProfile(null); self::assertNull($query->getQueryCacheProfile()); } - /** @group 7527 */ + #[Group('7527')] public function testValuesAreNotBeingResolvedForSpecifiedParameterTypes(): void { $unitOfWork = $this->createMock(UnitOfWork::class); @@ -602,7 +534,7 @@ public function testValuesAreNotBeingResolvedForSpecifiedParameterTypes(): void self::assertEmpty($query->getResult()); } - /** @group 7982 */ + #[Group('7982')] public function testNonExistentExecutor(): void { $this->expectException(QueryException::class); @@ -611,7 +543,7 @@ public function testNonExistentExecutor(): void $this->entityManager->createQuery('0')->execute(); } - /** @group 8106 */ + #[Group('8106')] public function testGetParameterColonNormalize(): void { $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u where u.name = :name'); @@ -626,33 +558,56 @@ public function testGetParameterColonNormalize(): void public function testGetQueryCacheDriverWithDefaults(): void { - $cache = $this->createMock(CacheItemPoolInterface::class); + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItemMock = $this->createMock(CacheItemInterface::class); + $cacheItemMock->method('set')->willReturnSelf(); + $cacheItemMock->method('expiresAfter')->willReturnSelf(); + $cache + ->expects(self::atLeastOnce()) + ->method('getItem') + ->willReturn($cacheItemMock); $this->entityManager->getConfiguration()->setQueryCache($cache); - $query = $this->entityManager->createQuery('select u from ' . CmsUser::class . ' u'); - - self::assertSame($cache, CacheAdapter::wrap($query->getQueryCacheDriver())); + $this->entityManager + ->createQuery('select u from ' . CmsUser::class . ' u') + ->getSQL(); } - public function testGetQueryCacheDriverWithCacheExplicitlySetLegacy(): void + public function testGetQueryCacheDriverWithCacheExplicitlySet(): void { - $cache = $this->createMock(Cache::class); + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItemMock = $this->createMock(CacheItemInterface::class); + $cacheItemMock->method('set')->willReturnSelf(); + $cacheItemMock->method('expiresAfter')->willReturnSelf(); + $cache + ->expects(self::atLeastOnce()) + ->method('getItem') + ->willReturn($cacheItemMock); - $query = $this->entityManager + $this->entityManager ->createQuery('select u from ' . CmsUser::class . ' u') - ->setQueryCacheDriver($cache); - - self::assertSame($cache, $query->getQueryCacheDriver()); + ->setQueryCache($cache) + ->getSQL(); } - public function testGetQueryCacheDriverWithCacheExplicitlySet(): void + private function createConnection(Result ...$results): Connection { - $cache = $this->createMock(CacheItemPoolInterface::class); + $driverConnection = $this->createMock(Driver\Connection::class); + $driverConnection->method('query') + ->will($this->onConsecutiveCalls(...$results)); - $query = $this->entityManager - ->createQuery('select u from ' . CmsUser::class . ' u') - ->setQueryCache($cache); + $platform = $this->getMockBuilder(AbstractPlatform::class) + ->onlyMethods(['supportsIdentityColumns']) + ->getMockForAbstractClass(); + $platform->method('supportsIdentityColumns') + ->willReturn(true); + + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($driverConnection); + $driver->method('getDatabasePlatform') + ->willReturn($platform); - self::assertSame($cache, CacheAdapter::wrap($query->getQueryCacheDriver())); + return new Connection([], $driver); } } diff --git a/tests/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Tests/ORM/Query/SelectSqlGenerationTest.php index 6c24d6c02d1..02c4921c54f 100644 --- a/tests/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -8,7 +8,7 @@ use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\Column; @@ -29,18 +29,18 @@ use Doctrine\Tests\Models\Company\CompanyPerson; use Doctrine\Tests\OrmTestCase; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use function class_exists; -use function get_class; -// DBAL 2 compatibility -class_exists('Doctrine\DBAL\Platforms\MySqlPlatform'); -class_exists('Doctrine\DBAL\Platforms\PostgreSqlPlatform'); +// DBAL 3 compatibility +class_exists('Doctrine\\DBAL\\Platforms\\SqlitePlatform'); class SelectSqlGenerationTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; + private int $hydrationMode = ORMQuery::HYDRATE_OBJECT; protected function setUp(): void { @@ -54,9 +54,10 @@ public function assertSqlGeneration( string $dqlToBeTested, string $sqlToBeConfirmed, array $queryHints = [], - array $queryParams = [] + array $queryParams = [], ): void { $query = $this->entityManager->createQuery($dqlToBeTested); + $query->setHydrationMode($this->hydrationMode); foreach ($queryParams as $name => $value) { $query->setParameter($name, $value); @@ -72,7 +73,7 @@ public function assertSqlGeneration( parent::assertEquals( $sqlToBeConfirmed, - $sqlGenerated + $sqlGenerated, ); $query->free(); @@ -85,7 +86,7 @@ public function assertInvalidSqlGeneration( string $dqlToBeTested, string $expectedException, array $queryHints = [], - array $queryParams = [] + array $queryParams = [], ): void { $this->expectException($expectedException); @@ -108,16 +109,16 @@ public function assertInvalidSqlGeneration( self::fail($sql); } - /** @group DDC-3697 */ + #[Group('DDC-3697')] public function testJoinWithRangeVariablePutsConditionIntoSqlWhereClause(): void { $this->assertSqlGeneration( 'SELECT c.id FROM Doctrine\Tests\Models\Company\CompanyPerson c JOIN Doctrine\Tests\Models\Company\CompanyPerson r WHERE c.spouse = r AND r.id = 42', - 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42' + 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42', ); } - /** @group DDC-3697 */ + #[Group('DDC-3697')] public function testJoinWithRangeVariableAndInheritancePutsConditionIntoSqlWhereClause(): void { /* @@ -127,7 +128,7 @@ public function testJoinWithRangeVariableAndInheritancePutsConditionIntoSqlWhere */ $this->assertSqlGeneration( 'SELECT c.id FROM Doctrine\Tests\Models\Company\CompanyPerson c JOIN Doctrine\Tests\Models\Company\CompanyPerson r WHERE c.spouse = r AND r.id = 42', - 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42' + 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42', ); } @@ -135,7 +136,7 @@ public function testSupportsSelectForAllFields(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_', ); } @@ -143,7 +144,7 @@ public function testSupportsSelectForOneField(): void { $this->assertSqlGeneration( 'SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.id AS id_0 FROM cms_users c0_' + 'SELECT c0_.id AS id_0 FROM cms_users c0_', ); } @@ -151,7 +152,7 @@ public function testSupportsSelectForOneNestedField(): void { $this->assertSqlGeneration( 'SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u', - 'SELECT c0_.id AS id_0 FROM cms_articles c1_ INNER JOIN cms_users c0_ ON c1_.user_id = c0_.id' + 'SELECT c0_.id AS id_0 FROM cms_articles c1_ INNER JOIN cms_users c0_ ON c1_.user_id = c0_.id', ); } @@ -159,7 +160,7 @@ public function testSupportsSelectForAllNestedField(): void { $this->assertSqlGeneration( 'SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u ORDER BY u.name ASC', - 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id ORDER BY c1_.name ASC' + 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id ORDER BY c1_.name ASC', ); } @@ -167,7 +168,7 @@ public function testNotExistsExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NOT EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NOT EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234)', ); } @@ -175,7 +176,7 @@ public function testSupportsSelectForMultipleColumnsOfASingleComponent(): void { $this->assertSqlGeneration( 'SELECT u.username, u.name FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.username AS username_0, c0_.name AS name_1 FROM cms_users c0_' + 'SELECT c0_.username AS username_0, c0_.name AS name_1 FROM cms_users c0_', ); } @@ -183,7 +184,7 @@ public function testSupportsSelectUsingMultipleFromComponents(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id', ); } @@ -191,7 +192,7 @@ public function testSupportsJoinOnMultipleComponents(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsPhonenumber p WITH u = p.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)', ); } @@ -199,17 +200,17 @@ public function testSupportsJoinOnMultipleComponentsWithJoinedInheritanceType(): { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)', ); $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id LEFT JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id LEFT JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)', ); $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s LEFT JOIN Doctrine\Tests\Models\Company\CompanyEvent e WITH s.id = e.id', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN (company_events c4_ LEFT JOIN company_auctions c5_ ON c4_.id = c5_.id LEFT JOIN company_raffles c6_ ON c4_.id = c6_.id) ON (c2_.id = c4_.id) WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN (company_events c4_ LEFT JOIN company_auctions c5_ ON c4_.id = c5_.id LEFT JOIN company_raffles c6_ ON c4_.id = c6_.id) ON (c2_.id = c4_.id) WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')", ); } @@ -217,7 +218,7 @@ public function testSupportsSelectWithCollectionAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON c0_.id = c1_.user_id', ); } @@ -225,7 +226,7 @@ public function testSupportsSelectWithSingleValuedAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\Forum\ForumUser u JOIN u.avatar a', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f1_.id AS id_2, f0_.avatar_id AS avatar_id_3 FROM forum_users f0_ INNER JOIN forum_avatars f1_ ON f0_.avatar_id = f1_.id' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f1_.id AS id_2, f0_.avatar_id AS avatar_id_3 FROM forum_users f0_ INNER JOIN forum_avatars f1_ ON f0_.avatar_id = f1_.id', ); } @@ -233,7 +234,7 @@ public function testSelectCorrelatedSubqueryComplexMathematicalExpression(): voi { $this->assertSqlGeneration( 'SELECT (SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p JOIN p.user ui WHERE ui.id = u.id) AS c FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT (SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_1 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = c2_.id) AS sclr_0 FROM cms_users c2_' + 'SELECT (SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_1 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = c2_.id) AS sclr_0 FROM cms_users c2_', ); } @@ -241,7 +242,7 @@ public function testSelectComplexMathematicalExpression(): void { $this->assertSqlGeneration( 'SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p JOIN p.user ui WHERE ui.id = ?1', - 'SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_0 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = ?' + 'SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr_0 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = ?', ); } @@ -252,16 +253,16 @@ public function testSingleAssociationPathExpressionInSubselect(): void $this->assertSqlGeneration( 'SELECT (SELECT p.user FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = u) user_id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', - 'SELECT (SELECT c0_.user_id FROM cms_phonenumbers c0_ WHERE c0_.user_id = c1_.id) AS sclr_0 FROM cms_users c1_ WHERE c1_.id = ?' + 'SELECT (SELECT c0_.user_id FROM cms_phonenumbers c0_ WHERE c0_.user_id = c1_.id) AS sclr_0 FROM cms_users c1_ WHERE c1_.id = ?', ); } - /** @group DDC-1077 */ + #[Group('DDC-1077')] public function testConstantValueInSelect(): void { $this->assertSqlGeneration( "SELECT u.name, 'foo' AS bar FROM Doctrine\Tests\Models\CMS\CmsUser u", - "SELECT c0_.name AS name_0, 'foo' AS sclr_1 FROM cms_users c0_" + "SELECT c0_.name AS name_0, 'foo' AS sclr_1 FROM cms_users c0_", ); } @@ -269,7 +270,7 @@ public function testSupportsOrderByWithAscAsDefault(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC', ); } @@ -277,7 +278,7 @@ public function testSupportsOrderByAsc(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id asc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC', ); } @@ -285,7 +286,7 @@ public function testSupportsOrderByDesc(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id desc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id DESC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id DESC', ); } @@ -293,7 +294,7 @@ public function testSupportsOrderByWithSimpleArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY LENGTH(u.username) + LENGTH(u.username) asc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY LENGTH(f0_.username) + LENGTH(f0_.username) ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY LENGTH(f0_.username) + LENGTH(f0_.username) ASC', ); } @@ -301,7 +302,7 @@ public function testSupportsSelectDistinct(): void { $this->assertSqlGeneration( 'SELECT DISTINCT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT DISTINCT c0_.name AS name_0 FROM cms_users c0_' + 'SELECT DISTINCT c0_.name AS name_0 FROM cms_users c0_', ); } @@ -309,7 +310,7 @@ public function testSupportsAggregateFunctionInSelectedFields(): void { $this->assertSqlGeneration( 'SELECT COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id', - 'SELECT COUNT(c0_.id) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id' + 'SELECT COUNT(c0_.id) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id', ); } @@ -317,31 +318,26 @@ public function testSupportsAggregateFunctionWithSimpleArithmetic(): void { $this->assertSqlGeneration( 'SELECT MAX(u.id + 4) * 2 FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT MAX(c0_.id + 4) * 2 AS sclr_0 FROM cms_users c0_' + 'SELECT MAX(c0_.id + 4) * 2 AS sclr_0 FROM cms_users c0_', ); } - /** @group DDC-3276 */ + #[Group('DDC-3276')] public function testSupportsAggregateCountFunctionWithSimpleArithmetic(): void { - $connMock = $this->entityManager->getConnection(); - $orgPlatform = $connMock->getDatabasePlatform(); - - $connMock->setDatabasePlatform(new MySQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); $this->assertSqlGeneration( 'SELECT COUNT(CONCAT(u.id, u.name)) FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id', - 'SELECT COUNT(CONCAT(c0_.id, c0_.name)) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id' + 'SELECT COUNT(CONCAT(c0_.id, c0_.name)) AS sclr_0 FROM cms_users c0_ GROUP BY c0_.id', ); - - $connMock->setDatabasePlatform($orgPlatform); } public function testSupportsWhereClauseWithPositionalParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.id = ?1', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id = ?', ); } @@ -349,7 +345,7 @@ public function testSupportsWhereClauseWithNamedParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.username = :name', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ?', ); } @@ -357,7 +353,7 @@ public function testSupportsWhereAndClauseWithNamedParameters(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.username = :name and u.username = :name2', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ? AND f0_.username = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ? AND f0_.username = ?', ); } @@ -365,7 +361,7 @@ public function testSupportsCombinedWhereClauseWithNamedParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where (u.username = :name OR u.username = :name2) AND u.id = :id', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE (f0_.username = ? OR f0_.username = ?) AND f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE (f0_.username = ? OR f0_.username = ?) AND f0_.id = ?', ); } @@ -373,7 +369,7 @@ public function testSupportsAggregateFunctionInASelectDistinct(): void { $this->assertSqlGeneration( 'SELECT COUNT(DISTINCT u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT COUNT(DISTINCT c0_.name) AS sclr_0 FROM cms_users c0_' + 'SELECT COUNT(DISTINCT c0_.name) AS sclr_0 FROM cms_users c0_', ); } @@ -382,7 +378,7 @@ public function testSupportsASqlKeywordInAStringLiteralParam(): void { $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE '%foo OR bar%'", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.name LIKE '%foo OR bar%'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.name LIKE '%foo OR bar%'", ); } @@ -390,7 +386,7 @@ public function testSupportsArithmeticExpressionsInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000', ); } @@ -398,7 +394,7 @@ public function testSupportsMultipleEntitiesInFromClause(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u2 WHERE u.id = u2.id', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id', ); } @@ -406,7 +402,7 @@ public function testSupportsMultipleEntitiesInFromClauseUsingPathExpression(): v { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.id = a.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id', ); } @@ -414,37 +410,35 @@ public function testSupportsPlainJoinWithoutClause(): void { $this->assertSqlGeneration( 'SELECT u.id, a.id from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a', - 'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id', ); $this->assertSqlGeneration( 'SELECT u.id, a.id from Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a', - 'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c1_.id AS id_1 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id', ); } - /** @group DDC-135 */ + #[Group('DDC-135')] public function testSupportsJoinAndWithClauseRestriction(): void { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')", ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a WITH a.topic LIKE '%foo%'", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')", ); } - /** - * @group DDC-135 - * @group DDC-177 - */ + #[Group('DDC-135')] + #[Group('DDC-177')] public function testJoinOnClauseNotYetSupportedThrowsException(): void { $this->expectException(QueryException::class); $sql = $this->entityManager->createQuery( - "SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a ON a.topic LIKE '%foo%'" + "SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a ON a.topic LIKE '%foo%'", )->getSql(); } @@ -452,7 +446,7 @@ public function testSupportsMultipleJoins(): void { $this->assertSqlGeneration( 'SELECT u.id, a.id, p.phonenumber, c.id from Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c', - 'SELECT c0_.id AS id_0, c1_.id AS id_1, c2_.phonenumber AS phonenumber_2, c3_.id AS id_3 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id INNER JOIN cms_phonenumbers c2_ ON c0_.id = c2_.user_id INNER JOIN cms_comments c3_ ON c1_.id = c3_.article_id' + 'SELECT c0_.id AS id_0, c1_.id AS id_1, c2_.phonenumber AS phonenumber_2, c3_.id AS id_3 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id INNER JOIN cms_phonenumbers c2_ ON c0_.id = c2_.user_id INNER JOIN cms_comments c3_ ON c1_.id = c3_.article_id', ); } @@ -460,16 +454,16 @@ public function testSupportsTrimFunction(): void { $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING ' ' FROM u.name) = 'someone'", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING ' ' FROM c0_.name) = 'someone'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING ' ' FROM c0_.name) = 'someone'", ); } - /** @group DDC-2668 */ + #[Group('DDC-2668')] public function testSupportsTrimLeadingZeroString(): void { $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING '0' FROM u.name) != ''", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''", ); } @@ -478,16 +472,16 @@ public function testSupportsBetweenClauseWithPositionalParameters(): void { $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id BETWEEN ?1 AND ?2', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.id BETWEEN ? AND ?' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.id BETWEEN ? AND ?', ); } - /** @group DDC-1802 */ + #[Group('DDC-1802')] public function testSupportsNotBetweenForSizeFunction(): void { $this->assertSqlGeneration( 'SELECT m.name FROM Doctrine\Tests\Models\StockExchange\Market m WHERE SIZE(m.stocks) NOT BETWEEN ?1 AND ?2', - 'SELECT e0_.name AS name_0 FROM exchange_markets e0_ WHERE (SELECT COUNT(*) FROM exchange_stocks e1_ WHERE e1_.market_id = e0_.id) NOT BETWEEN ? AND ?' + 'SELECT e0_.name AS name_0 FROM exchange_markets e0_ WHERE (SELECT COUNT(*) FROM exchange_stocks e1_ WHERE e1_.market_id = e0_.id) NOT BETWEEN ? AND ?', ); } @@ -497,7 +491,7 @@ public function testSupportsFunctionalExpressionsInWherePart(): void "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'", // String quoting in the SQL usually depends on the database platform. // This test works with a mock connection which uses ' for string quoting. - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(c0_.name) = 'someone'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE TRIM(c0_.name) = 'someone'", ); } @@ -505,7 +499,7 @@ public function testSupportsInstanceOfExpressionsInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')", ); } @@ -514,25 +508,25 @@ public function testSupportsInstanceOfExpressionInWherePartWithMultipleValues(): // This also uses FQCNs starting with or without a backslash in the INSTANCE OF parameter $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF (Doctrine\Tests\Models\Company\CompanyEmployee, \Doctrine\Tests\Models\Company\CompanyManager)', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')", ); } - /** @group DDC-1194 */ + #[Group('DDC-1194')] public function testSupportsInstanceOfExpressionsInWherePartPrefixedSlash(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF \Doctrine\Tests\Models\Company\CompanyEmployee', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')", ); } - /** @group DDC-1194 */ + #[Group('DDC-1194')] public function testSupportsInstanceOfExpressionsInWherePartWithUnrelatedClass(): void { $this->assertInvalidSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF \Doctrine\Tests\Models\CMS\CmsUser', - QueryException::class + QueryException::class, ); } @@ -540,7 +534,7 @@ public function testSupportsInstanceOfExpressionsInWherePartInDeeperLevel(): voi { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id WHERE c0_.discr IN ('manager')", ); } @@ -548,7 +542,7 @@ public function testSupportsInstanceOfExpressionsInWherePartInDeepestLevel(): vo { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyManager u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')", ); } @@ -558,7 +552,7 @@ public function testSupportsInstanceOfExpressionsUsingInputParameterInWherePart( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN (?)', [], - [1 => $this->entityManager->getClassMetadata(CompanyEmployee::class)] + [1 => $this->entityManager->getClassMetadata(CompanyEmployee::class)], ); } @@ -567,7 +561,7 @@ public function testSupportsSingleValuedInExpressionWithoutSpacesInWherePart(): { $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IN(46)', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.email_id IN (46)' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE c0_.email_id IN (46)', ); } @@ -575,7 +569,7 @@ public function testSupportsMultipleValuedInExpressionInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1, 2)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id IN (1, 2)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id IN (1, 2)', ); } @@ -583,16 +577,16 @@ public function testSupportsNotInExpressionInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :id NOT IN (1)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ? NOT IN (1)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ? NOT IN (1)', ); } - /** @group DDC-1802 */ + #[Group('DDC-1802')] public function testSupportsNotInExpressionForModFunction(): void { $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE MOD(u.id, 5) NOT IN(1,3,4)', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE MOD(c0_.id, 5) NOT IN (1, 3, 4)' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE MOD(c0_.id, 5) NOT IN (1, 3, 4)', ); } @@ -600,7 +594,7 @@ public function testInExpressionWithSingleValuedAssociationPathExpressionInWhere { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)', ); } @@ -609,7 +603,7 @@ public function testInvalidInExpressionWithSingleValuedAssociationPathExpression // We do not support SingleValuedAssociationPathExpression on inverse side $this->assertInvalidSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address IN (?1, ?2)', - QueryException::class + QueryException::class, ); } @@ -619,56 +613,52 @@ public function testInExpressionWithArithmeticExpression(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.username IN (FOO('Lo'), 'Lo', :name)", - "SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)" + "SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)", ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.id IN (1 + 1)', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)', ); } public function testSupportsConcatFunctionForMysqlAndPostgresql(): void { - $connMock = $this->entityManager->getConnection(); - $orgPlatform = $connMock->getDatabasePlatform(); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - $connMock->setDatabasePlatform(new MySQLPlatform()); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, 's') = ?1", - "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, 's') = ?" + "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, 's') = ?", ); $this->assertSqlGeneration( 'SELECT CONCAT(u.id, u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', - 'SELECT CONCAT(c0_.id, c0_.name) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?' + 'SELECT CONCAT(c0_.id, c0_.name) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?', ); - $connMock->setDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, 's') = ?1", - "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE c0_.name || 's' = ?" + "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE c0_.name || 's' = ?", ); $this->assertSqlGeneration( 'SELECT CONCAT(u.id, u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', - 'SELECT c0_.id || c0_.name AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?' + 'SELECT c0_.id || c0_.name AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?', ); - - $connMock->setDatabasePlatform($orgPlatform); } public function testSupportsExistsExpressionInWherePartWithCorrelatedSubquery(): void { $this->assertSqlGeneration( 'SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = u.id)', - 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = c0_.id)' + 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = c0_.id)', ); } - /** @group DDC-593 */ + #[Group('DDC-593')] public function testSubqueriesInComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id >= (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = :name)) AND (u.id <= (SELECT u3.id FROM Doctrine\Tests\Models\CMS\CmsUser u3 WHERE u3.name = :name))', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id >= (SELECT c1_.id FROM cms_users c1_ WHERE c1_.name = ?)) AND (c0_.id <= (SELECT c2_.id FROM cms_users c2_ WHERE c2_.name = ?))' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id >= (SELECT c1_.id FROM cms_users c1_ WHERE c1_.name = ?)) AND (c0_.id <= (SELECT c2_.id FROM cms_users c2_ WHERE c2_.name = ?))', ); } @@ -683,7 +673,7 @@ public function testSupportsMemberOfExpressionOneToMany(): void self::assertEquals( 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id AND c1_.phonenumber = ?)', - $q->getSql() + $q->getSql(), ); } @@ -698,7 +688,7 @@ public function testSupportsMemberOfExpressionManyToMany(): void self::assertEquals( 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (?))', - $q->getSql() + $q->getSql(), ); } @@ -714,7 +704,7 @@ public function testSupportsMemberOfExpressionManyToManyParameterArray(): void self::assertEquals( 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (?))', - $q->getSql() + $q->getSql(), ); } @@ -724,11 +714,11 @@ public function testSupportsMemberOfExpressionSelfReferencing(): void // Tough one: Many-many self-referencing ("friends") with class table inheritance $q = $this->entityManager->createQuery('SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p WHERE :param MEMBER OF p.friends'); $person = new CompanyPerson(); - $this->entityManager->getClassMetadata(get_class($person))->setIdentifierValues($person, ['id' => 101]); + $this->entityManager->getClassMetadata($person::class)->setIdentifierValues($person, ['id' => 101]); $q->setParameter('param', $person); self::assertEquals( 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ WHERE c3_.person_id = c0_.id AND c3_.friend_id IN (?))', - $q->getSql() + $q->getSql(), ); } @@ -739,7 +729,7 @@ public function testSupportsMemberOfWithSingleValuedAssociation(): void self::assertEquals( 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (c0_.email_id))', - $q->getSql() + $q->getSql(), ); } @@ -750,7 +740,7 @@ public function testSupportsMemberOfWithIdentificationVariable(): void self::assertEquals( 'SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id AND c1_.group_id IN (c0_.id))', - $q->getSql() + $q->getSql(), ); } @@ -783,7 +773,7 @@ public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition(): v FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp WHERE spouseEmp = emp.spouse)', // SQL - 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)' + 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)', ); } @@ -798,7 +788,7 @@ public function testExistsExpressionWithSimpleSelectReturningScalar(): void FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp WHERE spouseEmp = emp.spouse)', // SQL - 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT 1 AS sclr_3 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)' + 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT 1 AS sclr_3 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)', ); } @@ -823,8 +813,8 @@ public function testLimitAndOffsetFromQueryClass(): void $q->getSql(), self::logicalOr( self::identicalTo('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10'), - self::identicalTo('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10 OFFSET 0') - ) + self::identicalTo('SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LIMIT 10 OFFSET 0'), + ), ); } @@ -832,7 +822,7 @@ public function testSizeFunction(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1', ); } @@ -840,7 +830,7 @@ public function testSizeFunctionSupportsManyToMany(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.groups) > 1', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id) > 1' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id) > 1', ); } @@ -848,11 +838,11 @@ public function testEmptyCollectionComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS NOT EMPTY', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0', ); } @@ -860,7 +850,7 @@ public function testNestedExpressions(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.id > 10 and u.id < 42 and ((u.id * 2) > 5)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)', ); } @@ -868,7 +858,7 @@ public function testNestedExpressions2(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where (u.id > 10) and (u.id < 42 and ((u.id * 2) > 5)) or u.id <> 42', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42', ); } @@ -876,7 +866,7 @@ public function testNestedExpressions3(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where (u.id > 10) and (u.id between 1 and 10 or u.id in (1, 2, 3, 4, 5))', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id BETWEEN 1 AND 10 OR c0_.id IN (1, 2, 3, 4, 5))' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id BETWEEN 1 AND 10 OR c0_.id IN (1, 2, 3, 4, 5))', ); } @@ -884,7 +874,7 @@ public function testOrderByCollectionAssociationSize(): void { $this->assertSqlGeneration( 'select u, size(u.articles) as numArticles from Doctrine\Tests\Models\CMS\CmsUser u order by numArticles', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT COUNT(*) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ ORDER BY sclr_4 ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT COUNT(*) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ ORDER BY sclr_4 ASC', ); } @@ -892,58 +882,50 @@ public function testOrderBySupportsSingleValuedPathExpressionOwningSide(): void { $this->assertSqlGeneration( 'select a from Doctrine\Tests\Models\CMS\CmsArticle a order by a.user', - 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ ORDER BY c0_.user_id ASC' + 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ ORDER BY c0_.user_id ASC', ); } public function testOrderBySupportsSingleValuedPathExpressionInverseSide(): void { - $this->expectException('\Doctrine\ORM\Query\QueryException'); + $this->expectException(QueryException::class); $q = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u order by u.address'); $q->getSQL(); } public function testBooleanLiteralInWhereOnSqlite(): void { - $oldPlat = $this->entityManager->getConnection()->getDatabasePlatform(); - $this->entityManager->getConnection()->setDatabasePlatform(new SqlitePlatform()); - + $this->entityManager = $this->createTestEntityManagerWithPlatform(new SQLitePlatform()); $this->assertSqlGeneration( 'SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true', - 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 1' + 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 1', ); $this->assertSqlGeneration( 'SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false', - 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 0' + 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanField_1 FROM boolean_model b0_ WHERE b0_.booleanField = 0', ); - - $this->entityManager->getConnection()->setDatabasePlatform($oldPlat); } public function testBooleanLiteralInWhereOnPostgres(): void { - $oldPlat = $this->entityManager->getConnection()->getDatabasePlatform(); - $this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform()); - + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); $this->assertSqlGeneration( 'SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true', - 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = true' + 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = true', ); $this->assertSqlGeneration( 'SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false', - 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = false' + 'SELECT b0_.id AS id_0, b0_.booleanField AS booleanfield_1 FROM boolean_model b0_ WHERE b0_.booleanField = false', ); - - $this->entityManager->getConnection()->setDatabasePlatform($oldPlat); } public function testSingleValuedAssociationFieldInWhere(): void { $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1', - 'SELECT c0_.phonenumber AS phonenumber_0, c0_.user_id AS user_id_1 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?' + 'SELECT c0_.phonenumber AS phonenumber_0, c0_.user_id AS user_id_1 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?', ); } @@ -951,7 +933,7 @@ public function testSingleValuedAssociationNullCheckOnOwningSide(): void { $this->assertSqlGeneration( 'SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.user IS NULL', - 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_ WHERE c0_.user_id IS NULL' + 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_ WHERE c0_.user_id IS NULL', ); } @@ -964,68 +946,66 @@ public function testSingleValuedAssociationNullCheckOnInverseSide(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.address a WHERE a.id IS NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL', ); } - /** - * @group DDC-339 - * @group DDC-1572 - */ + #[Group('DDC-339')] + #[Group('DDC-1572')] public function testStringFunctionLikeExpression(): void { $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) LIKE '%foo OR bar%'", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE '%foo OR bar%'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE '%foo OR bar%'", ); $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) LIKE :str', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE ?' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE ?', ); $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(UPPER(u.name), '_moo') LIKE :str", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) || '_moo' LIKE ?" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) || '_moo' LIKE ?", ); // DDC-1572 $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE UPPER(u.name) LIKE UPPER(:str)', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) LIKE UPPER(?)' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(c0_.name) LIKE UPPER(?)', ); $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE UPPER(LOWER(u.name)) LIKE UPPER(LOWER(:str))', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) LIKE UPPER(LOWER(?))' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) LIKE UPPER(LOWER(?))', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE u.name', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE c0_.name)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE c0_.name)', ); } - /** @group DDC-1802 */ + #[Group('DDC-1802')] public function testStringFunctionNotLikeExpression(): void { $this->assertSqlGeneration( "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) NOT LIKE '%foo OR bar%'", - "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) NOT LIKE '%foo OR bar%'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE LOWER(c0_.name) NOT LIKE '%foo OR bar%'", ); $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE UPPER(LOWER(u.name)) NOT LIKE UPPER(LOWER(:str))', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) NOT LIKE UPPER(LOWER(?))' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) NOT LIKE UPPER(LOWER(?))', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic NOT LIKE u.name', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)', ); } - /** @group DDC-338 */ + #[Group('DDC-338')] public function testOrderedCollectionFetchJoined(): void { $this->assertSqlGeneration( 'SELECT r, l FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.legs l', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.departureDate AS departureDate_2, r1_.arrivalDate AS arrivalDate_3, r1_.from_id AS from_id_4, r1_.to_id AS to_id_5 FROM RoutingRoute r0_ INNER JOIN RoutingRouteLegs r2_ ON r0_.id = r2_.route_id INNER JOIN RoutingLeg r1_ ON r1_.id = r2_.leg_id ORDER BY r1_.departureDate ASC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.departureDate AS departureDate_2, r1_.arrivalDate AS arrivalDate_3, r1_.from_id AS from_id_4, r1_.to_id AS to_id_5 FROM RoutingRoute r0_ INNER JOIN RoutingRouteLegs r2_ ON r0_.id = r2_.route_id INNER JOIN RoutingLeg r1_ ON r1_.id = r2_.leg_id ORDER BY r1_.departureDate ASC', ); } @@ -1033,95 +1013,85 @@ public function testSubselectInSelect(): void { $this->assertSqlGeneration( "SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'", - "SELECT c0_.name AS name_0, (SELECT COUNT(c1_.phonenumber) AS sclr_2 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr_1 FROM cms_users c0_ WHERE c0_.name = 'jon'" + "SELECT c0_.name AS name_0, (SELECT COUNT(c1_.phonenumber) AS sclr_2 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr_1 FROM cms_users c0_ WHERE c0_.name = 'jon'", ); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticWriteLockQueryHint(): void { - if ($this->entityManager->getConnection()->getDatabasePlatform() instanceof SqlitePlatform) { + if ($this->entityManager->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('SqLite does not support Row locking at all.'); } $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", - [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_WRITE] + [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_WRITE], ); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticReadLockQueryHintPostgreSql(): void { - $this->entityManager->getConnection()->setDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE", - [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] + [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ], ); } - /** - * @group DDC-1693 - * @group locking - */ + #[Group('DDC-1693')] + #[Group('locking')] public function testLockModeNoneQueryHint(): void { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco'", - [ORMQuery::HINT_LOCK_MODE => LockMode::NONE] + [ORMQuery::HINT_LOCK_MODE => LockMode::NONE], ); } - /** @group DDC-430 */ + #[Group('DDC-430')] public function testSupportSelectWithMoreThan10InputParameters(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1 OR u.id = ?2 OR u.id = ?3 OR u.id = ?4 OR u.id = ?5 OR u.id = ?6 OR u.id = ?7 OR u.id = ?8 OR u.id = ?9 OR u.id = ?10 OR u.id = ?11', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?', ); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticReadLockQueryHintMySql(): void { - $this->entityManager->getConnection()->setDatabasePlatform(new MySQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' LOCK IN SHARE MODE", - [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] + [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ], ); } - /** - * @group locking - * @group DDC-178 - */ + #[Group('locking')] + #[Group('DDC-178')] public function testPessimisticReadLockQueryHintOracle(): void { - $this->entityManager->getConnection()->setDatabasePlatform(new OraclePlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", "SELECT c0_.id AS ID_0, c0_.status AS STATUS_1, c0_.username AS USERNAME_2, c0_.name AS NAME_3, c0_.email_id AS EMAIL_ID_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", - [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] + [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ], ); } - /** @group DDC-431 */ + #[Group('DDC-431')] public function testSupportToCustomDQLFunctions(): void { $config = $this->entityManager->getConfiguration(); @@ -1129,36 +1099,36 @@ public function testSupportToCustomDQLFunctions(): void $this->assertSqlGeneration( 'SELECT MYABS(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p', - 'SELECT ABS(c0_.phonenumber) AS sclr_0 FROM cms_phonenumbers c0_' + 'SELECT ABS(c0_.phonenumber) AS sclr_0 FROM cms_phonenumbers c0_', ); $config->setCustomNumericFunctions([]); } - /** @group DDC-826 */ + #[Group('DDC-826')] public function testMappedSuperclassAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT f FROM Doctrine\Tests\Models\DirectoryTree\File f JOIN f.parentDirectory d WHERE f.id = ?1', - 'SELECT f0_.id AS id_0, f0_.name AS name_1, f0_.extension AS extension_2, f0_.parentDirectory_id AS parentDirectory_id_3 FROM "file" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.name AS name_1, f0_.extension AS extension_2, f0_.parentDirectory_id AS parentDirectory_id_3 FROM "file" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?', ); } - /** @group DDC-1053 */ + #[Group('DDC-1053')] public function testGroupBy(): void { $this->assertSqlGeneration( 'SELECT g.id, count(u.id) FROM Doctrine\Tests\Models\CMS\CmsGroup g JOIN g.users u GROUP BY g.id', - 'SELECT c0_.id AS id_0, count(c1_.id) AS sclr_1 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id' + 'SELECT c0_.id AS id_0, count(c1_.id) AS sclr_1 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id', ); } - /** @group DDC-1053 */ + #[Group('DDC-1053')] public function testGroupByIdentificationVariable(): void { $this->assertSqlGeneration( 'SELECT g, count(u.id) FROM Doctrine\Tests\Models\CMS\CmsGroup g JOIN g.users u GROUP BY g', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, count(c1_.id) AS sclr_2 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id, c0_.name' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, count(c1_.id) AS sclr_2 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id, c0_.name', ); } @@ -1166,7 +1136,7 @@ public function testCaseContainingNullIf(): void { $this->assertSqlGeneration( 'SELECT NULLIF(g.id, g.name) AS NullIfEqual FROM Doctrine\Tests\Models\CMS\CmsGroup g', - 'SELECT NULLIF(c0_.id, c0_.name) AS sclr_0 FROM cms_groups c0_' + 'SELECT NULLIF(c0_.id, c0_.name) AS sclr_0 FROM cms_groups c0_', ); } @@ -1174,7 +1144,7 @@ public function testCaseContainingCoalesce(): void { $this->assertSqlGeneration( "SELECT COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u", - "SELECT COALESCE(NULLIF(c0_.name, ''), c0_.username) AS sclr_0 FROM cms_users c0_" + "SELECT COALESCE(NULLIF(c0_.name, ''), c0_.username) AS sclr_0 FROM cms_users c0_", ); } @@ -1185,7 +1155,7 @@ public function testSubSelectDiscriminator(): void { $this->assertSqlGeneration( 'SELECT u.name, (SELECT COUNT(cfc.id) total FROM Doctrine\Tests\Models\Company\CompanyFixContract cfc) as cfc_count FROM Doctrine\Tests\Models\CMS\CmsUser u', - "SELECT c0_.name AS name_0, (SELECT COUNT(c1_.id) AS sclr_2 FROM company_contracts c1_ WHERE c1_.discr IN ('fix')) AS sclr_1 FROM cms_users c0_" + "SELECT c0_.name AS name_0, (SELECT COUNT(c1_.id) AS sclr_2 FROM company_contracts c1_ WHERE c1_.discr IN ('fix')) AS sclr_1 FROM cms_users c0_", ); } @@ -1197,7 +1167,7 @@ public function testIdVariableResultVariableReuse(): void $query->getSql(); $query->free(); - } catch (Exception $e) { + } catch (Exception) { $exceptionThrown = true; } @@ -1208,7 +1178,7 @@ public function testSubSelectAliasesFromOuterQuery(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_', ); } @@ -1216,7 +1186,7 @@ public function testSubSelectAliasesFromOuterQueryWithSubquery(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id AND ui.name IN (SELECT uii.name FROM Doctrine\Tests\Models\CMS\CmsUser uii)) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id AND c1_.name IN (SELECT c2_.name FROM cms_users c2_)) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id AND c1_.name IN (SELECT c2_.name FROM cms_users c2_)) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_', ); } @@ -1224,16 +1194,16 @@ public function testSubSelectAliasesFromOuterQueryReuseInWhereClause(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo WHERE bar = ?0', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ WHERE sclr_4 = ?' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ WHERE sclr_4 = ?', ); } - /** @group DDC-1298 */ + #[Group('DDC-1298')] public function testSelectForeignKeyPKWithoutFields(): void { $this->assertSqlGeneration( 'SELECT t, s, l FROM Doctrine\Tests\Models\DDC117\DDC117Link l INNER JOIN l.target t INNER JOIN l.source s', - 'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1, d1_.article_id AS article_id_2, d1_.title AS title_3, d2_.source_id AS source_id_4, d2_.target_id AS target_id_5 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id' + 'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1, d1_.article_id AS article_id_2, d1_.title AS title_3, d2_.source_id AS source_id_4, d2_.target_id AS target_id_5 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id', ); } @@ -1241,7 +1211,7 @@ public function testGeneralCaseWithSingleWhenClause(): void { $this->assertSqlGeneration( 'SELECT g.id, CASE WHEN ((g.id / 2) > 18) THEN 1 ELSE 0 END AS test FROM Doctrine\Tests\Models\CMS\CmsGroup g', - 'SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_' + 'SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_', ); } @@ -1249,7 +1219,7 @@ public function testGeneralCaseWithMultipleWhenClause(): void { $this->assertSqlGeneration( 'SELECT g.id, CASE WHEN (g.id / 2 < 10) THEN 2 WHEN ((g.id / 2) > 20) THEN 1 ELSE 0 END AS test FROM Doctrine\Tests\Models\CMS\CmsGroup g', - 'SELECT c0_.id AS id_0, CASE WHEN (c0_.id / 2 < 10) THEN 2 WHEN ((c0_.id / 2) > 20) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_' + 'SELECT c0_.id AS id_0, CASE WHEN (c0_.id / 2 < 10) THEN 2 WHEN ((c0_.id / 2) > 20) THEN 1 ELSE 0 END AS sclr_1 FROM cms_groups c0_', ); } @@ -1257,7 +1227,7 @@ public function testSimpleCaseWithSingleWhenClause(): void { $this->assertSqlGeneration( "SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id = CASE g.name WHEN 'admin' THEN 1 ELSE 2 END", - "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = CASE c0_.name WHEN 'admin' THEN 1 ELSE 2 END" + "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = CASE c0_.name WHEN 'admin' THEN 1 ELSE 2 END", ); } @@ -1265,7 +1235,7 @@ public function testSimpleCaseWithMultipleWhenClause(): void { $this->assertSqlGeneration( "SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id = (CASE g.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END)", - "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = (CASE c0_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END)" + "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id = (CASE c0_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END)", ); } @@ -1273,7 +1243,7 @@ public function testGeneralCaseWithSingleWhenClauseInSubselect(): void { $this->assertSqlGeneration( 'SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id IN (SELECT CASE WHEN ((g2.id / 2) > 18) THEN 2 ELSE 1 END FROM Doctrine\Tests\Models\CMS\CmsGroup g2)', - 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN ((c1_.id / 2) > 18) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN ((c1_.id / 2) > 18) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)', ); } @@ -1281,7 +1251,7 @@ public function testGeneralCaseWithMultipleWhenClauseInSubselect(): void { $this->assertSqlGeneration( 'SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id IN (SELECT CASE WHEN (g.id / 2 < 10) THEN 3 WHEN ((g.id / 2) > 20) THEN 2 ELSE 1 END FROM Doctrine\Tests\Models\CMS\CmsGroup g2)', - 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN (c0_.id / 2 < 10) THEN 3 WHEN ((c0_.id / 2) > 20) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE WHEN (c0_.id / 2 < 10) THEN 3 WHEN ((c0_.id / 2) > 20) THEN 2 ELSE 1 END AS sclr_2 FROM cms_groups c1_)', ); } @@ -1289,7 +1259,7 @@ public function testSimpleCaseWithSingleWhenClauseInSubselect(): void { $this->assertSqlGeneration( "SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id IN (SELECT CASE g2.name WHEN 'admin' THEN 1 ELSE 2 END FROM Doctrine\Tests\Models\CMS\CmsGroup g2)", - "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 ELSE 2 END AS sclr_2 FROM cms_groups c1_)" + "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 ELSE 2 END AS sclr_2 FROM cms_groups c1_)", ); } @@ -1297,45 +1267,45 @@ public function testSimpleCaseWithMultipleWhenClauseInSubselect(): void { $this->assertSqlGeneration( "SELECT g FROM Doctrine\Tests\Models\CMS\CmsGroup g WHERE g.id IN (SELECT CASE g2.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END FROM Doctrine\Tests\Models\CMS\CmsGroup g2)", - "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END AS sclr_2 FROM cms_groups c1_)" + "SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_groups c0_ WHERE c0_.id IN (SELECT CASE c1_.name WHEN 'admin' THEN 1 WHEN 'moderator' THEN 2 ELSE 3 END AS sclr_2 FROM cms_groups c1_)", ); } - /** @group DDC-1696 */ + #[Group('DDC-1696')] public function testSimpleCaseWithStringPrimary(): void { $this->assertSqlGeneration( "SELECT g.id, CASE WHEN ((g.id / 2) > 18) THEN 'Foo' ELSE 'Bar' END AS test FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 'Foo' ELSE 'Bar' END AS sclr_1 FROM cms_groups c0_" + "SELECT c0_.id AS id_0, CASE WHEN ((c0_.id / 2) > 18) THEN 'Foo' ELSE 'Bar' END AS sclr_1 FROM cms_groups c0_", ); } - /** @group DDC-2205 */ + #[Group('DDC-2205')] public function testCaseNegativeValuesInThenExpression(): void { $this->assertSqlGeneration( "SELECT CASE g.name WHEN 'admin' THEN - 1 ELSE - 2 END FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT CASE c0_.name WHEN 'admin' THEN -1 ELSE -2 END AS sclr_0 FROM cms_groups c0_" + "SELECT CASE c0_.name WHEN 'admin' THEN -1 ELSE -2 END AS sclr_0 FROM cms_groups c0_", ); $this->assertSqlGeneration( "SELECT CASE g.name WHEN 'admin' THEN - 2 WHEN 'guest' THEN - 1 ELSE 0 END FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT CASE c0_.name WHEN 'admin' THEN -2 WHEN 'guest' THEN -1 ELSE 0 END AS sclr_0 FROM cms_groups c0_" + "SELECT CASE c0_.name WHEN 'admin' THEN -2 WHEN 'guest' THEN -1 ELSE 0 END AS sclr_0 FROM cms_groups c0_", ); $this->assertSqlGeneration( "SELECT CASE g.name WHEN 'admin' THEN (- 1) ELSE (- 2) END FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT CASE c0_.name WHEN 'admin' THEN (-1) ELSE (-2) END AS sclr_0 FROM cms_groups c0_" + "SELECT CASE c0_.name WHEN 'admin' THEN (-1) ELSE (-2) END AS sclr_0 FROM cms_groups c0_", ); $this->assertSqlGeneration( "SELECT CASE g.name WHEN 'admin' THEN ( - :value) ELSE ( + :value) END FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT CASE c0_.name WHEN 'admin' THEN (-?) ELSE (+?) END AS sclr_0 FROM cms_groups c0_" + "SELECT CASE c0_.name WHEN 'admin' THEN (-?) ELSE (+?) END AS sclr_0 FROM cms_groups c0_", ); $this->assertSqlGeneration( "SELECT CASE g.name WHEN 'admin' THEN ( - g.id) ELSE ( + g.id) END FROM Doctrine\Tests\Models\CMS\CmsGroup g", - "SELECT CASE c0_.name WHEN 'admin' THEN (-c0_.id) ELSE (+c0_.id) END AS sclr_0 FROM cms_groups c0_" + "SELECT CASE c0_.name WHEN 'admin' THEN (-c0_.id) ELSE (+c0_.id) END AS sclr_0 FROM cms_groups c0_", ); } @@ -1343,45 +1313,45 @@ public function testIdentityFunctionWithCompositePrimaryKey(): void { $this->assertSqlGeneration( "SELECT IDENTITY(p.poi, 'long') AS long FROM Doctrine\Tests\Models\Navigation\NavPhotos p", - 'SELECT n0_.poi_long AS sclr_0 FROM navigation_photos n0_' + 'SELECT n0_.poi_long AS sclr_0 FROM navigation_photos n0_', ); $this->assertSqlGeneration( "SELECT IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\Tests\Models\Navigation\NavPhotos p", - 'SELECT n0_.poi_lat AS sclr_0 FROM navigation_photos n0_' + 'SELECT n0_.poi_lat AS sclr_0 FROM navigation_photos n0_', ); $this->assertSqlGeneration( "SELECT IDENTITY(p.poi, 'long') AS long, IDENTITY(p.poi, 'lat') AS lat FROM Doctrine\Tests\Models\Navigation\NavPhotos p", - 'SELECT n0_.poi_long AS sclr_0, n0_.poi_lat AS sclr_1 FROM navigation_photos n0_' + 'SELECT n0_.poi_long AS sclr_0, n0_.poi_lat AS sclr_1 FROM navigation_photos n0_', ); $this->assertInvalidSqlGeneration( "SELECT IDENTITY(p.poi, 'invalid') AS invalid FROM Doctrine\Tests\Models\Navigation\NavPhotos p", - QueryException::class + QueryException::class, ); } - /** @group DDC-2519 */ + #[Group('DDC-2519')] public function testPartialWithAssociationIdentifier(): void { $this->assertSqlGeneration( 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l', - 'SELECT l0_.iUserIdSource AS iUserIdSource_0, l0_.iUserIdTarget AS iUserIdTarget_1 FROM legacy_users_reference l0_' + 'SELECT l0_.iUserIdSource AS iUserIdSource_0, l0_.iUserIdTarget AS iUserIdTarget_1 FROM legacy_users_reference l0_', ); $this->assertSqlGeneration( 'SELECT PARTIAL l.{_description, _source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l', - 'SELECT l0_.description AS description_0, l0_.iUserIdSource AS iUserIdSource_1, l0_.iUserIdTarget AS iUserIdTarget_2 FROM legacy_users_reference l0_' + 'SELECT l0_.description AS description_0, l0_.iUserIdSource AS iUserIdSource_1, l0_.iUserIdTarget AS iUserIdTarget_2 FROM legacy_users_reference l0_', ); } - /** @group DDC-1339 */ + #[Group('DDC-1339')] public function testIdentityFunctionInSelectClause(): void { $this->assertSqlGeneration( 'SELECT IDENTITY(u.email) as email_id FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.email_id AS sclr_0 FROM cms_users c0_' + 'SELECT c0_.email_id AS sclr_0 FROM cms_users c0_', ); } @@ -1390,265 +1360,263 @@ public function testIdentityFunctionInJoinedSubclass(): void //relation is in the subclass (CompanyManager) we are querying $this->assertSqlGeneration( 'SELECT m, IDENTITY(m.car) as car_id FROM Doctrine\Tests\Models\Company\CompanyManager m', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c2_.car_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c2_.car_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', ); //relation is in the base class (CompanyPerson). $this->assertSqlGeneration( 'SELECT m, IDENTITY(m.spouse) as spouse_id FROM Doctrine\Tests\Models\Company\CompanyManager m', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.spouse_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.spouse_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', ); } - /** @group DDC-1339 */ + #[Group('DDC-1339')] public function testIdentityFunctionDoesNotAcceptStateField(): void { $this->assertInvalidSqlGeneration( 'SELECT IDENTITY(u.name) as name FROM Doctrine\Tests\Models\CMS\CmsUser u', - QueryException::class + QueryException::class, ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInRootClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInRootClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInChildClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInChildClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInLeafClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeJoinInLeafClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m', 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInRootClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInRootClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInChildClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInChildClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInLeafClassWithDisabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5, c0_.salesPerson_id AS salesPerson_id_6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false], ); } - /** @group DDC-1389 */ + #[Group('DDC-1389')] public function testInheritanceTypeSingleTableInLeafClassWithEnabledForcePartialLoad(): void { $this->assertSqlGeneration( 'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc', "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.hoursWorked AS hoursWorked_2, c0_.pricePerHour AS pricePerHour_3, c0_.maxPrice AS maxPrice_4, c0_.discr AS discr_5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')", - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true], ); } - /** @group DDC-1161 */ + #[Group('DDC-1161')] public function testSelfReferenceWithOneToOneDoesNotDuplicateAlias(): void { $this->assertSqlGeneration( 'SELECT p, pp FROM Doctrine\Tests\Models\Company\CompanyPerson p JOIN p.spouse pp', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c3_.id AS id_6, c3_.name AS name_7, c4_.title AS title_8, c5_.salary AS salary_9, c5_.department AS department_10, c5_.startDate AS startDate_11, c0_.discr AS discr_12, c0_.spouse_id AS spouse_id_13, c1_.car_id AS car_id_14, c3_.discr AS discr_15, c3_.spouse_id AS spouse_id_16, c4_.car_id AS car_id_17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c3_.id AS id_6, c3_.name AS name_7, c4_.title AS title_8, c5_.salary AS salary_9, c5_.department AS department_10, c5_.startDate AS startDate_11, c0_.discr AS discr_12, c0_.spouse_id AS spouse_id_13, c1_.car_id AS car_id_14, c3_.discr AS discr_15, c3_.spouse_id AS spouse_id_16, c4_.car_id AS car_id_17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id', ); } - /** @group DDC-1384 */ + #[Group('DDC-1384')] public function testAliasDoesNotExceedPlatformDefinedLength(): void { $this->assertSqlGeneration( 'SELECT m FROM ' . __NAMESPACE__ . '\\DDC1384Model m', - 'SELECT d0_.aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalkerFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo AS ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo_0 FROM DDC1384Model d0_' + 'SELECT d0_.aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalkerFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo AS ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo_0 FROM DDC1384Model d0_', ); } - /** - * @group DDC-331 - * @group DDC-1384 - */ + #[Group('DDC-331')] + #[Group('DDC-1384')] public function testIssue331(): void { $this->assertSqlGeneration( 'SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id' + 'SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id', ); } - /** @group DDC-1435 */ + #[Group('DDC-1435')] public function testForeignKeyAsPrimaryKeySubselect(): void { $this->assertSqlGeneration( 'SELECT s FROM Doctrine\Tests\Models\DDC117\DDC117Article s WHERE EXISTS (SELECT r FROM Doctrine\Tests\Models\DDC117\DDC117Reference r WHERE r.source = s)', - 'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)' + 'SELECT d0_.article_id AS article_id_0, d0_.title AS title_1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)', ); } - /** @group DDC-1474 */ + #[Group('DDC-1474')] public function testSelectWithArithmeticExpressionBeforeField(): void { $this->assertSqlGeneration( 'SELECT - e.value AS value, e.id FROM ' . __NAMESPACE__ . '\DDC1474Entity e', - 'SELECT -d0_.value AS sclr_0, d0_.id AS id_1 FROM DDC1474Entity d0_' + 'SELECT -d0_.value AS sclr_0, d0_.id AS id_1 FROM DDC1474Entity d0_', ); $this->assertSqlGeneration( 'SELECT e.id, + e.value AS value FROM ' . __NAMESPACE__ . '\DDC1474Entity e', - 'SELECT d0_.id AS id_0, +d0_.value AS sclr_1 FROM DDC1474Entity d0_' + 'SELECT d0_.id AS id_0, +d0_.value AS sclr_1 FROM DDC1474Entity d0_', ); } - /** @group DDC-1430 */ + #[Group('DDC-1430')] public function testGroupByAllFieldsWhenObjectHasForeignKeys(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id', ); $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\CMS\CmsEmployee e GROUP BY e', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id', ); } - /** @group DDC-1236 */ + #[Group('DDC-1236')] public function testGroupBySupportsResultVariable(): void { $this->assertSqlGeneration( 'SELECT u, u.status AS st FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY st', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.status AS status_4, c0_.email_id AS email_id_5 FROM cms_users c0_ GROUP BY c0_.status' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.status AS status_4, c0_.email_id AS email_id_5 FROM cms_users c0_ GROUP BY c0_.status', ); } - /** @group DDC-1236 */ + #[Group('DDC-1236')] public function testGroupBySupportsIdentificationVariable(): void { $this->assertSqlGeneration( 'SELECT u AS user FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY id_0, status_1, username_2, name_3' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY id_0, status_1, username_2, name_3', ); } - /** @group DDC-1213 */ + #[Group('DDC-1213')] public function testSupportsBitComparison(): void { $this->assertSqlGeneration( 'SELECT BIT_OR(4,2), BIT_AND(4,2), u FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT (4 | 2) AS sclr_0, (4 & 2) AS sclr_1, c0_.id AS id_2, c0_.status AS status_3, c0_.username AS username_4, c0_.name AS name_5, c0_.email_id AS email_id_6 FROM cms_users c0_' + 'SELECT (4 | 2) AS sclr_0, (4 & 2) AS sclr_1, c0_.id AS id_2, c0_.status AS status_3, c0_.username AS username_4, c0_.name AS name_5, c0_.email_id AS email_id_6 FROM cms_users c0_', ); $this->assertSqlGeneration( 'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE BIT_OR(u.id,2) > 0', - 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0' + 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0', ); $this->assertSqlGeneration( 'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE BIT_AND(u.id , 4) > 0', - 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id & 4) > 0' + 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id & 4) > 0', ); $this->assertSqlGeneration( 'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE BIT_OR(u.id , 2) > 0 OR BIT_AND(u.id , 4) > 0', - 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0 OR (c0_.id & 4) > 0' + 'SELECT (c0_.id | 2) AS sclr_0, (c0_.id & 2) AS sclr_1 FROM cms_users c0_ WHERE (c0_.id | 2) > 0 OR (c0_.id & 4) > 0', ); } - /** @group DDC-1539 */ + #[Group('DDC-1539')] public function testParenthesesOnTheLeftHandOfComparison(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where ( (u.id + u.id) * u.id ) > 100', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + c0_.id) * c0_.id) > 100' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + c0_.id) * c0_.id) > 100', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where (u.id + u.id) * u.id > 100', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id + c0_.id) * c0_.id > 100' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id + c0_.id) * c0_.id > 100', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where 100 < (u.id + u.id) * u.id ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE 100 < (c0_.id + c0_.id) * c0_.id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE 100 < (c0_.id + c0_.id) * c0_.id', ); } @@ -1656,109 +1624,109 @@ public function testSupportsParenthesisExpressionInSubSelect(): void { $this->assertSqlGeneration( 'SELECT u.id, (SELECT (1000*SUM(subU.id)/SUM(subU.id)) FROM Doctrine\Tests\Models\CMS\CmsUser subU where subU.id = u.id) AS subSelect FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.id AS id_0, (SELECT (1000 * SUM(c1_.id) / SUM(c1_.id)) FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_1 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, (SELECT (1000 * SUM(c1_.id) / SUM(c1_.id)) FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_1 FROM cms_users c0_', ); } - /** @group DDC-1557 */ + #[Group('DDC-1557')] public function testSupportsSubSqlFunction(): void { $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_)', ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE LOWER(u2.name) LIKE \'%fabio%\')', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_ WHERE LOWER(c1_.name) LIKE \'%fabio%\')' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_ WHERE LOWER(c1_.name) LIKE \'%fabio%\')', ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.email IN ( SELECT TRIM(IDENTITY(u2.email)) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT TRIM(c1_.email_id) AS sclr_5 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT TRIM(c1_.email_id) AS sclr_5 FROM cms_users c1_)', ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.email IN ( SELECT IDENTITY(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT c1_.email_id AS sclr_5 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT c1_.email_id AS sclr_5 FROM cms_users c1_)', ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) = ( SELECT SUM(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_5 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_5 FROM cms_users c1_)', ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) <= ( SELECT SUM(u2.id) + COUNT(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) <= (SELECT SUM(c1_.id) + COUNT(c1_.email_id) AS sclr_5 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) <= (SELECT SUM(c1_.id) + COUNT(c1_.email_id) AS sclr_5 FROM cms_users c1_)', ); } - /** @group DDC-1574 */ + #[Group('DDC-1574')] public function testSupportsNewOperator(): void { $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id', ); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.id + c0_.id AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.id + c0_.id AS sclr_2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id', ); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', - 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' + 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id', ); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p', - 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) + c0_.id AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id' + 'SELECT c0_.name AS sclr_0, c1_.email AS sclr_1, c2_.city AS sclr_2, COUNT(c3_.phonenumber) + c0_.id AS sclr_3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id', ); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(a.id, a.country, a.city), new Doctrine\Tests\Models\CMS\CmsAddressDTO(u.name, e.email) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name', - 'SELECT c0_.id AS sclr_0, c0_.country AS sclr_1, c0_.city AS sclr_2, c1_.name AS sclr_3, c2_.email AS sclr_4 FROM cms_users c1_ INNER JOIN cms_emails c2_ ON c1_.email_id = c2_.id INNER JOIN cms_addresses c0_ ON c1_.id = c0_.user_id ORDER BY c1_.name ASC' + 'SELECT c0_.id AS sclr_0, c0_.country AS sclr_1, c0_.city AS sclr_2, c1_.name AS sclr_3, c2_.email AS sclr_4 FROM cms_users c1_ INNER JOIN cms_emails c2_ ON c1_.email_id = c2_.id INNER JOIN cms_addresses c0_ ON c1_.id = c0_.user_id ORDER BY c1_.name ASC', ); $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(a.id, (SELECT 1 FROM Doctrine\Tests\Models\CMS\CmsUser su), a.country, a.city), new Doctrine\Tests\Models\CMS\CmsAddressDTO(u.name, e.email) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name', - 'SELECT c0_.id AS sclr_0, (SELECT 1 AS sclr_2 FROM cms_users c1_) AS sclr_1, c0_.country AS sclr_3, c0_.city AS sclr_4, c2_.name AS sclr_5, c3_.email AS sclr_6 FROM cms_users c2_ INNER JOIN cms_emails c3_ ON c2_.email_id = c3_.id INNER JOIN cms_addresses c0_ ON c2_.id = c0_.user_id ORDER BY c2_.name ASC' + 'SELECT c0_.id AS sclr_0, (SELECT 1 AS sclr_2 FROM cms_users c1_) AS sclr_1, c0_.country AS sclr_3, c0_.city AS sclr_4, c2_.name AS sclr_5, c3_.email AS sclr_6 FROM cms_users c2_ INNER JOIN cms_emails c3_ ON c2_.email_id = c3_.id INNER JOIN cms_addresses c0_ ON c2_.id = c0_.user_id ORDER BY c2_.name ASC', ); } - /** @group DDC-2234 */ + #[Group('DDC-2234')] public function testWhereFunctionIsNullComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IS NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NULL', ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NULL AND IDENTITY(u.email) IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NULL AND c0_.email_id IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NULL AND c0_.email_id IS NOT NULL", ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NOT NULL', ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NOT NULL", ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(u.name, u.id) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.name, c0_.id) IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.name, c0_.id) IS NOT NULL', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(u.id, IDENTITY(u.email)) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.id, c0_.email_id) IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.id, c0_.email_id) IS NOT NULL', ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(IDENTITY(u.email), NULLIF(u.name, 'FabioBatSilva')) IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.email_id, NULLIF(c0_.name, 'FabioBatSilva')) IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.email_id, NULLIF(c0_.name, 'FabioBatSilva')) IS NOT NULL", ); } @@ -1772,7 +1740,7 @@ public function testCustomTypeValueSql(): void $this->assertSqlGeneration( 'SELECT p.customInteger FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p WHERE p.id = 1', - 'SELECT -(c0_.customInteger) AS customInteger_0 FROM customtype_parents c0_ WHERE c0_.id = 1' + 'SELECT -(c0_.customInteger) AS customInteger_0 FROM customtype_parents c0_ WHERE c0_.id = 1', ); } @@ -1786,7 +1754,7 @@ public function testCustomTypeValueSqlIgnoresIdentifierColumn(): void $this->assertSqlGeneration( 'SELECT p.id FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p WHERE p.id = 1', - 'SELECT c0_.id AS id_0 FROM customtype_parents c0_ WHERE c0_.id = 1' + 'SELECT c0_.id AS id_0 FROM customtype_parents c0_ WHERE c0_.id = 1', ); } @@ -1800,7 +1768,7 @@ public function testCustomTypeValueSqlForAllFields(): void $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p', - 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_' + 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_', ); } @@ -1814,33 +1782,33 @@ public function testCustomTypeValueSqlForPartialObject(): void $this->assertSqlGeneration( 'SELECT partial p.{id, customInteger} FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p', - 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_' + 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_', ); } - /** @group DDC-1529 */ + #[Group('DDC-1529')] public function testMultipleFromAndInheritanceCondition(): void { $this->assertSqlGeneration( 'SELECT fix, flex FROM Doctrine\Tests\Models\Company\CompanyFixContract fix, Doctrine\Tests\Models\Company\CompanyFlexContract flex', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c1_.id AS id_3, c1_.completed AS completed_4, c1_.hoursWorked AS hoursWorked_5, c1_.pricePerHour AS pricePerHour_6, c1_.maxPrice AS maxPrice_7, c0_.discr AS discr_8, c0_.salesPerson_id AS salesPerson_id_9, c1_.discr AS discr_10, c1_.salesPerson_id AS salesPerson_id_11 FROM company_contracts c0_, company_contracts c1_ WHERE (c0_.discr IN ('fix') AND c1_.discr IN ('flexible', 'flexultra'))" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c1_.id AS id_3, c1_.completed AS completed_4, c1_.hoursWorked AS hoursWorked_5, c1_.pricePerHour AS pricePerHour_6, c1_.maxPrice AS maxPrice_7, c0_.discr AS discr_8, c0_.salesPerson_id AS salesPerson_id_9, c1_.discr AS discr_10, c1_.salesPerson_id AS salesPerson_id_11 FROM company_contracts c0_, company_contracts c1_ WHERE (c0_.discr IN ('fix') AND c1_.discr IN ('flexible', 'flexultra'))", ); } - /** @group DDC-775 */ + #[Group('DDC-775')] public function testOrderByClauseSupportsSimpleArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.id + 1 ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.id + 1 ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.id + 1 ASC', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY ( ( (u.id + 1) * (u.id - 1) ) / 2)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY (((c0_.id + 1) * (c0_.id - 1)) / 2) ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY (((c0_.id + 1) * (c0_.id - 1)) / 2) ASC', ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY ((u.id + 5000) * u.id + 3) ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY ((c0_.id + 5000) * c0_.id + 3) ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY ((c0_.id + 5000) * c0_.id + 3) ASC', ); } @@ -1848,248 +1816,244 @@ public function testOrderByClauseSupportsFunction(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY CONCAT(u.username, u.name) ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.username || c0_.name ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.username || c0_.name ASC', ); } - /** @group DDC-1719 */ + #[Group('DDC-1719')] public function testStripNonAlphanumericCharactersFromAlias(): void { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Generic\NonAlphaColumnsEntity e', - 'SELECT n0_."simple-entity-id" AS simpleentityid_0, n0_."simple-entity-value" AS simpleentityvalue_1 FROM "not-a-simple-entity" n0_' + 'SELECT n0_."simple-entity-id" AS simpleentityid_0, n0_."simple-entity-value" AS simpleentityvalue_1 FROM "not-a-simple-entity" n0_', ); $this->assertSqlGeneration( 'SELECT e.value FROM Doctrine\Tests\Models\Generic\NonAlphaColumnsEntity e ORDER BY e.value', - 'SELECT n0_."simple-entity-value" AS simpleentityvalue_0 FROM "not-a-simple-entity" n0_ ORDER BY n0_."simple-entity-value" ASC' + 'SELECT n0_."simple-entity-value" AS simpleentityvalue_0 FROM "not-a-simple-entity" n0_ ORDER BY n0_."simple-entity-value" ASC', ); $this->assertSqlGeneration( 'SELECT TRIM(e.value) FROM Doctrine\Tests\Models\Generic\NonAlphaColumnsEntity e ORDER BY e.value', - 'SELECT TRIM(n0_."simple-entity-value") AS sclr_0 FROM "not-a-simple-entity" n0_ ORDER BY n0_."simple-entity-value" ASC' + 'SELECT TRIM(n0_."simple-entity-value") AS sclr_0 FROM "not-a-simple-entity" n0_ ORDER BY n0_."simple-entity-value" ASC', ); } - /** @group DDC-2435 */ + #[Group('DDC-2435')] public function testColumnNameWithNumbersAndNonAlphanumericCharacters(): void { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Quote\NumericEntity e', - 'SELECT t0_."1:1" AS 11_0, t0_."2:2" AS 22_1 FROM table t0_' + 'SELECT t0_."1:1" AS 11_0, t0_."2:2" AS 22_1 FROM table t0_', ); $this->assertSqlGeneration( 'SELECT e.value FROM Doctrine\Tests\Models\Quote\NumericEntity e', - 'SELECT t0_."2:2" AS 22_0 FROM table t0_' + 'SELECT t0_."2:2" AS 22_0 FROM table t0_', ); $this->assertSqlGeneration( 'SELECT TRIM(e.value) FROM Doctrine\Tests\Models\Quote\NumericEntity e', - 'SELECT TRIM(t0_."2:2") AS sclr_0 FROM table t0_' + 'SELECT TRIM(t0_."2:2") AS sclr_0 FROM table t0_', ); } - /** @group DDC-1845 */ + #[Group('DDC-1845')] public function testQuotedTableDeclaration(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Quote\User u', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1 FROM "quote-user" q0_' + 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1 FROM "quote-user" q0_', ); } - /** @group DDC-1845 */ + #[Group('DDC-1845')] public function testQuotedWalkJoinVariableDeclaration(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\Quote\User u JOIN u.address a', <<<'SQL' SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."address-id" AS addressid_2, q1_."address-zip" AS addresszip_3, q1_.type AS type_4, q1_."user-id" AS userid_5, q1_."city-id" AS cityid_6 FROM "quote-user" q0_ INNER JOIN "quote-address" q1_ ON q0_."user-id" = q1_."user-id" AND q1_.type IN ('simple', 'full') -SQL +SQL, ); $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\Quote\User u JOIN u.phones p', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."phone-number" AS phonenumber_2, q1_."user-id" AS userid_3 FROM "quote-user" q0_ INNER JOIN "quote-phone" q1_ ON q0_."user-id" = q1_."user-id"' + 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."phone-number" AS phonenumber_2, q1_."user-id" AS userid_3 FROM "quote-user" q0_ INNER JOIN "quote-phone" q1_ ON q0_."user-id" = q1_."user-id"', ); $this->assertSqlGeneration( 'SELECT u, g FROM Doctrine\Tests\Models\Quote\User u JOIN u.groups g', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q1_."parent-id" AS parentid_4 FROM "quote-user" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."user-id" = q2_."user-id" INNER JOIN "quote-group" q1_ ON q1_."group-id" = q2_."group-id"' + 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q1_."parent-id" AS parentid_4 FROM "quote-user" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."user-id" = q2_."user-id" INNER JOIN "quote-group" q1_ ON q1_."group-id" = q2_."group-id"', ); $this->assertSqlGeneration( 'SELECT a, u FROM Doctrine\Tests\Models\Quote\Address a JOIN a.user u', - 'SELECT q0_."address-id" AS addressid_0, q0_."address-zip" AS addresszip_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_.type AS type_4, q0_."user-id" AS userid_5, q0_."city-id" AS cityid_6 FROM "quote-address" q0_ INNER JOIN "quote-user" q1_ ON q0_."user-id" = q1_."user-id" WHERE q0_.type IN (\'simple\', \'full\')' + 'SELECT q0_."address-id" AS addressid_0, q0_."address-zip" AS addresszip_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_.type AS type_4, q0_."user-id" AS userid_5, q0_."city-id" AS cityid_6 FROM "quote-address" q0_ INNER JOIN "quote-user" q1_ ON q0_."user-id" = q1_."user-id" WHERE q0_.type IN (\'simple\', \'full\')', ); $this->assertSqlGeneration( 'SELECT g, u FROM Doctrine\Tests\Models\Quote\Group g JOIN g.users u', - 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_."parent-id" AS parentid_4 FROM "quote-group" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."group-id" = q2_."group-id" INNER JOIN "quote-user" q1_ ON q1_."user-id" = q2_."user-id"' + 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_."parent-id" AS parentid_4 FROM "quote-group" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."group-id" = q2_."group-id" INNER JOIN "quote-user" q1_ ON q1_."user-id" = q2_."user-id"', ); $this->assertSqlGeneration( 'SELECT g, p FROM Doctrine\Tests\Models\Quote\Group g JOIN g.parent p', - 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q0_."parent-id" AS parentid_4, q1_."parent-id" AS parentid_5 FROM "quote-group" q0_ INNER JOIN "quote-group" q1_ ON q0_."parent-id" = q1_."group-id"' + 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q0_."parent-id" AS parentid_4, q1_."parent-id" AS parentid_5 FROM "quote-group" q0_ INNER JOIN "quote-group" q1_ ON q0_."parent-id" = q1_."group-id"', ); } - /** @group DDC-2208 */ + #[Group('DDC-2208')] public function testCaseThenParameterArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE 0 END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id', ); $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE e.salary + 0 END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE c0_.salary + 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE c0_.salary + 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id', ); $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN (e.salary - :value) WHEN e.salary >= :value THEN (:value - e.salary) ELSE (e.salary + :value) END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN (c0_.salary - ?) WHEN c0_.salary >= ? THEN (? - c0_.salary) ELSE (c0_.salary + ?) END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN (c0_.salary - ?) WHEN c0_.salary >= ? THEN (? - c0_.salary) ELSE (c0_.salary + ?) END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id', ); } - /** @group DDC-2268 */ + #[Group('DDC-2268')] public function testCaseThenFunction(): void { $this->assertSqlGeneration( 'SELECT CASE WHEN LENGTH(u.name) <> 0 THEN CONCAT(u.id, u.name) ELSE u.id END AS name FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT CASE WHEN LENGTH(c0_.name) <> 0 THEN c0_.id || c0_.name ELSE c0_.id END AS sclr_0 FROM cms_users c0_' + 'SELECT CASE WHEN LENGTH(c0_.name) <> 0 THEN c0_.id || c0_.name ELSE c0_.id END AS sclr_0 FROM cms_users c0_', ); $this->assertSqlGeneration( 'SELECT CASE WHEN LENGTH(u.name) <> LENGTH(TRIM(u.name)) THEN TRIM(u.name) ELSE u.name END AS name FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT CASE WHEN LENGTH(c0_.name) <> LENGTH(TRIM(c0_.name)) THEN TRIM(c0_.name) ELSE c0_.name END AS sclr_0 FROM cms_users c0_' + 'SELECT CASE WHEN LENGTH(c0_.name) <> LENGTH(TRIM(c0_.name)) THEN TRIM(c0_.name) ELSE c0_.name END AS sclr_0 FROM cms_users c0_', ); $this->assertSqlGeneration( 'SELECT CASE WHEN LENGTH(u.name) > :value THEN SUBSTRING(u.name, 0, :value) ELSE TRIM(u.name) END AS name FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT CASE WHEN LENGTH(c0_.name) > ? THEN SUBSTRING(c0_.name FROM 0 FOR ?) ELSE TRIM(c0_.name) END AS sclr_0 FROM cms_users c0_' + 'SELECT CASE WHEN LENGTH(c0_.name) > ? THEN SUBSTRING(c0_.name FROM 0 FOR ?) ELSE TRIM(c0_.name) END AS sclr_0 FROM cms_users c0_', ); } - /** @group DDC-2268 */ + #[Group('DDC-2268')] public function testSupportsMoreThanTwoParametersInConcatFunction(): void { - $connMock = $this->entityManager->getConnection(); - $orgPlatform = $connMock->getDatabasePlatform(); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - $connMock->setDatabasePlatform(new MySQLPlatform()); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1", - "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, c0_.status, 's') = ?" + "SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE CONCAT(c0_.name, c0_.status, 's') = ?", ); $this->assertSqlGeneration( 'SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', - 'SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?' + 'SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?', ); - - $connMock->setDatabasePlatform($orgPlatform); } - /** @group DDC-2188 */ + #[Group('DDC-2188')] public function testArithmeticPriority(): void { $this->assertSqlGeneration( 'SELECT 100/(2*2) FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT 100 / (2 * 2) AS sclr_0 FROM cms_users c0_' + 'SELECT 100 / (2 * 2) AS sclr_0 FROM cms_users c0_', ); $this->assertSqlGeneration( 'SELECT (u.id / (u.id * 2)) FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_' + 'SELECT (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_', ); $this->assertSqlGeneration( 'SELECT 100/(2*2) + (u.id / (u.id * 2)) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id / (u.id * 2)) > 0', - 'SELECT 100 / (2 * 2) + (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_ WHERE (c0_.id / (c0_.id * 2)) > 0' + 'SELECT 100 / (2 * 2) + (c0_.id / (c0_.id * 2)) AS sclr_0 FROM cms_users c0_ WHERE (c0_.id / (c0_.id * 2)) > 0', ); } - /** @group DDC-2475 */ + #[Group('DDC-2475')] public function testOrderByClauseShouldReplaceOrderByRelationMapping(): void { $this->assertSqlGeneration( 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC', ); $this->assertSqlGeneration( 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b ORDER BY b.passengerName DESC', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC', ); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportIsNullExpression(): void { $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING c0_.username IS NULL' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING c0_.username IS NULL', ); $this->assertSqlGeneration( 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING MAX(u.name) IS NULL', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING MAX(c0_.name) IS NULL' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING MAX(c0_.name) IS NULL', ); } - /** @group DDC-2506 */ + #[Group('DDC-2506')] public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable(): void { $this->assertSqlGeneration( 'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1', - 'SELECT c0_.id AS id_0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)' + 'SELECT c0_.id AS id_0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)', ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceLeftJoinWithCondition(): void { // Regression test for the bug $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')", ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceLeftJoinWithConditionAndWhere(): void { // Ensure other WHERE predicates are passed through to the main WHERE clause $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id WHERE e.salary > 1000', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000", ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceInnerJoinWithCondition(): void { // Test inner joins too $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e INNER JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')", ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceLeftJoinNonAssociationWithConditionAndWhere(): void { // Test that the discriminator IN() predicate is still added into // the where clause when not joining onto that table $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c LEFT JOIN Doctrine\Tests\Models\Company\CompanyEmployee e WITH e.id = c.salesPerson WHERE c.completed = true', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ LEFT JOIN (company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id) ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ LEFT JOIN (company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id) ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')", ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceJoinCreatesOnCondition(): void { // Test that the discriminator IN() predicate is still added @@ -2097,11 +2061,11 @@ public function testSingleTableInheritanceJoinCreatesOnCondition(): void // via a join association $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s WHERE c.completed = true', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')", ); } - /** @group DDC-2235 */ + #[Group('DDC-2235')] public function testSingleTableInheritanceCreatesOnConditionAndWhere(): void { // Test that when joining onto an entity using single table inheritance via @@ -2111,57 +2075,56 @@ public function testSingleTableInheritanceCreatesOnConditionAndWhere(): void 'SELECT e, COUNT(c) FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN e.contracts c WHERE e.department = :department', "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, COUNT(c3_.id) AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN company_contract_employees c4_ ON c1_.id = c4_.employee_id INNER JOIN company_contracts c3_ ON c3_.id = c4_.contract_id AND c3_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?", [], - ['department' => 'foobar'] + ['department' => 'foobar'], ); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportResultVariableInExpression(): void { $this->assertSqlGeneration( 'SELECT u.name AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING foo IN (?1)', - 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 IN (?)' + 'SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 IN (?)', ); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportResultVariableLikeExpression(): void { $this->assertSqlGeneration( "SELECT u.name AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING foo LIKE '3'", - "SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 LIKE '3'" + "SELECT c0_.name AS name_0 FROM cms_users c0_ HAVING name_0 LIKE '3'", ); } - /** @group DDC-3085 */ + #[Group('DDC-3085')] public function testHavingSupportResultVariableNullComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u AS user, SUM(a.id) AS score FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN Doctrine\Tests\Models\CMS\CmsAddress a WITH a.user = u GROUP BY u HAVING score IS NOT NULL AND score >= 5', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, SUM(c1_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON (c1_.user_id = c0_.id) GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id HAVING sclr_4 IS NOT NULL AND sclr_4 >= 5' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, SUM(c1_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON (c1_.user_id = c0_.id) GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id HAVING sclr_4 IS NOT NULL AND sclr_4 >= 5', ); } - /** @group DDC-1858 */ + #[Group('DDC-1858')] public function testHavingSupportResultVariableInAggregateFunction(): void { $this->assertSqlGeneration( 'SELECT COUNT(u.name) AS countName FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING countName IS NULL', - 'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING sclr_0 IS NULL' + 'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING sclr_0 IS NULL', ); } /** * GitHub issue #4764: https://github.com/doctrine/orm/issues/4764 - * - * @group DDC-3907 - * @dataProvider mathematicOperatorsProvider */ + #[DataProvider('mathematicOperatorsProvider')] + #[Group('DDC-3907')] public function testHavingRegressionUsingVariableWithMathOperatorsExpression($operator): void { $this->assertSqlGeneration( 'SELECT COUNT(u.name) AS countName FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING 1 ' . $operator . ' countName > 0', - 'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING 1 ' . $operator . ' sclr_0 > 0' + 'SELECT COUNT(c0_.name) AS sclr_0 FROM cms_users c0_ HAVING 1 ' . $operator . ' sclr_0 > 0', ); } @@ -2194,39 +2157,30 @@ public function parse(Parser $parser): void $parser->match(TokenType::T_CLOSE_PARENTHESIS); } } -/** @Entity */ +#[Entity] class DDC1384Model { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalkerFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo; } -/** @Entity */ +#[Entity] class DDC1474Entity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue() - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] protected $id; - /** - * @var float - * @Column(type="float") - */ - private $value; - - public function __construct(string $float) - { - $this->value = $float; + public function __construct( + #[Column(type: 'float')] + private string $value, + ) { } public function getId(): int diff --git a/tests/Tests/ORM/Query/SqlExpressionVisitorTest.php b/tests/Tests/ORM/Query/SqlExpressionVisitorTest.php index 680d8ca1ccd..79bb44f1910 100644 --- a/tests/Tests/ORM/Query/SqlExpressionVisitorTest.php +++ b/tests/Tests/ORM/Query/SqlExpressionVisitorTest.php @@ -11,17 +11,11 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use function method_exists; - class SqlExpressionVisitorTest extends TestCase { - /** @var SqlExpressionVisitor */ - private $visitor; - - /** @var BasicEntityPersister&MockObject */ - private $persister; - /** @var ClassMetadata */ - private $classMetadata; + private SqlExpressionVisitor $visitor; + private BasicEntityPersister&MockObject $persister; + private ClassMetadata $classMetadata; protected function setUp(): void { @@ -32,10 +26,6 @@ protected function setUp(): void public function testWalkNotCompositeExpression(): void { - if (! method_exists(CriteriaBuilder::class, 'not')) { - self::markTestSkipped('doctrine/collections in version ^2.1 is required for this test to run.'); - } - $cb = new CriteriaBuilder(); $this->persister @@ -45,8 +35,8 @@ public function testWalkNotCompositeExpression(): void $expr = $this->visitor->walkCompositeExpression( $cb->not( - $cb->eq('foo', 1) - ) + $cb->eq('foo', 1), + ), ); self::assertEquals('NOT (dummy expression)', $expr); diff --git a/tests/Tests/ORM/Query/SqlWalkerTest.php b/tests/Tests/ORM/Query/SqlWalkerTest.php index c760bf23b8b..87daa3eba2e 100644 --- a/tests/Tests/ORM/Query/SqlWalkerTest.php +++ b/tests/Tests/ORM/Query/SqlWalkerTest.php @@ -8,34 +8,34 @@ use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\SqlWalker; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; /** * Tests for {@see \Doctrine\ORM\Query\SqlWalker} - * - * @covers \Doctrine\ORM\Query\SqlWalker */ +#[CoversClass(SqlWalker::class)] class SqlWalkerTest extends OrmTestCase { - /** @var SqlWalker */ - private $sqlWalker; + private SqlWalker $sqlWalker; protected function setUp(): void { $this->sqlWalker = new SqlWalker(new Query($this->getTestEntityManager()), new ParserResult(), []); } - /** @dataProvider getColumnNamesAndSqlAliases */ + #[DataProvider('getColumnNamesAndSqlAliases')] public function testGetSQLTableAlias($tableName, $expectedAlias): void { self::assertSame($expectedAlias, $this->sqlWalker->getSQLTableAlias($tableName)); } - /** @dataProvider getColumnNamesAndSqlAliases */ + #[DataProvider('getColumnNamesAndSqlAliases')] public function testGetSQLTableAliasIsSameForMultipleCalls($tableName): void { self::assertSame( $this->sqlWalker->getSQLTableAlias($tableName), - $this->sqlWalker->getSQLTableAlias($tableName) + $this->sqlWalker->getSQLTableAlias($tableName), ); } diff --git a/tests/Tests/ORM/Query/TreeWalkerAdapterTest.php b/tests/Tests/ORM/Query/TreeWalkerAdapterTest.php deleted file mode 100644 index 4613de998af..00000000000 --- a/tests/Tests/ORM/Query/TreeWalkerAdapterTest.php +++ /dev/null @@ -1,65 +0,0 @@ -createMock(AbstractQuery::class), - $this->createMock(ParserResult::class), - [] - ) extends TreeWalkerAdapter{ - }; - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9551'); - $walker->setQueryComponent('foo', [ - 'metadata' => new ClassMetadata(stdClass::class), - 'parent' => null, - 'relation' => null, - 'map' => null, - 'nestingLevel' => 0, - 'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0], - ]); - } - - public function testSetQueryComponent(): void - { - $walker = new class ( - $this->createMock(AbstractQuery::class), - $this->createMock(ParserResult::class), - [] - ) extends TreeWalkerAdapter{ - public function doSetQueryComponent(): void - { - $this->setQueryComponent('foo', [ - 'metadata' => new ClassMetadata(stdClass::class), - 'parent' => null, - 'relation' => null, - 'map' => null, - 'nestingLevel' => 0, - 'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0], - ]); - } - }; - - $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9551'); - $walker->doSetQueryComponent(); - - self::assertArrayHasKey('foo', $walker->getQueryComponents()); - } -} diff --git a/tests/Tests/ORM/Query/UpdateSqlGenerationTest.php b/tests/Tests/ORM/Query/UpdateSqlGenerationTest.php index 55aada305b3..88b3e0b4bb9 100644 --- a/tests/Tests/ORM/Query/UpdateSqlGenerationTest.php +++ b/tests/Tests/ORM/Query/UpdateSqlGenerationTest.php @@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\Tests\DbalTypes\NegativeToPositiveType; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; /** * Test case for testing the saving and referencing of query identifiers. @@ -20,8 +21,7 @@ */ class UpdateSqlGenerationTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $entityManager; + private EntityManagerInterface $entityManager; protected function setUp(): void { @@ -47,7 +47,7 @@ public function testSupportsQueriesWithoutWhere(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1', - 'UPDATE cms_users SET name = ?' + 'UPDATE cms_users SET name = ?', ); } @@ -55,7 +55,7 @@ public function testSupportsMultipleFieldsWithoutWhere(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1, u.username = ?2', - 'UPDATE cms_users SET name = ?, username = ?' + 'UPDATE cms_users SET name = ?, username = ?', ); } @@ -63,7 +63,7 @@ public function testSupportsWhereClauses(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.id = ?2', - 'UPDATE cms_users SET name = ? WHERE id = ?' + 'UPDATE cms_users SET name = ? WHERE id = ?', ); } @@ -71,7 +71,7 @@ public function testSupportsWhereClausesOnTheUpdatedField(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.name = ?2', - 'UPDATE cms_users SET name = ? WHERE name = ?' + 'UPDATE cms_users SET name = ? WHERE name = ?', ); } @@ -79,7 +79,7 @@ public function testSupportsMultipleWhereClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.name = ?2 AND u.status = ?3', - 'UPDATE cms_users SET name = ? WHERE name = ? AND status = ?' + 'UPDATE cms_users SET name = ? WHERE name = ? AND status = ?', ); } @@ -87,7 +87,7 @@ public function testSupportsInClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.id IN (1, 3, 4)', - 'UPDATE cms_users SET name = ? WHERE id IN (1, 3, 4)' + 'UPDATE cms_users SET name = ? WHERE id IN (1, 3, 4)', ); } @@ -95,7 +95,7 @@ public function testSupportsParametrizedInClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.id IN (?2, ?3, ?4)', - 'UPDATE cms_users SET name = ? WHERE id IN (?, ?, ?)' + 'UPDATE cms_users SET name = ? WHERE id IN (?, ?, ?)', ); } @@ -103,7 +103,7 @@ public function testSupportsNotInClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.name = ?1 WHERE u.id NOT IN (1, 3, 4)', - 'UPDATE cms_users SET name = ? WHERE id NOT IN (1, 3, 4)' + 'UPDATE cms_users SET name = ? WHERE id NOT IN (1, 3, 4)', ); } @@ -111,7 +111,7 @@ public function testSupportsGreaterThanClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = ?1 WHERE u.id > ?2', - 'UPDATE cms_users SET status = ? WHERE id > ?' + 'UPDATE cms_users SET status = ? WHERE id > ?', ); } @@ -119,7 +119,7 @@ public function testSupportsGreaterThanOrEqualToClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = ?1 WHERE u.id >= ?2', - 'UPDATE cms_users SET status = ? WHERE id >= ?' + 'UPDATE cms_users SET status = ? WHERE id >= ?', ); } @@ -127,7 +127,7 @@ public function testSupportsLessThanClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = ?1 WHERE u.id < ?2', - 'UPDATE cms_users SET status = ? WHERE id < ?' + 'UPDATE cms_users SET status = ? WHERE id < ?', ); } @@ -135,7 +135,7 @@ public function testSupportsLessThanOrEqualToClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = ?1 WHERE u.id <= ?2', - 'UPDATE cms_users SET status = ? WHERE id <= ?' + 'UPDATE cms_users SET status = ? WHERE id <= ?', ); } @@ -143,7 +143,7 @@ public function testSupportsBetweenClause(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = ?1 WHERE u.id BETWEEN :from AND :to', - 'UPDATE cms_users SET status = ? WHERE id BETWEEN ? AND ?' + 'UPDATE cms_users SET status = ? WHERE id BETWEEN ? AND ?', ); } @@ -151,7 +151,7 @@ public function testSingleValuedAssociationFieldInWhere(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CMS\CmsPhonenumber p SET p.phonenumber = 1234 WHERE p.user = ?1', - 'UPDATE cms_phonenumbers SET phonenumber = 1234 WHERE user_id = ?' + 'UPDATE cms_phonenumbers SET phonenumber = 1234 WHERE user_id = ?', ); } @@ -159,16 +159,16 @@ public function testSingleValuedAssociationFieldInSetClause(): void { $this->assertSqlGeneration( 'update Doctrine\Tests\Models\CMS\CmsComment c set c.article = null where c.article=?1', - 'UPDATE cms_comments SET article_id = NULL WHERE article_id = ?' + 'UPDATE cms_comments SET article_id = NULL WHERE article_id = ?', ); } - /** @group DDC-980 */ + #[Group('DDC-980')] public function testSubselectTableAliasReferencing(): void { $this->assertSqlGeneration( "UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = 'inactive' WHERE SIZE(u.groups) = 10", - "UPDATE cms_users SET status = 'inactive' WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10" + "UPDATE cms_users SET status = 'inactive' WHERE (SELECT COUNT(*) FROM cms_users_groups c0_ WHERE c0_.user_id = cms_users.id) = 10", ); } @@ -176,7 +176,7 @@ public function testCustomTypeValueSqlCompletelyIgnoredInUpdateStatements(): voi { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\CustomType\CustomTypeParent p SET p.customInteger = 1 WHERE p.id = 1', - 'UPDATE customtype_parents SET customInteger = 1 WHERE id = 1' + 'UPDATE customtype_parents SET customInteger = 1 WHERE id = 1', ); } @@ -184,7 +184,7 @@ public function testUpdateWithSubselectAsNewValue(): void { $this->assertSqlGeneration( 'UPDATE Doctrine\Tests\Models\Company\CompanyFixContract fc SET fc.fixPrice = (SELECT ce2.salary FROM Doctrine\Tests\Models\Company\CompanyEmployee ce2 WHERE ce2.id = 2) WHERE fc.id = 1', - "UPDATE company_contracts SET fixPrice = (SELECT c0_.salary FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id WHERE c1_.id = 2) WHERE (id = 1) AND discr IN ('fix')" + "UPDATE company_contracts SET fixPrice = (SELECT c0_.salary FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id WHERE c1_.id = 2) WHERE (id = 1) AND discr IN ('fix')", ); } } diff --git a/tests/Tests/ORM/QueryBuilderTest.php b/tests/Tests/ORM/QueryBuilderTest.php index f8ca83c9e9a..fd610bced44 100644 --- a/tests/Tests/ORM/QueryBuilderTest.php +++ b/tests/Tests/ORM/QueryBuilderTest.php @@ -4,10 +4,11 @@ namespace Doctrine\Tests\ORM; +use BadMethodCallException; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Order; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Cache; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Expr\Join; @@ -21,10 +22,10 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmTestCase; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\Group; use function array_filter; use function class_exists; -use function get_class; /** * Test case for the QueryBuilder class used to build DQL query string in a @@ -32,10 +33,7 @@ */ class QueryBuilderTest extends OrmTestCase { - use VerifyDeprecations; - - /** @var EntityManagerMock */ - private $entityManager; + private EntityManagerMock $entityManager; protected function setUp(): void { @@ -56,16 +54,7 @@ public function testSelectSetsType(): void ->delete(CmsUser::class, 'u') ->select('u.id', 'u.username'); - self::assertEquals($qb->getType(), QueryBuilder::SELECT); - } - - public function testEmptySelectSetsType(): void - { - $qb = $this->entityManager->createQueryBuilder() - ->delete(CmsUser::class, 'u') - ->select(); - - self::assertEquals($qb->getType(), QueryBuilder::SELECT); + $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); } public function testDeleteSetsType(): void @@ -74,7 +63,7 @@ public function testDeleteSetsType(): void ->from(CmsUser::class, 'u') ->delete(); - self::assertEquals($qb->getType(), QueryBuilder::DELETE); + $this->assertValidQueryBuilder($qb, 'DELETE Doctrine\Tests\Models\CMS\CmsUser u'); } public function testUpdateSetsType(): void @@ -83,7 +72,7 @@ public function testUpdateSetsType(): void ->from(CmsUser::class, 'u') ->update(); - self::assertEquals($qb->getType(), QueryBuilder::UPDATE); + $this->assertValidQueryBuilder($qb, 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u'); } public function testSimpleSelect(): void @@ -95,6 +84,15 @@ public function testSimpleSelect(): void $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); } + public function testSimpleSelectArray(): void + { + $qb = $this->entityManager->createQueryBuilder() + ->from(CmsUser::class, 'u') + ->select(['u.id', 'u.username']); + + $this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); + } + public function testSimpleDelete(): void { $qb = $this->entityManager->createQueryBuilder() @@ -150,7 +148,7 @@ public function testComplexInnerJoin(): void $this->assertValidQueryBuilder( $qb, - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id', ); } @@ -164,7 +162,7 @@ public function testComplexInnerJoinWithComparisonCondition(): void $this->assertValidQueryBuilder( $qb, - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id', ); } @@ -176,12 +174,12 @@ public function testComplexInnerJoinWithCompositeCondition(): void ->from(CmsUser::class, 'u') ->innerJoin('u.articles', 'a', Join::ON, $qb->expr()->andX( $qb->expr()->eq('u.id', 'a.author_id'), - $qb->expr()->isNotNull('u.name') + $qb->expr()->isNotNull('u.name'), )); $this->assertValidQueryBuilder( $qb, - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id AND u.name IS NOT NULL' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id AND u.name IS NOT NULL', ); } @@ -193,12 +191,12 @@ public function testComplexInnerJoinWithFuncCondition(): void ->from(CmsUser::class, 'u') ->innerJoin('u.articles', 'a', Join::WITH, $qb->expr()->in( 'u.id', - [1, 2, 3] + [1, 2, 3], )); $this->assertValidQueryBuilder( $qb, - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a WITH u.id IN(1, 2, 3)' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a WITH u.id IN(1, 2, 3)', ); } @@ -211,7 +209,7 @@ public function testComplexInnerJoinWithIndexBy(): void $this->assertValidQueryBuilder( $qb, - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INDEX BY a.name ON u.id = a.author_id' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INDEX BY a.name ON u.id = a.author_id', ); } @@ -290,6 +288,18 @@ public function testWhere(): void $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :uid'); } + public function testWhereWithUnexpectedNamedArguments(): void + { + $qb = $this->entityManager->createQueryBuilder() + ->select('u') + ->from(CmsUser::class, 'u'); + + $this->expectException(BadMethodCallException::class); + $this->expectExceptionMessage('Invalid call to Doctrine\ORM\QueryBuilder::where(), unknown named arguments: foo, bar'); + + $qb->where(foo: 'u.id = :uid', bar: 'u.name = :name'); + } + public function testComplexAndWhere(): void { $qb = $this->entityManager->createQueryBuilder() @@ -493,7 +503,7 @@ public function testAddMultipleSameCriteriaWhere(): void $criteria = new Criteria(); $criteria->where($criteria->expr()->andX( $criteria->expr()->eq('field', 'value1'), - $criteria->expr()->eq('field', 'value2') + $criteria->expr()->eq('field', 'value2'), )); $qb->addCriteria($criteria); @@ -503,7 +513,7 @@ public function testAddMultipleSameCriteriaWhere(): void self::assertNotNull($qb->getParameter('field_1')); } - /** @group DDC-2844 */ + #[Group('DDC-2844')] public function testAddCriteriaWhereWithMultipleParametersWithSameField(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -520,7 +530,7 @@ public function testAddCriteriaWhereWithMultipleParametersWithSameField(): void self::assertSame('value2', $qb->getParameter('field_1')->getValue()); } - /** @group DDC-2844 */ + #[Group('DDC-2844')] public function testAddCriteriaWhereWithMultipleParametersWithDifferentFields(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -537,7 +547,7 @@ public function testAddCriteriaWhereWithMultipleParametersWithDifferentFields(): self::assertSame('value2', $qb->getParameter('field2')->getValue()); } - /** @group DDC-2844 */ + #[Group('DDC-2844')] public function testAddCriteriaWhereWithMultipleParametersWithSubpathsAndDifferentProperties(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -554,7 +564,7 @@ public function testAddCriteriaWhereWithMultipleParametersWithSubpathsAndDiffere self::assertSame('value2', $qb->getParameter('field2')->getValue()); } - /** @group DDC-2844 */ + #[Group('DDC-2844')] public function testAddCriteriaWhereWithMultipleParametersWithSubpathsAndSameProperty(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -586,7 +596,7 @@ public function testAddCriteriaOrder(): void self::assertEquals('u.field DESC', (string) $orderBy[0]); } - /** @group DDC-3108 */ + #[Group('DDC-3108')] public function testAddCriteriaOrderOnJoinAlias(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -642,7 +652,7 @@ public function testGetQuery(): void ->from(CmsUser::class, 'u'); $q = $qb->getQuery(); - self::assertEquals(Query::class, get_class($q)); + self::assertEquals(Query::class, $q::class); } public function testSetParameter(): void @@ -839,21 +849,6 @@ public function testGetEntityManager(): void self::assertEquals($this->entityManager, $qb->getEntityManager()); } - public function testInitialStateIsClean(): void - { - $qb = $this->entityManager->createQueryBuilder(); - self::assertEquals(QueryBuilder::STATE_CLEAN, $qb->getState()); - } - - public function testAlteringQueryChangesStateToDirty(): void - { - $qb = $this->entityManager->createQueryBuilder() - ->select('u') - ->from(CmsUser::class, 'u'); - - self::assertEquals(QueryBuilder::STATE_DIRTY, $qb->getState()); - } - public function testSelectWithFuncExpression(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -907,7 +902,7 @@ public function testResetAllDQLParts(): void self::assertCount(0, $qb->getDQLPart('orderBy')); } - /** @group DDC-867 */ + #[Group('DDC-867')] public function testDeepClone(): void { $qb = $this->entityManager->createQueryBuilder() @@ -925,7 +920,7 @@ public function testDeepClone(): void self::assertEquals(2, $expr->count(), 'Modifying the second query should affect the first one.'); } - /** @group DDC-3108 */ + #[Group('DDC-3108')] public function testAddCriteriaWhereWithJoinAlias(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -943,7 +938,7 @@ public function testAddCriteriaWhereWithJoinAlias(): void self::assertSame('value2', $qb->getParameter('alias2_field')->getValue()); } - /** @group DDC-3108 */ + #[Group('DDC-3108')] public function testAddCriteriaWhereWithDefaultAndJoinAlias(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -961,7 +956,7 @@ public function testAddCriteriaWhereWithDefaultAndJoinAlias(): void self::assertSame('value2', $qb->getParameter('alias2_field')->getValue()); } - /** @group DDC-3108 */ + #[Group('DDC-3108')] public function testAddCriteriaWhereOnJoinAliasWithDuplicateFields(): void { $qb = $this->entityManager->createQueryBuilder(); @@ -981,7 +976,7 @@ public function testAddCriteriaWhereOnJoinAliasWithDuplicateFields(): void self::assertSame('value3', $qb->getParameter('alias2_field_2')->getValue()); } - /** @group DDC-1933 */ + #[Group('DDC-1933')] public function testParametersAreCloned(): void { $originalQb = new QueryBuilder($this->entityManager); @@ -1044,7 +1039,7 @@ public function testBCAddJoinWithoutRootAlias(): void self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.groups g', $qb->getDQL()); } - /** @group DDC-1211 */ + #[Group('DDC-1211')] public function testEmptyStringLiteral(): void { $expr = $this->entityManager->getExpressionBuilder(); @@ -1056,7 +1051,7 @@ public function testEmptyStringLiteral(): void self::assertEquals("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ''", $qb->getDQL()); } - /** @group DDC-1211 */ + #[Group('DDC-1211')] public function testEmptyNumericLiteral(): void { $expr = $this->entityManager->getExpressionBuilder(); @@ -1068,7 +1063,7 @@ public function testEmptyNumericLiteral(): void self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 0', $qb->getDQL()); } - /** @group DDC-1227 */ + #[Group('DDC-1227')] public function testAddFromString(): void { $qb = $this->entityManager->createQueryBuilder() @@ -1078,7 +1073,7 @@ public function testAddFromString(): void self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL()); } - /** @group DDC-1619 */ + #[Group('DDC-1619')] public function testAddDistinct(): void { $qb = $this->entityManager->createQueryBuilder() @@ -1101,7 +1096,7 @@ public function testDistinctUpdatesState(): void self::assertEquals('SELECT DISTINCT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL()); } - /** @group DDC-2192 */ + #[Group('DDC-2192')] public function testWhereAppend(): void { $this->expectException(InvalidArgumentException::class); @@ -1151,7 +1146,7 @@ public function testSecondLevelCacheQueryBuilderOptions(): void self::assertEquals(Cache::MODE_REFRESH, $query->getCacheMode()); } - /** @group DDC-2253 */ + #[Group('DDC-2253')] public function testRebuildsFromParts(): void { $qb = $this->entityManager->createQueryBuilder() @@ -1194,7 +1189,7 @@ public function testGetAllAliasesWithJoins(): void self::assertEquals(['u', 'g'], $aliases); } - /** @group 6699 */ + #[Group('6699')] public function testGetParameterTypeJuggling(): void { $builder = $this->entityManager->createQueryBuilder() @@ -1209,7 +1204,7 @@ public function testGetParameterTypeJuggling(): void self::assertSame(0, $builder->getParameter('0')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithNameZeroIsNotOverridden(): void { $builder = $this->entityManager->createQueryBuilder() @@ -1226,7 +1221,7 @@ public function testSetParameterWithNameZeroIsNotOverridden(): void self::assertSame('Doctrine', $builder->getParameter('name')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): void { $builder = $this->entityManager->createQueryBuilder() @@ -1243,7 +1238,7 @@ public function testSetParameterWithNameZeroDoesNotOverrideAnotherParameter(): v self::assertSame('Doctrine', $builder->getParameter('name')->getValue()); } - /** @group 6699 */ + #[Group('6699')] public function testSetParameterWithTypeJugglingWorks(): void { $builder = $this->entityManager->createQueryBuilder() @@ -1274,24 +1269,61 @@ public function testJoin(): void self::assertSame('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN Doctrine\Tests\Models\CMS\CmsArticle a0 INNER JOIN Doctrine\Tests\Models\CMS\CmsArticle a1', $builder->getDQL()); } - public function testUpdateDeprecationMessage(): void + public function testUpdateWithoutAlias(): void { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/9733'); + $qb = $this->entityManager->createQueryBuilder(); - $qb = $this->entityManager->createQueryBuilder() - ->update(CmsUser::class . ' u') - ->set('u.username', ':username'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Doctrine\ORM\QueryBuilder::update(): The alias for entity Doctrine\Tests\Models\CMS\CmsUser u must not be omitted.'); + $qb->update(CmsUser::class . ' u'); + } - $this->assertValidQueryBuilder($qb, 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.username = :username'); + public function testDeleteWithoutAlias(): void + { + $qb = $this->entityManager->createQueryBuilder(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Doctrine\ORM\QueryBuilder::delete(): The alias for entity Doctrine\Tests\Models\CMS\CmsUser u must not be omitted.'); + $qb->delete(CmsUser::class . ' u'); } - public function testDeleteDeprecationMessage(): void + public function testCreateNamedParameter(): void { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/9733'); + $qb = $this->entityManager->createQueryBuilder(); - $qb = $this->entityManager->createQueryBuilder() - ->delete(CmsUser::class . ' u'); + $qb->select('u') + ->from(CmsUser::class, 'u') + ->where( + $qb->expr()->eq('u.name', $qb->createNamedParameter('john doe', Types::STRING)), + ) + ->orWhere( + $qb->expr()->eq('u.rank', $qb->createNamedParameter(100, Types::INTEGER)), + ); - $this->assertValidQueryBuilder($qb, 'DELETE Doctrine\Tests\Models\CMS\CmsUser u '); + self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :dcValue1 OR u.rank = :dcValue2', $qb->getDQL()); + self::assertEquals('john doe', $qb->getParameter('dcValue1')->getValue()); + self::assertEquals(Types::STRING, $qb->getParameter('dcValue1')->getType()); + self::assertEquals(100, $qb->getParameter('dcValue2')->getValue()); + self::assertEquals(Types::INTEGER, $qb->getParameter('dcValue2')->getType()); + } + + public function testCreateNamedParameterCustomPlaceholder(): void + { + $qb = $this->entityManager->createQueryBuilder(); + + $qb->select('u') + ->from(CmsUser::class, 'u') + ->where( + $qb->expr()->eq('u.name', $qb->createNamedParameter('john doe', Types::STRING, ':test')), + ) + ->andWhere( + $qb->expr()->eq('u.rank', $qb->createNamedParameter(100, Types::INTEGER)), + ); + + self::assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :test AND u.rank = :dcValue1', $qb->getDQL()); + self::assertEquals('john doe', $qb->getParameter('test')->getValue()); + self::assertEquals(Types::STRING, $qb->getParameter('test')->getType()); + self::assertEquals(100, $qb->getParameter('dcValue1')->getValue()); + self::assertEquals(Types::INTEGER, $qb->getParameter('dcValue1')->getType()); } } diff --git a/tests/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php b/tests/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php index f4260d232a7..83d39881f58 100644 --- a/tests/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php +++ b/tests/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php @@ -4,7 +4,6 @@ namespace Doctrine\Tests\ORM\Repository; -use Closure; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; @@ -12,24 +11,19 @@ use Doctrine\Tests\Models\DDC753\DDC753DefaultRepository; use Doctrine\Tests\Models\DDC753\DDC753EntityWithDefaultCustomRepository; use Doctrine\Tests\Models\DDC869\DDC869PaymentRepository; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** * Tests for {@see \Doctrine\ORM\Repository\DefaultRepositoryFactory} - * - * @covers \Doctrine\ORM\Repository\DefaultRepositoryFactory */ +#[CoversClass(DefaultRepositoryFactory::class)] class DefaultRepositoryFactoryTest extends TestCase { - /** @var EntityManagerInterface&MockObject */ - private $entityManager; - - /** @var Configuration&MockObject */ - private $configuration; - - /** @var DefaultRepositoryFactory */ - private $repositoryFactory; + private EntityManagerInterface&MockObject $entityManager; + private Configuration&MockObject $configuration; + private DefaultRepositoryFactory $repositoryFactory; protected function setUp(): void { @@ -48,11 +42,11 @@ public function testCreatesRepositoryFromDefaultRepositoryClass(): void $this->entityManager ->expects(self::any()) ->method('getClassMetadata') - ->willReturnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])); + ->willReturnCallback($this->buildClassMetadata(...)); self::assertInstanceOf( DDC869PaymentRepository::class, - $this->repositoryFactory->getRepository($this->entityManager, self::class) + $this->repositoryFactory->getRepository($this->entityManager, self::class), ); } @@ -61,11 +55,11 @@ public function testCreatedRepositoriesAreCached(): void $this->entityManager ->expects(self::any()) ->method('getClassMetadata') - ->willReturnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])); + ->willReturnCallback($this->buildClassMetadata(...)); self::assertSame( $this->repositoryFactory->getRepository($this->entityManager, self::class), - $this->repositoryFactory->getRepository($this->entityManager, self::class) + $this->repositoryFactory->getRepository($this->entityManager, self::class), ); } @@ -81,7 +75,7 @@ public function testCreatesRepositoryFromCustomClassMetadata(): void self::assertInstanceOf( DDC753DefaultRepository::class, - $this->repositoryFactory->getRepository($this->entityManager, self::class) + $this->repositoryFactory->getRepository($this->entityManager, self::class), ); } @@ -92,11 +86,11 @@ public function testCachesDistinctRepositoriesPerDistinctEntityManager(): void $em1->expects(self::any()) ->method('getClassMetadata') - ->willReturnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])); + ->willReturnCallback($this->buildClassMetadata(...)); $em2->expects(self::any()) ->method('getClassMetadata') - ->willReturnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])); + ->willReturnCallback($this->buildClassMetadata(...)); $repo1 = $this->repositoryFactory->getRepository($em1, self::class); $repo2 = $this->repositoryFactory->getRepository($em2, self::class); @@ -114,10 +108,10 @@ public function testCachesDistinctRepositoriesPerDistinctEntityManager(): void * * @template TEntity of object */ - private function buildClassMetadata(string $className): ClassMetadata + private function buildClassMetadata(string $className): ClassMetadata&MockObject { $metadata = $this->createMock(ClassMetadata::class); - $metadata->method('getName')->willReturn($className); + $metadata->method('getName')->will(self::returnValue($className)); $metadata->name = $className; $metadata->customRepositoryClassName = null; @@ -125,8 +119,7 @@ private function buildClassMetadata(string $className): ClassMetadata return $metadata; } - /** @return EntityManagerInterface&MockObject */ - private function createEntityManager(): EntityManagerInterface + private function createEntityManager(): EntityManagerInterface&MockObject { $entityManager = $this->createMock(EntityManagerInterface::class); $entityManager->method('getConfiguration')->willReturn($this->configuration); diff --git a/tests/Tests/ORM/Tools/AttachEntityListenersListenerTest.php b/tests/Tests/ORM/Tools/AttachEntityListenersListenerTest.php index 71a48876b81..26fc5abe8d2 100644 --- a/tests/Tests/ORM/Tools/AttachEntityListenersListenerTest.php +++ b/tests/Tests/ORM/Tools/AttachEntityListenersListenerTest.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Mapping\EntityListeners; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\Tools\AttachEntityListenersListener; use Doctrine\Tests\OrmTestCase; @@ -19,19 +20,16 @@ class AttachEntityListenersListenerTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $em; + private EntityManagerInterface $em; - /** @var AttachEntityListenersListener */ - private $listener; + private AttachEntityListenersListener $listener; - /** @var ClassMetadataFactory */ - private $factory; + private ClassMetadataFactory $factory; protected function setUp(): void { $this->listener = new AttachEntityListenersListener(); - $driver = $this->createAnnotationDriver(); + $driver = $this->createAttributeDriver(); $this->em = $this->getTestEntityManager(); $evm = $this->em->getEventManager(); $this->factory = new ClassMetadataFactory(); @@ -47,7 +45,7 @@ public function testAttachEntityListeners(): void AttachEntityListenersListenerTestFooEntity::class, AttachEntityListenersListenerTestListener::class, Events::postLoad, - 'postLoadHandler' + 'postLoadHandler', ); $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class); @@ -74,14 +72,14 @@ public function testAttachToExistingEntityListeners(): void $this->listener->addEntityListener( AttachEntityListenersListenerTestBarEntity::class, AttachEntityListenersListenerTestListener2::class, - Events::prePersist + Events::prePersist, ); $this->listener->addEntityListener( AttachEntityListenersListenerTestBarEntity::class, AttachEntityListenersListenerTestListener2::class, Events::postPersist, - 'postPersistHandler' + 'postPersistHandler', ); $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestBarEntity::class); @@ -107,18 +105,18 @@ public function testAttachToExistingEntityListeners(): void public function testDuplicateEntityListenerException(): void { - $this->expectException('Doctrine\ORM\Mapping\MappingException'); + $this->expectException(MappingException::class); $this->expectExceptionMessage('Entity Listener "Doctrine\Tests\ORM\Tools\AttachEntityListenersListenerTestListener#postPersist()" in "Doctrine\Tests\ORM\Tools\AttachEntityListenersListenerTestFooEntity" was already declared, but it must be declared only once.'); $this->listener->addEntityListener( AttachEntityListenersListenerTestFooEntity::class, AttachEntityListenersListenerTestListener::class, - Events::postPersist + Events::postPersist, ); $this->listener->addEntityListener( AttachEntityListenersListenerTestFooEntity::class, AttachEntityListenersListenerTestListener::class, - Events::postPersist + Events::postPersist, ); $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class); @@ -129,7 +127,6 @@ public function testAttachWithoutSpecifyingAnEventName(): void $this->listener->addEntityListener( AttachEntityListenersListenerTestFooEntity::class, AttachEntityListenersListenerTestListener::class, - null ); $metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class); @@ -150,30 +147,24 @@ public function testAttachWithoutSpecifyingAnEventName(): void } } -/** @Entity */ +#[Entity] class AttachEntityListenersListenerTestFooEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } -/** - * @Entity - * @EntityListeners({"AttachEntityListenersListenerTestListener"}) - */ +#[Entity] +#[EntityListeners(['AttachEntityListenersListenerTestListener'])] class AttachEntityListenersListenerTestBarEntity { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] public $id; } diff --git a/tests/Tests/ORM/Tools/Console/Command/ClearCacheCollectionRegionCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/ClearCacheCollectionRegionCommandTest.php index 5608a0177ce..f3992f68ef8 100644 --- a/tests/Tests/ORM/Tools/Console/Command/ClearCacheCollectionRegionCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/ClearCacheCollectionRegionCommandTest.php @@ -8,17 +8,16 @@ use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\Models\Cache\State; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ClearCacheCollectionRegionCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; + private Application $application; - /** @var CollectionRegionCommand */ - private $command; + private CollectionRegionCommand $command; protected function setUp(): void { @@ -42,7 +41,7 @@ public function testClearAllRegion(): void 'command' => $command->getName(), '--all' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString(' // Clearing all second-level cache collection regions', $tester->getDisplay()); @@ -59,12 +58,12 @@ public function testClearByOwnerEntityClassName(): void 'owner-class' => State::class, 'association' => 'cities', ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Clearing second-level cache for collection "Doctrine\Tests\Models\Cache\State#cities"', - $tester->getDisplay() + $tester->getDisplay(), ); } @@ -80,12 +79,12 @@ public function testClearCacheEntryName(): void 'association' => 'cities', 'owner-id' => 1, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Clearing second-level cache entry for collection "Doctrine\Tests\Models\Cache\State#cities" owner', - $tester->getDisplay() + $tester->getDisplay(), ); self::assertStringContainsString('identified by "1"', $tester->getDisplay()); @@ -103,12 +102,12 @@ public function testFlushRegionName(): void 'association' => 'cities', '--flush' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Flushing cache provider configured for "Doctrine\Tests\Models\Cache\State#cities"', - $tester->getDisplay() + $tester->getDisplay(), ); } } diff --git a/tests/Tests/ORM/Tools/Console/Command/ClearCacheEntityRegionCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/ClearCacheEntityRegionCommandTest.php index 4a7528b0d87..ab138725572 100644 --- a/tests/Tests/ORM/Tools/Console/Command/ClearCacheEntityRegionCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/ClearCacheEntityRegionCommandTest.php @@ -8,20 +8,19 @@ use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\Models\Cache\Country; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use function preg_replace; use function trim; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ClearCacheEntityRegionCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; + private Application $application; - /** @var EntityRegionCommand */ - private $command; + private EntityRegionCommand $command; protected function setUp(): void { @@ -45,7 +44,7 @@ public function testClearAllRegion(): void 'command' => $command->getName(), '--all' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString(' // Clearing all second-level cache entity regions', $tester->getDisplay()); @@ -61,12 +60,12 @@ public function testClearByEntityClassName(): void 'command' => $command->getName(), 'entity-class' => Country::class, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Clearing second-level cache for entity "Doctrine\Tests\Models\Cache\Country"', - $tester->getDisplay() + $tester->getDisplay(), ); } @@ -81,12 +80,12 @@ public function testClearCacheEntryName(): void 'entity-class' => Country::class, 'entity-id' => 1, ], - ['decorated' => false] + ['decorated' => false], ); self::assertSame( 'Clearing second-level cache entry for entity "Doctrine\Tests\Models\Cache\Country" identified by "1"', - trim(preg_replace('#\s+//\s#', ' ', $tester->getDisplay())) + trim(preg_replace('#\s+//\s#', ' ', $tester->getDisplay())), ); } @@ -101,12 +100,12 @@ public function testFlushRegionName(): void 'entity-class' => Country::class, '--flush' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Flushing cache provider configured for entity named "Doctrine\Tests\Models\Cache\Country"', - $tester->getDisplay() + $tester->getDisplay(), ); } } diff --git a/tests/Tests/ORM/Tools/Console/Command/ClearCacheQueryRegionCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/ClearCacheQueryRegionCommandTest.php index 1eb39924249..c2b467fb2dd 100644 --- a/tests/Tests/ORM/Tools/Console/Command/ClearCacheQueryRegionCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/ClearCacheQueryRegionCommandTest.php @@ -7,17 +7,16 @@ use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand; use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; -/** @group DDC-2183 */ +#[Group('DDC-2183')] class ClearCacheQueryRegionCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; + private Application $application; - /** @var QueryRegionCommand */ - private $command; + private QueryRegionCommand $command; protected function setUp(): void { @@ -41,7 +40,7 @@ public function testClearAllRegion(): void 'command' => $command->getName(), '--all' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString(' // Clearing all second-level cache query regions', $tester->getDisplay()); @@ -57,12 +56,12 @@ public function testClearDefaultRegionName(): void 'command' => $command->getName(), 'region-name' => null, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Clearing second-level cache query region named "query_cache_region"', - $tester->getDisplay() + $tester->getDisplay(), ); } @@ -76,12 +75,12 @@ public function testClearByRegionName(): void 'command' => $command->getName(), 'region-name' => 'my_region', ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Clearing second-level cache query region named "my_region"', - $tester->getDisplay() + $tester->getDisplay(), ); } @@ -96,12 +95,12 @@ public function testFlushRegionName(): void 'region-name' => 'my_region', '--flush' => true, ], - ['decorated' => false] + ['decorated' => false], ); self::assertStringContainsString( ' // Flushing cache provider configured for second-level cache query region named "my_region"', - $tester->getDisplay() + $tester->getDisplay(), ); } } diff --git a/tests/Tests/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommandTest.php deleted file mode 100644 index 5e2b96a6543..00000000000 --- a/tests/Tests/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -createMock(EntityGenerator::class); - $command = new ConvertDoctrine1SchemaCommand(); - $command->setEntityGenerator($entityGenerator); - - $output = $this->createMock(OutputInterface::class); - $output->expects(self::once()) - ->method('writeln') - ->with(self::equalTo('No Metadata Classes to process.')); - - $command->convertDoctrine1Schema([], sys_get_temp_dir(), 'annotation', 4, null, $output); - } -} diff --git a/tests/Tests/ORM/Tools/Console/Command/EnsureProductionSettingsCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/EnsureProductionSettingsCommandTest.php deleted file mode 100644 index d11f0831df7..00000000000 --- a/tests/Tests/ORM/Tools/Console/Command/EnsureProductionSettingsCommandTest.php +++ /dev/null @@ -1,117 +0,0 @@ -createMock(EntityManagerInterface::class); - - $configuration = $this->createMock(Configuration::class); - $configuration->expects(self::once()) - ->method('ensureProductionSettings'); - - $em->method('getConfiguration') - ->willReturn($configuration); - - $em->expects(self::never()) - ->method('getConnection'); - - self::assertSame(0, $this->executeCommand($em)); - } - - public function testExecuteFailed(): void - { - $em = $this->createMock(EntityManagerInterface::class); - - $configuration = $this->createMock(Configuration::class); - $configuration->expects(self::once()) - ->method('ensureProductionSettings') - ->willThrowException(new RuntimeException()); - - $em->method('getConfiguration') - ->willReturn($configuration); - - $em->expects(self::never()) - ->method('getConnection'); - - self::assertSame(1, $this->executeCommand($em)); - } - - public function testExecuteWithComplete(): void - { - $em = $this->createMock(EntityManagerInterface::class); - - $configuration = $this->createMock(Configuration::class); - $configuration->expects(self::once()) - ->method('ensureProductionSettings'); - - $em->method('getConfiguration') - ->willReturn($configuration); - - $connection = $this->createMock(Connection::class); - - $connection->expects(self::once()) - ->method('connect'); - - $em->method('getConnection') - ->willReturn($connection); - - self::assertSame(0, $this->executeCommand($em, ['--complete' => true])); - } - - public function testExecuteWithCompleteFailed(): void - { - $em = $this->createMock(EntityManagerInterface::class); - - $configuration = $this->createMock(Configuration::class); - $configuration->expects(self::once()) - ->method('ensureProductionSettings'); - - $em->method('getConfiguration') - ->willReturn($configuration); - - $connection = $this->createMock(Connection::class); - - $connection->expects(self::once()) - ->method('connect') - ->willThrowException(new RuntimeException()); - - $em->method('getConnection') - ->willReturn($connection); - - self::assertSame(1, $this->executeCommand($em, ['--complete' => true])); - } - - private function executeCommand( - EntityManagerInterface $em, - array $input = [] - ): int { - $application = new Application(); - $application->add(new EnsureProductionSettingsCommand(new SingleManagerProvider($em))); - - $command = $application->find('orm:ensure-production-settings'); - $tester = new CommandTester($command); - - return $tester->execute( - array_merge([ - 'command' => $command->getName(), - ], $input) - ); - } -} diff --git a/tests/Tests/ORM/Tools/Console/Command/GenerateRepositoriesCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/GenerateRepositoriesCommandTest.php deleted file mode 100644 index 961f7702236..00000000000 --- a/tests/Tests/ORM/Tools/Console/Command/GenerateRepositoriesCommandTest.php +++ /dev/null @@ -1,181 +0,0 @@ -path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('doctrine_'); - - mkdir($this->path); - - $metadataDriver = $this->_em->getConfiguration()->getMetadataDriverImpl(); - $metadataDriver->addPaths([__DIR__ . '/../../../../Models/DDC3231/']); - - $this->application = new Application(); - $this->application->add(new GenerateRepositoriesCommand(new SingleManagerProvider($this->_em))); - } - - public function tearDown(): void - { - $dirs = []; - - $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->path)); - foreach ($ri as $file) { - assert($file instanceof SplFileInfo); - if ($file->isFile()) { - unlink($file->getPathname()); - } elseif ($file->getBasename() === '.') { - $dirs[] = $file->getRealPath(); - } - } - - arsort($dirs); - - foreach ($dirs as $dir) { - rmdir($dir); - } - - parent::tearDown(); - } - - public function testGenerateRepositories(): void - { - $this->generateRepositories('DDC3231User1'); - - $cname = 'Doctrine\Tests\Models\DDC3231\DDC3231User1Repository'; - $fname = str_replace('\\', DIRECTORY_SEPARATOR, $cname) . '.php'; - - self::assertFileExists($this->path . DIRECTORY_SEPARATOR . $fname); - self::assertFileExists($this->path . DIRECTORY_SEPARATOR . 'DDC3231User1NoNamespaceRepository.php'); - - require $this->path . DIRECTORY_SEPARATOR . $fname; - require $this->path . DIRECTORY_SEPARATOR . 'DDC3231User1NoNamespaceRepository.php'; - - self::assertTrue(class_exists($cname)); - self::assertTrue(class_exists('DDC3231User1NoNamespaceRepository')); - - $repo1 = new ReflectionClass($cname); - $repo2 = new ReflectionClass('DDC3231User1NoNamespaceRepository'); - - self::assertSame(EntityRepository::class, $repo1->getParentClass()->getName()); - self::assertSame(EntityRepository::class, $repo2->getParentClass()->getName()); - } - - public function testGenerateRepositoriesCustomDefaultRepository(): void - { - $this->generateRepositories('DDC3231User2', DDC3231EntityRepository::class); - - $cname = 'Doctrine\Tests\Models\DDC3231\DDC3231User2Repository'; - $fname = str_replace('\\', DIRECTORY_SEPARATOR, $cname) . '.php'; - - self::assertFileExists($this->path . DIRECTORY_SEPARATOR . $fname); - self::assertFileExists($this->path . DIRECTORY_SEPARATOR . 'DDC3231User2NoNamespaceRepository.php'); - - require $this->path . DIRECTORY_SEPARATOR . $fname; - require $this->path . DIRECTORY_SEPARATOR . 'DDC3231User2NoNamespaceRepository.php'; - - self::assertTrue(class_exists($cname)); - self::assertTrue(class_exists('DDC3231User2NoNamespaceRepository')); - - $repo1 = new ReflectionClass($cname); - $repo2 = new ReflectionClass('DDC3231User2NoNamespaceRepository'); - - self::assertSame(DDC3231EntityRepository::class, $repo1->getParentClass()->getName()); - self::assertSame(DDC3231EntityRepository::class, $repo2->getParentClass()->getName()); - } - - private function generateRepositories(string $filter, ?string $defaultRepository = null): void - { - if ($defaultRepository) { - $this->_em->getConfiguration()->setDefaultRepositoryClassName($defaultRepository); - } - - $command = $this->application->find('orm:generate-repositories'); - $tester = new CommandTester($command); - - $tester->execute( - [ - 'command' => $command->getName(), - 'dest-path' => $this->path, - '--filter' => $filter, - ] - ); - } - - public function testNoMetadataClassesToProcess(): void - { - $configuration = $this->createMock(Configuration::class); - $metadataFactory = $this->createMock(ClassMetadataFactory::class); - $em = $this->createMock(EntityManagerInterface::class); - - $configuration->method('getDefaultRepositoryClassName') - ->willReturn('fooRepository'); - - $metadataFactory->method('getAllMetadata') - ->willReturn([]); - - $em->method('getMetadataFactory') - ->willReturn($metadataFactory); - - $em->method('getConfiguration') - ->willReturn($configuration); - - $application = new Application(); - $application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)])); - $application->add(new GenerateRepositoriesCommand()); - - $command = $application->find('orm:generate-repositories'); - $tester = new CommandTester($command); - - $tester->execute( - [ - 'command' => $command->getName(), - 'dest-path' => $this->path, - ] - ); - - self::assertStringContainsString('Command orm:generate-repositories is deprecated and will be removed in Doctrine ORM 3.0.', $tester->getDisplay()); - self::assertStringContainsString('[OK] No Metadata Classes to process.', $tester->getDisplay()); - } -} diff --git a/tests/Tests/ORM/Tools/Console/Command/InfoCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/InfoCommandTest.php index 021e77ec282..21908dcf334 100644 --- a/tests/Tests/ORM/Tools/Console/Command/InfoCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/InfoCommandTest.php @@ -9,25 +9,18 @@ use Doctrine\ORM\Mapping\MappingException; use Doctrine\ORM\Tools\Console\Command\InfoCommand; use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; -use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Tests\Models\Cache\AttractionInfo; use Doctrine\Tests\Models\Cache\City; use Doctrine\Tests\OrmFunctionalTestCase; use Symfony\Component\Console\Application; -use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Tester\CommandTester; class InfoCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; - - /** @var InfoCommand */ - private $command; - - /** @var CommandTester */ - private $tester; + private Application $application; + private InfoCommand $command; + private CommandTester $tester; protected function setUp(): void { @@ -65,8 +58,7 @@ public function testEmptyEntityClassNames(): void ->willReturn($configuration); $application = new Application(); - $application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)])); - $application->add(new InfoCommand()); + $application->add(new InfoCommand(new SingleManagerProvider($em))); $command = $application->find('orm:info'); $tester = new CommandTester($command); @@ -75,12 +67,12 @@ public function testEmptyEntityClassNames(): void self::assertStringContainsString( ' ! [CAUTION] You do not have any mapped Doctrine ORM entities according to the current configuration', - $tester->getDisplay() + $tester->getDisplay(), ); self::assertStringContainsString( ' ! If you have entities or mapping files you should check your mapping configuration for errors.', - $tester->getDisplay() + $tester->getDisplay(), ); } @@ -104,8 +96,7 @@ public function testInvalidEntityClassMetadata(): void ->willThrowException(new MappingException('exception message')); $application = new Application(); - $application->setHelperSet(new HelperSet(['em' => new EntityManagerHelper($em)])); - $application->add(new InfoCommand()); + $application->add(new InfoCommand(new SingleManagerProvider($em))); $command = $application->find('orm:info'); $tester = new CommandTester($command); diff --git a/tests/Tests/ORM/Tools/Console/Command/MappingDescribeCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/MappingDescribeCommandTest.php index 151bcd019df..14174f4177d 100644 --- a/tests/Tests/ORM/Tools/Console/Command/MappingDescribeCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/MappingDescribeCommandTest.php @@ -8,24 +8,22 @@ use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\Models\Cache\AttractionInfo; use Doctrine\Tests\OrmFunctionalTestCase; +use InvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; /** * Tests for {@see \Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand} - * - * @covers \Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand */ +#[CoversClass(MappingDescribeCommand::class)] class MappingDescribeCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; + private Application $application; - /** @var MappingDescribeCommand */ - private $command; + private MappingDescribeCommand $command; - /** @var CommandTester */ - private $tester; + private CommandTester $tester; protected function setUp(): void { @@ -44,7 +42,7 @@ public function testShowSpecificFuzzySingle(): void [ 'command' => $this->command->getName(), 'entityName' => 'AttractionInfo', - ] + ], ); $display = $this->tester->getDisplay(); @@ -55,25 +53,25 @@ public function testShowSpecificFuzzySingle(): void public function testShowSpecificFuzzyAmbiguous(): void { - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('possible matches'); $this->tester->execute( [ 'command' => $this->command->getName(), 'entityName' => 'Attraction', - ] + ], ); } public function testShowSpecificNotFound(): void { - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Could not find any mapped Entity classes matching "AttractionFooBar"'); $this->tester->execute( [ 'command' => $this->command->getName(), 'entityName' => 'AttractionFooBar', - ] + ], ); } } diff --git a/tests/Tests/ORM/Tools/Console/Command/RunDqlCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/RunDqlCommandTest.php index 045a0a5a362..28795c05254 100644 --- a/tests/Tests/ORM/Tools/Console/Command/RunDqlCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/RunDqlCommandTest.php @@ -8,6 +8,7 @@ use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\Models\Generic\DateTimeModel; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandTester; @@ -15,19 +16,15 @@ /** * Tests for {@see \Doctrine\ORM\Tools\Console\Command\RunDqlCommand} - * - * @covers \Doctrine\ORM\Tools\Console\Command\RunDqlCommand */ +#[CoversClass(RunDqlCommand::class)] class RunDqlCommandTest extends OrmFunctionalTestCase { - /** @var Application */ - private $application; + private Application $application; - /** @var RunDqlCommand */ - private $command; + private RunDqlCommand $command; - /** @var CommandTester */ - private $tester; + private CommandTester $tester; protected function setUp(): void { @@ -59,8 +56,8 @@ public function testWillRunQuery(): void [ 'command' => $this->command->getName(), 'dql' => 'SELECT e FROM ' . DateTimeModel::class . ' e', - ] - ) + ], + ), ); self::assertStringContainsString(DateTimeModel::class, $this->tester->getDisplay()); @@ -78,8 +75,8 @@ public function testWillShowQuery(): void 'command' => $this->command->getName(), 'dql' => 'SELECT e FROM ' . DateTimeModel::class . ' e', '--show-sql' => 'true', - ] - ) + ], + ), ); self::assertStringMatchesFormat('SELECT %a', trim($this->tester->getDisplay())); diff --git a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CommandTestCase.php b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CommandTestCase.php index 080ff541d3f..82279e337b0 100644 --- a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CommandTestCase.php +++ b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CommandTestCase.php @@ -4,8 +4,8 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command\SchemaTool; -use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\ORM\ORMSetup; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Tools\Console\Command\SchemaTool\AbstractCommand; use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\OrmFunctionalTestCase; @@ -14,18 +14,18 @@ abstract class CommandTestCase extends OrmFunctionalTestCase { /** @param class-string $commandClass */ - protected function getCommandTester(string $commandClass, ?string $commandName = null): CommandTester + protected function getCommandTester(string $commandClass, string|null $commandName = null): CommandTester { - $entityManager = $this->getEntityManager(null, ORMSetup::createDefaultAnnotationDriver([ + $entityManager = $this->getEntityManager(null, new AttributeDriver([ __DIR__ . '/Models', ])); - if (! $entityManager->getConnection()->getDatabasePlatform() instanceof SqlitePlatform) { + if (! $entityManager->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('We are testing the symfony/console integration'); } $command = new $commandClass( - new SingleManagerProvider($entityManager) + new SingleManagerProvider($entityManager), ); if ($commandName !== null) { diff --git a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CreateCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CreateCommandTest.php index 83c68b62a16..ac728a16658 100644 --- a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CreateCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/CreateCommandTest.php @@ -5,10 +5,11 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command\SchemaTool; use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; class CreateCommandTest extends CommandTestCase { - /** @doesNotPerformAssertions */ + #[DoesNotPerformAssertions] public function testItPrintsTheSql(): void { $tester = $this->getCommandTester(CreateCommand::class); diff --git a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/DropCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/DropCommandTest.php index f032ae729b0..22fce5e9371 100644 --- a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/DropCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/DropCommandTest.php @@ -6,10 +6,11 @@ use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand; use Doctrine\Tests\ORM\Tools\Console\Command\SchemaTool\Models\Keyboard; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; final class DropCommandTest extends CommandTestCase { - /** @doesNotPerformAssertions */ + #[DoesNotPerformAssertions] public function testItPrintsTheSql(): void { $this->createSchemaForModels(Keyboard::class); diff --git a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/Models/Keyboard.php b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/Models/Keyboard.php index 0570cd6f6ac..af20d0090b5 100644 --- a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/Models/Keyboard.php +++ b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/Models/Keyboard.php @@ -10,24 +10,18 @@ use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Table; -/** - * @Entity - * @Table(name="keyboard") - */ +#[Table(name: 'keyboard')] +#[Entity] final class Keyboard { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'NONE')] private $id; - /** - * @var string - * @Column(type="string", length=255); - */ + /** @var string */ + #[Column(type: 'string', length: 255)] private $name; public function __construct(int $id, string $name) diff --git a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php deleted file mode 100644 index aee2db729f6..00000000000 --- a/tests/Tests/ORM/Tools/Console/Command/SchemaTool/UpdateCommandTest.php +++ /dev/null @@ -1,49 +0,0 @@ -getCommandTester(UpdateCommand::class); - $tester->execute( - ['--dump-sql' => true], - ['capture_stderr_separately' => true] - ); - - self::$sharedConn->executeStatement($tester->getDisplay()); - } - - /** - * @dataProvider getCasesForWarningMessageFromCompleteOption - */ - public function testWarningMessageFromCompleteOption(?string $name, string $expectedMessage): void - { - $tester = $this->getCommandTester(UpdateCommand::class, $name); - $tester->execute( - [], - ['capture_stderr_separately' => true] - ); - - self::assertStringContainsString($expectedMessage, $tester->getErrorOutput()); - } - - public static function getCasesForWarningMessageFromCompleteOption(): iterable - { - yield 'default_name' => [ - null, - '[WARNING] Not passing the "--complete" option to "orm:schema-tool:update" is deprecated', - ]; - - yield 'custom_name' => [ - 'doctrine:schema:update', - '[WARNING] Not passing the "--complete" option to "doctrine:schema:update" is deprecated', - ]; - } -} diff --git a/tests/Tests/ORM/Tools/Console/Command/ValidateSchemaCommandTest.php b/tests/Tests/ORM/Tools/Console/Command/ValidateSchemaCommandTest.php index 5ccd0f7215c..d17347b242d 100644 --- a/tests/Tests/ORM/Tools/Console/Command/ValidateSchemaCommandTest.php +++ b/tests/Tests/ORM/Tools/Console/Command/ValidateSchemaCommandTest.php @@ -4,35 +4,40 @@ namespace Doctrine\Tests\ORM\Tools\Console\Command; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand; use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\Console\Application; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; +use function method_exists; + /** * Tests for {@see \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand} - * - * @covers \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand */ +#[CoversClass(ValidateSchemaCommand::class)] class ValidateSchemaCommandTest extends OrmFunctionalTestCase { - /** @var ValidateSchemaCommand */ - private $command; + private ValidateSchemaCommand $command; - /** @var CommandTester */ - private $tester; + private CommandTester $tester; protected function setUp(): void { parent::setUp(); - if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform) { + if (! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLitePlatform) { self::markTestSkipped('Only with sqlite'); } + if (! method_exists(SchemaDiff::class, 'toSaveSql')) { + self::markTestSkipped('FIXME for DBAL 4.'); + } + $application = new Application(); $application->add(new ValidateSchemaCommand(new SingleManagerProvider($this->_em))); @@ -45,7 +50,7 @@ public function testNotInSync(): void $this->tester->execute( [ 'command' => $this->command->getName(), - ] + ], ); $display = $this->tester->getDisplay(); @@ -57,7 +62,7 @@ public function testNotInSync(): void public function testNotInSyncVerbose(): void { $schemaManager = $this->createSchemaManager(); - if ($schemaManager->tablesExist('cache_login')) { + if ($schemaManager->tablesExist(['cache_login'])) { $schemaManager->dropTable('cache_login'); } @@ -67,7 +72,7 @@ public function testNotInSyncVerbose(): void ], [ 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, - ] + ], ); $display = $this->tester->getDisplay(); diff --git a/tests/Tests/ORM/Tools/Console/ConsoleRunnerTest.php b/tests/Tests/ORM/Tools/Console/ConsoleRunnerTest.php index 890f440d3e7..3d3d89f4b4b 100644 --- a/tests/Tests/ORM/Tools/Console/ConsoleRunnerTest.php +++ b/tests/Tests/ORM/Tools/Console/ConsoleRunnerTest.php @@ -5,32 +5,29 @@ namespace Doctrine\Tests\ORM\Tools\Console; use Composer\InstalledVersions; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; +use DBALConsole\Command\ReservedWordsCommand; use Doctrine\ORM\Tools\Console\ConsoleRunner; use Doctrine\ORM\Tools\Console\EntityManagerProvider; -use Doctrine\Tests\DoctrineTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Helper\HelperSet; -/** - * @group DDC-3186 - * @covers \Doctrine\ORM\Tools\Console\ConsoleRunner - */ -final class ConsoleRunnerTest extends DoctrineTestCase -{ - use VerifyDeprecations; +use function class_exists; +#[CoversClass(ConsoleRunner::class)] +#[Group('DDC-3186')] +final class ConsoleRunnerTest extends TestCase +{ public function testCreateApplicationShouldReturnAnApplicationWithTheCorrectCommands(): void { - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8327'); + $app = ConsoleRunner::createApplication($this->createStub(EntityManagerProvider::class)); - $helperSet = new HelperSet(); - $app = ConsoleRunner::createApplication($helperSet); - - self::assertSame($helperSet, $app->getHelperSet()); self::assertSame(InstalledVersions::getVersion('doctrine/orm'), $app->getVersion()); + if (class_exists(ReservedWordsCommand::class)) { + self::assertTrue($app->has('dbal:reserved-words')); + } - self::assertTrue($app->has('dbal:reserved-words')); self::assertTrue($app->has('dbal:run-sql')); self::assertTrue($app->has('orm:clear-cache:region:collection')); self::assertTrue($app->has('orm:clear-cache:region:entity')); @@ -38,17 +35,8 @@ public function testCreateApplicationShouldReturnAnApplicationWithTheCorrectComm self::assertTrue($app->has('orm:clear-cache:metadata')); self::assertTrue($app->has('orm:clear-cache:query')); self::assertTrue($app->has('orm:clear-cache:result')); - self::assertTrue($app->has('orm:convert-d1-schema')); - self::assertTrue($app->has('orm:convert-mapping')); - self::assertTrue($app->has('orm:convert:d1-schema')); - self::assertTrue($app->has('orm:convert:mapping')); - self::assertTrue($app->has('orm:ensure-production-settings')); - self::assertTrue($app->has('orm:generate-entities')); self::assertTrue($app->has('orm:generate-proxies')); - self::assertTrue($app->has('orm:generate-repositories')); - self::assertTrue($app->has('orm:generate:entities')); self::assertTrue($app->has('orm:generate:proxies')); - self::assertTrue($app->has('orm:generate:repositories')); self::assertTrue($app->has('orm:info')); self::assertTrue($app->has('orm:mapping:describe')); self::assertTrue($app->has('orm:run-dql')); @@ -61,16 +49,8 @@ public function testCreateApplicationShouldReturnAnApplicationWithTheCorrectComm public function testCreateApplicationShouldAppendGivenCommands(): void { $command = 'my:lovely-command'; - $app = ConsoleRunner::createApplication(new HelperSet(), [new Command($command)]); + $app = ConsoleRunner::createApplication($this->createStub(EntityManagerProvider::class), [new Command($command)]); self::assertTrue($app->has($command)); } - - public function testCreateApplicationWithProvider(): void - { - $provider = $this->createMock(EntityManagerProvider::class); - $app = ConsoleRunner::createApplication($provider, []); - - self::assertTrue($app->has('orm:info')); - } } diff --git a/tests/Tests/ORM/Tools/Console/MetadataFilterTest.php b/tests/Tests/ORM/Tools/Console/MetadataFilterTest.php index 19f40eb34f6..5db5f94bf5c 100644 --- a/tests/Tests/ORM/Tools/Console/MetadataFilterTest.php +++ b/tests/Tests/ORM/Tools/Console/MetadataFilterTest.php @@ -4,35 +4,34 @@ namespace Doctrine\Tests\ORM\Tools\Console; +use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Tools\Console\MetadataFilter; -use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\CoversClass; use function count; /** * Tests for {@see \Doctrine\ORM\Tools\Console\MetadataFilter} - * - * @covers \Doctrine\ORM\Tools\Console\MetadataFilter */ +#[CoversClass(MetadataFilter::class)] class MetadataFilterTest extends OrmTestCase { - /** @var DisconnectedClassMetadataFactory */ - private $cmf; + private ClassMetadataFactory $cmf; protected function setUp(): void { parent::setUp(); - $driver = $this->createAnnotationDriver(); + $driver = $this->createAttributeDriver(); $em = $this->getTestEntityManager(); $em->getConfiguration()->setMetadataDriverImpl($driver); - $this->cmf = new DisconnectedClassMetadataFactory(); + $this->cmf = new ClassMetadataFactory(); $this->cmf->setEntityManager($em); } @@ -147,68 +146,56 @@ public function testFilterWithRegex(): void } } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityAaa { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityBbb { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityCcc { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityFoo { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityBar { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } -/** @Entity */ +#[Entity] class MetadataFilterTestEntityFooBar { - /** - * @var int - * @Id - * @Column - */ + /** @var int */ + #[Id] + #[Column] protected $id; } diff --git a/tests/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php b/tests/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php deleted file mode 100644 index 7a074e02e35..00000000000 --- a/tests/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php +++ /dev/null @@ -1,98 +0,0 @@ -createMock(AbstractPlatform::class); - $platform->method('supportsIdentityColumns') - ->willReturn(true); - - $connection = $this->createMock(Connection::class); - $connection->method('getDatabasePlatform') - ->willReturn($platform); - $connection->method('getEventManager') - ->willReturn(new EventManager()); - - $config = new Configuration(); - TestUtil::configureProxies($config); - $config->setMetadataDriverImpl($metadataDriver); - - return new EntityManagerMock($connection, $config); - } - - public function testTest(): void - { - if (! class_exists('Symfony\Component\Yaml\Yaml', true)) { - self::markTestSkipped('Please install Symfony YAML Component into the include path of your PHP installation.'); - } - - $cme = new ClassMetadataExporter(); - $converter = new ConvertDoctrine1Schema(__DIR__ . '/doctrine1schema'); - - $exporter = $cme->getExporter('yml', __DIR__ . '/convert'); - $exporter->setOverwriteExistingFiles(true); - $exporter->setMetadata($converter->getMetadata()); - $exporter->export(); - - self::assertTrue(file_exists(__DIR__ . '/convert/User.dcm.yml')); - self::assertTrue(file_exists(__DIR__ . '/convert/Profile.dcm.yml')); - - $metadataDriver = new YamlDriver(__DIR__ . '/convert'); - $em = $this->createEntityManager($metadataDriver); - $cmf = new DisconnectedClassMetadataFactory(); - $cmf->setEntityManager($em); - $metadata = $cmf->getAllMetadata(); - $profileClass = $cmf->getMetadataFor('Profile'); - $userClass = $cmf->getMetadataFor('User'); - - self::assertEquals(2, count($metadata)); - self::assertEquals('Profile', $profileClass->name); - self::assertEquals('User', $userClass->name); - self::assertEquals(4, count($profileClass->fieldMappings)); - self::assertEquals(5, count($userClass->fieldMappings)); - self::assertEquals('text', $userClass->fieldMappings['clob']['type']); - self::assertEquals('test_alias', $userClass->fieldMappings['theAlias']['columnName']); - self::assertEquals('theAlias', $userClass->fieldMappings['theAlias']['fieldName']); - - self::assertEquals('Profile', $profileClass->associationMappings['User']['sourceEntity']); - self::assertEquals('User', $profileClass->associationMappings['User']['targetEntity']); - - self::assertEquals('username', $userClass->table['uniqueConstraints']['username']['columns'][0]); - } - - public function tearDown(): void - { - @unlink(__DIR__ . '/convert/User.dcm.yml'); - @unlink(__DIR__ . '/convert/Profile.dcm.yml'); - @rmdir(__DIR__ . '/convert'); - } -} diff --git a/tests/Tests/ORM/Tools/DebugTest.php b/tests/Tests/ORM/Tools/DebugTest.php index 4d64b9d8ee6..abd37306573 100644 --- a/tests/Tests/ORM/Tools/DebugTest.php +++ b/tests/Tests/ORM/Tools/DebugTest.php @@ -11,17 +11,15 @@ use DateTimeZone; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Tools\Debug; -use Doctrine\Tests\DoctrineTestCase; +use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; use stdClass; use function print_r; use function strpos; use function substr; -use function version_compare; -use const PHP_VERSION; - -class DebugTest extends DoctrineTestCase +class DebugTest extends OrmTestCase { public function testExportObject(): void { @@ -95,11 +93,8 @@ public function testExportArrayTraversable(): void self::assertContains('foobar', $var->__STORAGE__); } - /** - * @param array $expected - * - * @dataProvider provideAttributesCases - */ + /** @param array $expected */ + #[DataProvider('provideAttributesCases')] public function testExportParentAttributes(TestAsset\ParentClass $class, array $expected): void { $actualRepresentation = print_r($class, true); @@ -127,23 +122,12 @@ public function testCollectionsAreCastIntoArrays(): void self::assertEquals(['foo', 'bar'], $var); } - /** - * @phpstan-return array - */ - public function provideAttributesCases(): iterable + /** @phpstan-return array */ + public static function provideAttributesCases(): iterable { return [ 'different-attributes' => [ new TestAsset\ChildClass(), - version_compare(PHP_VERSION, '8.1', '<') ? - [ - 'childPublicAttribute' => 4, - 'childProtectedAttribute:protected' => 5, - 'childPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ChildClass:private' => 6, - 'parentPublicAttribute' => 1, - 'parentProtectedAttribute:protected' => 2, - 'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ParentClass:private' => 3, - ] : [ 'parentPublicAttribute' => 1, 'parentProtectedAttribute:protected' => 2, @@ -155,13 +139,6 @@ public function provideAttributesCases(): iterable ], 'same-attributes' => [ new TestAsset\ChildWithSameAttributesClass(), - version_compare(PHP_VERSION, '8.1', '<') ? - [ - 'parentPublicAttribute' => 4, - 'parentProtectedAttribute:protected' => 5, - 'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ChildWithSameAttributesClass:private' => 6, - 'parentPrivateAttribute:Doctrine\Tests\ORM\Tools\TestAsset\ParentClass:private' => 3, - ] : [ 'parentPublicAttribute' => 4, 'parentProtectedAttribute:protected' => 5, diff --git a/tests/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Tests/ORM/Tools/EntityGeneratorTest.php deleted file mode 100644 index fdf1dff1222..00000000000 --- a/tests/Tests/ORM/Tools/EntityGeneratorTest.php +++ /dev/null @@ -1,1291 +0,0 @@ -namespace = uniqid('doctrine_'); - $this->tmpDir = sys_get_temp_dir(); - mkdir($this->tmpDir . DIRECTORY_SEPARATOR . $this->namespace); - $this->generator = new EntityGenerator(); - $this->generator->setGenerateAnnotations(true); - $this->generator->setGenerateStubMethods(true); - $this->generator->setRegenerateEntityIfExists(false); - $this->generator->setUpdateEntityIfExists(true); - $this->generator->setFieldVisibility(EntityGenerator::FIELD_VISIBLE_PROTECTED); - } - - public function tearDown(): void - { - $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->tmpDir . '/' . $this->namespace)); - foreach ($ri as $file) { - assert($file instanceof SplFileInfo); - if ($file->isFile()) { - unlink($file->getPathname()); - } - } - - rmdir($this->tmpDir . '/' . $this->namespace); - } - - /** @param ClassMetadata[] $embeddedClasses */ - public function generateBookEntityFixture(array $embeddedClasses = []): ClassMetadata - { - $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook'); - $metadata->namespace = $this->namespace; - $metadata->customRepositoryClassName = $this->namespace . '\EntityGeneratorBookRepository'; - - $metadata->table['name'] = 'book'; - $metadata->table['uniqueConstraints']['name_uniq'] = ['columns' => ['name']]; - $metadata->table['indexes']['status_idx'] = ['columns' => ['status']]; - $metadata->mapField(['fieldName' => 'name', 'type' => 'string']); - $metadata->mapField(['fieldName' => 'status', 'type' => 'string', 'options' => ['default' => 'published']]); - $metadata->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]); - $metadata->mapOneToOne( - ['fieldName' => 'author', 'targetEntity' => EntityGeneratorAuthor::class, 'mappedBy' => 'book'] - ); - $metadata->mapManyToMany( - [ - 'fieldName' => 'comments', - 'targetEntity' => EntityGeneratorComment::class, - 'fetch' => ClassMetadata::FETCH_EXTRA_LAZY, - 'joinTable' => [ - 'name' => 'book_comment', - 'joinColumns' => [['name' => 'book_id', 'referencedColumnName' => 'id']], - 'inverseJoinColumns' => [['name' => 'comment_id', 'referencedColumnName' => 'id']], - ], - ] - ); - $metadata->addLifecycleCallback('loading', 'postLoad'); - $metadata->addLifecycleCallback('logChange', 'prePersist'); - $metadata->addLifecycleCallback('logChange', 'preRemove'); - $metadata->addLifecycleCallback('logchange', 'preUpdate'); - $metadata->addLifecycleCallback('willBeRemoved', 'preRemove'); - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - - foreach ($embeddedClasses as $fieldName => $embeddedClass) { - $this->mapNestedEmbedded($fieldName, $metadata, $embeddedClass); - $this->mapEmbedded($fieldName, $metadata, $embeddedClass); - } - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - return $metadata; - } - - private function generateEntityTypeFixture(array $field): ClassMetadata - { - $metadata = new ClassMetadata($this->namespace . '\EntityType'); - $metadata->namespace = $this->namespace; - - $metadata->table['name'] = 'entity_type'; - $metadata->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]); - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); - - $name = $field['fieldName']; - $type = $field['dbType']; - $metadata->mapField(['fieldName' => $name, 'type' => $type]); - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - return $metadata; - } - - private function generateIsbnEmbeddableFixture(array $embeddedClasses = [], $columnPrefix = null): ClassMetadata - { - $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorIsbn'); - $metadata->namespace = $this->namespace; - $metadata->isEmbeddedClass = true; - $metadata->mapField(['fieldName' => 'prefix', 'type' => 'integer']); - $metadata->mapField(['fieldName' => 'groupNumber', 'type' => 'integer']); - $metadata->mapField(['fieldName' => 'publisherNumber', 'type' => 'integer']); - $metadata->mapField(['fieldName' => 'titleNumber', 'type' => 'integer']); - $metadata->mapField(['fieldName' => 'checkDigit', 'type' => 'integer']); - - foreach ($embeddedClasses as $fieldName => $embeddedClass) { - $this->mapEmbedded($fieldName, $metadata, $embeddedClass, $columnPrefix); - } - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - return $metadata; - } - - private function generateTestEmbeddableFixture(): ClassMetadata - { - $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorTestEmbeddable'); - $metadata->namespace = $this->namespace; - $metadata->isEmbeddedClass = true; - $metadata->mapField(['fieldName' => 'field1', 'type' => 'integer']); - $metadata->mapField(['fieldName' => 'field2', 'type' => 'integer', 'nullable' => true]); - $metadata->mapField(['fieldName' => 'field3', 'type' => 'datetime']); - $metadata->mapField(['fieldName' => 'field4', 'type' => 'datetime', 'nullable' => true]); - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - return $metadata; - } - - /** @param string|bool $columnPrefix */ - private function mapEmbedded( - string $fieldName, - ClassMetadata $classMetadata, - ClassMetadata $embeddableMetadata, - $columnPrefix = false - ): void { - $classMetadata->mapEmbedded( - ['fieldName' => $fieldName, 'class' => $embeddableMetadata->name, 'columnPrefix' => $columnPrefix] - ); - } - - private function mapNestedEmbedded( - string $fieldName, - ClassMetadata $classMetadata, - ClassMetadata $embeddableMetadata - ): void { - foreach ($embeddableMetadata->embeddedClasses as $property => $embeddableClass) { - $classMetadata->mapEmbedded( - [ - 'fieldName' => $fieldName . '.' . $property, - 'class' => $embeddableClass['class'], - 'columnPrefix' => $embeddableClass['columnPrefix'], - 'declaredField' => $embeddableClass['declaredField'] - ? $fieldName . '.' . $embeddableClass['declaredField'] - : $fieldName, - 'originalField' => $embeddableClass['originalField'] ?: $property, - ] - ); - } - } - - private function loadEntityClass(ClassMetadata $metadata): void - { - $className = basename(str_replace('\\', '/', $metadata->name)); - $path = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php'; - - self::assertFileExists($path); - - require_once $path; - } - - /** @return mixed An instance of the given metadata's class. */ - public function newInstance(ClassMetadata $metadata) - { - $this->loadEntityClass($metadata); - - return new $metadata->name(); - } - - /** @group GH-6314 */ - public function testEmbeddedEntityWithNamedColumnPrefix(): void - { - $columnPrefix = 'GH6314Prefix_'; - $testMetadata = $this->generateTestEmbeddableFixture(); - $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], $columnPrefix); - $isbnEntity = $this->newInstance($isbnMetadata); - $refClass = new ReflectionClass($isbnEntity); - self::assertTrue($refClass->hasProperty('testEmbedded')); - - $docComment = $refClass->getProperty('testEmbedded')->getDocComment(); - $needle = sprintf('@ORM\Embedded(class="%s", columnPrefix="%s")', $testMetadata->name, $columnPrefix); - self::assertStringContainsString($needle, $docComment); - } - - /** @group GH-6314 */ - public function testEmbeddedEntityWithoutColumnPrefix(): void - { - $testMetadata = $this->generateTestEmbeddableFixture(); - $isbnMetadata = $this->generateIsbnEmbeddableFixture(['testEmbedded' => $testMetadata], false); - $isbnEntity = $this->newInstance($isbnMetadata); - $refClass = new ReflectionClass($isbnEntity); - self::assertTrue($refClass->hasProperty('testEmbedded')); - - $docComment = $refClass->getProperty('testEmbedded')->getDocComment(); - $needle = sprintf('@ORM\Embedded(class="%s", columnPrefix=false)', $testMetadata->name); - self::assertStringContainsString($needle, $docComment); - } - - public function testGeneratedEntityClass(): void - { - $testMetadata = $this->generateTestEmbeddableFixture(); - $isbnMetadata = $this->generateIsbnEmbeddableFixture(['test' => $testMetadata]); - $metadata = $this->generateBookEntityFixture(['isbn' => $isbnMetadata]); - - $book = $this->newInstance($metadata); - self::assertTrue(class_exists($metadata->name), 'Class does not exist.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', '__construct'), 'EntityGeneratorBook::__construct() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getId'), 'EntityGeneratorBook::getId() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setName'), 'EntityGeneratorBook::setName() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getName'), 'EntityGeneratorBook::getName() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setStatus'), 'EntityGeneratorBook::setStatus() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getStatus'), 'EntityGeneratorBook::getStatus() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setAuthor'), 'EntityGeneratorBook::setAuthor() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getAuthor'), 'EntityGeneratorBook::getAuthor() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getComments'), 'EntityGeneratorBook::getComments() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'addComment'), 'EntityGeneratorBook::addComment() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'removeComment'), 'EntityGeneratorBook::removeComment() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'setIsbn'), 'EntityGeneratorBook::setIsbn() missing.'); - self::assertTrue(method_exists($metadata->namespace . '\EntityGeneratorBook', 'getIsbn'), 'EntityGeneratorBook::getIsbn() missing.'); - - $reflClass = new ReflectionClass($metadata->name); - - self::assertCount(6, $reflClass->getProperties()); - self::assertCount(16, $reflClass->getMethods()); - - self::assertEquals('published', $book->getStatus()); - - $book->setName('Jonathan H. Wage'); - self::assertEquals('Jonathan H. Wage', $book->getName()); - - $reflMethod = new ReflectionMethod($metadata->name, 'addComment'); - $addCommentParameters = $reflMethod->getParameters(); - self::assertEquals('comment', $addCommentParameters[0]->getName()); - - $reflMethod = new ReflectionMethod($metadata->name, 'removeComment'); - $removeCommentParameters = $reflMethod->getParameters(); - self::assertEquals('comment', $removeCommentParameters[0]->getName()); - - $author = new EntityGeneratorAuthor(); - $book->setAuthor($author); - self::assertEquals($author, $book->getAuthor()); - - $comment = new EntityGeneratorComment(); - self::assertInstanceOf($metadata->name, $book->addComment($comment)); - self::assertInstanceOf(ArrayCollection::class, $book->getComments()); - self::assertEquals(new ArrayCollection([$comment]), $book->getComments()); - self::assertIsBool($book->removeComment($comment)); - self::assertEquals(new ArrayCollection([]), $book->getComments()); - - $this->newInstance($isbnMetadata); - $isbn = new $isbnMetadata->name(); - - $book->setIsbn($isbn); - self::assertSame($isbn, $book->getIsbn()); - - $reflMethod = new ReflectionMethod($metadata->name, 'setIsbn'); - $reflParameters = $reflMethod->getParameters(); - self::assertEquals($isbnMetadata->name, $reflParameters[0]->getType()->getName()); - } - - public function testBooleanDefaultValue(): void - { - $metadata = $this->generateBookEntityFixture(['isbn' => $this->generateIsbnEmbeddableFixture()]); - - $metadata->mapField(['fieldName' => 'foo', 'type' => 'boolean', 'options' => ['default' => '1']]); - - $testEmbeddableMetadata = $this->generateTestEmbeddableFixture(); - $this->mapEmbedded('testEmbedded', $metadata, $testEmbeddableMetadata); - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - self::assertFileExists($this->tmpDir . '/' . $this->namespace . '/EntityGeneratorBook.php~'); - - $book = $this->newInstance($metadata); - $reflClass = new ReflectionClass($metadata->name); - - self::assertTrue($book->getfoo()); - } - - public function testEntityUpdatingWorks(): void - { - $metadata = $this->generateBookEntityFixture(['isbn' => $this->generateIsbnEmbeddableFixture()]); - - $metadata->mapField(['fieldName' => 'test', 'type' => 'string']); - - $testEmbeddableMetadata = $this->generateTestEmbeddableFixture(); - $this->mapEmbedded('testEmbedded', $metadata, $testEmbeddableMetadata); - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - self::assertFileExists($this->tmpDir . '/' . $this->namespace . '/EntityGeneratorBook.php~'); - - $book = $this->newInstance($metadata); - $reflClass = new ReflectionClass($metadata->name); - - self::assertTrue($reflClass->hasProperty('name'), "Regenerating keeps property 'name'."); - self::assertTrue($reflClass->hasProperty('status'), "Regenerating keeps property 'status'."); - self::assertTrue($reflClass->hasProperty('id'), "Regenerating keeps property 'id'."); - self::assertTrue($reflClass->hasProperty('isbn'), "Regenerating keeps property 'isbn'."); - - self::assertTrue($reflClass->hasProperty('test'), 'Check for property test failed.'); - self::assertTrue($reflClass->getProperty('test')->isProtected(), 'Check for protected property test failed.'); - self::assertTrue($reflClass->hasProperty('testEmbedded'), 'Check for property testEmbedded failed.'); - self::assertTrue($reflClass->getProperty('testEmbedded')->isProtected(), 'Check for protected property testEmbedded failed.'); - self::assertTrue($reflClass->hasMethod('getTest'), "Check for method 'getTest' failed."); - self::assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed."); - self::assertTrue($reflClass->hasMethod('setTest'), "Check for method 'setTest' failed."); - self::assertTrue($reflClass->getMethod('setTest')->isPublic(), "Check for public visibility of method 'setTest' failed."); - self::assertTrue($reflClass->hasMethod('getTestEmbedded'), "Check for method 'getTestEmbedded' failed."); - self::assertTrue( - $reflClass->getMethod('getTestEmbedded')->isPublic(), - "Check for public visibility of method 'getTestEmbedded' failed." - ); - self::assertTrue($reflClass->hasMethod('setTestEmbedded'), "Check for method 'setTestEmbedded' failed."); - self::assertTrue( - $reflClass->getMethod('setTestEmbedded')->isPublic(), - "Check for public visibility of method 'setTestEmbedded' failed." - ); - } - - /** @group DDC-3152 */ - public function testDoesNotRegenerateExistingMethodsWithDifferentCase(): void - { - $metadata = $this->generateBookEntityFixture(['isbn' => $this->generateIsbnEmbeddableFixture()]); - - // Workaround to change existing fields case (just to simulate the use case) - $metadata->fieldMappings['status']['fieldName'] = 'STATUS'; - $metadata->embeddedClasses['ISBN'] = $metadata->embeddedClasses['isbn']; - unset($metadata->embeddedClasses['isbn']); - - // Should not throw a PHP fatal error - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - self::assertFileExists($this->tmpDir . '/' . $this->namespace . '/EntityGeneratorBook.php~'); - - $this->newInstance($metadata); - $reflClass = new ReflectionClass($metadata->name); - - self::assertTrue($reflClass->hasProperty('status')); - self::assertTrue($reflClass->hasProperty('STATUS')); - self::assertTrue($reflClass->hasProperty('isbn')); - self::assertTrue($reflClass->hasProperty('ISBN')); - self::assertTrue($reflClass->hasMethod('getStatus')); - self::assertTrue($reflClass->hasMethod('setStatus')); - self::assertTrue($reflClass->hasMethod('getIsbn')); - self::assertTrue($reflClass->hasMethod('setIsbn')); - } - - /** @group DDC-2121 */ - public function testMethodDocBlockShouldStartWithBackSlash(): void - { - $embeddedMetadata = $this->generateIsbnEmbeddableFixture(); - $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]); - $book = $this->newInstance($metadata); - - $this->assertPhpDocVarType('\Doctrine\Common\Collections\Collection', new ReflectionProperty($book, 'comments')); - $this->assertPhpDocReturnType('\Doctrine\Common\Collections\Collection', new ReflectionMethod($book, 'getComments')); - $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new ReflectionMethod($book, 'addComment')); - $this->assertPhpDocReturnType('EntityGeneratorBook', new ReflectionMethod($book, 'addComment')); - $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorComment', new ReflectionMethod($book, 'removeComment')); - $this->assertPhpDocReturnType('boolean', new ReflectionMethod($book, 'removeComment')); - - $this->assertPhpDocVarType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor', new ReflectionProperty($book, 'author')); - $this->assertPhpDocReturnType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new ReflectionMethod($book, 'getAuthor')); - $this->assertPhpDocParamType('\Doctrine\Tests\ORM\Tools\EntityGeneratorAuthor|null', new ReflectionMethod($book, 'setAuthor')); - - $expectedClassName = '\\' . $embeddedMetadata->name; - $this->assertPhpDocVarType($expectedClassName, new ReflectionProperty($book, 'isbn')); - $this->assertPhpDocReturnType($expectedClassName, new ReflectionMethod($book, 'getIsbn')); - $this->assertPhpDocParamType($expectedClassName, new ReflectionMethod($book, 'setIsbn')); - } - - public function testEntityExtendsStdClass(): void - { - $this->generator->setClassToExtend('stdClass'); - $metadata = $this->generateBookEntityFixture(); - - $book = $this->newInstance($metadata); - self::assertInstanceOf('stdClass', $book); - - $metadata = $this->generateIsbnEmbeddableFixture(); - $isbn = $this->newInstance($metadata); - self::assertInstanceOf('stdClass', $isbn); - } - - public function testLifecycleCallbacks(): void - { - $metadata = $this->generateBookEntityFixture(); - - $book = $this->newInstance($metadata); - $reflClass = new ReflectionClass($metadata->name); - - self::assertTrue($reflClass->hasMethod('loading'), 'Check for postLoad lifecycle callback.'); - self::assertTrue($reflClass->hasMethod('willBeRemoved'), 'Check for preRemove lifecycle callback.'); - self::assertTrue($reflClass->hasMethod('logChange'), 'Check for prePersist and preRemove lifecycle callback.'); - } - - public function testLoadMetadata(): void - { - $embeddedMetadata = $this->generateIsbnEmbeddableFixture(); - $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]); - - $book = $this->newInstance($metadata); - - $reflectionService = new RuntimeReflectionService(); - - $cm = new ClassMetadata($metadata->name); - $cm->initializeReflection($reflectionService); - - $driver = $this->createAnnotationDriver(); - $driver->loadMetadataForClass($cm->name, $cm); - - self::assertEquals($cm->columnNames, $metadata->columnNames); - self::assertEquals($cm->getTableName(), $metadata->getTableName()); - self::assertEqualsIgnoringCase($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks); - self::assertEquals($cm->identifier, $metadata->identifier); - self::assertEquals($cm->idGenerator, $metadata->idGenerator); - self::assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName); - self::assertEquals($cm->embeddedClasses, $metadata->embeddedClasses); - self::assertEquals($cm->isEmbeddedClass, $metadata->isEmbeddedClass); - - self::assertEquals(ClassMetadata::FETCH_EXTRA_LAZY, $cm->associationMappings['comments']['fetch']); - - $this->newInstance($embeddedMetadata); - - $cm = new ClassMetadata($embeddedMetadata->name); - $cm->initializeReflection($reflectionService); - - $driver->loadMetadataForClass($cm->name, $cm); - - self::assertEquals($cm->columnNames, $embeddedMetadata->columnNames); - self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses); - self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass); - } - - public function testLoadPrefixedMetadata(): void - { - $this->generator->setAnnotationPrefix('ORM\\'); - $embeddedMetadata = $this->generateIsbnEmbeddableFixture(); - $metadata = $this->generateBookEntityFixture(['isbn' => $embeddedMetadata]); - - $reader = new AnnotationReader(); - $driver = new AnnotationDriver($reader, []); - - $book = $this->newInstance($metadata); - - $reflectionService = new RuntimeReflectionService(); - - $cm = new ClassMetadata($metadata->name); - $cm->initializeReflection($reflectionService); - - $driver->loadMetadataForClass($cm->name, $cm); - - self::assertEquals($cm->columnNames, $metadata->columnNames); - self::assertEquals($cm->getTableName(), $metadata->getTableName()); - self::assertEqualsIgnoringCase($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks); - self::assertEquals($cm->identifier, $metadata->identifier); - self::assertEquals($cm->idGenerator, $metadata->idGenerator); - self::assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName); - - $this->newInstance($embeddedMetadata); - - $cm = new ClassMetadata($embeddedMetadata->name); - $cm->initializeReflection($reflectionService); - - $driver->loadMetadataForClass($cm->name, $cm); - - self::assertEquals($cm->columnNames, $embeddedMetadata->columnNames); - self::assertEquals($cm->embeddedClasses, $embeddedMetadata->embeddedClasses); - self::assertEquals($cm->isEmbeddedClass, $embeddedMetadata->isEmbeddedClass); - } - - /** @group DDC-3272 */ - public function testMappedSuperclassAnnotationGeneration(): void - { - $metadata = new ClassMetadata($this->namespace . '\EntityGeneratorBook'); - $metadata->namespace = $this->namespace; - $metadata->isMappedSuperclass = true; - - $this->generator->setAnnotationPrefix('ORM\\'); - $this->generator->writeEntityClass($metadata, $this->tmpDir); - $this->newInstance($metadata); // force instantiation (causes autoloading to kick in) - - $driver = new AnnotationDriver(new AnnotationReader(), []); - $cm = new ClassMetadata($metadata->name); - - $cm->initializeReflection(new RuntimeReflectionService()); - $driver->loadMetadataForClass($cm->name, $cm); - - self::assertTrue($cm->isMappedSuperclass); - } - - /** @dataProvider getParseTokensInEntityFileData */ - public function testParseTokensInEntityFile($php, $classes): void - { - $r = new ReflectionObject($this->generator); - $m = $r->getMethod('parseTokensInEntityFile'); - $m->setAccessible(true); - - $p = $r->getProperty('staticReflection'); - $p->setAccessible(true); - - $ret = $m->invoke($this->generator, $php); - self::assertEquals($classes, array_keys($p->getValue($this->generator))); - } - - /** @group DDC-1784 */ - public function testGenerateEntityWithSequenceGenerator(): void - { - $metadata = new ClassMetadata($this->namespace . '\DDC1784Entity'); - $metadata->namespace = $this->namespace; - $metadata->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]); - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); - $metadata->setSequenceGeneratorDefinition( - [ - 'sequenceName' => 'DDC1784_ID_SEQ', - 'allocationSize' => 1, - 'initialValue' => 2, - ] - ); - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - $filename = $this->tmpDir . DIRECTORY_SEPARATOR - . $this->namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php'; - - self::assertFileExists($filename); - require_once $filename; - - $reflection = new ReflectionProperty($metadata->name, 'id'); - $docComment = $reflection->getDocComment(); - - self::assertStringContainsString('@ORM\Id', $docComment); - self::assertStringContainsString('@ORM\Column(name="id", type="integer")', $docComment); - self::assertStringContainsString('@ORM\GeneratedValue(strategy="SEQUENCE")', $docComment); - self::assertStringContainsString( - '@ORM\SequenceGenerator(sequenceName="DDC1784_ID_SEQ", allocationSize=1, initialValue=2)', - $docComment - ); - } - - /** @group DDC-2079 */ - public function testGenerateEntityWithMultipleInverseJoinColumns(): void - { - $metadata = new ClassMetadata($this->namespace . '\DDC2079Entity'); - $metadata->namespace = $this->namespace; - $metadata->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]); - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); - $metadata->mapManyToMany( - [ - 'fieldName' => 'centroCustos', - 'targetEntity' => 'DDC2079CentroCusto', - 'joinTable' => [ - 'name' => 'unidade_centro_custo', - 'joinColumns' => [ - ['name' => 'idorcamento', 'referencedColumnName' => 'idorcamento'], - ['name' => 'idunidade', 'referencedColumnName' => 'idunidade'], - ], - 'inverseJoinColumns' => [ - ['name' => 'idcentrocusto', 'referencedColumnName' => 'idcentrocusto'], - ['name' => 'idpais', 'referencedColumnName' => 'idpais'], - ], - ], - ] - ); - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - $filename = $this->tmpDir . DIRECTORY_SEPARATOR - . $this->namespace . DIRECTORY_SEPARATOR . 'DDC2079Entity.php'; - - self::assertFileExists($filename); - require_once $filename; - - $property = new ReflectionProperty($metadata->name, 'centroCustos'); - $docComment = $property->getDocComment(); - - //joinColumns - self::assertStringContainsString( - '@ORM\JoinColumn(name="idorcamento", referencedColumnName="idorcamento"),', - $docComment - ); - self::assertStringContainsString( - '@ORM\JoinColumn(name="idunidade", referencedColumnName="idunidade")', - $docComment - ); - //inverseJoinColumns - self::assertStringContainsString( - '@ORM\JoinColumn(name="idcentrocusto", referencedColumnName="idcentrocusto"),', - $docComment - ); - self::assertStringContainsString( - '@ORM\JoinColumn(name="idpais", referencedColumnName="idpais")', - $docComment - ); - } - - /** @group DDC-2172 */ - public function testGetInheritanceTypeString(): void - { - $reflection = new ReflectionClass(ClassMetadata::class); - $method = new ReflectionMethod($this->generator, 'getInheritanceTypeString'); - $constants = $reflection->getConstants(); - $pattern = '/^INHERITANCE_TYPE_/'; - - $method->setAccessible(true); - - foreach ($constants as $name => $value) { - if (! preg_match($pattern, $name)) { - continue; - } - - $expected = preg_replace($pattern, '', $name); - $actual = $method->invoke($this->generator, $value); - - self::assertEquals($expected, $actual); - } - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid provided InheritanceType: INVALID'); - - $method->invoke($this->generator, 'INVALID'); - } - - /** @group DDC-2172 */ - public function testGetChangeTrackingPolicyString(): void - { - $reflection = new ReflectionClass('\Doctrine\ORM\Mapping\ClassMetadata'); - $method = new ReflectionMethod($this->generator, 'getChangeTrackingPolicyString'); - $constants = $reflection->getConstants(); - $pattern = '/^CHANGETRACKING_/'; - - $method->setAccessible(true); - - foreach ($constants as $name => $value) { - if (! preg_match($pattern, $name)) { - continue; - } - - $expected = preg_replace($pattern, '', $name); - $actual = $method->invoke($this->generator, $value); - - self::assertEquals($expected, $actual); - } - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid provided ChangeTrackingPolicy: INVALID'); - - $method->invoke($this->generator, 'INVALID'); - } - - /** @group DDC-2172 */ - public function testGetIdGeneratorTypeString(): void - { - $reflection = new ReflectionClass(ClassMetadata::class); - $method = new ReflectionMethod($this->generator, 'getIdGeneratorTypeString'); - $constants = $reflection->getConstants(); - $pattern = '/^GENERATOR_TYPE_/'; - - $method->setAccessible(true); - - foreach ($constants as $name => $value) { - if (! preg_match($pattern, $name)) { - continue; - } - - if ($name === 'GENERATOR_TYPE_TABLE') { - continue; - } - - $expected = preg_replace($pattern, '', $name); - $actual = $method->invoke($this->generator, $value); - - self::assertEquals($expected, $actual); - } - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid provided IdGeneratorType: INVALID'); - - $method->invoke($this->generator, 'INVALID'); - } - - /** - * @dataProvider getEntityTypeAliasDataProvider - * @group DDC-1694 - */ - public function testEntityTypeAlias(array $field): void - { - $metadata = $this->generateEntityTypeFixture($field); - $path = $this->tmpDir . '/' . $this->namespace . '/EntityType.php'; - - self::assertFileExists($path); - require_once $path; - - $entity = new $metadata->name(); - $reflClass = new ReflectionClass($metadata->name); - - $type = $field['phpType']; - $name = $field['fieldName']; - $value = $field['value']; - $getter = 'get' . ucfirst($name); - $setter = 'set' . ucfirst($name); - - $this->assertPhpDocVarType($type, $reflClass->getProperty($name)); - $this->assertPhpDocParamType($type, $reflClass->getMethod($setter)); - $this->assertPhpDocReturnType($type, $reflClass->getMethod($getter)); - - self::assertSame($entity, $entity->{$setter}($value)); - self::assertEquals($value, $entity->{$getter}()); - } - - /** @group DDC-2372 */ - public function testTraitPropertiesAndMethodsAreNotDuplicated(): void - { - $cmf = new ClassMetadataFactory(); - $em = $this->getTestEntityManager(); - $cmf->setEntityManager($em); - - $user = new DDC2372User(); - $metadata = $cmf->getMetadataFor(get_class($user)); - $metadata->name = $this->namespace . '\DDC2372User'; - $metadata->namespace = $this->namespace; - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - self::assertFileExists($this->tmpDir . '/' . $this->namespace . '/DDC2372User.php'); - require $this->tmpDir . '/' . $this->namespace . '/DDC2372User.php'; - - $reflClass = new ReflectionClass($metadata->name); - - self::assertFalse($reflClass->hasProperty('address')); - self::assertFalse($reflClass->hasMethod('setAddress')); - self::assertFalse($reflClass->hasMethod('getAddress')); - } - - /** @group DDC-2372 */ - public function testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses(): void - { - $cmf = new ClassMetadataFactory(); - $em = $this->getTestEntityManager(); - $cmf->setEntityManager($em); - - $user = new DDC2372Admin(); - $metadata = $cmf->getMetadataFor(get_class($user)); - $metadata->name = $this->namespace . '\DDC2372Admin'; - $metadata->namespace = $this->namespace; - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - self::assertFileExists($this->tmpDir . '/' . $this->namespace . '/DDC2372Admin.php'); - require $this->tmpDir . '/' . $this->namespace . '/DDC2372Admin.php'; - - $reflClass = new ReflectionClass($metadata->name); - - self::assertFalse($reflClass->hasProperty('address')); - self::assertFalse($reflClass->hasMethod('setAddress')); - self::assertFalse($reflClass->hasMethod('getAddress')); - } - - /** @group DDC-1590 */ - public function testMethodsAndPropertiesAreNotDuplicatedInChildClasses(): void - { - $cmf = new ClassMetadataFactory(); - $em = $this->getTestEntityManager(); - - $cmf->setEntityManager($em); - - $ns = $this->namespace; - $nsdir = $this->tmpDir . '/' . $ns; - - $content = str_replace( - 'namespace Doctrine\Tests\Models\DDC1590', - 'namespace ' . $ns, - file_get_contents(__DIR__ . '/../../Models/DDC1590/DDC1590User.php') - ); - - $fname = $nsdir . '/DDC1590User.php'; - file_put_contents($fname, $content); - require $fname; - - $metadata = $cmf->getMetadataFor($ns . '\DDC1590User'); - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - // class DDC1590User extends DDC1590Entity { ... } - $source = file_get_contents($fname); - - // class _DDC1590User extends DDC1590Entity { ... } - $source2 = str_replace('class DDC1590User', 'class _DDC1590User', $source); - $fname2 = $nsdir . '/_DDC1590User.php'; - file_put_contents($fname2, $source2); - require $fname2; - - // class __DDC1590User { ... } - $source3 = str_replace('class DDC1590User extends DDC1590Entity', 'class __DDC1590User', $source); - $fname3 = $nsdir . '/__DDC1590User.php'; - file_put_contents($fname3, $source3); - require $fname3; - - // class _DDC1590User extends DDC1590Entity { ... } - $rc2 = new ReflectionClass($ns . '\_DDC1590User'); - - self::assertTrue($rc2->hasProperty('name')); - self::assertTrue($rc2->hasProperty('id')); - self::assertTrue($rc2->hasProperty('createdAt')); - - self::assertTrue($rc2->hasMethod('getName')); - self::assertTrue($rc2->hasMethod('setName')); - self::assertTrue($rc2->hasMethod('getId')); - self::assertFalse($rc2->hasMethod('setId')); - self::assertTrue($rc2->hasMethod('getCreatedAt')); - self::assertTrue($rc2->hasMethod('setCreatedAt')); - - // class __DDC1590User { ... } - $rc3 = new ReflectionClass($ns . '\__DDC1590User'); - - self::assertTrue($rc3->hasProperty('name')); - self::assertFalse($rc3->hasProperty('id')); - self::assertFalse($rc3->hasProperty('createdAt')); - - self::assertTrue($rc3->hasMethod('getName')); - self::assertTrue($rc3->hasMethod('setName')); - self::assertFalse($rc3->hasMethod('getId')); - self::assertFalse($rc3->hasMethod('setId')); - self::assertFalse($rc3->hasMethod('getCreatedAt')); - self::assertFalse($rc3->hasMethod('setCreatedAt')); - } - - /** @group DDC-3304 */ - public function testGeneratedMutableEmbeddablesClass(): void - { - $embeddedMetadata = $this->generateTestEmbeddableFixture(); - $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]); - - $isbn = $this->newInstance($metadata); - - self::assertTrue(class_exists($metadata->name), 'Class does not exist.'); - self::assertFalse(method_exists($metadata->name, '__construct'), 'EntityGeneratorIsbn::__construct present.'); - self::assertTrue(method_exists($metadata->name, 'getPrefix'), 'EntityGeneratorIsbn::getPrefix() missing.'); - self::assertTrue(method_exists($metadata->name, 'setPrefix'), 'EntityGeneratorIsbn::setPrefix() missing.'); - self::assertTrue(method_exists($metadata->name, 'getGroupNumber'), 'EntityGeneratorIsbn::getGroupNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'setGroupNumber'), 'EntityGeneratorIsbn::setGroupNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'getPublisherNumber'), 'EntityGeneratorIsbn::getPublisherNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'setPublisherNumber'), 'EntityGeneratorIsbn::setPublisherNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'getTitleNumber'), 'EntityGeneratorIsbn::getTitleNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'setTitleNumber'), 'EntityGeneratorIsbn::setTitleNumber() missing.'); - self::assertTrue(method_exists($metadata->name, 'getCheckDigit'), 'EntityGeneratorIsbn::getCheckDigit() missing.'); - self::assertTrue(method_exists($metadata->name, 'setCheckDigit'), 'EntityGeneratorIsbn::setCheckDigit() missing.'); - self::assertTrue(method_exists($metadata->name, 'getTest'), 'EntityGeneratorIsbn::getTest() missing.'); - self::assertTrue(method_exists($metadata->name, 'setTest'), 'EntityGeneratorIsbn::setTest() missing.'); - - $isbn->setPrefix(978); - self::assertSame(978, $isbn->getPrefix()); - - $this->newInstance($embeddedMetadata); - $test = new $embeddedMetadata->name(); - - $isbn->setTest($test); - self::assertSame($test, $isbn->getTest()); - - $reflMethod = new ReflectionMethod($metadata->name, 'setTest'); - $reflParameters = $reflMethod->getParameters(); - self::assertEquals($embeddedMetadata->name, $reflParameters[0]->getType()->getName()); - } - - /** @group DDC-3304 */ - public function testGeneratedImmutableEmbeddablesClass(): void - { - $this->generator->setEmbeddablesImmutable(true); - $embeddedMetadata = $this->generateTestEmbeddableFixture(); - $metadata = $this->generateIsbnEmbeddableFixture(['test' => $embeddedMetadata]); - - $this->loadEntityClass($embeddedMetadata); - $this->loadEntityClass($metadata); - - self::assertTrue(class_exists($metadata->name), 'Class does not exist.'); - self::assertTrue(method_exists($metadata->name, '__construct'), 'EntityGeneratorIsbn::__construct missing.'); - self::assertTrue(method_exists($metadata->name, 'getPrefix'), 'EntityGeneratorIsbn::getPrefix() missing.'); - self::assertFalse(method_exists($metadata->name, 'setPrefix'), 'EntityGeneratorIsbn::setPrefix() present.'); - self::assertTrue(method_exists($metadata->name, 'getGroupNumber'), 'EntityGeneratorIsbn::getGroupNumber() missing.'); - self::assertFalse(method_exists($metadata->name, 'setGroupNumber'), 'EntityGeneratorIsbn::setGroupNumber() present.'); - self::assertTrue(method_exists($metadata->name, 'getPublisherNumber'), 'EntityGeneratorIsbn::getPublisherNumber() missing.'); - self::assertFalse(method_exists($metadata->name, 'setPublisherNumber'), 'EntityGeneratorIsbn::setPublisherNumber() present.'); - self::assertTrue(method_exists($metadata->name, 'getTitleNumber'), 'EntityGeneratorIsbn::getTitleNumber() missing.'); - self::assertFalse(method_exists($metadata->name, 'setTitleNumber'), 'EntityGeneratorIsbn::setTitleNumber() present.'); - self::assertTrue(method_exists($metadata->name, 'getCheckDigit'), 'EntityGeneratorIsbn::getCheckDigit() missing.'); - self::assertFalse(method_exists($metadata->name, 'setCheckDigit'), 'EntityGeneratorIsbn::setCheckDigit() present.'); - self::assertTrue(method_exists($metadata->name, 'getTest'), 'EntityGeneratorIsbn::getTest() missing.'); - self::assertFalse(method_exists($metadata->name, 'setTest'), 'EntityGeneratorIsbn::setTest() present.'); - - $test = new $embeddedMetadata->name(1, new DateTime()); - $isbn = new $metadata->name($test, 978, 3, 12, 732320, 83); - - $reflMethod = new ReflectionMethod($isbn, '__construct'); - $reflParameters = $reflMethod->getParameters(); - - self::assertCount(6, $reflParameters); - - self::assertSame($embeddedMetadata->name, $reflParameters[0]->getType()->getName()); - self::assertSame('test', $reflParameters[0]->getName()); - self::assertFalse($reflParameters[0]->isOptional()); - - self::assertSame('prefix', $reflParameters[1]->getName()); - self::assertFalse($reflParameters[1]->isOptional()); - - self::assertSame('groupNumber', $reflParameters[2]->getName()); - self::assertFalse($reflParameters[2]->isOptional()); - - self::assertSame('publisherNumber', $reflParameters[3]->getName()); - self::assertFalse($reflParameters[3]->isOptional()); - - self::assertSame('titleNumber', $reflParameters[4]->getName()); - self::assertFalse($reflParameters[4]->isOptional()); - - self::assertSame('checkDigit', $reflParameters[5]->getName()); - self::assertFalse($reflParameters[5]->isOptional()); - - $reflMethod = new ReflectionMethod($test, '__construct'); - $reflParameters = $reflMethod->getParameters(); - - self::assertCount(4, $reflParameters); - - self::assertSame('field1', $reflParameters[0]->getName()); - self::assertFalse($reflParameters[0]->isOptional()); - - self::assertSame('DateTime', $reflParameters[1]->getType()->getName()); - self::assertSame('field3', $reflParameters[1]->getName()); - self::assertFalse($reflParameters[1]->isOptional()); - - self::assertSame('field2', $reflParameters[2]->getName()); - self::assertTrue($reflParameters[2]->isOptional()); - - self::assertSame('DateTime', $reflParameters[3]->getType()->getName()); - self::assertSame('field4', $reflParameters[3]->getName()); - self::assertTrue($reflParameters[3]->isOptional()); - } - - public function testRegenerateEntityClass(): void - { - $metadata = $this->generateBookEntityFixture(); - $this->loadEntityClass($metadata); - - $className = basename(str_replace('\\', '/', $metadata->name)); - $path = $this->tmpDir . '/' . $this->namespace . '/' . $className . '.php'; - $classTest = file_get_contents($path); - - $this->generator->setRegenerateEntityIfExists(true); - $this->generator->setBackupExisting(false); - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - $classNew = file_get_contents($path); - - self::assertSame($classTest, $classNew); - } - - /** - * @return mixed[] - * @phpstan-return list - */ - public static function getEntityTypeAliasDataProvider(): array - { - return [ - [ - [ - 'fieldName' => 'datetimetz', - 'phpType' => '\\DateTime', - 'dbType' => 'datetimetz', - 'value' => new DateTime(), - ], - ], - [ - [ - 'fieldName' => 'datetime', - 'phpType' => '\\DateTime', - 'dbType' => 'datetime', - 'value' => new DateTime(), - ], - ], - [ - [ - 'fieldName' => 'date', - 'phpType' => '\\DateTime', - 'dbType' => 'date', - 'value' => new DateTime(), - ], - ], - [ - [ - 'fieldName' => 'time', - 'phpType' => '\DateTime', - 'dbType' => 'time', - 'value' => new DateTime(), - ], - ], - [ - [ - 'fieldName' => 'object', - 'phpType' => '\stdClass', - 'dbType' => 'object', - 'value' => new stdClass(), - ], - ], - [ - [ - 'fieldName' => 'bigint', - 'phpType' => 'int', - 'dbType' => 'bigint', - 'value' => 11, - ], - ], - [ - [ - 'fieldName' => 'smallint', - 'phpType' => 'int', - 'dbType' => 'smallint', - 'value' => 22, - ], - ], - [ - [ - 'fieldName' => 'text', - 'phpType' => 'string', - 'dbType' => 'text', - 'value' => 'text', - ], - ], - [ - [ - 'fieldName' => 'blob', - 'phpType' => 'string', - 'dbType' => 'blob', - 'value' => 'blob', - ], - ], - [ - [ - 'fieldName' => 'guid', - 'phpType' => 'string', - 'dbType' => 'guid', - 'value' => '00000000-0000-0000-0000-000000000001', - ], - ], - [ - [ - 'fieldName' => 'decimal', - 'phpType' => 'string', - 'dbType' => 'decimal', - 'value' => '12.34', - ], - ], - ]; - } - - /** @phpstan-return list */ - public static function getParseTokensInEntityFileData(): array - { - return [ - [ - 'getDocComment(); - $regex = '/@var\s+([\S]+)$/m'; - - self::assertMatchesRegularExpression($regex, $docComment); - self::assertEquals(1, preg_match($regex, $docComment, $matches)); - self::assertEquals($type, $matches[1]); - } - - private function assertPhpDocReturnType(string $type, ReflectionMethod $method): void - { - $docComment = $method->getDocComment(); - $regex = '/@return\s+([\S]+)(\s+.*)$/m'; - - self::assertMatchesRegularExpression($regex, $docComment); - self::assertEquals(1, preg_match($regex, $docComment, $matches)); - self::assertEquals($type, $matches[1]); - } - - private function assertPhpDocParamType(string $type, ReflectionMethod $method): void - { - self::assertEquals(1, preg_match('/@param\s+([^\s]+)/', $method->getDocComment(), $matches)); - self::assertEquals($type, $matches[1]); - } - - /** - * @group 6703 - * @dataProvider columnOptionsProvider - */ - public function testOptionsAreGeneratedProperly(string $expectedAnnotation, array $fieldConfiguration): void - { - $metadata = new ClassMetadata($this->namespace . '\GH6703Options'); - $metadata->namespace = $this->namespace; - $metadata->mapField(['fieldName' => 'id', 'type' => 'integer', 'id' => true]); - $metadata->mapField(['fieldName' => 'test'] + $fieldConfiguration); - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - $filename = $this->tmpDir . DIRECTORY_SEPARATOR . $this->namespace . DIRECTORY_SEPARATOR . 'GH6703Options.php'; - - self::assertFileExists($filename); - require_once $filename; - - $property = new ReflectionProperty($metadata->name, 'test'); - $docComment = $property->getDocComment(); - - self::assertStringContainsString($expectedAnnotation, $docComment); - } - - public static function columnOptionsProvider(): array - { - return [ - 'string-default' => [ - '@ORM\Column(name="test", type="string", length=10, options={"default"="testing"})', - ['type' => 'string', 'length' => 10, 'options' => ['default' => 'testing']], - ], - 'string-fixed' => [ - '@ORM\Column(name="test", type="string", length=10, options={"fixed"=true})', - ['type' => 'string', 'length' => 10, 'options' => ['fixed' => true]], - ], - 'string-comment' => [ - '@ORM\Column(name="test", type="string", length=10, options={"comment"="testing"})', - ['type' => 'string', 'length' => 10, 'options' => ['comment' => 'testing']], - ], - 'string-comment-quote' => [ - '@ORM\Column(name="test", type="string", length=10, options={"comment"="testing ""quotes"""})', - ['type' => 'string', 'length' => 10, 'options' => ['comment' => 'testing "quotes"']], - ], - 'string-collation' => [ - '@ORM\Column(name="test", type="string", length=10, options={"collation"="utf8mb4_general_ci"})', - ['type' => 'string', 'length' => 10, 'options' => ['collation' => 'utf8mb4_general_ci']], - ], - 'string-check' => [ - '@ORM\Column(name="test", type="string", length=10, options={"check"="CHECK (test IN (""test""))"})', - ['type' => 'string', 'length' => 10, 'options' => ['check' => 'CHECK (test IN (""test""))']], - ], - 'string-all' => [ - '@ORM\Column(name="test", type="string", length=10, options={"default"="testing","fixed"=true,"comment"="testing","collation"="utf8mb4_general_ci","check"="CHECK (test IN (""test""))"})', - [ - 'type' => 'string', - 'length' => 10, - 'options' => [ - 'default' => 'testing', - 'fixed' => true, - 'comment' => 'testing', - 'collation' => 'utf8mb4_general_ci', - 'check' => 'CHECK (test IN (""test""))', - ], - ], - ], - 'int-default' => [ - '@ORM\Column(name="test", type="integer", options={"default"="10"})', - ['type' => 'integer', 'options' => ['default' => 10]], - ], - 'int-unsigned' => [ - '@ORM\Column(name="test", type="integer", options={"unsigned"=true})', - ['type' => 'integer', 'options' => ['unsigned' => true]], - ], - 'int-comment' => [ - '@ORM\Column(name="test", type="integer", options={"comment"="testing"})', - ['type' => 'integer', 'options' => ['comment' => 'testing']], - ], - 'int-check' => [ - '@ORM\Column(name="test", type="integer", options={"check"="CHECK (test > 5)"})', - ['type' => 'integer', 'options' => ['check' => 'CHECK (test > 5)']], - ], - 'int-all' => [ - '@ORM\Column(name="test", type="integer", options={"default"="10","unsigned"=true,"comment"="testing","check"="CHECK (test > 5)"})', - [ - 'type' => 'integer', - 'options' => [ - 'default' => 10, - 'unsigned' => true, - 'comment' => 'testing', - 'check' => 'CHECK (test > 5)', - ], - ], - ], - ]; - } -} - -class EntityGeneratorAuthor -{ -} -class EntityGeneratorComment -{ -} diff --git a/tests/Tests/ORM/Tools/EntityRepositoryGeneratorTest.php b/tests/Tests/ORM/Tools/EntityRepositoryGeneratorTest.php deleted file mode 100644 index 69f0594631f..00000000000 --- a/tests/Tests/ORM/Tools/EntityRepositoryGeneratorTest.php +++ /dev/null @@ -1,188 +0,0 @@ -namespace = uniqid('doctrine_'); - $this->tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->namespace; - mkdir($this->tmpDir); - - $this->generator = new EntityGenerator(); - $this->generator->setGenerateAnnotations(true); - $this->generator->setGenerateStubMethods(true); - $this->generator->setRegenerateEntityIfExists(false); - $this->generator->setUpdateEntityIfExists(true); - $this->generator->setFieldVisibility(EntityGenerator::FIELD_VISIBLE_PROTECTED); - - $this->repositoryGenerator = new EntityRepositoryGenerator(); - } - - public function tearDown(): void - { - $dirs = []; - - $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->tmpDir)); - foreach ($ri as $file) { - assert($file instanceof SplFileInfo); - if ($file->isFile()) { - unlink($file->getPathname()); - } elseif ($file->getBasename() === '.') { - $dirs[] = $file->getRealPath(); - } - } - - arsort($dirs); - - foreach ($dirs as $dir) { - rmdir($dir); - } - } - - /** @group DDC-3231 */ - public function testGeneratedEntityRepositoryClass(): void - { - $em = $this->getTestEntityManager(); - $ns = $this->namespace; - - $className = $ns . '\DDC3231User1Tmp'; - $this->writeEntityClass(DDC3231User1::class, $className); - - $rpath = $this->writeRepositoryClass($className); - - self::assertFileExists($rpath); - - require $rpath; - - $repo = new ReflectionClass($em->getRepository($className)); - - self::assertTrue($repo->inNamespace()); - self::assertSame($className . 'Repository', $repo->getName()); - self::assertSame(EntityRepository::class, $repo->getParentClass()->getName()); - - require_once __DIR__ . '/../../Models/DDC3231/DDC3231User1NoNamespace.php'; - - $className2 = 'DDC3231User1NoNamespaceTmp'; - $this->writeEntityClass(DDC3231User1NoNamespace::class, $className2); - - $rpath2 = $this->writeRepositoryClass($className2); - - self::assertFileExists($rpath2); - - require $rpath2; - - $repo2 = new ReflectionClass($em->getRepository($className2)); - - self::assertFalse($repo2->inNamespace()); - self::assertSame($className2 . 'Repository', $repo2->getName()); - self::assertSame(EntityRepository::class, $repo2->getParentClass()->getName()); - } - - /** @group DDC-3231 */ - public function testGeneratedEntityRepositoryClassCustomDefaultRepository(): void - { - $em = $this->getTestEntityManager(); - $ns = $this->namespace; - - $className = $ns . '\DDC3231User2Tmp'; - $this->writeEntityClass(DDC3231User2::class, $className); - - $rpath = $this->writeRepositoryClass($className, DDC3231EntityRepository::class); - - self::assertNotNull($rpath); - self::assertFileExists($rpath); - - require $rpath; - - $repo = new ReflectionClass($em->getRepository($className)); - - self::assertTrue($repo->inNamespace()); - self::assertSame($className . 'Repository', $repo->getName()); - self::assertSame(DDC3231EntityRepository::class, $repo->getParentClass()->getName()); - - require_once __DIR__ . '/../../Models/DDC3231/DDC3231User2NoNamespace.php'; - - $className2 = 'DDC3231User2NoNamespaceTmp'; - $this->writeEntityClass('DDC3231User2NoNamespace', $className2); - - $rpath2 = $this->writeRepositoryClass($className2, DDC3231EntityRepository::class); - - self::assertNotNull($rpath2); - self::assertFileExists($rpath2); - - require $rpath2; - - $repo2 = new ReflectionClass($em->getRepository($className2)); - - self::assertFalse($repo2->inNamespace()); - self::assertSame($className2 . 'Repository', $repo2->getName()); - self::assertSame(DDC3231EntityRepository::class, $repo2->getParentClass()->getName()); - } - - private function writeEntityClass(string $className, string $newClassName): void - { - $cmf = new ClassMetadataFactory(); - $em = $this->getTestEntityManager(); - - $cmf->setEntityManager($em); - - $metadata = $cmf->getMetadataFor($className); - $metadata->namespace = $this->namespace; - $metadata->name = $newClassName; - $metadata->customRepositoryClassName = $newClassName . 'Repository'; - - $this->generator->writeEntityClass($metadata, $this->tmpDir); - - require $this->tmpDir . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $newClassName) . '.php'; - } - - private function writeRepositoryClass(string $className, ?string $defaultRepository = null): string - { - $this->repositoryGenerator->setDefaultRepositoryName($defaultRepository); - - $this->repositoryGenerator->writeEntityRepositoryClass($className . 'Repository', $this->tmpDir); - - return $this->tmpDir . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . 'Repository.php'; - } -} diff --git a/tests/Tests/ORM/Tools/Export/AnnotationClassMetadataExporterTest.php b/tests/Tests/ORM/Tools/Export/AnnotationClassMetadataExporterTest.php deleted file mode 100644 index 7f4d6cfe5c3..00000000000 --- a/tests/Tests/ORM/Tools/Export/AnnotationClassMetadataExporterTest.php +++ /dev/null @@ -1,18 +0,0 @@ -createMock(AbstractPlatform::class); - $platform->method('supportsIdentityColumns') - ->willReturn(true); - - $connection = $this->createMock(Connection::class); - $connection->method('getDatabasePlatform') - ->willReturn($platform); - $connection->method('getEventManager') - ->willReturn(new EventManager()); - - $config = new Configuration(); - TestUtil::configureProxies($config); - $config->setMetadataDriverImpl($metadataDriver); - - return new EntityManagerMock($connection, $config); - } - - protected function createMetadataDriver(string $type, string $path): MappingDriver - { - $mappingDriver = [ - 'php' => PHPDriver::class, - 'annotation' => AnnotationDriver::class, - 'xml' => XmlDriver::class, - 'yaml' => YamlDriver::class, - ]; - - self::assertArrayHasKey($type, $mappingDriver, "There is no metadata driver for the type '" . $type . "'."); - - $class = $mappingDriver[$type]; - - return $type === 'annotation' - ? $this->createAnnotationDriver([$path]) - : new $class($path); - } - - protected function createClassMetadataFactory(EntityManagerInterface $em, string $type): ClassMetadataFactory - { - $factory = $type === 'annotation' - ? new ClassMetadataFactory() - : new DisconnectedClassMetadataFactory(); - - $factory->setEntityManager($em); - - return $factory; - } - - public function testExportDirectoryAndFilesAreCreated(): void - { - $this->deleteDirectory(__DIR__ . '/export/' . $this->getType()); - - $type = $this->getType(); - $metadataDriver = $this->createMetadataDriver($type, __DIR__ . '/' . $type); - $em = $this->createEntityManager($metadataDriver); - $cmf = $this->createClassMetadataFactory($em, $type); - $metadata = $cmf->getAllMetadata(); - - $metadata[0]->name = ExportedUser::class; - - self::assertEquals(ExportedUser::class, $metadata[0]->name); - - $type = $this->getType(); - $cme = new ClassMetadataExporter(); - $exporter = $cme->getExporter($type, __DIR__ . '/export/' . $type); - - if ($type === 'annotation') { - $entityGenerator = new EntityGenerator(); - - $exporter->setEntityGenerator($entityGenerator); - } - - $this->extension = $exporter->getExtension(); - - $exporter->setMetadata($metadata); - $exporter->export(); - - if ($type === 'annotation') { - self::assertFileExists(__DIR__ . '/export/' . $type . '/' . str_replace('\\', '/', ExportedUser::class) . $this->extension); - } else { - self::assertFileExists(__DIR__ . '/export/' . $type . '/Doctrine.Tests.ORM.Tools.Export.ExportedUser' . $this->extension); - } - } - - /** @depends testExportDirectoryAndFilesAreCreated */ - public function testExportedMetadataCanBeReadBackIn(): ClassMetadata - { - $type = $this->getType(); - - $metadataDriver = $this->createMetadataDriver($type, __DIR__ . '/export/' . $type); - $em = $this->createEntityManager($metadataDriver); - $cmf = $this->createClassMetadataFactory($em, $type); - $metadata = $cmf->getAllMetadata(); - - self::assertCount(1, $metadata); - - $class = current($metadata); - - self::assertEquals(ExportedUser::class, $class->name); - - return $class; - } - - /** @depends testExportedMetadataCanBeReadBackIn */ - public function testTableIsExported(ClassMetadata $class): ClassMetadata - { - self::assertEquals('cms_users', $class->table['name']); - self::assertEquals( - ['engine' => 'MyISAM', 'foo' => ['bar' => 'baz']], - $class->table['options'] - ); - - return $class; - } - - /** @depends testTableIsExported */ - public function testTypeIsExported(ClassMetadata $class): ClassMetadata - { - self::assertFalse($class->isMappedSuperclass); - - return $class; - } - - /** @depends testTypeIsExported */ - public function testIdentifierIsExported(ClassMetadata $class): ClassMetadata - { - self::assertEquals(ClassMetadata::GENERATOR_TYPE_IDENTITY, $class->generatorType, 'Generator Type wrong'); - self::assertEquals(['id'], $class->identifier); - self::assertTrue(isset($class->fieldMappings['id']['id']) && $class->fieldMappings['id']['id'] === true); - - return $class; - } - - /** @depends testIdentifierIsExported */ - public function testFieldsAreExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue(isset($class->fieldMappings['id']['id']) && $class->fieldMappings['id']['id'] === true); - self::assertEquals('id', $class->fieldMappings['id']['fieldName']); - self::assertEquals('integer', $class->fieldMappings['id']['type']); - self::assertEquals('id', $class->fieldMappings['id']['columnName']); - - self::assertEquals('name', $class->fieldMappings['name']['fieldName']); - self::assertEquals('string', $class->fieldMappings['name']['type']); - self::assertEquals(50, $class->fieldMappings['name']['length']); - self::assertEquals('name', $class->fieldMappings['name']['columnName']); - - self::assertEquals('email', $class->fieldMappings['email']['fieldName']); - self::assertEquals('string', $class->fieldMappings['email']['type']); - self::assertEquals('user_email', $class->fieldMappings['email']['columnName']); - self::assertEquals('CHAR(32) NOT NULL', $class->fieldMappings['email']['columnDefinition']); - - self::assertTrue($class->fieldMappings['age']['options']['unsigned']); - - return $class; - } - - /** @depends testExportDirectoryAndFilesAreCreated */ - public function testFieldsAreProperlySerialized(): void - { - $type = $this->getType(); - - if ($type === 'xml') { - $xml = simplexml_load_file(__DIR__ . '/export/' . $type . '/Doctrine.Tests.ORM.Tools.Export.ExportedUser.dcm.xml'); - - $xml->registerXPathNamespace('d', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); - $nodes = $xml->xpath("/d:doctrine-mapping/d:entity/d:field[@name='name' and @type='string' and @nullable='true']"); - self::assertEquals(1, count($nodes)); - - $nodes = $xml->xpath("/d:doctrine-mapping/d:entity/d:field[@name='name' and @type='string' and @unique='true']"); - self::assertEquals(1, count($nodes)); - } else { - self::markTestSkipped('Test not available for ' . $type . ' driver'); - } - } - - /** @depends testFieldsAreExported */ - public function testOneToOneAssociationsAreExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue(isset($class->associationMappings['address'])); - self::assertEquals(Address::class, $class->associationMappings['address']['targetEntity']); - self::assertEquals('address_id', $class->associationMappings['address']['joinColumns'][0]['name']); - self::assertEquals('id', $class->associationMappings['address']['joinColumns'][0]['referencedColumnName']); - self::assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onDelete']); - - self::assertTrue($class->associationMappings['address']['isCascadeRemove']); - self::assertTrue($class->associationMappings['address']['isCascadePersist']); - self::assertFalse($class->associationMappings['address']['isCascadeRefresh']); - self::assertFalse($class->associationMappings['address']['isCascadeMerge']); - self::assertFalse($class->associationMappings['address']['isCascadeDetach']); - self::assertTrue($class->associationMappings['address']['orphanRemoval']); - self::assertEquals(ClassMetadata::FETCH_EAGER, $class->associationMappings['address']['fetch']); - - return $class; - } - - /** @depends testFieldsAreExported */ - public function testManyToOneAssociationsAreExported($class): void - { - self::assertTrue(isset($class->associationMappings['mainGroup'])); - self::assertEquals(Group::class, $class->associationMappings['mainGroup']['targetEntity']); - } - - /** @depends testOneToOneAssociationsAreExported */ - public function testOneToManyAssociationsAreExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue(isset($class->associationMappings['phonenumbers'])); - self::assertEquals(Phonenumber::class, $class->associationMappings['phonenumbers']['targetEntity']); - self::assertEquals('user', $class->associationMappings['phonenumbers']['mappedBy']); - self::assertEquals(['number' => 'ASC'], $class->associationMappings['phonenumbers']['orderBy']); - - self::assertTrue($class->associationMappings['phonenumbers']['isCascadeRemove']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadeMerge']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); - self::assertTrue($class->associationMappings['phonenumbers']['orphanRemoval']); - self::assertEquals(ClassMetadata::FETCH_LAZY, $class->associationMappings['phonenumbers']['fetch']); - - return $class; - } - - /** @depends testOneToManyAssociationsAreExported */ - public function testManyToManyAssociationsAreExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue(isset($class->associationMappings['groups'])); - self::assertEquals(Group::class, $class->associationMappings['groups']['targetEntity']); - self::assertEquals('cms_users_groups', $class->associationMappings['groups']['joinTable']['name']); - - self::assertEquals('user_id', $class->associationMappings['groups']['joinTable']['joinColumns'][0]['name']); - self::assertEquals('id', $class->associationMappings['groups']['joinTable']['joinColumns'][0]['referencedColumnName']); - - self::assertEquals('group_id', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['name']); - self::assertEquals('id', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); - self::assertEquals('INT NULL', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['columnDefinition']); - - self::assertTrue($class->associationMappings['groups']['isCascadeRemove']); - self::assertTrue($class->associationMappings['groups']['isCascadePersist']); - self::assertTrue($class->associationMappings['groups']['isCascadeRefresh']); - self::assertTrue($class->associationMappings['groups']['isCascadeMerge']); - self::assertTrue($class->associationMappings['groups']['isCascadeDetach']); - self::assertEquals(ClassMetadata::FETCH_EXTRA_LAZY, $class->associationMappings['groups']['fetch']); - - return $class; - } - - /** @depends testManyToManyAssociationsAreExported */ - public function testLifecycleCallbacksAreExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue(isset($class->lifecycleCallbacks['prePersist'])); - self::assertCount(2, $class->lifecycleCallbacks['prePersist']); - self::assertEquals('doStuffOnPrePersist', $class->lifecycleCallbacks['prePersist'][0]); - self::assertEquals('doOtherStuffOnPrePersistToo', $class->lifecycleCallbacks['prePersist'][1]); - - self::assertTrue(isset($class->lifecycleCallbacks['postPersist'])); - self::assertCount(1, $class->lifecycleCallbacks['postPersist']); - self::assertEquals('doStuffOnPostPersist', $class->lifecycleCallbacks['postPersist'][0]); - - return $class; - } - - /** @depends testLifecycleCallbacksAreExported */ - public function testCascadeIsExported(ClassMetadata $class): ClassMetadata - { - self::assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadeMerge']); - self::assertTrue($class->associationMappings['phonenumbers']['isCascadeRemove']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); - self::assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); - self::assertTrue($class->associationMappings['phonenumbers']['orphanRemoval']); - - return $class; - } - - /** @depends testCascadeIsExported */ - public function testInversedByIsExported(ClassMetadata $class): void - { - self::assertEquals('user', $class->associationMappings['address']['inversedBy']); - } - - /** @depends testExportDirectoryAndFilesAreCreated */ - public function testCascadeAllCollapsed(): void - { - $type = $this->getType(); - - if ($type === 'xml') { - $xml = simplexml_load_file(__DIR__ . '/export/' . $type . '/Doctrine.Tests.ORM.Tools.Export.ExportedUser.dcm.xml'); - - $xml->registerXPathNamespace('d', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); - $nodes = $xml->xpath("/d:doctrine-mapping/d:entity/d:one-to-many[@field='interests']/d:cascade/d:*"); - self::assertEquals(1, count($nodes)); - - self::assertEquals('cascade-all', $nodes[0]->getName()); - } elseif ($type === 'yaml') { - $yaml = new Parser(); - $value = $yaml->parse(file_get_contents(__DIR__ . '/export/' . $type . '/Doctrine.Tests.ORM.Tools.Export.ExportedUser.dcm.yml')); - - self::assertTrue(isset($value[ExportedUser::class]['oneToMany']['interests']['cascade'])); - self::assertEquals(1, count($value[ExportedUser::class]['oneToMany']['interests']['cascade'])); - self::assertEquals('all', $value[ExportedUser::class]['oneToMany']['interests']['cascade'][0]); - } else { - self::markTestSkipped('Test not available for ' . $type . ' driver'); - } - } - - /** @depends testExportedMetadataCanBeReadBackIn */ - public function testEntityListenersAreExported(ClassMetadata $class): void - { - self::assertNotEmpty($class->entityListeners); - self::assertCount(2, $class->entityListeners[Events::prePersist]); - self::assertCount(2, $class->entityListeners[Events::postPersist]); - self::assertEquals(UserListener::class, $class->entityListeners[Events::prePersist][0]['class']); - self::assertEquals('customPrePersist', $class->entityListeners[Events::prePersist][0]['method']); - self::assertEquals(GroupListener::class, $class->entityListeners[Events::prePersist][1]['class']); - self::assertEquals('prePersist', $class->entityListeners[Events::prePersist][1]['method']); - self::assertEquals(UserListener::class, $class->entityListeners[Events::postPersist][0]['class']); - self::assertEquals('customPostPersist', $class->entityListeners[Events::postPersist][0]['method']); - self::assertEquals(AddressListener::class, $class->entityListeners[Events::postPersist][1]['class']); - self::assertEquals('customPostPersist', $class->entityListeners[Events::postPersist][1]['method']); - } - - protected function deleteDirectory(string $path): void - { - if (is_file($path)) { - unlink($path); - } elseif (is_dir($path)) { - $files = glob(rtrim($path, '/') . '/*'); - - if (is_array($files)) { - foreach ($files as $file) { - $this->deleteDirectory($file); - } - } - - rmdir($path); - } - } -} - -class Address -{ -} -class Phonenumber -{ -} -class Group -{ -} -class UserListener -{ - /** @PrePersist */ - public function customPrePersist(): void - { - } - - /** @PostPersist */ - public function customPostPersist(): void - { - } -} -class GroupListener -{ - /** @PrePersist */ - public function prePersist(): void - { - } -} -class AddressListener -{ - /** @PostPersist */ - public function customPostPersist(): void - { - } -} diff --git a/tests/Tests/ORM/Tools/Export/PhpClassMetadataExporterTest.php b/tests/Tests/ORM/Tools/Export/PhpClassMetadataExporterTest.php deleted file mode 100644 index 34b9eccf6a8..00000000000 --- a/tests/Tests/ORM/Tools/Export/PhpClassMetadataExporterTest.php +++ /dev/null @@ -1,18 +0,0 @@ -mapField( - [ - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', - 'id' => true, - ] - ); - - $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); - $metadata->setSequenceGeneratorDefinition( - [ - 'sequenceName' => 'seq_entity_test_id', - 'allocationSize' => 5, - 'initialValue' => 1, - ] - ); - - $expectedFileContent = <<<'XML' - - - - - - - - - -XML; - - self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata)); - } - - /** - * @group 1214 - * @group 1216 - * @group DDC-3439 - */ - public function testFieldOptionsExport(): void - { - $exporter = new XmlExporter(); - $metadata = new ClassMetadata('entityTest'); - - $metadata->mapField( - [ - 'fieldName' => 'myField', - 'type' => 'string', - 'columnName' => 'my_field', - 'options' => [ - 'default' => 'default_string', - 'comment' => 'The comment for the field', - ], - ] - ); - - $expectedFileContent = <<<'XML' - - - - - - - - - - - -XML; - - self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata)); - } - - public function testPolicyExport(): void - { - $exporter = new XmlExporter(); - $metadata = new ClassMetadata('entityTest'); - - // DEFERRED_IMPLICIT - $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); - - $expectedFileContent = <<<'XML' - - - - -XML; - - self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata)); - - // DEFERRED_EXPLICIT - $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); - - $expectedFileContent = <<<'XML' - - - - -XML; - - self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata)); - - // NOTIFY - $metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); - - $expectedFileContent = <<<'XML' - - - - -XML; - - self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata)); - } -} diff --git a/tests/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php b/tests/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php deleted file mode 100644 index b20ececd891..00000000000 --- a/tests/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @OneToMany(targetEntity="Doctrine\Tests\ORM\Tools\Export\Phonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true) - * @OrderBy({"number"="ASC"}) - */ - public $phonenumbers; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Doctrine\Tests\ORM\Tools\Export\Group", cascade={"all"}, fetch="EXTRA_LAZY") - * @JoinTable(name="cms_users_groups", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id", nullable=false, unique=false)}, - * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id", columnDefinition="INT NULL")} - * ) - */ - public $groups; - - /** @PrePersist */ - public function doStuffOnPrePersist(): void - { - } - - /** @PrePersist */ - public function doOtherStuffOnPrePersistToo(): void - { - } - - /** @PostPersist */ - public function doStuffOnPostPersist(): void - { - } -} diff --git a/tests/Tests/ORM/Tools/Export/php/Doctrine.Tests.ORM.Tools.Export.User.php b/tests/Tests/ORM/Tools/Export/php/Doctrine.Tests.ORM.Tools.Export.User.php deleted file mode 100644 index d1820ebea35..00000000000 --- a/tests/Tests/ORM/Tools/Export/php/Doctrine.Tests.ORM.Tools.Export.User.php +++ /dev/null @@ -1,159 +0,0 @@ -setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); -$metadata->setPrimaryTable( - [ - 'name' => 'cms_users', - 'options' => ['engine' => 'MyISAM', 'foo' => ['bar' => 'baz']], - ] -); -$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT); -$metadata->addLifecycleCallback('doStuffOnPrePersist', Events::prePersist); -$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', Events::prePersist); -$metadata->addLifecycleCallback('doStuffOnPostPersist', Events::postPersist); -$metadata->mapField( - [ - 'id' => true, - 'fieldName' => 'id', - 'type' => 'integer', - 'columnName' => 'id', - ] -); -$metadata->mapField( - [ - 'fieldName' => 'name', - 'type' => 'string', - 'length' => 50, - 'unique' => true, - 'nullable' => true, - 'columnName' => 'name', - ] -); -$metadata->mapField( - [ - 'fieldName' => 'email', - 'type' => 'string', - 'columnName' => 'user_email', - 'columnDefinition' => 'CHAR(32) NOT NULL', - ] -); -$metadata->mapField( - [ - 'fieldName' => 'age', - 'type' => 'integer', - 'options' => ['unsigned' => true], - ] -); -$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); -$metadata->mapManyToOne( - [ - 'fieldName' => 'mainGroup', - 'targetEntity' => Group::class, - ] -); -$metadata->mapOneToOne( - [ - 'fieldName' => 'address', - 'targetEntity' => Address::class, - 'inversedBy' => 'user', - 'cascade' => - [0 => 'persist'], - 'mappedBy' => null, - 'joinColumns' => - [ - 0 => - [ - 'name' => 'address_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE', - ], - ], - 'orphanRemoval' => true, - 'fetch' => ClassMetadata::FETCH_EAGER, - ] -); -$metadata->mapOneToOne( - [ - 'fieldName' => 'cart', - 'targetEntity' => Cart::class, - 'mappedBy' => 'user', - 'cascade' => - [0 => 'persist'], - 'inversedBy' => null, - 'orphanRemoval' => false, - 'fetch' => ClassMetadata::FETCH_EAGER, - ] -); -$metadata->mapOneToMany( - [ - 'fieldName' => 'phonenumbers', - 'targetEntity' => Phonenumber::class, - 'cascade' => - [ - 1 => 'persist', - 2 => 'merge', - ], - 'mappedBy' => 'user', - 'orphanRemoval' => true, - 'fetch' => ClassMetadata::FETCH_LAZY, - 'orderBy' => - ['number' => 'ASC'], - ] -); -$metadata->mapManyToMany( - [ - 'fieldName' => 'groups', - 'targetEntity' => Export\Group::class, - 'fetch' => ClassMetadata::FETCH_EXTRA_LAZY, - 'cascade' => - [ - 0 => 'remove', - 1 => 'persist', - 2 => 'refresh', - 3 => 'merge', - 4 => 'detach', - ], - 'mappedBy' => null, - 'joinTable' => - [ - 'name' => 'cms_users_groups', - 'joinColumns' => - [ - 0 => - [ - 'name' => 'user_id', - 'referencedColumnName' => 'id', - 'unique' => false, - 'nullable' => false, - ], - ], - 'inverseJoinColumns' => - [ - 0 => - [ - 'name' => 'group_id', - 'referencedColumnName' => 'id', - 'columnDefinition' => 'INT NULL', - ], - ], - ], - 'orderBy' => null, - ] -); -$metadata->addEntityListener(Events::prePersist, UserListener::class, 'customPrePersist'); -$metadata->addEntityListener(Events::postPersist, UserListener::class, 'customPostPersist'); -$metadata->addEntityListener(Events::prePersist, GroupListener::class, 'prePersist'); -$metadata->addEntityListener(Events::postPersist, AddressListener::class, 'customPostPersist'); diff --git a/tests/Tests/ORM/Tools/Export/xml/Doctrine.Tests.ORM.Tools.Export.User.dcm.xml b/tests/Tests/ORM/Tools/Export/xml/Doctrine.Tests.ORM.Tools.Export.User.dcm.xml deleted file mode 100644 index 4092c8574c5..00000000000 --- a/tests/Tests/ORM/Tools/Export/xml/Doctrine.Tests.ORM.Tools.Export.User.dcm.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Tests/ORM/Tools/Export/yaml/Doctrine.Tests.ORM.Tools.Export.User.dcm.yml b/tests/Tests/ORM/Tools/Export/yaml/Doctrine.Tests.ORM.Tools.Export.User.dcm.yml deleted file mode 100644 index 23332e3db2f..00000000000 --- a/tests/Tests/ORM/Tools/Export/yaml/Doctrine.Tests.ORM.Tools.Export.User.dcm.yml +++ /dev/null @@ -1,85 +0,0 @@ -Doctrine\Tests\ORM\Tools\Export\User: - type: entity - table: cms_users - options: - engine: MyISAM - foo: { bar: baz } - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - length: 50 - nullable: true - unique: true - email: - type: string - column: user_email - columnDefinition: CHAR(32) NOT NULL - age: - type: integer - options: - unsigned: true - oneToOne: - address: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Address - joinColumn: - name: address_id - referencedColumnName: id - onDelete: CASCADE - cascade: [ persist ] - inversedBy: user - orphanRemoval: true - fetch: EAGER - cart: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Cart - mappedBy: user - cascade: [ remove ] - manyToOne: - mainGroup: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Group - oneToMany: - phonenumbers: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Phonenumber - mappedBy: user - orderBy: - number: ASC - cascade: [ persist, merge ] - orphanRemoval: true - fetch: LAZY - interests: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Interests - mappedBy: user - cascade: [ persist, merge, remove, refresh, detach ] - orphanRemoval: true - manyToMany: - groups: - targetEntity: Doctrine\Tests\ORM\Tools\Export\Group - fetch: EXTRA_LAZY - joinTable: - name: cms_users_groups - joinColumns: - user_id: - referencedColumnName: id - nullable: false - unique: false - inverseJoinColumns: - group_id: - referencedColumnName: id - columnDefinition: INT NULL - cascade: - - all - lifecycleCallbacks: - prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] - postPersist: [ doStuffOnPostPersist ] - entityListeners: - Doctrine\Tests\ORM\Tools\Export\UserListener: - prePersist: [customPrePersist] - postPersist: [customPostPersist] - Doctrine\Tests\ORM\Tools\Export\GroupListener: - prePersist: [prePersist] - Doctrine\Tests\ORM\Tools\Export\AddressListener: - postPersist: [customPostPersist] diff --git a/tests/Tests/ORM/Tools/Pagination/CountOutputWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/CountOutputWalkerTest.php index fef4f8f045f..0b64126628e 100644 --- a/tests/Tests/ORM/Tools/Pagination/CountOutputWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/CountOutputWalkerTest.php @@ -13,56 +13,56 @@ class CountOutputWalkerTest extends PaginationTestCase public function testCountQuery(): void { $query = $this->entityManager->createQuery( - 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a' + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a', ); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, c1_.id AS id_1, a2_.id AS id_2, a2_.name AS name_3, b0_.author_id AS author_id_4, b0_.category_id AS category_id_5 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id) dctrn_result) dctrn_table', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryMixedResultsWithName(): void { $query = $this->entityManager->createQuery( - 'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a' + 'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a', ); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result) dctrn_table', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryGroupBy(): void { $query = $this->entityManager->createQuery( - 'SELECT p.name FROM Doctrine\Tests\ORM\Tools\Pagination\Person p GROUP BY p.name' + 'SELECT p.name FROM Doctrine\Tests\ORM\Tools\Pagination\Person p GROUP BY p.name', ); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertSame( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT p0_.name AS name_0 FROM Person p0_ GROUP BY p0_.name) dctrn_table', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryHaving(): void { $query = $this->entityManager->createQuery( - 'SELECT g, u, count(u.id) AS userCount FROM Doctrine\Tests\ORM\Tools\Pagination\Group g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0' + 'SELECT g, u, count(u.id) AS userCount FROM Doctrine\Tests\ORM\Tools\Pagination\Group g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0', ); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertSame( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT count(u0_.id) AS sclr_0, g1_.id AS id_1, u0_.id AS id_2 FROM groups g1_ LEFT JOIN user_group u2_ ON g1_.id = u2_.group_id LEFT JOIN User u0_ ON u0_.id = u2_.user_id GROUP BY g1_.id HAVING sclr_0 > 0) dctrn_table', - $query->getSQL() + $query->getSQL(), ); } @@ -74,14 +74,14 @@ public function testCountQueryOrderBySqlServer(): void } $query = $this->entityManager->createQuery( - 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p ORDER BY p.id' + 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p ORDER BY p.id', ); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_) dctrn_result) dctrn_table', - $query->getSQL() + $query->getSQL(), ); } } diff --git a/tests/Tests/ORM/Tools/Pagination/CountWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/CountWalkerTest.php index 8773c4f437b..6a223e67f50 100644 --- a/tests/Tests/ORM/Tools/Pagination/CountWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/CountWalkerTest.php @@ -6,93 +6,108 @@ use Doctrine\ORM\Query; use Doctrine\ORM\Tools\Pagination\CountWalker; +use PHPUnit\Framework\Attributes\Group; use RuntimeException; -/** @group DDC-1613 */ +#[Group('DDC-1613')] class CountWalkerTest extends PaginationTestCase { public function testCountQuery(): void { $query = $this->entityManager->createQuery( - 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a' + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id', - $query->getSQL() + $query->getSQL(), + ); + } + + public function testCountQueryWithoutDistinctUsesCountStar(): void + { + $query = $this->entityManager->createQuery( + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a', + ); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); + $query->setHint(CountWalker::HINT_DISTINCT, false); + + self::assertEquals( + 'SELECT count(*) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id', + $query->getSQL(), ); } public function testCountQueryMixedResultsWithName(): void { $query = $this->entityManager->createQuery( - 'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a' + 'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT a0_.id) AS sclr_0 FROM Author a0_', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryKeepsGroupBy(): void { $query = $this->entityManager->createQuery( - 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b GROUP BY b.id' + 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b GROUP BY b.id', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ GROUP BY b0_.id', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryRemovesOrderBy(): void { $query = $this->entityManager->createQuery( - 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a ORDER BY a.name' + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a ORDER BY a.name', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryRemovesLimits(): void { $query = $this->entityManager->createQuery( - 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a' + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id', - $query->getSQL() + $query->getSQL(), ); } public function testCountQueryHavingException(): void { $query = $this->entityManager->createQuery( - 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\Tests\Models\CMS\CmsGroup g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0' + 'SELECT g, COUNT(u.id) AS userCount FROM Doctrine\Tests\Models\CMS\CmsGroup g LEFT JOIN g.users u GROUP BY g.id HAVING userCount > 0', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); @@ -106,15 +121,15 @@ public function testCountQueryHavingException(): void public function testCountQueryWithArbitraryJoin(): void { $query = $this->entityManager->createQuery( - 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p LEFT JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c' + 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p LEFT JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c', ); $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [CountWalker::class]); $query->setHint(CountWalker::HINT_DISTINCT, true); - $query->setFirstResult(null)->setMaxResults(null); + $query->setFirstResult(0)->setMaxResults(null); self::assertEquals( 'SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ LEFT JOIN Category c1_ ON (b0_.category_id = c1_.id)', - $query->getSQL() + $query->getSQL(), ); } } diff --git a/tests/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php index 95d2047d593..c009fcc8538 100644 --- a/tests/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php @@ -4,295 +4,239 @@ namespace Doctrine\Tests\ORM\Tools\Pagination; -use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\ORM\Query; use Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker; +use PHPUnit\Framework\Attributes\Group; use Symfony\Component\Cache\Adapter\ArrayAdapter; -use function class_exists; - -// DBAL 2 compatibility -class_exists('Doctrine\DBAL\Platforms\MySqlPlatform'); -class_exists('Doctrine\DBAL\Platforms\PostgreSqlPlatform'); - final class LimitSubqueryOutputWalkerTest extends PaginationTestCase { - /** - * @var AbstractPlatform|null - */ - private $originalDatabasePlatform; - - protected function setUp(): void - { - parent::setUp(); - - $this->originalDatabasePlatform = $this->entityManager->getConnection()->getDatabasePlatform(); - } - - protected function tearDown(): void + public function testSubqueryClonedCompletely(): void { - if ($this->originalDatabasePlatform) { - $this->entityManager->getConnection()->setDatabasePlatform($this->originalDatabasePlatform); - } - - parent::tearDown(); - } - - private function replaceDatabasePlatform(AbstractPlatform $platform): void - { - $this->entityManager->getConnection()->setDatabasePlatform($platform); + $query = $this->createQuery('SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p'); + $query->setParameter('dummy-param', 123); + $query->setHint('dummy-hint', 'dummy-value'); + $query->setCacheable(true); + + $walker = new LimitSubqueryOutputWalker($query, new Query\ParserResult(), []); + + self::assertNotSame($query, $walker->getQuery()); + self::assertTrue($walker->getQuery()->hasHint('dummy-hint')); + self::assertSame('dummy-value', $walker->getQuery()->getHint('dummy-hint')); + self::assertNotSame($query->getParameters(), $walker->getQuery()->getParameters()); + self::assertInstanceOf(Query\Parameter::class, $param = $walker->getQuery()->getParameter('dummy-param')); + self::assertSame(123, $param->getValue()); + self::assertFalse($walker->getQuery()->isCacheable()); } public function testLimitSubquery(): void { - $query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a'); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result', + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a', ); } public function testLimitSubqueryWithSortPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); - - $query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0, MIN(sclr_5) AS dctrn_minrownum FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS sclr_5, m0_.author_id AS author_id_6, m0_.category_id AS category_id_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0, MIN(sclr_5) AS dctrn_minrownum FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS sclr_5, m0_.author_id AS author_id_6, m0_.category_id AS category_id_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title', ); } public function testLimitSubqueryWithScalarSortPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); - - $query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC', + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity', ); } public function testLimitSubqueryWithMixedSortPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - $query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'); - - self::assertSame( - 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC', + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC', ); } public function testLimitSubqueryWithHiddenScalarSortPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - $query = $this->createQuery('SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'); - - self::assertSame( - 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_1, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS sclr_0, u1_.id AS id_1, g0_.id AS id_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS sclr_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY id_1 ORDER BY dctrn_minrownum ASC', + 'SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC', ); } public function testLimitSubqueryPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); $this->testLimitSubquery(); } public function testLimitSubqueryWithSortOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - $query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title'); - - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS SCLR_5, m0_.author_id AS AUTHOR_ID_6, m0_.category_id AS CATEGORY_ID_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, ROW_NUMBER() OVER(ORDER BY m0_.title ASC) AS SCLR_5, m0_.author_id AS AUTHOR_ID_6, m0_.category_id AS CATEGORY_ID_7 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC', + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title', ); } public function testLimitSubqueryWithScalarSortOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - $query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity'); - - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC', + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity', ); } public function testLimitSubqueryWithMixedSortOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); - - $query = $this->createQuery('SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_1, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT COUNT(g0_.id) AS SCLR_0, u1_.id AS ID_1, g0_.id AS ID_2, ROW_NUMBER() OVER(ORDER BY COUNT(g0_.id) ASC, u1_.id DESC) AS SCLR_3 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id) dctrn_result GROUP BY ID_1 ORDER BY dctrn_minrownum ASC', + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC', ); } public function testLimitSubqueryOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - $query = $this->createQuery('SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a'); - - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0 FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, m0_.author_id AS AUTHOR_ID_5, m0_.category_id AS CATEGORY_ID_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_0 FROM (SELECT m0_.id AS ID_0, m0_.title AS TITLE_1, c1_.id AS ID_2, a2_.id AS ID_3, a2_.name AS NAME_4, m0_.author_id AS AUTHOR_ID_5, m0_.category_id AS CATEGORY_ID_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result', + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a', ); } public function testCountQueryMixedResultsWithName(): void { - $query = $this->createQuery('SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a'); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, sum(a0_.name) AS sclr_2 FROM Author a0_) dctrn_result', + 'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a', ); } - /** @group DDC-3336 */ + #[Group('DDC-3336')] public function testCountQueryWithArithmeticOrderByCondition(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - $query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY (1 - 1000) * 1 DESC'); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, (1 - 1000) * 1 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1 FROM Author a0_) dctrn_result_inner ORDER BY (1 - 1000) * 1 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, (1 - 1000) * 1 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1 FROM Author a0_) dctrn_result_inner ORDER BY (1 - 1000) * 1 DESC) dctrn_result', + 'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY (1 - 1000) * 1 DESC', ); } public function testCountQueryWithComplexScalarOrderByItemWithoutJoin(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); - - $query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_2 * imageWidth_3 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageHeight_2 * imageWidth_3 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_2 * imageWidth_3 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageHeight_2 * imageWidth_3 DESC) dctrn_result', + 'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC', ); } public function testCountQueryWithComplexScalarOrderByItemJoinedWithoutPartial(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); - - $query = $this->createQuery('SELECT u FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_3 * imageWidth_4 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.image AS image_2, a1_.imageHeight AS imageHeight_3, a1_.imageWidth AS imageWidth_4, a1_.imageAltDesc AS imageAltDesc_5, a1_.user_id AS user_id_6 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_3 * imageWidth_4 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_3 * imageWidth_4 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.image AS image_2, a1_.imageHeight AS imageHeight_3, a1_.imageWidth AS imageWidth_4, a1_.imageAltDesc AS imageAltDesc_5, a1_.user_id AS user_id_6 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_3 * imageWidth_4 DESC) dctrn_result', + 'SELECT u FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC', ); } public function testCountQueryWithComplexScalarOrderByItemJoinedWithPartial(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); + $entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); + + $query = $entityManager->createQuery( + 'SELECT u, partial a.{id, imageAltDesc} FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC', + ); - $query = $this->createQuery('SELECT u, partial a.{id, imageAltDesc} FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.avatar a ORDER BY a.imageHeight * a.imageWidth DESC'); + $query->setHydrationMode(Query::HYDRATE_ARRAY); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class); self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_5 * imageWidth_6 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.imageAltDesc AS imageAltDesc_2, a1_.id AS id_3, a1_.image AS image_4, a1_.imageHeight AS imageHeight_5, a1_.imageWidth AS imageWidth_6, a1_.imageAltDesc AS imageAltDesc_7, a1_.user_id AS user_id_8 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_5 * imageWidth_6 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageHeight_5 * imageWidth_6 FROM (SELECT u0_.id AS id_0, a1_.id AS id_1, a1_.imageAltDesc AS imageAltDesc_2, a1_.id AS id_3, a1_.image AS image_4, a1_.imageHeight AS imageHeight_5, a1_.imageWidth AS imageWidth_6, a1_.imageAltDesc AS imageAltDesc_7 FROM User u0_ INNER JOIN Avatar a1_ ON u0_.id = a1_.user_id) dctrn_result_inner ORDER BY imageHeight_5 * imageWidth_6 DESC) dctrn_result', + $query->getSQL(), ); } public function testCountQueryWithComplexScalarOrderByItemOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - $query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC'); - - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.image AS IMAGE_1, a0_.imageHeight AS IMAGEHEIGHT_2, a0_.imageWidth AS IMAGEWIDTH_3, a0_.imageAltDesc AS IMAGEALTDESC_4, ROW_NUMBER() OVER(ORDER BY a0_.imageHeight * a0_.imageWidth DESC) AS SCLR_5, a0_.user_id AS USER_ID_6 FROM Avatar a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_0, MIN(SCLR_5) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.image AS IMAGE_1, a0_.imageHeight AS IMAGEHEIGHT_2, a0_.imageWidth AS IMAGEWIDTH_3, a0_.imageAltDesc AS IMAGEALTDESC_4, ROW_NUMBER() OVER(ORDER BY a0_.imageHeight * a0_.imageWidth DESC) AS SCLR_5, a0_.user_id AS USER_ID_6 FROM Avatar a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC', + 'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageHeight * a.imageWidth DESC', ); } - /** @group DDC-3434 */ + #[Group('DDC-3434')] public function testLimitSubqueryWithHiddenSelectionInOrderBy(): void { - $query = $this->createQuery( - 'SELECT a, a.name AS HIDDEN ord FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY ord DESC' - ); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, a0_.name AS name_2 FROM Author a0_) dctrn_result_inner ORDER BY name_2 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, a0_.name AS name_2 FROM Author a0_) dctrn_result_inner ORDER BY name_2 DESC) dctrn_result', + 'SELECT a, a.name AS HIDDEN ord FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY ord DESC', ); } public function testLimitSubqueryWithColumnWithSortDirectionInName(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); - $query = $this->createQuery('SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageAltDesc DESC'); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageAltDesc_4 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageAltDesc_4 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, imageAltDesc_4 FROM (SELECT a0_.id AS id_0, a0_.image AS image_1, a0_.imageHeight AS imageHeight_2, a0_.imageWidth AS imageWidth_3, a0_.imageAltDesc AS imageAltDesc_4, a0_.user_id AS user_id_5 FROM Avatar a0_) dctrn_result_inner ORDER BY imageAltDesc_4 DESC) dctrn_result', + 'SELECT a FROM Doctrine\Tests\ORM\Tools\Pagination\Avatar a ORDER BY a.imageAltDesc DESC', ); } public function testLimitSubqueryWithOrderByInnerJoined(): void { - $query = $this->createQuery('SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b JOIN b.author a ORDER BY a.name ASC'); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT b0_.id AS id_0, a1_.id AS id_1, a1_.name AS name_2, b0_.author_id AS author_id_3, b0_.category_id AS category_id_4 FROM BlogPost b0_ INNER JOIN Author a1_ ON b0_.author_id = a1_.id) dctrn_result_inner ORDER BY name_2 ASC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, name_2 FROM (SELECT b0_.id AS id_0, a1_.id AS id_1, a1_.name AS name_2, b0_.author_id AS author_id_3, b0_.category_id AS category_id_4 FROM BlogPost b0_ INNER JOIN Author a1_ ON b0_.author_id = a1_.id) dctrn_result_inner ORDER BY name_2 ASC) dctrn_result', + 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b JOIN b.author a ORDER BY a.name ASC', ); } public function testLimitSubqueryWithOrderByAndSubSelectInWhereClauseMySql(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); - - $query = $this->createQuery( - 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b -WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1) -ORDER BY b.id DESC' - ); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result', + 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1) ORDER BY b.id DESC', ); } public function testLimitSubqueryWithOrderByAndSubSelectInWhereClausePgSql(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - $query = $this->createQuery( - 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b -WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1) -ORDER BY b.id DESC' - ); - - self::assertSame( - 'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', + 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b WHERE ((SELECT COUNT(simple.id) FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost simple) = 1) ORDER BY b.id DESC', ); } @@ -301,16 +245,11 @@ public function testLimitSubqueryWithOrderByAndSubSelectInWhereClausePgSql(): vo */ public function testLimitSubqueryOrderByFieldFromMappedSuperclass(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - // now use the third one in query - $query = $this->createQuery( - 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\Banner b ORDER BY b.id DESC' - ); - - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.name AS name_1 FROM Banner b0_) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.name AS name_1 FROM Banner b0_) dctrn_result_inner ORDER BY id_0 DESC) dctrn_result', + 'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\Banner b ORDER BY b.id DESC', ); } @@ -319,22 +258,11 @@ public function testLimitSubqueryOrderByFieldFromMappedSuperclass(): void */ public function testLimitSubqueryOrderBySubSelectOrderByExpression(): void { - $this->replaceDatabasePlatform(new MySQLPlatform()); - - $query = $this->createQuery( - 'SELECT a, - ( - SELECT MIN(bp.title) - FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp - WHERE bp.author = a - ) AS HIDDEN first_blog_post - FROM Doctrine\Tests\ORM\Tools\Pagination\Author a - ORDER BY first_blog_post DESC' - ); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new MySQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, sclr_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result_inner ORDER BY sclr_2 DESC) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0 FROM (SELECT DISTINCT id_0, sclr_2 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result_inner ORDER BY sclr_2 DESC) dctrn_result', + 'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY first_blog_post DESC', ); } @@ -343,22 +271,11 @@ public function testLimitSubqueryOrderBySubSelectOrderByExpression(): void */ public function testLimitSubqueryOrderBySubSelectOrderByExpressionPg(): void { - $this->replaceDatabasePlatform(new PostgreSQLPlatform()); - - $query = $this->createQuery( - 'SELECT a, - ( - SELECT MIN(bp.title) - FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp - WHERE bp.author = a - ) AS HIDDEN first_blog_post - FROM Doctrine\Tests\ORM\Tools\Pagination\Author a - ORDER BY first_blog_post DESC' - ); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new PostgreSQLPlatform()); - self::assertSame( - 'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC LIMIT 20 OFFSET 10', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', + 'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY first_blog_post DESC', ); } @@ -367,22 +284,11 @@ public function testLimitSubqueryOrderBySubSelectOrderByExpressionPg(): void */ public function testLimitSubqueryOrderBySubSelectOrderByExpressionOracle(): void { - $this->replaceDatabasePlatform(new OraclePlatform()); - - $query = $this->createQuery( - 'SELECT a, - ( - SELECT MIN(bp.title) - FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp - WHERE bp.author = a - ) AS HIDDEN first_blog_post - FROM Doctrine\Tests\ORM\Tools\Pagination\Author a - ORDER BY first_blog_post DESC' - ); + $this->entityManager = $this->createTestEntityManagerWithPlatform(new OraclePlatform()); - self::assertSame( - 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC) a WHERE ROWNUM <= 30) WHERE doctrine_rownum >= 11', - $query->getSQL() + $this->assertQuerySql( + 'SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC', + 'SELECT a, ( SELECT MIN(bp.title) FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost bp WHERE bp.author = a ) AS HIDDEN first_blog_post FROM Doctrine\Tests\ORM\Tools\Pagination\Author a ORDER BY first_blog_post DESC', ); } @@ -395,14 +301,14 @@ public function testParsingQueryWithDifferentLimitOffsetValuesTakesOnlyOneCacheE self::assertSame( 'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 20 OFFSET 10', - $query->getSQL() + $query->getSQL(), ); $query->setFirstResult(30)->setMaxResults(40); self::assertSame( 'SELECT DISTINCT id_0 FROM (SELECT m0_.id AS id_0, m0_.title AS title_1, c1_.id AS id_2, a2_.id AS id_3, a2_.name AS name_4, m0_.author_id AS author_id_5, m0_.category_id AS category_id_6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result LIMIT 40 OFFSET 30', - $query->getSQL() + $query->getSQL(), ); self::assertCount(1, $queryCache->getValues()); @@ -417,4 +323,12 @@ private function createQuery(string $dql): Query return $query; } + + private function assertQuerySql(string $expectedSql, string $dql): void + { + $sql = $this->entityManager->getConnection()->getDatabasePlatform()->modifyLimitQuery($expectedSql, 20, 10); + $query = $this->createQuery($dql); + + self::assertSame($sql, $query->getSQL()); + } } diff --git a/tests/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php index d54c1b95bc5..f9c3ba7fe48 100644 --- a/tests/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php @@ -7,8 +7,9 @@ use Doctrine\ORM\Query; use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker; use Doctrine\ORM\Tools\Pagination\Paginator; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1613 */ +#[Group('DDC-1613')] class LimitSubqueryWalkerTest extends PaginationTestCase { public function testLimitSubquery(): void @@ -21,7 +22,7 @@ public function testLimitSubquery(): void self::assertEquals( 'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -36,7 +37,7 @@ public function testHintCanDisableDistinct(): void self::assertEquals( 'SELECT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -50,7 +51,7 @@ public function testLimitSubqueryWithSort(): void self::assertEquals( 'SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id ORDER BY m0_.title ASC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -64,7 +65,7 @@ public function testLimitSubqueryWithSortFunction(): void self::assertSame( 'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id GROUP BY m0_.id ORDER BY COUNT(c1_.id) ASC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -78,7 +79,7 @@ public function testCountQueryMixedResultsWithName(): void self::assertEquals( 'SELECT DISTINCT a0_.id AS id_0 FROM Author a0_', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -92,7 +93,7 @@ public function testAggQueryMixedResultsWithNameAndSort(): void self::assertSame( 'SELECT DISTINCT a0_.id AS id_0, sum(a0_.name) AS sclr_1 FROM Author a0_ ORDER BY sclr_1 DESC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -106,11 +107,11 @@ public function testAggQueryMultipleMixedResultsWithSort(): void self::assertSame( 'SELECT DISTINCT a0_.id AS id_0, sum(a0_.name) AS sclr_1, (SELECT count(a1_.id) AS sclr_3 FROM Author a1_ WHERE a1_.id = a0_.id) AS sclr_2 FROM Author a0_ ORDER BY sclr_1 DESC, sclr_2 ASC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } - /** @group DDC-2890 */ + #[Group('DDC-2890')] public function testLimitSubqueryWithSortOnAssociation(): void { $dql = 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p ORDER BY p.author'; @@ -121,7 +122,7 @@ public function testLimitSubqueryWithSortOnAssociation(): void self::assertEquals( 'SELECT DISTINCT m0_.id AS id_0, m0_.author_id AS sclr_1 FROM MyBlogPost m0_ ORDER BY m0_.author_id ASC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -138,7 +139,7 @@ public function testLimitSubqueryWithArbitraryJoin(): void self::assertEquals( 'SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id)', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } @@ -152,7 +153,7 @@ public function testLimitSubqueryWithSortWithArbitraryJoin(): void self::assertEquals( 'SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id) ORDER BY m0_.title ASC', - $limitQuery->getSQL() + $limitQuery->getSQL(), ); } } diff --git a/tests/Tests/ORM/Tools/Pagination/PaginationTestCase.php b/tests/Tests/ORM/Tools/Pagination/PaginationTestCase.php index 9473e32ec88..4a5c6e47875 100644 --- a/tests/Tests/ORM/Tools/Pagination/PaginationTestCase.php +++ b/tests/Tests/ORM/Tools/Pagination/PaginationTestCase.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -31,243 +32,184 @@ protected function setUp(): void } -/** @Entity */ +#[Entity] class MyBlogPost { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Author - * @ManyToOne(targetEntity="Author") - */ + /** @var Author */ + #[ManyToOne(targetEntity: 'Author')] public $author; - /** - * @var Category - * @ManyToOne(targetEntity="Category") - */ + /** @var Category */ + #[ManyToOne(targetEntity: 'Category')] public $category; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $title; } -/** @Entity */ +#[Entity] class MyAuthor { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class MyCategory { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class BlogPost { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var Author - * @ManyToOne(targetEntity="Author") - */ + /** @var Author */ + #[ManyToOne(targetEntity: 'Author')] public $author; - /** - * @var Category - * @ManyToOne(targetEntity="Category") - */ + /** @var Category */ + #[ManyToOne(targetEntity: 'Category')] public $category; } -/** @Entity */ +#[Entity] class Author { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; } -/** @Entity */ +#[Entity] class Person { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $biography; } -/** @Entity */ +#[Entity] class Category { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; } -/** - * @Entity - * @Table(name="groups") - */ +#[Table(name: 'groups')] +#[Entity] class Group { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="User", mappedBy="groups") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'User', mappedBy: 'groups')] public $users; } -/** @Entity */ +#[Entity] class User { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Group", inversedBy="users") - * @JoinTable( - * name="user_group", - * joinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns = {@JoinColumn(name="group_id", referencedColumnName="id")} - * ) - */ + /** @phpstan-var Collection */ + #[JoinTable(name: 'user_group')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] + #[ManyToMany(targetEntity: 'Group', inversedBy: 'users')] public $groups; - /** - * @var Avatar - * @OneToOne(targetEntity="Avatar", mappedBy="user") - */ + /** @var Avatar */ + #[OneToOne(targetEntity: 'Avatar', mappedBy: 'user')] public $avatar; } -/** @Entity */ +#[Entity] class Avatar { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] public $id; - /** - * @var User - * @OneToOne(targetEntity="User", inversedBy="avatar") - * @JoinColumn(name="user_id", referencedColumnName="id") - */ + /** @var User */ + #[OneToOne(targetEntity: 'User', inversedBy: 'avatar')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] public $user; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $image; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $imageHeight; - /** - * @var int - * @Column(type="integer") - */ + /** @var int */ + #[Column(type: 'integer')] public $imageWidth; - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $imageAltDesc; } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class Identified { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue] + private int $id; public function getId(): int { @@ -275,12 +217,10 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class Banner extends Identified { - /** - * @var string - * @Column(type="string", length=255) - */ + /** @var string */ + #[Column(type: 'string', length: 255)] public $name; } diff --git a/tests/Tests/ORM/Tools/Pagination/PaginatorTest.php b/tests/Tests/ORM/Tools/Pagination/PaginatorTest.php index 23ff3b90be4..fcd8a9b2aa7 100644 --- a/tests/Tests/ORM/Tools/Pagination/PaginatorTest.php +++ b/tests/Tests/ORM/Tools/Pagination/PaginatorTest.php @@ -6,36 +6,43 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Result; use Doctrine\ORM\Decorator\EntityManagerDecorator; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Internal\Hydration\AbstractHydrator; use Doctrine\ORM\Query; +use Doctrine\ORM\Query\QueryException; use Doctrine\ORM\Tools\Pagination\Paginator; -use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; use PHPUnit\Framework\MockObject\MockObject; class PaginatorTest extends OrmTestCase { - use MockBuilderCompatibilityTools; - - /** @var Connection&MockObject */ - private $connection; - /** @var EntityManagerInterface&MockObject */ - private $em; - /** @var AbstractHydrator&MockObject */ - private $hydrator; + private Connection&MockObject $connection; + private EntityManagerInterface&MockObject $em; + private AbstractHydrator&MockObject $hydrator; protected function setUp(): void { - $this->connection = $this->getMockBuilderWithOnlyMethods(ConnectionMock::class, ['executeQuery']) - ->setConstructorArgs([[], $this->createMock(Driver::class)]) + $platform = $this->getMockBuilder(AbstractPlatform::class) + ->onlyMethods(['supportsIdentityColumns']) + ->getMockForAbstractClass(); + $platform->method('supportsIdentityColumns') + ->willReturn(true); + + $driver = $this->createMock(Driver::class); + $driver->method('getDatabasePlatform') + ->willReturn($platform); + + $this->connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['executeQuery']) + ->setConstructorArgs([[], $driver]) ->getMock(); - $this->em = $this->getMockBuilderWithOnlyMethods(EntityManagerDecorator::class, ['newHydrator']) - ->setConstructorArgs([$this->getTestEntityManager($this->connection)]) + $this->em = $this->getMockBuilder(EntityManagerDecorator::class) + ->onlyMethods(['newHydrator']) + ->setConstructorArgs([$this->createTestEntityManagerWithConnection($this->connection)]) ->getMock(); $this->hydrator = $this->createMock(AbstractHydrator::class); @@ -59,7 +66,7 @@ public function testExtraParametersAreStrippedWhenWalkerRemovingOriginalSelectEl WHERE a.user = u AND 1 = :paramInSubSelect ) AS HIDDEN max_version FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u - WHERE u.id = :paramInWhere' + WHERE u.id = :paramInWhere', ); $query->setParameters(['paramInWhere' => $paramInWhere, 'paramInSubSelect' => $paramInSubSelect]); $query->setMaxResults(1); @@ -99,7 +106,7 @@ public function testgetIteratorDoesCareAboutExtraParametersWithoutOutputWalkersW { $result = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); $this->connection->expects(self::exactly(1))->method('executeQuery')->willReturn($result); - $this->expectException(Query\QueryException::class); + $this->expectException(QueryException::class); $this->expectExceptionMessage('Too many parameters: the query defines 1 parameters and you bound 2'); $this->createPaginatorWithExtraParametersWithoutOutputWalkers([[10]])->getIterator(); diff --git a/tests/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php index 160ee998c77..11282b0b768 100644 --- a/tests/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php @@ -11,6 +11,7 @@ use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneIdForeignKeyEntity; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; class RootTypeWalkerTest extends PaginationTestCase { @@ -23,9 +24,7 @@ protected function setUp(): void } } - /** - * @dataProvider exampleQueries - */ + #[DataProvider('exampleQueries')] public function testResolveTypeMapping(string $dqlQuery, string $expectedType): void { $query = $this->entityManager->createQuery($dqlQuery); diff --git a/tests/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php b/tests/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php index 6f5c9c6dd22..4889b983528 100644 --- a/tests/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php +++ b/tests/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php @@ -8,13 +8,13 @@ use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Tools\Pagination\WhereInWalker; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; -/** @group DDC-1613 */ +#[Group('DDC-1613')] class WhereInWalkerTest extends PaginationTestCase { - /** - * @dataProvider exampleQueries - */ + #[DataProvider('exampleQueries')] public function testDqlQueryTransformation(string $dql, string $expectedSql): void { $query = $this->entityManager->createQuery($dql); diff --git a/tests/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index 80de7a424a5..779a7740ac7 100644 --- a/tests/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -19,29 +19,26 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Tools\ResolveTargetEntityListener; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\Group; class ResolveTargetEntityListenerTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $em; + private EntityManagerInterface $em; - /** @var ResolveTargetEntityListener */ - private $listener; + private ResolveTargetEntityListener $listener; - /** @var ClassMetadataFactory */ - private $factory; + private ClassMetadataFactory $factory; protected function setUp(): void { - $annotationDriver = $this->createAnnotationDriver(); - $this->em = $this->getTestEntityManager(); - $this->em->getConfiguration()->setMetadataDriverImpl($annotationDriver); + $this->em->getConfiguration()->setMetadataDriverImpl($this->createAttributeDriver()); $this->factory = $this->em->getMetadataFactory(); $this->listener = new ResolveTargetEntityListener(); } - /** @group DDC-1544 */ + #[Group('DDC-1544')] public function testResolveTargetEntityListenerCanResolveTargetEntity(): void { $evm = $this->em->getEventManager(); @@ -52,19 +49,17 @@ public function testResolveTargetEntityListenerCanResolveTargetEntity(): void $cm = $this->factory->getMetadataFor(ResolveTargetEntity::class); $meta = $cm->associationMappings; - self::assertSame(TargetEntity::class, $meta['manyToMany']['targetEntity']); - self::assertSame(ResolveTargetEntity::class, $meta['manyToOne']['targetEntity']); - self::assertSame(ResolveTargetEntity::class, $meta['oneToMany']['targetEntity']); - self::assertSame(TargetEntity::class, $meta['oneToOne']['targetEntity']); + self::assertSame(TargetEntity::class, $meta['manyToMany']->targetEntity); + self::assertSame(ResolveTargetEntity::class, $meta['manyToOne']->targetEntity); + self::assertSame(ResolveTargetEntity::class, $meta['oneToMany']->targetEntity); + self::assertSame(TargetEntity::class, $meta['oneToOne']->targetEntity); self::assertSame($cm, $this->factory->getMetadataFor(ResolveTarget::class)); } - /** - * @group DDC-3385 - * @group 1181 - * @group 385 - */ + #[Group('DDC-3385')] + #[Group('1181')] + #[Group('385')] public function testResolveTargetEntityListenerCanRetrieveTargetEntityByInterfaceName(): void { $this->listener->addResolveTargetEntity(ResolveTarget::class, ResolveTargetEntity::class, []); @@ -76,7 +71,7 @@ public function testResolveTargetEntityListenerCanRetrieveTargetEntityByInterfac self::assertSame($this->factory->getMetadataFor(ResolveTargetEntity::class), $cm); } - /** @group DDC-2109 */ + #[Group('DDC-2109')] public function testAssertTableColumnsAreNotAddedInManyToMany(): void { $evm = $this->em->getEventManager(); @@ -87,15 +82,13 @@ public function testAssertTableColumnsAreNotAddedInManyToMany(): void $cm = $this->factory->getMetadataFor(ResolveTargetEntity::class); $meta = $cm->associationMappings['manyToMany']; - self::assertSame(TargetEntity::class, $meta['targetEntity']); - self::assertEquals(['resolvetargetentity_id', 'target_id'], $meta['joinTableColumns']); + self::assertSame(TargetEntity::class, $meta->targetEntity); + self::assertEquals(['resolvetargetentity_id', 'target_id'], $meta->joinTableColumns); } - /** - * @group 1572 - * @group functional - * @coversNothing - */ + #[CoversNothing] + #[Group('1572')] + #[Group('functional')] public function testDoesResolveTargetEntitiesInDQLAlsoWithInterfaces(): void { $evm = $this->em->getEventManager(); @@ -108,7 +101,7 @@ public function testDoesResolveTargetEntitiesInDQLAlsoWithInterfaces(): void $this ->em ->createQuery('SELECT f FROM Doctrine\Tests\ORM\Tools\ResolveTarget f') - ->getSQL() + ->getSQL(), ); } } @@ -122,41 +115,28 @@ interface Target extends ResolveTarget { } -/** @Entity */ +#[Entity] class ResolveTargetEntity implements ResolveTarget { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="Doctrine\Tests\ORM\Tools\Target") - */ + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; + + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'Doctrine\Tests\ORM\Tools\Target')] private $manyToMany; - /** - * @var ResolveTarget - * @ManyToOne(targetEntity="Doctrine\Tests\ORM\Tools\ResolveTarget", inversedBy="oneToMany") - */ - private $manyToOne; + #[ManyToOne(targetEntity: 'Doctrine\Tests\ORM\Tools\ResolveTarget', inversedBy: 'oneToMany')] + private ResolveTarget $manyToOne; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="Doctrine\Tests\ORM\Tools\ResolveTarget", mappedBy="manyToOne") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'Doctrine\Tests\ORM\Tools\ResolveTarget', mappedBy: 'manyToOne')] private $oneToMany; - /** - * @var Target - * @OneToOne(targetEntity="Doctrine\Tests\ORM\Tools\Target") - * @JoinColumn(name="target_entity_id", referencedColumnName="id") - */ - private $oneToOne; + #[OneToOne(targetEntity: 'Doctrine\Tests\ORM\Tools\Target')] + #[JoinColumn(name: 'target_entity_id', referencedColumnName: 'id')] + private Target $oneToOne; public function getId(): int { @@ -164,16 +144,13 @@ public function getId(): int } } -/** @Entity */ +#[Entity] class TargetEntity implements Target { - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="AUTO") - */ - private $id; + #[Id] + #[Column(type: 'integer')] + #[GeneratedValue(strategy: 'AUTO')] + private int $id; public function getId(): int { diff --git a/tests/Tests/ORM/Tools/SchemaToolTest.php b/tests/Tests/ORM/Tools/SchemaToolTest.php index dde41a70fa6..36037fcf569 100644 --- a/tests/Tests/ORM/Tools/SchemaToolTest.php +++ b/tests/Tests/ORM/Tools/SchemaToolTest.php @@ -40,13 +40,14 @@ use Doctrine\Tests\Models\Forum\ForumUser; use Doctrine\Tests\Models\NullDefault\NullDefaultColumn; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\Group; use function count; use function current; class SchemaToolTest extends OrmTestCase { - public function testAddUniqueIndexForUniqueFieldAnnotation(): void + public function testAddUniqueIndexForUniqueFieldAttribute(): void { $em = $this->getTestEntityManager(); $schemaTool = new SchemaTool($em); @@ -67,15 +68,15 @@ public function testAddUniqueIndexForUniqueFieldAnnotation(): void self::assertTrue($schema->getTable('cms_users')->columnsAreIndexed(['username']), 'username column should be indexed.'); } - public function testAnnotationOptionsAttribute(): void + public function testAttributeOptionsArgument(): void { $em = $this->getTestEntityManager(); $schemaTool = new SchemaTool($em); $schema = $schemaTool->getSchemaFromMetadata( - [$em->getClassMetadata(TestEntityWithAnnotationOptionsAttribute::class)] + [$em->getClassMetadata(TestEntityWithAttributeOptionsArgument::class)], ); - $table = $schema->getTable('TestEntityWithAnnotationOptionsAttribute'); + $table = $schema->getTable('TestEntityWithAttributeOptionsArgument'); foreach ([$table->getOptions(), $table->getColumn('test')->getPlatformOptions()] as $options) { self::assertArrayHasKey('foo', $options); @@ -85,7 +86,7 @@ public function testAnnotationOptionsAttribute(): void } } - /** @group DDC-200 */ + #[Group('DDC-200')] public function testPassColumnDefinitionToJoinColumn(): void { $customColumnDef = 'MEDIUMINT(6) UNSIGNED NOT NULL'; @@ -93,9 +94,9 @@ public function testPassColumnDefinitionToJoinColumn(): void $em = $this->getTestEntityManager(); $schemaTool = new SchemaTool($em); - $avatar = $em->getClassMetadata(ForumAvatar::class); - $avatar->fieldMappings['id']['columnDefinition'] = $customColumnDef; - $user = $em->getClassMetadata(ForumUser::class); + $avatar = $em->getClassMetadata(ForumAvatar::class); + $avatar->fieldMappings['id']->columnDefinition = $customColumnDef; + $user = $em->getClassMetadata(ForumUser::class); $classes = [$avatar, $user]; @@ -107,7 +108,7 @@ public function testPassColumnDefinitionToJoinColumn(): void self::assertEquals($customColumnDef, $table->getColumn('avatar_id')->getColumnDefinition()); } - /** @group 6830 */ + #[Group('6830')] public function testPassColumnOptionsToJoinColumn(): void { $em = $this->getTestEntityManager(); @@ -128,22 +129,22 @@ public function testPassColumnOptionsToJoinColumn(): void self::assertSame( $tableCategory->getColumn('id')->getFixed(), $tableBoard->getColumn('category_id')->getFixed(), - 'Foreign key/join column should have the same value of option `fixed` as the referenced column' + 'Foreign key/join column should have the same value of option `fixed` as the referenced column', ); self::assertEquals( - $tableCategory->getColumn('id')->getCustomSchemaOptions(), - $tableBoard->getColumn('category_id')->getCustomSchemaOptions(), - 'Foreign key/join column should have the same custom options as the referenced column' + $tableCategory->getColumn('id')->getPlatformOptions(), + $tableBoard->getColumn('category_id')->getPlatformOptions(), + 'Foreign key/join column should have the same custom options as the referenced column', ); self::assertEquals( ['collation' => 'latin1_bin', 'foo' => 'bar'], - $tableBoard->getColumn('category_id')->getPlatformOptions() + $tableBoard->getColumn('category_id')->getPlatformOptions(), ); } - /** @group DDC-283 */ + #[Group('DDC-283')] public function testPostGenerateEvents(): void { $listener = new GenerateSchemaEventListener(); @@ -151,7 +152,7 @@ public function testPostGenerateEvents(): void $em = $this->getTestEntityManager(); $em->getEventManager()->addEventListener( [ToolEvents::postGenerateSchemaTable, ToolEvents::postGenerateSchema], - $listener + $listener, ); $schemaTool = new SchemaTool($em); @@ -171,22 +172,17 @@ public function testPostGenerateEvents(): void self::assertTrue($listener->schemaCalled); } - public function testNullDefaultNotAddedToCustomSchemaOptions(): void + public function testNullDefaultNotAddedToPlatformOptions(): void { $em = $this->getTestEntityManager(); $schemaTool = new SchemaTool($em); - $customSchemaOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(NullDefaultColumn::class)]) + self::assertSame([], $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(NullDefaultColumn::class)]) ->getTable('NullDefaultColumn') ->getColumn('nullDefault') - ->getCustomSchemaOptions(); - - self::assertSame([], $customSchemaOptions); + ->getPlatformOptions()); } - /** - * @requires PHP 8.1 - */ public function testEnumTypeAddedToCustomSchemaOptions(): void { $em = $this->getTestEntityManager(); @@ -201,19 +197,19 @@ public function testEnumTypeAddedToCustomSchemaOptions(): void self::assertSame(Suit::class, $platformOptions['enumType']); } - /** @group DDC-3671 */ - public function testSchemaHasProperIndexesFromUniqueConstraintAnnotation(): void + #[Group('DDC-3671')] + public function testSchemaHasProperIndexesFromUniqueConstraintAttribute(): void { $em = $this->getTestEntityManager(); $schemaTool = new SchemaTool($em); $classes = [ - $em->getClassMetadata(UniqueConstraintAnnotationModel::class), + $em->getClassMetadata(UniqueConstraintAttributeModel::class), ]; $schema = $schemaTool->getSchemaFromMetadata($classes); - self::assertTrue($schema->hasTable('unique_constraint_annotation_table')); - $table = $schema->getTable('unique_constraint_annotation_table'); + self::assertTrue($schema->hasTable('unique_constraint_attribute_table')); + $table = $schema->getTable('unique_constraint_attribute_table'); self::assertCount(2, $table->getIndexes()); self::assertTrue($table->hasIndex('primary')); @@ -269,7 +265,7 @@ public function testDerivedCompositeKey(): void $em->getClassMetadata(JoinedDerivedIdentityClass::class), $em->getClassMetadata(JoinedDerivedRootClass::class), $em->getClassMetadata(JoinedDerivedChildClass::class), - ] + ], ); self::assertTrue($schema->hasTable('joined_derived_identity')); @@ -349,7 +345,7 @@ public function testIncorrectUniqueConstraintsBasedOnFields(): void $schemaTool->getSchemaFromMetadata([$class]); } - /** @group schema-configuration */ + #[Group('schema-configuration')] public function testConfigurationSchemaIgnoredEntity(): void { $em = $this->getTestEntityManager(); @@ -375,7 +371,7 @@ public function testConfigurationSchemaIgnoredEntity(): void self::assertFalse($schema->hasTable('second_entity'), 'Table second_entity should not exist.'); } - /** @group GH-11314 */ + #[Group('GH-11314')] public function testLoadUniqueConstraintWithoutName(): void { $em = $this->getTestEntityManager(); @@ -397,24 +393,16 @@ public function testLoadUniqueConstraintWithoutName(): void } } -/** - * @Entity - * @Table(options={"foo": "bar", "baz": {"key": "val"}}) - */ -class TestEntityWithAnnotationOptionsAttribute +#[Table(options: ['foo' => 'bar', 'baz' => ['key' => 'val']])] +#[Entity] +class TestEntityWithAttributeOptionsArgument { - /** - * @var int - * @Id - * @Column - */ - private $id; - - /** - * @var string - * @Column(type="string", options={"foo": "bar", "baz": {"key": "val"}}) - */ - private $test; + #[Id] + #[Column] + private int $id; + + #[Column(type: 'string', options: ['foo' => 'bar', 'baz' => ['key' => 'val']])] + private string $test; } class GenerateSchemaEventListener @@ -436,174 +424,116 @@ public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs): void } } -/** - * @Entity - * @Table(name="unique_constraint_annotation_table", uniqueConstraints={ - * @UniqueConstraint(name="uniq_hash", columns={"hash"}) - * }) - */ -class UniqueConstraintAnnotationModel +#[Table(name: 'unique_constraint_attribute_table')] +#[UniqueConstraint(name: 'uniq_hash', columns: ['hash'])] +#[Entity] +class UniqueConstraintAttributeModel { - /** - * @var int - * @Id - * @Column - */ - private $id; - - /** - * @var string - * @Column(name="hash", type="string", length=8, nullable=false, unique=true) - */ - private $hash; + #[Id] + #[Column] + private int $id; + + #[Column(name: 'hash', type: 'string', length: 8, nullable: false, unique: true)] + private string $hash; } -/** - * @Entity - * @Table(name="first_entity") - */ +#[Table(name: 'first_entity')] +#[Entity] class FirstEntity { - /** - * @var int - * @Id - * @Column(name="id") - */ + /** @var int */ + #[Id] + #[Column(name: 'id')] public $id; - /** - * @var SecondEntity - * @OneToOne(targetEntity="SecondEntity") - * @JoinColumn(name="id", referencedColumnName="first_entity_id") - */ + /** @var SecondEntity */ + #[OneToOne(targetEntity: 'SecondEntity')] + #[JoinColumn(name: 'id', referencedColumnName: 'first_entity_id')] public $secondEntity; - /** - * @var string - * @Column(name="name") - */ + /** @var string */ + #[Column(name: 'name')] public $name; } -/** - * @Entity - * @Table(name="second_entity") - */ +#[Table(name: 'second_entity')] +#[Entity] class SecondEntity { - /** - * @var int - * @Id - * @Column(name="first_entity_id") - */ + /** @var int */ + #[Id] + #[Column(name: 'first_entity_id')] public $firstEntityId; - /** - * @var string - * @Column(name="name") - */ + /** @var string */ + #[Column(name: 'name')] public $name; } -/** @Entity */ +#[Entity] class GH6830Board { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var GH6830Category - * @ManyToOne(targetEntity=GH6830Category::class, inversedBy="boards") - * @JoinColumn(name="category_id", referencedColumnName="id") - */ + /** @var GH6830Category */ + #[ManyToOne(targetEntity: GH6830Category::class, inversedBy: 'boards')] + #[JoinColumn(name: 'category_id', referencedColumnName: 'id')] public $category; } -/** @Entity */ +#[Entity] class GH6830Category { - /** - * @Id - * @Column(type="string", length=8, options={"fixed":true, "collation":"latin1_bin", "foo":"bar"}) - * @var string - */ + /** @var string */ + #[Id] + #[Column(type: 'string', length: 8, options: ['fixed' => true, 'collation' => 'latin1_bin', 'foo' => 'bar'])] public $id; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity=GH6830Board::class, mappedBy="category") - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: GH6830Board::class, mappedBy: 'category')] public $boards; } -/** - * @Entity - * @Table( - * name="field_index", - * indexes={ - * @Index(name="index", fields={"index", "fieldName"}), - * }, - * uniqueConstraints={ - * @UniqueConstraint(name="uniq", fields={"index", "table"}) - * } - * ) - */ +#[Table(name: 'field_index')] +#[Index(name: 'index', fields: ['index', 'fieldName'])] +#[UniqueConstraint(name: 'uniq', fields: ['index', 'table'])] +#[Entity] class IndexByFieldEntity { - /** - * @var int - * @Id - * @Column(type="integer") - */ + /** @var int */ + #[Id] + #[Column(type: 'integer')] public $id; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $index; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $table; - /** - * @var string - * @Column - */ + /** @var string */ + #[Column] public $fieldName; } -/** - * @Entity - * @Table(uniqueConstraints={@UniqueConstraint(columns={"field", "anotherField"})}) - */ +#[Entity] +#[UniqueConstraint(columns: ['field', 'anotherField'])] class GH11314Entity { - /** - * @Column(type="integer") - * @Id - * @var int - */ - private $id; - - /** - * @Column(name="field", type="string") - * @var string - */ - private $field; - - /** - * @Column(name="anotherField", type="string") - * @var string - */ - private $anotherField; + #[Column] + #[Id] + private int $id; + + #[Column(name: 'field')] + private string $field; + + #[Column(name: 'anotherField')] + private string $anotherField; } class IncorrectIndexByFieldEntity @@ -626,7 +556,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->mapField(['fieldName' => 'index']); @@ -640,7 +570,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'indexes' => [ ['columns' => ['index'], 'fields' => ['fieldName']], ], - ] + ], ); } } @@ -665,7 +595,7 @@ public static function loadMetadata(ClassMetadata $metadata): void [ 'id' => true, 'fieldName' => 'id', - ] + ], ); $metadata->mapField(['fieldName' => 'index']); @@ -679,7 +609,7 @@ public static function loadMetadata(ClassMetadata $metadata): void 'uniqueConstraints' => [ ['columns' => ['index'], 'fields' => ['fieldName']], ], - ] + ], ); } } diff --git a/tests/Tests/ORM/Tools/SchemaValidatorTest.php b/tests/Tests/ORM/Tools/SchemaValidatorTest.php index 7b24a2321b8..8942380ce5b 100644 --- a/tests/Tests/ORM/Tools/SchemaValidatorTest.php +++ b/tests/Tests/ORM/Tools/SchemaValidatorTest.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\InheritanceType; +use Doctrine\ORM\Mapping\InverseJoinColumn; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\JoinTable; use Doctrine\ORM\Mapping\ManyToMany; @@ -27,14 +28,14 @@ use Doctrine\Tests\Models\BigIntegers\BigIntegers; use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\OrmTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; class SchemaValidatorTest extends OrmTestCase { - /** @var EntityManagerInterface */ - private $em = null; + private EntityManagerInterface|null $em = null; - /** @var SchemaValidator */ - private $validator = null; + private SchemaValidator|null $validator = null; protected function setUp(): void { @@ -42,7 +43,7 @@ protected function setUp(): void $this->validator = new SchemaValidator($this->em); } - /** @dataProvider modelSetProvider */ + #[DataProvider('modelSetProvider')] public function testCmsModelSet(string $path): void { $this->em->getConfiguration() @@ -64,7 +65,7 @@ public static function modelSetProvider(): array ]; } - /** @group DDC-1439 */ + #[Group('DDC-1439')] public function testInvalidManyToManyJoinColumnSchema(): void { $class1 = $this->em->getClassMetadata(InvalidEntity1::class); @@ -77,11 +78,11 @@ public function testInvalidManyToManyJoinColumnSchema(): void "The inverse join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the target entity 'Doctrine\Tests\ORM\Tools\InvalidEntity2', however 'key4' are missing.", "The join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the source entity 'Doctrine\Tests\ORM\Tools\InvalidEntity1', however 'key2' are missing.", ], - $ce + $ce, ); } - /** @group DDC-1439 */ + #[Group('DDC-1439')] public function testInvalidToOneJoinColumnSchema(): void { $class1 = $this->em->getClassMetadata(InvalidEntity1::class); @@ -94,11 +95,11 @@ public function testInvalidToOneJoinColumnSchema(): void "The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\Tests\ORM\Tools\InvalidEntity1'.", "The join columns of the association 'assoc' have to match to ALL identifier columns of the target entity 'Doctrine\Tests\ORM\Tools\InvalidEntity1', however 'key1, key2' are missing.", ], - $ce + $ce, ); } - /** @group DDC-1587 */ + #[Group('DDC-1587')] public function testValidOneToOneAsIdentifierSchema(): void { $class1 = $this->em->getClassMetadata(DDC1587ValidEntity2::class); @@ -109,7 +110,7 @@ public function testValidOneToOneAsIdentifierSchema(): void self::assertEquals([], $ce); } - /** @group DDC-1649 */ + #[Group('DDC-1649')] public function testInvalidTripleAssociationAsKeyMapping(): void { $classThree = $this->em->getClassMetadata(DDC1649Three::class); @@ -120,11 +121,11 @@ public function testInvalidTripleAssociationAsKeyMapping(): void "Cannot map association 'Doctrine\Tests\ORM\Tools\DDC1649Three#two as identifier, because the target entity 'Doctrine\Tests\ORM\Tools\DDC1649Two' also maps an association as identifier.", "The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\Tests\ORM\Tools\DDC1649Two'.", ], - $ce + $ce, ); } - /** @group DDC-3274 */ + #[Group('DDC-3274')] public function testInvalidBiDirectionalRelationMappingMissingInversedByAttribute(): void { $class = $this->em->getClassMetadata(DDC3274One::class); @@ -136,11 +137,11 @@ public function testInvalidBiDirectionalRelationMappingMissingInversedByAttribut 'relationship, but the specified mappedBy association on the target-entity ' . "Doctrine\Tests\ORM\Tools\DDC3274Two#one does not contain the required 'inversedBy=\"two\"' attribute.", ], - $ce + $ce, ); } - /** @group 9536 */ + #[Group('9536')] public function testInvalidBiDirectionalRelationMappingMissingMappedByAttribute(): void { $class = $this->em->getClassMetadata(Issue9536Owner::class); @@ -153,11 +154,11 @@ public function testInvalidBiDirectionalRelationMappingMissingMappedByAttribute( "Doctrine\Tests\ORM\Tools\Issue9536Target#two does not contain the required 'mappedBy=\"one\"' " . 'attribute.', ], - $ce + $ce, ); } - /** @group DDC-3322 */ + #[Group('DDC-3322')] public function testInvalidOrderByInvalidField(): void { $class = $this->em->getClassMetadata(DDC3322One::class); @@ -168,11 +169,11 @@ public function testInvalidOrderByInvalidField(): void 'The association Doctrine\Tests\ORM\Tools\DDC3322One#invalidAssoc is ordered by a foreign field ' . 'invalidField that is not a field on the target entity Doctrine\Tests\ORM\Tools\DDC3322ValidEntity1.', ], - $ce + $ce, ); } - /** @group DDC-3322 */ + #[Group('DDC-3322')] public function testInvalidOrderByCollectionValuedAssociation(): void { $class = $this->em->getClassMetadata(DDC3322Two::class); @@ -183,11 +184,11 @@ public function testInvalidOrderByCollectionValuedAssociation(): void 'The association Doctrine\Tests\ORM\Tools\DDC3322Two#invalidAssoc is ordered by a field oneToMany ' . 'on Doctrine\Tests\ORM\Tools\DDC3322ValidEntity1 that is a collection-valued association.', ], - $ce + $ce, ); } - /** @group DDC-3322 */ + #[Group('DDC-3322')] public function testInvalidOrderByAssociationInverseSide(): void { $class = $this->em->getClassMetadata(DDC3322Three::class); @@ -198,23 +199,10 @@ public function testInvalidOrderByAssociationInverseSide(): void 'The association Doctrine\Tests\ORM\Tools\DDC3322Three#invalidAssoc is ordered by a field oneToOneInverse ' . 'on Doctrine\Tests\ORM\Tools\DDC3322ValidEntity1 that is the inverse side of an association.', ], - $ce + $ce, ); } - /** @group 8052 */ - public function testInvalidAssociationInsideEmbeddable(): void - { - $class = $this->em->getClassMetadata(EmbeddableWithAssociation::class); - $ce = $this->validator->validateClass($class); - - self::assertEquals( - ["Embeddable 'Doctrine\Tests\ORM\Tools\EmbeddableWithAssociation' does not support associations"], - $ce - ); - } - - /** @group 8771 */ public function testMappedSuperclassNotPresentInDiscriminator(): void { $class1 = $this->em->getClassMetadata(MappedSuperclassEntity::class); @@ -238,476 +226,335 @@ public function testInvalidAssociationTowardsMappedSuperclass(): void self::assertEquals( ["The target entity 'Doctrine\Tests\ORM\Tools\InvalidMappedSuperClass' specified on Doctrine\Tests\ORM\Tools\InvalidMappedSuperClass#selfWhatever is a mapped superclass. This is not possible since there is no table that a foreign key could refer to."], - $ce + $ce, ); } - /** - * @requires PHP 7.4 - */ public function testBigIntProperty(): void { $class = $this->em->getClassMetadata(BigIntegers::class); self::assertSame( ['The field \'Doctrine\Tests\Models\BigIntegers\BigIntegers#three\' has the property type \'float\' that differs from the metadata field type \'int|string\' returned by the \'bigint\' DBAL type.'], - $this->validator->validateClass($class) + $this->validator->validateClass($class), ); } } -/** @MappedSuperclass */ +#[MappedSuperclass] abstract class MappedSuperclassEntity extends ParentEntity { } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"child" = ChildEntity::class}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['child' => ChildEntity::class])] abstract class ParentEntity { - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key; } -/** @Entity */ +#[Entity] class ChildEntity extends MappedSuperclassEntity { } -/** @Entity */ +#[Entity] class InvalidEntity1 { - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key1; - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key2; - /** - * @var ArrayCollection - * @ManyToMany (targetEntity="InvalidEntity2") - * @JoinTable (name="Entity1Entity2", - * joinColumns={@JoinColumn(name="key1", referencedColumnName="key1")}, - * inverseJoinColumns={@JoinColumn(name="key3", referencedColumnName="key3")} - * ) - */ + /** @var ArrayCollection */ + #[JoinTable(name: 'Entity1Entity2')] + #[JoinColumn(name: 'key1', referencedColumnName: 'key1')] + #[InverseJoinColumn(name: 'key3', referencedColumnName: 'key3')] + #[ManyToMany(targetEntity: 'InvalidEntity2')] protected $entity2; } -/** @Entity */ +#[Entity] class InvalidEntity2 { - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key3; - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key4; - /** - * @var InvalidEntity1 - * @ManyToOne(targetEntity="InvalidEntity1") - */ + /** @var InvalidEntity1 */ + #[ManyToOne(targetEntity: 'InvalidEntity1')] protected $assoc; } -/** - * @Entity(repositoryClass="Entity\Repository\Agent") - * @Table(name="agent") - */ +#[Table(name: 'agent')] +#[Entity(repositoryClass: 'Entity\Repository\Agent')] class DDC1587ValidEntity1 { - /** - * @var int - * @Id - * @GeneratedValue - * @Column(name="pk", type="integer") - */ - private $pk; - - /** - * @var string - * @Column(name="name", type="string", length=32) - */ - private $name; - - /** - * @var Identifier - * @OneToOne(targetEntity="DDC1587ValidEntity2", cascade={"all"}, mappedBy="agent") - */ + #[Id] + #[GeneratedValue] + #[Column(name: 'pk', type: 'integer')] + private int $pk; + + #[Column(name: 'name', type: 'string', length: 32)] + private string $name; + + /** @var Identifier */ + #[OneToOne(targetEntity: 'DDC1587ValidEntity2', cascade: ['all'], mappedBy: 'agent')] private $identifier; } -/** - * @Entity - * @Table - */ +#[Table] +#[Entity] class DDC1587ValidEntity2 { - /** - * @var DDC1587ValidEntity1 - * @Id - * @OneToOne(targetEntity="DDC1587ValidEntity1", inversedBy="identifier") - * @JoinColumn(name="pk_agent", referencedColumnName="pk", nullable=false) - */ - private $agent; - - /** - * @var string - * @Column(name="num", type="string", length=16, nullable=true) - */ - private $num; + #[Id] + #[OneToOne(targetEntity: 'DDC1587ValidEntity1', inversedBy: 'identifier')] + #[JoinColumn(name: 'pk_agent', referencedColumnName: 'pk', nullable: false)] + private DDC1587ValidEntity1 $agent; + + #[Column(name: 'num', type: 'string', length: 16, nullable: true)] + private string $num; } -/** @Entity */ +#[Entity] class DDC1649One { - /** - * @var mixed - * @Id - * @Column - * @GeneratedValue - */ + /** @var mixed */ + #[Id] + #[Column] + #[GeneratedValue] public $id; } -/** @Entity */ +#[Entity] class DDC1649Two { - /** - * @var DDC1649One - * @Id - * @ManyToOne(targetEntity="DDC1649One") - */ + /** @var DDC1649One */ + #[Id] + #[ManyToOne(targetEntity: 'DDC1649One')] public $one; } -/** @Entity */ +#[Entity] class DDC1649Three { - /** - * @var DDC1649Two - * @Id - * @ManyToOne(targetEntity="DDC1649Two") - * @JoinColumn(name="id", referencedColumnName="id") - */ - private $two; + #[Id] + #[ManyToOne(targetEntity: 'DDC1649Two')] + #[JoinColumn(name: 'id', referencedColumnName: 'id')] + private DDC1649Two $two; } -/** @Entity */ +#[Entity] class DDC3274One { - /** - * @var mixed - * @Id - * @Column - * @GeneratedValue - */ + /** @var mixed */ + #[Id] + #[Column] + #[GeneratedValue] private $id; - /** - * @var ArrayCollection - * @OneToMany(targetEntity="DDC3274Two", mappedBy="one") - */ - private $two; + #[OneToMany(targetEntity: 'DDC3274Two', mappedBy: 'one')] + private ArrayCollection $two; } -/** @Entity */ +#[Entity] class DDC3274Two { - /** - * @var DDC3274One - * @Id - * @ManyToOne(targetEntity="DDC3274One") - */ - private $one; + #[Id] + #[ManyToOne(targetEntity: 'DDC3274One')] + private DDC3274One $one; } -/** @Entity */ +#[Entity] class Issue9536Target { - /** - * @var mixed - * @Id - * @Column - * @GeneratedValue - */ + /** @var mixed */ + #[Id] + #[Column] + #[GeneratedValue] private $id; - /** - * @var Issue9536Owner - * @OneToOne(targetEntity="Issue9536Owner") - */ - private $two; + #[OneToOne(targetEntity: 'Issue9536Owner')] + private Issue9536Owner $two; } -/** @Entity */ +#[Entity] class Issue9536Owner { - /** - * @var mixed - * @Id - * @Column - * @GeneratedValue - */ + /** @var mixed */ + #[Id] + #[Column] + #[GeneratedValue] private $id; - /** - * @var Issue9536Target - * @OneToOne(targetEntity="Issue9536Target", inversedBy="two") - */ - private $one; + #[OneToOne(targetEntity: 'Issue9536Target', inversedBy: 'two')] + private Issue9536Target $one; } -/** @Entity */ +#[Entity] class DDC3322ValidEntity1 { - /** - * @var mixed - * @Id - * @Column - * @GeneratedValue - */ + /** @var mixed */ + #[Id] + #[Column] + #[GeneratedValue] private $id; - /** - * @var DDC3322One - * @ManyToOne(targetEntity="DDC3322One", inversedBy="validAssoc") - */ - private $oneValid; - - /** - * @var DDC3322One - * @ManyToOne(targetEntity="DDC3322One", inversedBy="invalidAssoc") - */ - private $oneInvalid; - - /** - * @var DDC3322Two - * @ManyToOne(targetEntity="DDC3322Two", inversedBy="validAssoc") - */ - private $twoValid; - - /** - * @var DDC3322Two - * @ManyToOne(targetEntity="DDC3322Two", inversedBy="invalidAssoc") - */ - private $twoInvalid; - - /** - * @var DDC3322Three - * @ManyToOne(targetEntity="DDC3322Three", inversedBy="validAssoc") - */ - private $threeValid; - - /** - * @var DDC3322Three - * @ManyToOne(targetEntity="DDC3322Three", inversedBy="invalidAssoc") - */ - private $threeInvalid; - - /** - * @var DDC3322ValidEntity2 - * @OneToMany(targetEntity="DDC3322ValidEntity2", mappedBy="manyToOne") - */ - private $oneToMany; - - /** - * @var DDC3322ValidEntity2 - * @ManyToOne(targetEntity="DDC3322ValidEntity2", inversedBy="oneToMany") - */ - private $manyToOne; - - /** - * @var DDC3322ValidEntity2 - * @OneToOne(targetEntity="DDC3322ValidEntity2", mappedBy="oneToOneOwning") - */ - private $oneToOneInverse; - - /** - * @var DDC3322ValidEntity2 - * @OneToOne(targetEntity="DDC3322ValidEntity2", inversedBy="oneToOneInverse") - */ - private $oneToOneOwning; + #[ManyToOne(targetEntity: 'DDC3322One', inversedBy: 'validAssoc')] + private DDC3322One $oneValid; + + #[ManyToOne(targetEntity: 'DDC3322One', inversedBy: 'invalidAssoc')] + private DDC3322One $oneInvalid; + + #[ManyToOne(targetEntity: 'DDC3322Two', inversedBy: 'validAssoc')] + private DDC3322Two $twoValid; + + #[ManyToOne(targetEntity: 'DDC3322Two', inversedBy: 'invalidAssoc')] + private DDC3322Two $twoInvalid; + + #[ManyToOne(targetEntity: 'DDC3322Three', inversedBy: 'validAssoc')] + private DDC3322Three $threeValid; + + #[ManyToOne(targetEntity: 'DDC3322Three', inversedBy: 'invalidAssoc')] + private DDC3322Three $threeInvalid; + + #[OneToMany(targetEntity: 'DDC3322ValidEntity2', mappedBy: 'manyToOne')] + private DDC3322ValidEntity2 $oneToMany; + + #[ManyToOne(targetEntity: 'DDC3322ValidEntity2', inversedBy: 'oneToMany')] + private DDC3322ValidEntity2 $manyToOne; + + #[OneToOne(targetEntity: 'DDC3322ValidEntity2', mappedBy: 'oneToOneOwning')] + private DDC3322ValidEntity2 $oneToOneInverse; + + #[OneToOne(targetEntity: 'DDC3322ValidEntity2', inversedBy: 'oneToOneInverse')] + private DDC3322ValidEntity2 $oneToOneOwning; } -/** @Entity */ +#[Entity] class DDC3322ValidEntity2 { - /** - * @var int - * @Id - * @Column - * @GeneratedValue - */ - private $id; + #[Id] + #[Column] + #[GeneratedValue] + private int $id; - /** - * @var DDC3322ValidEntity1 - * @ManyToOne(targetEntity="DDC3322ValidEntity1", inversedBy="oneToMany") - */ - private $manyToOne; - - /** - * @var DDC3322ValidEntity1 - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="manyToOne") - */ - private $oneToMany; - - /** - * @var DDC3322ValidEntity1 - * @OneToOne(targetEntity="DDC3322ValidEntity1", inversedBy="oneToOneInverse") - */ - private $oneToOneOwning; - - /** - * @var DDC3322ValidEntity1 - * @OneToOne(targetEntity="DDC3322ValidEntity1", mappedBy="oneToOneOwning") - */ - private $oneToOneInverse; + #[ManyToOne(targetEntity: 'DDC3322ValidEntity1', inversedBy: 'oneToMany')] + private DDC3322ValidEntity1 $manyToOne; + + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'manyToOne')] + private DDC3322ValidEntity1 $oneToMany; + + #[OneToOne(targetEntity: 'DDC3322ValidEntity1', inversedBy: 'oneToOneInverse')] + private DDC3322ValidEntity1 $oneToOneOwning; + + #[OneToOne(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneToOneOwning')] + private DDC3322ValidEntity1 $oneToOneInverse; } -/** @Entity */ +#[Entity] class DDC3322One { - /** - * @var int - * @Id - * @Column - * @GeneratedValue - */ - private $id; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="oneValid") - * @OrderBy({"id" = "ASC"}) - */ + #[Id] + #[Column] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneValid')] + #[OrderBy(['id' => 'ASC'])] private $validAssoc; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="oneInvalid") - * @OrderBy({"invalidField" = "ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'oneInvalid')] + #[OrderBy(['invalidField' => 'ASC'])] private $invalidAssoc; } -/** @Entity */ +#[Entity] class DDC3322Two { - /** - * @var int - * @Id - * @Column - * @GeneratedValue - */ - private $id; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="twoValid") - * @OrderBy({"manyToOne" = "ASC"}) - */ + #[Id] + #[Column] + #[GeneratedValue] + private int $id; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'twoValid')] + #[OrderBy(['manyToOne' => 'ASC'])] private $validAssoc; - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="twoInvalid") - * @OrderBy({"oneToMany" = "ASC"}) - */ + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'twoInvalid')] + #[OrderBy(['oneToMany' => 'ASC'])] private $invalidAssoc; } -/** @Entity */ +#[Entity] class DDC3322Three { - /** - * @var int - * @Id - * @Column - * @GeneratedValue - */ - private $id; - - /** - * @var DDC3322ValidEntity1 - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="threeValid") - * @OrderBy({"oneToOneOwning" = "ASC"}) - */ - private $validAssoc; - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="DDC3322ValidEntity1", mappedBy="threeInvalid") - * @OrderBy({"oneToOneInverse" = "ASC"}) - */ + #[Id] + #[Column] + #[GeneratedValue] + private int $id; + + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'threeValid')] + #[OrderBy(['oneToOneOwning' => 'ASC'])] + private DDC3322ValidEntity1 $validAssoc; + + /** @phpstan-var Collection */ + #[OneToMany(targetEntity: 'DDC3322ValidEntity1', mappedBy: 'threeInvalid')] + #[OrderBy(['oneToOneInverse' => 'ASC'])] private $invalidAssoc; } -/** @Embeddable */ +#[Embeddable] class EmbeddableWithAssociation { - /** - * @var ECommerceCart - * @OneToOne(targetEntity="Doctrine\Tests\Models\ECommerce\ECommerceCart") - */ - private $cart; + #[OneToOne(targetEntity: 'Doctrine\Tests\Models\ECommerce\ECommerceCart')] + private ECommerceCart $cart; } -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"child" = Issue9095Child::class}) - */ +#[Entity] +#[InheritanceType('SINGLE_TABLE')] +#[DiscriminatorMap(['child' => Issue9095Child::class])] abstract class Issue9095Parent { - /** - * @var mixed - * @Id - * @Column - */ + /** @var mixed */ + #[Id] + #[Column] protected $key; } -/** @Entity */ +#[Entity] abstract class Issue9095AbstractChild extends Issue9095Parent { } -/** @Entity */ +#[Entity] class Issue9095Child extends Issue9095AbstractChild { } -/** @MappedSuperclass */ +#[MappedSuperclass] class InvalidMappedSuperClass { - /** - * @phpstan-var Collection - * @ManyToMany(targetEntity="InvalidMappedSuperClass", mappedBy="invalid") - */ + /** @phpstan-var Collection */ + #[ManyToMany(targetEntity: 'InvalidMappedSuperClass', mappedBy: 'invalid')] private $selfWhatever; } diff --git a/tests/Tests/ORM/Tools/SetupTest.php b/tests/Tests/ORM/Tools/SetupTest.php deleted file mode 100644 index df5b0141661..00000000000 --- a/tests/Tests/ORM/Tools/SetupTest.php +++ /dev/null @@ -1,171 +0,0 @@ -originalAutoloaderCount = count(spl_autoload_functions()); - $this->originalIncludePath = get_include_path(); - } - - public function tearDown(): void - { - if (! $this->originalIncludePath) { - return; - } - - set_include_path($this->originalIncludePath); - $loaders = spl_autoload_functions(); - $numberOfLoaders = count($loaders); - for ($i = 0; $i < $numberOfLoaders; $i++) { - if ($i > $this->originalAutoloaderCount + 1) { - spl_autoload_unregister($loaders[$i]); - } - } - } - - public function testDirectoryAutoload(): void - { - Setup::registerAutoloadDirectory(__DIR__ . '/../../../../../vendor/doctrine/common/lib'); - - self::assertCount($this->originalAutoloaderCount + 2, spl_autoload_functions()); - } - - public function testAnnotationConfiguration(): void - { - $config = Setup::createAnnotationMetadataConfiguration([], true, null, null, false); - - self::assertInstanceOf(Configuration::class, $config); - self::assertEquals(sys_get_temp_dir(), $config->getProxyDir()); - self::assertEquals('DoctrineProxies', $config->getProxyNamespace()); - self::assertInstanceOf(AnnotationDriver::class, $config->getMetadataDriverImpl()); - } - - /** - * @requires PHP >= 8.0 - */ - public function testAttributeConfiguration(): void - { - $config = Setup::createAttributeMetadataConfiguration([], true); - - self::assertInstanceOf(Configuration::class, $config); - self::assertEquals(sys_get_temp_dir(), $config->getProxyDir()); - self::assertEquals('DoctrineProxies', $config->getProxyNamespace()); - self::assertInstanceOf(AttributeDriver::class, $config->getMetadataDriverImpl()); - } - - /** - * @requires PHP < 8 - */ - public function testAttributeConfigurationFailsOnPHP7(): void - { - self::expectException(LogicException::class); - self::expectExceptionMessage( - 'The attribute metadata driver cannot be enabled on PHP 7. Please upgrade to PHP 8 or choose a different metadata driver.' - ); - - Setup::createAttributeMetadataConfiguration([], true); - } - - public function testXMLConfiguration(): void - { - $config = Setup::createXMLMetadataConfiguration([], true); - - self::assertInstanceOf(Configuration::class, $config); - self::assertInstanceOf(XmlDriver::class, $config->getMetadataDriverImpl()); - } - - public function testYAMLConfiguration(): void - { - $config = Setup::createYAMLMetadataConfiguration([], true); - - self::assertInstanceOf(Configuration::class, $config); - self::assertInstanceOf(YamlDriver::class, $config->getMetadataDriverImpl()); - } - - /** @group 5904 */ - public function testCacheNamespaceShouldBeGeneratedWhenCacheIsGivenButHasNoNamespace(): void - { - $config = Setup::createConfiguration(false, '/foo', DoctrineProvider::wrap(new ArrayAdapter())); - $cache = $config->getMetadataCacheImpl(); - - self::assertSame('dc2_1effb2475fcfba4f9e8b8a1dbc8f3caf_', $cache->getNamespace()); - } - - /** @group 5904 */ - public function testConfiguredCacheNamespaceShouldBeUsedAsPrefixOfGeneratedNamespace(): void - { - $originalCache = DoctrineProvider::wrap(new ArrayAdapter()); - $originalCache->setNamespace('foo'); - - $config = Setup::createConfiguration(false, '/foo', $originalCache); - $cache = $config->getMetadataCacheImpl(); - self::assertSame($originalCache, $cache); - self::assertSame('foo:dc2_1effb2475fcfba4f9e8b8a1dbc8f3caf_', $cache->getNamespace()); - } - - /** @group DDC-1350 */ - public function testConfigureProxyDir(): void - { - $config = Setup::createAnnotationMetadataConfiguration([], true, '/foo', null, false); - self::assertEquals('/foo', $config->getProxyDir()); - } - - /** @group DDC-1350 */ - public function testConfigureCache(): void - { - $adapter = new ArrayAdapter(); - $cache = DoctrineProvider::wrap($adapter); - $config = Setup::createAnnotationMetadataConfiguration([], true, null, $cache, false); - - self::assertSame($adapter, $config->getResultCache()->getCache()->getPool()); - self::assertSame($cache, $config->getResultCacheImpl()); - self::assertSame($adapter, $config->getQueryCache()->getCache()->getPool()); - self::assertSame($cache, $config->getQueryCacheImpl()); - - self::assertSame($adapter, $config->getMetadataCache()->getCache()->getPool()); - } - - /** @group DDC-3190 */ - public function testConfigureCacheCustomInstance(): void - { - $adapter = new ArrayAdapter(); - $cache = DoctrineProvider::wrap($adapter); - $config = Setup::createConfiguration(true, null, $cache); - - self::assertSame($adapter, $config->getResultCache()->getCache()->getPool()); - self::assertSame($cache, $config->getResultCacheImpl()); - self::assertSame($adapter, $config->getQueryCache()->getCache()->getPool()); - self::assertSame($cache, $config->getQueryCacheImpl()); - - self::assertSame($adapter, $config->getMetadataCache()->getCache()->getPool()); - } -} diff --git a/tests/Tests/ORM/Tools/doctrine1schema/schema.yml b/tests/Tests/ORM/Tools/doctrine1schema/schema.yml deleted file mode 100644 index 65433d6ee81..00000000000 --- a/tests/Tests/ORM/Tools/doctrine1schema/schema.yml +++ /dev/null @@ -1,28 +0,0 @@ -User: - tableName: users - columns: - username: - type: string(255) - length: 100 - notnull: true - unique: true - password: - type: string(255) - clob: clob - test_alias as theAlias: - type: string(255) - indexes: - username: - fields: [username] - type: unique - -Profile: - columns: - first_name: string(255) - last_name: string(255) - user_id: integer - relations: - User: - onDelete: CASCADE - foreignType: one - type: one diff --git a/tests/Tests/ORM/UnitOfWorkTest.php b/tests/Tests/ORM/UnitOfWorkTest.php index 39fcc1bb942..238848696c3 100644 --- a/tests/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Tests/ORM/UnitOfWorkTest.php @@ -5,14 +5,13 @@ namespace Doctrine\Tests\ORM; use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; use Doctrine\ORM\EntityNotFoundException; -use Doctrine\ORM\Events; use Doctrine\ORM\Exception\EntityIdentityCollisionException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; @@ -20,15 +19,10 @@ use Doctrine\ORM\Mapping\GeneratedValue; use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\ManyToOne; -use Doctrine\ORM\Mapping\OneToMany; use Doctrine\ORM\Mapping\Version; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\UnitOfWork; -use Doctrine\Persistence\Event\LifecycleEventArgs; -use Doctrine\Persistence\NotifyPropertyChanged; -use Doctrine\Persistence\PropertyChangedListener; -use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Mocks\EntityPersisterMock; use Doctrine\Tests\Mocks\UnitOfWorkMock; @@ -37,19 +31,13 @@ use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Forum\ForumAvatar; use Doctrine\Tests\Models\Forum\ForumUser; -use Doctrine\Tests\Models\GeoNames\City; -use Doctrine\Tests\Models\GeoNames\Country; use Doctrine\Tests\OrmTestCase; -use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; -use Exception; +use Exception as BaseException; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use stdClass; -use function assert; -use function count; -use function gc_collect_cycles; -use function get_class; -use function method_exists; use function random_int; use function uniqid; @@ -58,32 +46,22 @@ */ class UnitOfWorkTest extends OrmTestCase { - use MockBuilderCompatibilityTools; - use VerifyDeprecations; - /** * SUT - * - * @var UnitOfWorkMock */ - private $_unitOfWork; + private UnitOfWorkMock $_unitOfWork; /** * Provides a sequence mock to the UnitOfWork - * - * @var ConnectionMock&MockObject */ - private $_connectionMock; + private Connection $connection; /** * The EntityManager mock that provides the mock persisters - * - * @var EntityManagerMock */ - private $_emMock; + private EntityManagerMock $_emMock; - /** @var EventManager&MockObject */ - private $eventManager; + private EventManager&MockObject $eventManager; protected function setUp(): void { @@ -93,19 +71,7 @@ protected function setUp(): void $platform->method('supportsIdentityColumns') ->willReturn(true); - if (method_exists($platform, 'getSQLResultCasing')) { - $platform->method('getSQLResultCasing') - ->willReturnCallback(static function (string $column): string { - return $column; - }); - } - - $driverStatement = $this->createMock(Driver\Statement::class); - - if (method_exists($driverStatement, 'rowCount')) { - $driverStatement->method('rowCount') - ->willReturn(0); - } + $driverStatement = $this->createMock(Statement::class); $driverConnection = $this->createMock(Driver\Connection::class); $driverConnection->method('prepare') @@ -119,9 +85,9 @@ protected function setUp(): void $driver->method('connect') ->willReturn($driverConnection); - $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock(); - $this->_connectionMock = new Connection([], $driver, null, $this->eventManager); - $this->_emMock = new EntityManagerMock($this->_connectionMock); + $this->eventManager = $this->getMockBuilder(EventManager::class)->getMock(); + $this->connection = new Connection([], $driver, null, $this->eventManager); + $this->_emMock = new EntityManagerMock($this->connection); // SUT $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); @@ -210,87 +176,6 @@ public function testCascadedIdentityColumnInsert(): void self::assertCount(0, $avatarPersister->getDeletes()); } - public function testChangeTrackingNotify(): void - { - $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(NotifyChangedEntity::class)); - $this->_unitOfWork->setEntityPersister(NotifyChangedEntity::class, $persister); - $itemPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(NotifyChangedRelatedItem::class)); - $this->_unitOfWork->setEntityPersister(NotifyChangedRelatedItem::class, $itemPersister); - - $entity = new NotifyChangedEntity(); - $entity->setData('thedata'); - $this->_unitOfWork->persist($entity); - - $this->_unitOfWork->commit(); - self::assertCount(1, $persister->getInserts()); - $persister->reset(); - - self::assertTrue($this->_unitOfWork->isInIdentityMap($entity)); - - $entity->setData('newdata'); - $entity->setTransient('newtransientvalue'); - - self::assertTrue($this->_unitOfWork->isScheduledForDirtyCheck($entity)); - - self::assertEquals(['data' => ['thedata', 'newdata']], $this->_unitOfWork->getEntityChangeSet($entity)); - - $item = new NotifyChangedRelatedItem(); - $entity->getItems()->add($item); - $item->setOwner($entity); - $this->_unitOfWork->persist($item); - - $this->_unitOfWork->commit(); - self::assertCount(1, $itemPersister->getInserts()); - $persister->reset(); - $itemPersister->reset(); - - $entity->getItems()->removeElement($item); - $item->setOwner(null); - self::assertTrue($entity->getItems()->isDirty()); - $this->_unitOfWork->commit(); - $updates = $itemPersister->getUpdates(); - self::assertCount(1, $updates); - self::assertSame($updates[0], $item); - } - - public function testChangeTrackingNotifyIndividualCommit(): void - { - $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\ORM\NotifyChangedEntity')); - $this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedEntity', $persister); - $itemPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\ORM\NotifyChangedRelatedItem')); - $this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedRelatedItem', $itemPersister); - - $entity = new NotifyChangedEntity(); - $entity->setData('thedata'); - - $entity2 = new NotifyChangedEntity(); - $entity2->setData('thedata'); - - $this->_unitOfWork->persist($entity); - $this->_unitOfWork->persist($entity2); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8459'); - - $this->_unitOfWork->commit($entity); - $this->_unitOfWork->commit(); - - self::assertEquals(2, count($persister->getInserts())); - - $persister->reset(); - - self::assertTrue($this->_unitOfWork->isInIdentityMap($entity2)); - - $entity->setData('newdata'); - $entity2->setData('newdata'); - - $this->_unitOfWork->commit($entity); - - self::assertTrue($this->_unitOfWork->isScheduledForDirtyCheck($entity2)); - self::assertEquals(['data' => ['thedata', 'newdata']], $this->_unitOfWork->getEntityChangeSet($entity2)); - self::assertFalse($this->_unitOfWork->isScheduledForDirtyCheck($entity)); - self::assertEquals([], $this->_unitOfWork->getEntityChangeSet($entity)); - } - public function testGetEntityStateOnVersionedEntityWithAssignedIdentifier(): void { $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(VersionedAssignedIdentifierEntity::class)); @@ -352,20 +237,16 @@ public function testNoUndefinedIndexNoticeOnScheduleForUpdateWithoutChanges(): v self::assertEmpty($this->_unitOfWork->getScheduledEntityUpdates()); } - /** - * @param mixed $invalidValue - * - * @group DDC-3490 - * @dataProvider invalidAssociationValuesDataProvider - */ - public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue($invalidValue): void + #[DataProvider('invalidAssociationValuesDataProvider')] + #[Group('DDC-3490')] + public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue(mixed $invalidValue): void { $this->_unitOfWork->setEntityPersister( ForumUser::class, new EntityPersisterMock( $this->_emMock, - $this->_emMock->getClassMetadata(ForumUser::class) - ) + $this->_emMock->getClassMetadata(ForumUser::class), + ), ); $user = new ForumUser(); @@ -377,19 +258,15 @@ public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue($inva $this->_unitOfWork->persist($user); } - /** - * @param mixed $invalidValue - * - * @group DDC-3490 - * @dataProvider invalidAssociationValuesDataProvider - */ - public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationValue($invalidValue): void + #[DataProvider('invalidAssociationValuesDataProvider')] + #[Group('DDC-3490')] + public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationValue(mixed $invalidValue): void { $metadata = $this->_emMock->getClassMetadata(ForumUser::class); $this->_unitOfWork->setEntityPersister( ForumUser::class, - new EntityPersisterMock($this->_emMock, $metadata) + new EntityPersisterMock($this->_emMock, $metadata), ); $user = new ForumUser(); @@ -404,10 +281,8 @@ public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationV $this->_unitOfWork->computeChangeSet($metadata, $user); } - /** - * @group DDC-3619 - * @group 1338 - */ + #[Group('DDC-3619')] + #[Group('1338')] public function testRemovedAndRePersistedEntitiesAreInTheIdentityMapAndAreNotGarbageCollected(): void { $entity = new ForumUser(); @@ -429,73 +304,6 @@ public function testRemovedAndRePersistedEntitiesAreInTheIdentityMapAndAreNotGar self::assertTrue($this->_unitOfWork->isInIdentityMap($entity)); } - /** - * @group 5849 - * @group 5850 - */ - public function testPersistedEntityAndClearManager(): void - { - $entity1 = new City(123, 'London'); - $entity2 = new Country(456, 'United Kingdom'); - - $this->_unitOfWork->persist($entity1); - self::assertTrue($this->_unitOfWork->isInIdentityMap($entity1)); - - $this->_unitOfWork->persist($entity2); - self::assertTrue($this->_unitOfWork->isInIdentityMap($entity2)); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8460'); - - $this->_unitOfWork->clear(Country::class); - self::assertTrue($this->_unitOfWork->isInIdentityMap($entity1)); - self::assertFalse($this->_unitOfWork->isInIdentityMap($entity2)); - self::assertTrue($this->_unitOfWork->isScheduledForInsert($entity1)); - self::assertFalse($this->_unitOfWork->isScheduledForInsert($entity2)); - } - - /** @group #5579 */ - public function testEntityChangeSetIsNotClearedAfterFlushOnSingleEntity(): void - { - $entity1 = new NotifyChangedEntity(); - $entity2 = new NotifyChangedEntity(); - - $entity1->setData('thedata'); - $entity2->setData('thedata'); - - $this->_unitOfWork->persist($entity1); - $this->_unitOfWork->persist($entity2); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8459'); - - $this->_unitOfWork->commit($entity1); - self::assertEmpty($this->_unitOfWork->getEntityChangeSet($entity1)); - self::assertCount(1, $this->_unitOfWork->getEntityChangeSet($entity2)); - } - - /** @group #5579 */ - public function testEntityChangeSetIsNotClearedAfterFlushOnArrayOfEntities(): void - { - $entity1 = new NotifyChangedEntity(); - $entity2 = new NotifyChangedEntity(); - $entity3 = new NotifyChangedEntity(); - - $entity1->setData('thedata'); - $entity2->setData('thedata'); - $entity3->setData('thedata'); - - $this->_unitOfWork->persist($entity1); - $this->_unitOfWork->persist($entity2); - $this->_unitOfWork->persist($entity3); - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issues/8459'); - - $this->_unitOfWork->commit([$entity1, $entity3]); - - self::assertEmpty($this->_unitOfWork->getEntityChangeSet($entity1)); - self::assertEmpty($this->_unitOfWork->getEntityChangeSet($entity3)); - self::assertCount(1, $this->_unitOfWork->getEntityChangeSet($entity2)); - } - /** * Data Provider * @@ -513,17 +321,13 @@ public static function invalidAssociationValuesDataProvider(): array ]; } - /** - * @param object $entity - * - * @dataProvider entitiesWithValidIdentifiersProvider - */ - public function testAddToIdentityMapValidIdentifiers($entity, string $idHash): void + #[DataProvider('entitiesWithValidIdentifiersProvider')] + public function testAddToIdentityMapValidIdentifiers(object $entity, string $idHash): void { $this->_unitOfWork->persist($entity); $this->_unitOfWork->addToIdentityMap($entity); - self::assertSame($entity, $this->_unitOfWork->getByIdHash($idHash, get_class($entity))); + self::assertSame($entity, $this->_unitOfWork->getByIdHash($idHash, $entity::class)); } /** @phpstan-return array */ @@ -572,13 +376,9 @@ public function testRegisteringAManagedInstanceRequiresANonEmptyIdentifier(): vo $this->_unitOfWork->registerManaged(new EntityWithBooleanIdentifier(), [], []); } - /** - * @param object $entity - * @param array $identifier - * - * @dataProvider entitiesWithInvalidIdentifiersProvider - */ - public function testAddToIdentityMapInvalidIdentifiers($entity, array $identifier): void + /** @param array $identifier */ + #[DataProvider('entitiesWithInvalidIdentifiersProvider')] + public function testAddToIdentityMapInvalidIdentifiers(object $entity, array $identifier): void { $this->expectException(ORMInvalidArgumentException::class); @@ -604,122 +404,12 @@ public static function entitiesWithInvalidIdentifiersProvider(): array ]; } - /** - * @group 5689 - * @group 1465 - */ - public function testObjectHashesOfMergedEntitiesAreNotUsedInOriginalEntityDataMap(): void - { - $user = new CmsUser(); - $user->name = 'ocramius'; - $mergedUser = $this->_unitOfWork->merge($user); - - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($user), 'No original data was stored'); - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($mergedUser), 'No original data was stored'); - - $user = null; - $mergedUser = null; - - // force garbage collection of $user (frees the used object hashes, which may be recycled) - gc_collect_cycles(); - - $newUser = new CmsUser(); - $newUser->name = 'ocramius'; - - $this->_unitOfWork->persist($newUser); - - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($newUser), 'No original data was stored'); - } - - /** - * @group DDC-1955 - * @group 5570 - * @group 6174 - */ - public function testMergeWithNewEntityWillPersistItAndTriggerPrePersistListenersWithMergedEntityData(): void - { - $entity = new EntityWithRandomlyGeneratedField(); - - $generatedFieldValue = $entity->generatedField; - - $this - ->eventManager - ->expects(self::any()) - ->method('hasListeners') - ->willReturnCallback(static function ($eventName) { - return $eventName === Events::prePersist; - }); - $this - ->eventManager - ->expects(self::once()) - ->method('dispatchEvent') - ->with( - self::anything(), - self::callback(static function (LifecycleEventArgs $args) use ($entity, $generatedFieldValue) { - $object = $args->getObject(); - assert($object instanceof EntityWithRandomlyGeneratedField); - - self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); - self::assertNotSame($entity, $object); - self::assertSame($generatedFieldValue, $object->generatedField); - - return true; - }) - ); - - $object = $this->_unitOfWork->merge($entity); - assert($object instanceof EntityWithRandomlyGeneratedField); - - self::assertNotSame($object, $entity); - self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); - self::assertSame($object->generatedField, $entity->generatedField); - } - - /** - * @group DDC-1955 - * @group 5570 - * @group 6174 - */ - public function testMergeWithExistingEntityWillNotPersistItNorTriggerPrePersistListeners(): void - { - $persistedEntity = new EntityWithRandomlyGeneratedField(); - $mergedEntity = new EntityWithRandomlyGeneratedField(); - - $mergedEntity->id = $persistedEntity->id; - $mergedEntity->generatedField = random_int( - $persistedEntity->generatedField + 1, - $persistedEntity->generatedField + 1000 - ); - - $this - ->eventManager - ->expects(self::any()) - ->method('hasListeners') - ->willReturnCallback(static function ($eventName) { - return $eventName === Events::prePersist; - }); - $this->eventManager->expects(self::never())->method('dispatchEvent'); - - $this->_unitOfWork->registerManaged( - $persistedEntity, - ['id' => $persistedEntity->id], - ['generatedField' => $persistedEntity->generatedField] - ); - - $merged = $this->_unitOfWork->merge($mergedEntity); - assert($merged instanceof EntityWithRandomlyGeneratedField); - - self::assertSame($merged, $persistedEntity); - self::assertSame($persistedEntity->generatedField, $mergedEntity->generatedField); - } - /** * Unlike next test, this one demonstrates that the problem does * not necessarily reproduce if all the pieces are being flushed together. - * - * @group DDC-2922 - * @group #1521 */ + #[Group('DDC-2922')] + #[Group('#1521')] public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughCascadedAssociationsFirst(): void { $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class)); @@ -753,10 +443,9 @@ public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughCascadedAs /** * This test exhibits the bug describe in the ticket, where an object that * ought to be reachable causes errors. - * - * @group DDC-2922 - * @group #1521 */ + #[Group('DDC-2922')] + #[Group('#1521')] public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughNonCascadedAssociationsFirst(): void { $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class)); @@ -804,10 +493,9 @@ public function testNewAssociatedEntityPersistenceOfNewEntitiesThroughNonCascade /** * This test exhibits the bug describe in the ticket, where an object that * ought to be reachable causes errors. - * - * @group DDC-2922 - * @group #1521 */ + #[Group('DDC-2922')] + #[Group('#1521')] public function testPreviousDetectedIllegalNewNonCascadedEntitiesAreCleanedUpOnSubsequentCommits(): void { $persister1 = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(CascadePersistedEntity::class)); @@ -827,7 +515,7 @@ public function testPreviousDetectedIllegalNewNonCascadedEntitiesAreCleanedUpOnS $this->_unitOfWork->commit(); self::fail('An exception was supposed to be raised'); - } catch (ORMInvalidArgumentException $ignored) { + } catch (ORMInvalidArgumentException) { self::assertEmpty($persister1->getInserts()); self::assertEmpty($persister2->getInserts()); } @@ -841,22 +529,32 @@ public function testPreviousDetectedIllegalNewNonCascadedEntitiesAreCleanedUpOnS self::assertCount(0, $persister2->getInserts()); } - /** @group #7946 Throw OptimisticLockException when connection::commit() returns false. */ - public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitReturnFalse(): void + #[Group('#7946')] // Throw OptimisticLockException when connection::commit() returns false. + public function testCommitThrowOptimisticLockExceptionWhenConnectionCommitFails(): void { + $platform = $this->getMockBuilder(AbstractPlatform::class) + ->onlyMethods(['supportsIdentityColumns']) + ->getMockForAbstractClass(); + $platform->method('supportsIdentityColumns') + ->willReturn(true); + $driver = $this->createMock(Driver::class); $driver->method('connect') ->willReturn($this->createMock(Driver\Connection::class)); + $driver->method('getDatabasePlatform') + ->willReturn($platform); // Set another connection mock that fail on commit - $this->_connectionMock = $this->getMockBuilderWithOnlyMethods(ConnectionMock::class, ['commit']) + $this->connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['commit']) ->setConstructorArgs([[], $driver]) ->getMock(); - $this->_emMock = new EntityManagerMock($this->_connectionMock); - $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); + $this->_emMock = new EntityManagerMock($this->connection); + $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); - $this->_connectionMock->method('commit')->willReturn(false); + $this->connection->method('commit') + ->willThrowException($this->createStub(Exception::class)); // Setup fake persister and id generator $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata(ForumUser::class)); @@ -899,7 +597,7 @@ public function testRemovedEntityIsRemovedFromManyToManyCollection(): void // Test that the removed entity has been removed from the many to many collection self::assertEmpty( $user->groups, - 'the removed entity should have been removed from the many to many collection' + 'the removed entity should have been removed from the many to many collection', ); // Collection is clean, snapshot has been updated @@ -934,28 +632,8 @@ public function testRemovedEntityIsRemovedFromOneToManyCollection(): void self::assertEmpty($user->phonenumbers->getSnapshot()); } - public function testItTriggersADeprecationNoticeWhenApplicationProvidedIdsCollide(): void - { - // We're using application-provided IDs and assign the same ID twice - // Note this is about colliding IDs in the identity map in memory. - // Duplicate database-level IDs would be spotted when the EM is flushed. - - $phone1 = new CmsPhonenumber(); - $phone1->phonenumber = '1234'; - $this->_unitOfWork->persist($phone1); - - $phone2 = new CmsPhonenumber(); - $phone2->phonenumber = '1234'; - - $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10785'); - - $this->_unitOfWork->persist($phone2); - } - public function testItThrowsWhenApplicationProvidedIdsCollide(): void { - $this->_emMock->getConfiguration()->setRejectIdCollisionInIdentityMap(true); - // We're using application-provided IDs and assign the same ID twice // Note this is about colliding IDs in the identity map in memory. // Duplicate database-level IDs would be spotted when the EM is flushed. @@ -975,19 +653,23 @@ public function testItThrowsWhenApplicationProvidedIdsCollide(): void public function testItPreservesTheOriginalExceptionOnRollbackFailure(): void { - $this->_connectionMock = new class extends ConnectionMock { - public function commit(): bool + $driver = $this->createStub(Driver::class); + $driver->method('connect') + ->willReturn($this->createMock(Driver\Connection::class)); + + $connection = new class (['platform' => $this->createStub(AbstractPlatform::class)], $driver) extends Connection { + public function commit(): void { - return false; // this should cause an exception + throw new BaseException('Commit failed'); } - public function rollBack(): bool + public function rollBack(): void { - throw new Exception('Rollback exception'); + throw new BaseException('Rollback exception'); } }; - $this->_emMock = new EntityManagerMock($this->_connectionMock); - $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); + $this->_emMock = new EntityManagerMock($connection); + $this->_unitOfWork = new UnitOfWorkMock($this->_emMock); $this->_emMock->setUnitOfWork($this->_unitOfWork); // Setup fake persister and id generator @@ -1003,7 +685,7 @@ public function rollBack(): bool try { $this->_unitOfWork->commit(); self::fail('Exception expected'); - } catch (Exception $e) { + } catch (BaseException $e) { self::assertSame('Rollback exception', $e->getMessage()); self::assertNotNull($e->getPrevious()); self::assertSame('Commit failed', $e->getPrevious()->getMessage()); @@ -1011,199 +693,56 @@ public function rollBack(): bool } } -/** @Entity */ -class NotifyChangedEntity implements NotifyPropertyChanged -{ - /** @phpstan-var list */ - private $_listeners = []; - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var string - * @Column(type="string", length=255) - */ - private $data; - - /** @var mixed */ - private $transient; // not persisted - - /** - * @phpstan-var Collection - * @OneToMany(targetEntity="NotifyChangedRelatedItem", mappedBy="owner") - */ - private $items; - - public function __construct() - { - $this->items = new ArrayCollection(); - } - - public function getId(): int - { - return $this->id; - } - - public function getItems(): Collection - { - return $this->items; - } - - public function setTransient($value): void - { - if ($value !== $this->transient) { - $this->onPropertyChanged('transient', $this->transient, $value); - $this->transient = $value; - } - } - - /** @return mixed */ - public function getData() - { - return $this->data; - } - - /** @param mixed $data */ - public function setData($data): void - { - if ($data !== $this->data) { - $this->onPropertyChanged('data', $this->data, $data); - $this->data = $data; - } - } - - public function addPropertyChangedListener(PropertyChangedListener $listener): void - { - $this->_listeners[] = $listener; - } - - /** - * @param mixed $propName - * @param mixed $oldValue - * @param mixed $newValue - */ - protected function onPropertyChanged($propName, $oldValue, $newValue): void - { - if ($this->_listeners) { - foreach ($this->_listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } - } -} - -/** @Entity */ -class NotifyChangedRelatedItem -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @var NotifyChangedEntity|null - * @ManyToOne(targetEntity="NotifyChangedEntity", inversedBy="items") - */ - private $owner; - - public function getId(): int - { - return $this->id; - } - - public function getOwner(): ?NotifyChangedEntity - { - return $this->owner; - } - - public function setOwner(?NotifyChangedEntity $owner): void - { - $this->owner = $owner; - } -} - -/** @Entity */ +#[Entity] class VersionedAssignedIdentifierEntity { - /** - * @var int - * @Id - * @Column(type="integer") - */ - public $id; + #[Id] + #[Column(type: 'integer')] + public int $id; - /** - * @var int - * @Version - * @Column(type="integer") - */ - public $version; + #[Version] + #[Column(type: 'integer')] + public int $version; } -/** @Entity */ +#[Entity] class EntityWithStringIdentifier { - /** - * @Id - * @Column(type="string", length=255) - * @var string|null - */ - public $id; + #[Id] + #[Column(type: 'string', length: 255)] + public string|null $id = null; } -/** @Entity */ +#[Entity] class EntityWithBooleanIdentifier { - /** - * @Id - * @Column(type="boolean") - * @var bool|null - */ - public $id; + #[Id] + #[Column(type: 'boolean')] + public bool|null $id = null; } -/** @Entity */ +#[Entity] class EntityWithCompositeStringIdentifier { - /** - * @Id - * @Column(type="string", length=255) - * @var string|null - */ - public $id1; + #[Id] + #[Column(type: 'string', length: 255)] + public string|null $id1 = null; - /** - * @Id - * @Column(type="string", length=255) - * @var string|null - */ - public $id2; + #[Id] + #[Column(type: 'string', length: 255)] + public string|null $id2 = null; } -/** @Entity */ +#[Entity] class EntityWithRandomlyGeneratedField { - /** - * @var string - * @Id - * @Column(type="string", length=255) - */ - public $id; + #[Id] + #[Column(type: 'string', length: 255)] + public string $id; - /** - * @var int - * @Column(type="integer") - */ - public $generatedField; + #[Column(type: 'integer')] + public int $generatedField; public function __construct() { @@ -1212,16 +751,13 @@ public function __construct() } } -/** @Entity */ +#[Entity] class CascadePersistedEntity { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - private $id; + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + private string $id; public function __construct() { @@ -1229,22 +765,16 @@ public function __construct() } } -/** @Entity */ +#[Entity] class EntityWithCascadingAssociation { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - private $id; + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + private string $id; - /** - * @var CascadePersistedEntity|null - * @ManyToOne(targetEntity=CascadePersistedEntity::class, cascade={"persist"}) - */ - public $cascaded; + #[ManyToOne(targetEntity: CascadePersistedEntity::class, cascade: ['persist'])] + public CascadePersistedEntity|null $cascaded = null; public function __construct() { @@ -1252,22 +782,16 @@ public function __construct() } } -/** @Entity */ +#[Entity] class EntityWithNonCascadingAssociation { - /** - * @var string - * @Id - * @Column(type="string", length=255) - * @GeneratedValue(strategy="NONE") - */ - private $id; + #[Id] + #[Column(type: 'string', length: 255)] + #[GeneratedValue(strategy: 'NONE')] + private string $id; - /** - * @var CascadePersistedEntity|null - * @ManyToOne(targetEntity=CascadePersistedEntity::class) - */ - public $nonCascaded; + #[ManyToOne(targetEntity: CascadePersistedEntity::class)] + public CascadePersistedEntity|null $nonCascaded = null; public function __construct() { diff --git a/tests/Tests/ORM/Utility/HierarchyDiscriminatorResolverTest.php b/tests/Tests/ORM/Utility/HierarchyDiscriminatorResolverTest.php index be037861967..2f4d789c809 100644 --- a/tests/Tests/ORM/Utility/HierarchyDiscriminatorResolverTest.php +++ b/tests/Tests/ORM/Utility/HierarchyDiscriminatorResolverTest.php @@ -29,7 +29,7 @@ public function testResolveDiscriminatorsForClass(): void [ [$classMetadata->name, $classMetadata], [$childClassMetadata->name, $childClassMetadata], - ] + ], ); $discriminators = HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($classMetadata, $em); diff --git a/tests/Tests/ORM/Utility/IdentifierFlattenerEnumIdTest.php b/tests/Tests/ORM/Utility/IdentifierFlattenerEnumIdTest.php index 8b893bf06cc..126195ea6db 100644 --- a/tests/Tests/ORM/Utility/IdentifierFlattenerEnumIdTest.php +++ b/tests/Tests/ORM/Utility/IdentifierFlattenerEnumIdTest.php @@ -4,18 +4,19 @@ namespace Doctrine\Tests\ORM\Utility; +use Doctrine\ORM\Utility\IdentifierFlattener; use Doctrine\Tests\Models\Enums\Suit; use Doctrine\Tests\Models\Enums\TypedCardEnumCompositeId; use Doctrine\Tests\Models\Enums\TypedCardEnumId; use Doctrine\Tests\Models\Enums\Unit; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; /** * Test the IdentifierFlattener utility class - * - * @requires PHP 8.1 - * @covers \Doctrine\ORM\Utility\IdentifierFlattener */ +#[CoversClass(IdentifierFlattener::class)] class IdentifierFlattenerEnumIdTest extends OrmFunctionalTestCase { protected function setUp(): void @@ -24,11 +25,11 @@ protected function setUp(): void $this->createSchemaForModels( TypedCardEnumId::class, - TypedCardEnumCompositeId::class + TypedCardEnumCompositeId::class, ); } - /** @group utilities */ + #[Group('utilities')] public function testFlattenIdentifierWithEnumId(): void { $typedCardEnumIdEntity = new TypedCardEnumId(); @@ -55,7 +56,7 @@ public function testFlattenIdentifierWithEnumId(): void self::assertEquals(Suit::Clubs, $findTypedCardEnumIdEntity->suit); } - /** @group utilities */ + #[Group('utilities')] public function testFlattenIdentifierWithCompositeEnumId(): void { $typedCardEnumCompositeIdEntity = new TypedCardEnumCompositeId(); diff --git a/tests/Tests/ORM/Utility/IdentifierFlattenerTest.php b/tests/Tests/ORM/Utility/IdentifierFlattenerTest.php index 08fe5ff304e..aa5f6b87c9c 100644 --- a/tests/Tests/ORM/Utility/IdentifierFlattenerTest.php +++ b/tests/Tests/ORM/Utility/IdentifierFlattenerTest.php @@ -10,20 +10,19 @@ use Doctrine\Tests\Models\VersionedOneToOne\FirstRelatedEntity; use Doctrine\Tests\Models\VersionedOneToOne\SecondRelatedEntity; use Doctrine\Tests\OrmFunctionalTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; /** * Test the IdentifierFlattener utility class - * - * @covers \Doctrine\ORM\Utility\IdentifierFlattener */ +#[CoversClass(IdentifierFlattener::class)] class IdentifierFlattenerTest extends OrmFunctionalTestCase { /** * Identifier flattener - * - * @var IdentifierFlattener */ - private $identifierFlattener; + private IdentifierFlattener $identifierFlattener; protected function setUp(): void { @@ -31,18 +30,18 @@ protected function setUp(): void $this->identifierFlattener = new IdentifierFlattener( $this->_em->getUnitOfWork(), - $this->_em->getMetadataFactory() + $this->_em->getMetadataFactory(), ); $this->createSchemaForModels( FirstRelatedEntity::class, SecondRelatedEntity::class, Flight::class, - City::class + City::class, ); } - /** @group utilities */ + #[Group('utilities')] public function testFlattenIdentifierWithOneToOneId(): void { $secondRelatedEntity = new SecondRelatedEntity(); @@ -70,9 +69,9 @@ public function testFlattenIdentifierWithOneToOneId(): void self::assertArrayHasKey('secondEntity', $id, 'It should be called secondEntity'); self::assertInstanceOf( - '\Doctrine\Tests\Models\VersionedOneToOne\SecondRelatedEntity', + SecondRelatedEntity::class, $id['secondEntity'], - 'The entity should be an instance of SecondRelatedEntity' + 'The entity should be an instance of SecondRelatedEntity', ); $flatIds = $this->identifierFlattener->flattenIdentifier($class, $id); @@ -84,7 +83,7 @@ public function testFlattenIdentifierWithOneToOneId(): void self::assertEquals($id['secondEntity']->id, $flatIds['secondEntity']); } - /** @group utilities */ + #[Group('utilities')] public function testFlattenIdentifierWithMutlipleIds(): void { $leeds = new City('Leeds'); diff --git a/tests/Tests/OrmFunctionalTestCase.php b/tests/Tests/OrmFunctionalTestCase.php index 73c812acbe1..efd380dc1de 100644 --- a/tests/Tests/OrmFunctionalTestCase.php +++ b/tests/Tests/OrmFunctionalTestCase.php @@ -4,7 +4,7 @@ namespace Doctrine\Tests; -use Doctrine\DBAL\Connection; +use Doctrine\Common\EventManager; use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; @@ -21,18 +21,155 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\ORMSetup; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Tools\DebugUnitOfWorkListener; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\ORM\Tools\ToolsException; use Doctrine\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Tests\DbalExtensions\Connection; use Doctrine\Tests\DbalExtensions\QueryLog; use Doctrine\Tests\DbalTypes\Rot13Type; use Doctrine\Tests\EventListener\CacheMetadataListener; +use Doctrine\Tests\Models\Cache\Action; +use Doctrine\Tests\Models\Cache\Address; +use Doctrine\Tests\Models\Cache\Attraction; +use Doctrine\Tests\Models\Cache\AttractionContactInfo; +use Doctrine\Tests\Models\Cache\AttractionInfo; +use Doctrine\Tests\Models\Cache\AttractionLocationInfo; +use Doctrine\Tests\Models\Cache\Bar; +use Doctrine\Tests\Models\Cache\Beach; +use Doctrine\Tests\Models\Cache\City; +use Doctrine\Tests\Models\Cache\Client; +use Doctrine\Tests\Models\Cache\ComplexAction; +use Doctrine\Tests\Models\Cache\Country; +use Doctrine\Tests\Models\Cache\Flight; +use Doctrine\Tests\Models\Cache\Login; +use Doctrine\Tests\Models\Cache\Person; +use Doctrine\Tests\Models\Cache\Restaurant; +use Doctrine\Tests\Models\Cache\State; +use Doctrine\Tests\Models\Cache\Token; +use Doctrine\Tests\Models\Cache\Travel; +use Doctrine\Tests\Models\Cache\Traveler; +use Doctrine\Tests\Models\Cache\TravelerProfile; +use Doctrine\Tests\Models\Cache\TravelerProfileInfo; +use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsArticle; +use Doctrine\Tests\Models\CMS\CmsComment; +use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsGroup; +use Doctrine\Tests\Models\CMS\CmsPhonenumber; +use Doctrine\Tests\Models\CMS\CmsTag; +use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\Company\CompanyAuction; +use Doctrine\Tests\Models\Company\CompanyCar; +use Doctrine\Tests\Models\Company\CompanyContract; +use Doctrine\Tests\Models\Company\CompanyEmployee; +use Doctrine\Tests\Models\Company\CompanyEvent; +use Doctrine\Tests\Models\Company\CompanyManager; +use Doctrine\Tests\Models\Company\CompanyOrganization; +use Doctrine\Tests\Models\Company\CompanyPerson; +use Doctrine\Tests\Models\Company\CompanyRaffle; +use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass; +use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedRootClass; +use Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass; +use Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass; +use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild; +use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent; +use Doctrine\Tests\Models\CustomType\CustomTypeChild; +use Doctrine\Tests\Models\CustomType\CustomTypeParent; +use Doctrine\Tests\Models\CustomType\CustomTypeUpperCase; +use Doctrine\Tests\Models\DDC117\DDC117ApproveChanges; +use Doctrine\Tests\Models\DDC117\DDC117Article; +use Doctrine\Tests\Models\DDC117\DDC117ArticleDetails; +use Doctrine\Tests\Models\DDC117\DDC117Editor; +use Doctrine\Tests\Models\DDC117\DDC117Link; +use Doctrine\Tests\Models\DDC117\DDC117Reference; +use Doctrine\Tests\Models\DDC117\DDC117Translation; +use Doctrine\Tests\Models\DDC2504\DDC2504ChildClass; +use Doctrine\Tests\Models\DDC2504\DDC2504OtherClass; +use Doctrine\Tests\Models\DDC2504\DDC2504RootClass; +use Doctrine\Tests\Models\DDC3346\DDC3346Article; +use Doctrine\Tests\Models\DDC3346\DDC3346Author; +use Doctrine\Tests\Models\DDC3699\DDC3699Child; +use Doctrine\Tests\Models\DDC3699\DDC3699Parent; +use Doctrine\Tests\Models\DDC3699\DDC3699RelationMany; +use Doctrine\Tests\Models\DDC3699\DDC3699RelationOne; +use Doctrine\Tests\Models\DirectoryTree\AbstractContentItem; +use Doctrine\Tests\Models\DirectoryTree\Directory; +use Doctrine\Tests\Models\DirectoryTree\File; +use Doctrine\Tests\Models\ECommerce\ECommerceCart; +use Doctrine\Tests\Models\ECommerce\ECommerceCategory; +use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; +use Doctrine\Tests\Models\ECommerce\ECommerceFeature; +use Doctrine\Tests\Models\ECommerce\ECommerceProduct; +use Doctrine\Tests\Models\ECommerce\ECommerceShipping; +use Doctrine\Tests\Models\Generic\BooleanModel; +use Doctrine\Tests\Models\Generic\DateTimeModel; +use Doctrine\Tests\Models\Generic\DecimalModel; +use Doctrine\Tests\Models\GeoNames\Admin1; +use Doctrine\Tests\Models\GeoNames\Admin1AlternateName; +use Doctrine\Tests\Models\Issue5989\Issue5989Employee; +use Doctrine\Tests\Models\Issue5989\Issue5989Manager; +use Doctrine\Tests\Models\Issue5989\Issue5989Person; +use Doctrine\Tests\Models\Legacy\LegacyArticle; +use Doctrine\Tests\Models\Legacy\LegacyCar; +use Doctrine\Tests\Models\Legacy\LegacyUser; +use Doctrine\Tests\Models\Legacy\LegacyUserReference; +use Doctrine\Tests\Models\Navigation\NavCountry; +use Doctrine\Tests\Models\Navigation\NavPhotos; +use Doctrine\Tests\Models\Navigation\NavPointOfInterest; +use Doctrine\Tests\Models\Navigation\NavTour; +use Doctrine\Tests\Models\Navigation\NavUser; +use Doctrine\Tests\Models\Pagination\Company; +use Doctrine\Tests\Models\Pagination\Department; +use Doctrine\Tests\Models\Pagination\Logo; +use Doctrine\Tests\Models\Pagination\User1; +use Doctrine\Tests\Models\Quote\FullAddress; +use Doctrine\Tests\Models\Quote\Group; +use Doctrine\Tests\Models\Quote\NumericEntity; +use Doctrine\Tests\Models\Quote\Phone; +use Doctrine\Tests\Models\Routing\RoutingLeg; +use Doctrine\Tests\Models\Routing\RoutingLocation; +use Doctrine\Tests\Models\Routing\RoutingRoute; +use Doctrine\Tests\Models\Routing\RoutingRouteBooking; +use Doctrine\Tests\Models\StockExchange\Bond; +use Doctrine\Tests\Models\StockExchange\Market; +use Doctrine\Tests\Models\StockExchange\Stock; +use Doctrine\Tests\Models\Taxi\Car; +use Doctrine\Tests\Models\Taxi\Driver; +use Doctrine\Tests\Models\Taxi\PaidRide; +use Doctrine\Tests\Models\Taxi\Ride; +use Doctrine\Tests\Models\Tweet\Tweet; +use Doctrine\Tests\Models\Tweet\User; +use Doctrine\Tests\Models\Tweet\UserList; +use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedManyToManyExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToManyExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\InversedOneToOneEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToManyExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneExtraLazyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity; +use Doctrine\Tests\Models\ValueConversionType\OwningOneToOneEntity; +use Doctrine\Tests\Models\VersionedManyToOne\Article; +use Doctrine\Tests\Models\VersionedManyToOne\Category; use Exception; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Constraint\Count; -use PHPUnit\Framework\Warning; use Psr\Cache\CacheItemPoolInterface; use RuntimeException; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -124,223 +261,223 @@ abstract class OrmFunctionalTestCase extends OrmTestCase */ protected static $modelSets = [ 'cms' => [ - Models\CMS\CmsUser::class, - Models\CMS\CmsPhonenumber::class, - Models\CMS\CmsAddress::class, - Models\CMS\CmsEmail::class, - Models\CMS\CmsGroup::class, - Models\CMS\CmsTag::class, - Models\CMS\CmsArticle::class, - Models\CMS\CmsComment::class, + CmsUser::class, + CmsPhonenumber::class, + CmsAddress::class, + CmsEmail::class, + CmsGroup::class, + CmsTag::class, + CmsArticle::class, + CmsComment::class, ], 'company' => [ - Models\Company\CompanyPerson::class, - Models\Company\CompanyEmployee::class, - Models\Company\CompanyManager::class, - Models\Company\CompanyOrganization::class, - Models\Company\CompanyEvent::class, - Models\Company\CompanyAuction::class, - Models\Company\CompanyRaffle::class, - Models\Company\CompanyCar::class, - Models\Company\CompanyContract::class, + CompanyPerson::class, + CompanyEmployee::class, + CompanyManager::class, + CompanyOrganization::class, + CompanyEvent::class, + CompanyAuction::class, + CompanyRaffle::class, + CompanyCar::class, + CompanyContract::class, ], 'ecommerce' => [ - Models\ECommerce\ECommerceCart::class, - Models\ECommerce\ECommerceCustomer::class, - Models\ECommerce\ECommerceProduct::class, - Models\ECommerce\ECommerceShipping::class, - Models\ECommerce\ECommerceFeature::class, - Models\ECommerce\ECommerceCategory::class, + ECommerceCart::class, + ECommerceCustomer::class, + ECommerceProduct::class, + ECommerceShipping::class, + ECommerceFeature::class, + ECommerceCategory::class, ], 'generic' => [ - Models\Generic\BooleanModel::class, - Models\Generic\DateTimeModel::class, - Models\Generic\DecimalModel::class, + BooleanModel::class, + DateTimeModel::class, + DecimalModel::class, ], 'routing' => [ - Models\Routing\RoutingLeg::class, - Models\Routing\RoutingLocation::class, - Models\Routing\RoutingRoute::class, - Models\Routing\RoutingRouteBooking::class, + RoutingLeg::class, + RoutingLocation::class, + RoutingRoute::class, + RoutingRouteBooking::class, ], 'navigation' => [ - Models\Navigation\NavUser::class, - Models\Navigation\NavCountry::class, - Models\Navigation\NavPhotos::class, - Models\Navigation\NavTour::class, - Models\Navigation\NavPointOfInterest::class, + NavUser::class, + NavCountry::class, + NavPhotos::class, + NavTour::class, + NavPointOfInterest::class, ], 'directorytree' => [ - Models\DirectoryTree\AbstractContentItem::class, - Models\DirectoryTree\File::class, - Models\DirectoryTree\Directory::class, + AbstractContentItem::class, + File::class, + Directory::class, ], 'ddc117' => [ - Models\DDC117\DDC117Article::class, - Models\DDC117\DDC117Reference::class, - Models\DDC117\DDC117Translation::class, - Models\DDC117\DDC117ArticleDetails::class, - Models\DDC117\DDC117ApproveChanges::class, - Models\DDC117\DDC117Editor::class, - Models\DDC117\DDC117Link::class, + DDC117Article::class, + DDC117Reference::class, + DDC117Translation::class, + DDC117ArticleDetails::class, + DDC117ApproveChanges::class, + DDC117Editor::class, + DDC117Link::class, ], 'ddc3699' => [ - Models\DDC3699\DDC3699Parent::class, - Models\DDC3699\DDC3699RelationOne::class, - Models\DDC3699\DDC3699RelationMany::class, - Models\DDC3699\DDC3699Child::class, + DDC3699Parent::class, + DDC3699RelationOne::class, + DDC3699RelationMany::class, + DDC3699Child::class, ], 'stockexchange' => [ - Models\StockExchange\Bond::class, - Models\StockExchange\Stock::class, - Models\StockExchange\Market::class, + Bond::class, + Stock::class, + Market::class, ], 'legacy' => [ - Models\Legacy\LegacyUser::class, - Models\Legacy\LegacyUserReference::class, - Models\Legacy\LegacyArticle::class, - Models\Legacy\LegacyCar::class, + LegacyUser::class, + LegacyUserReference::class, + LegacyArticle::class, + LegacyCar::class, ], 'customtype' => [ - Models\CustomType\CustomTypeChild::class, - Models\CustomType\CustomTypeParent::class, - Models\CustomType\CustomTypeUpperCase::class, + CustomTypeChild::class, + CustomTypeParent::class, + CustomTypeUpperCase::class, ], 'compositekeyinheritance' => [ - Models\CompositeKeyInheritance\JoinedRootClass::class, - Models\CompositeKeyInheritance\JoinedChildClass::class, - Models\CompositeKeyInheritance\SingleRootClass::class, - Models\CompositeKeyInheritance\SingleChildClass::class, + JoinedRootClass::class, + JoinedChildClass::class, + SingleRootClass::class, + SingleChildClass::class, ], 'compositekeyrelations' => [ Models\CompositeKeyRelations\InvoiceClass::class, Models\CompositeKeyRelations\CustomerClass::class, ], 'taxi' => [ - Models\Taxi\PaidRide::class, - Models\Taxi\Ride::class, - Models\Taxi\Car::class, - Models\Taxi\Driver::class, + PaidRide::class, + Ride::class, + Car::class, + Driver::class, ], 'cache' => [ - Models\Cache\Country::class, - Models\Cache\State::class, - Models\Cache\City::class, - Models\Cache\Traveler::class, - Models\Cache\TravelerProfileInfo::class, - Models\Cache\TravelerProfile::class, - Models\Cache\Travel::class, - Models\Cache\Attraction::class, - Models\Cache\Restaurant::class, - Models\Cache\Beach::class, - Models\Cache\Bar::class, - Models\Cache\Flight::class, - Models\Cache\Token::class, - Models\Cache\Login::class, - Models\Cache\Client::class, - Models\Cache\Person::class, - Models\Cache\Address::class, - Models\Cache\Action::class, - Models\Cache\ComplexAction::class, - Models\Cache\AttractionInfo::class, - Models\Cache\AttractionContactInfo::class, - Models\Cache\AttractionLocationInfo::class, + Country::class, + State::class, + City::class, + Traveler::class, + TravelerProfileInfo::class, + TravelerProfile::class, + Travel::class, + Attraction::class, + Restaurant::class, + Beach::class, + Bar::class, + Flight::class, + Token::class, + Login::class, + Client::class, + Person::class, + Address::class, + Action::class, + ComplexAction::class, + AttractionInfo::class, + AttractionContactInfo::class, + AttractionLocationInfo::class, ], 'tweet' => [ - Models\Tweet\User::class, - Models\Tweet\Tweet::class, - Models\Tweet\UserList::class, + User::class, + Tweet::class, + UserList::class, ], 'ddc2504' => [ - Models\DDC2504\DDC2504RootClass::class, - Models\DDC2504\DDC2504ChildClass::class, - Models\DDC2504\DDC2504OtherClass::class, + DDC2504RootClass::class, + DDC2504ChildClass::class, + DDC2504OtherClass::class, ], 'ddc3346' => [ - Models\DDC3346\DDC3346Author::class, - Models\DDC3346\DDC3346Article::class, + DDC3346Author::class, + DDC3346Article::class, ], 'quote' => [ Models\Quote\Address::class, Models\Quote\City::class, - Models\Quote\FullAddress::class, - Models\Quote\Group::class, - Models\Quote\NumericEntity::class, - Models\Quote\Phone::class, + FullAddress::class, + Group::class, + NumericEntity::class, + Phone::class, Models\Quote\User::class, ], 'vct_onetoone' => [ - Models\ValueConversionType\InversedOneToOneEntity::class, - Models\ValueConversionType\OwningOneToOneEntity::class, + InversedOneToOneEntity::class, + OwningOneToOneEntity::class, ], 'vct_onetoone_compositeid' => [ - Models\ValueConversionType\InversedOneToOneCompositeIdEntity::class, - Models\ValueConversionType\OwningOneToOneCompositeIdEntity::class, + InversedOneToOneCompositeIdEntity::class, + OwningOneToOneCompositeIdEntity::class, ], 'vct_onetoone_compositeid_foreignkey' => [ - Models\ValueConversionType\AuxiliaryEntity::class, - Models\ValueConversionType\InversedOneToOneCompositeIdForeignKeyEntity::class, - Models\ValueConversionType\OwningOneToOneCompositeIdForeignKeyEntity::class, + AuxiliaryEntity::class, + InversedOneToOneCompositeIdForeignKeyEntity::class, + OwningOneToOneCompositeIdForeignKeyEntity::class, ], 'vct_onetomany' => [ - Models\ValueConversionType\InversedOneToManyEntity::class, - Models\ValueConversionType\OwningManyToOneEntity::class, + InversedOneToManyEntity::class, + OwningManyToOneEntity::class, ], 'vct_onetomany_compositeid' => [ - Models\ValueConversionType\InversedOneToManyCompositeIdEntity::class, - Models\ValueConversionType\OwningManyToOneCompositeIdEntity::class, + InversedOneToManyCompositeIdEntity::class, + OwningManyToOneCompositeIdEntity::class, ], 'vct_onetomany_compositeid_foreignkey' => [ - Models\ValueConversionType\AuxiliaryEntity::class, - Models\ValueConversionType\InversedOneToManyCompositeIdForeignKeyEntity::class, - Models\ValueConversionType\OwningManyToOneCompositeIdForeignKeyEntity::class, + AuxiliaryEntity::class, + InversedOneToManyCompositeIdForeignKeyEntity::class, + OwningManyToOneCompositeIdForeignKeyEntity::class, ], 'vct_onetomany_extralazy' => [ - Models\ValueConversionType\InversedOneToManyExtraLazyEntity::class, - Models\ValueConversionType\OwningManyToOneExtraLazyEntity::class, + InversedOneToManyExtraLazyEntity::class, + OwningManyToOneExtraLazyEntity::class, ], 'vct_manytomany' => [ - Models\ValueConversionType\InversedManyToManyEntity::class, - Models\ValueConversionType\OwningManyToManyEntity::class, + InversedManyToManyEntity::class, + OwningManyToManyEntity::class, ], 'vct_manytomany_compositeid' => [ - Models\ValueConversionType\InversedManyToManyCompositeIdEntity::class, - Models\ValueConversionType\OwningManyToManyCompositeIdEntity::class, + InversedManyToManyCompositeIdEntity::class, + OwningManyToManyCompositeIdEntity::class, ], 'vct_manytomany_compositeid_foreignkey' => [ - Models\ValueConversionType\AuxiliaryEntity::class, - Models\ValueConversionType\InversedManyToManyCompositeIdForeignKeyEntity::class, - Models\ValueConversionType\OwningManyToManyCompositeIdForeignKeyEntity::class, + AuxiliaryEntity::class, + InversedManyToManyCompositeIdForeignKeyEntity::class, + OwningManyToManyCompositeIdForeignKeyEntity::class, ], 'vct_manytomany_extralazy' => [ - Models\ValueConversionType\InversedManyToManyExtraLazyEntity::class, - Models\ValueConversionType\OwningManyToManyExtraLazyEntity::class, + InversedManyToManyExtraLazyEntity::class, + OwningManyToManyExtraLazyEntity::class, ], 'geonames' => [ Models\GeoNames\Country::class, - Models\GeoNames\Admin1::class, - Models\GeoNames\Admin1AlternateName::class, + Admin1::class, + Admin1AlternateName::class, Models\GeoNames\City::class, ], 'custom_id_object_type' => [ - Models\CustomType\CustomIdObjectTypeParent::class, - Models\CustomType\CustomIdObjectTypeChild::class, + CustomIdObjectTypeParent::class, + CustomIdObjectTypeChild::class, ], 'pagination' => [ - Models\Pagination\Company::class, - Models\Pagination\Logo::class, - Models\Pagination\Department::class, + Company::class, + Logo::class, + Department::class, Models\Pagination\User::class, - Models\Pagination\User1::class, + User1::class, ], 'versioned_many_to_one' => [ - Models\VersionedManyToOne\Category::class, - Models\VersionedManyToOne\Article::class, + Category::class, + Article::class, ], 'issue5989' => [ - Models\Issue5989\Issue5989Person::class, - Models\Issue5989\Issue5989Employee::class, - Models\Issue5989\Issue5989Manager::class, + Issue5989Person::class, + Issue5989Employee::class, + Issue5989Manager::class, ], 'issue9300' => [ Models\Issue9300\Issue9300Child::class, @@ -353,7 +490,7 @@ final protected function createSchemaForModels(string ...$models): void { try { $this->_schemaTool->createSchema($this->getMetadataForModels($models)); - } catch (ToolsException $e) { + } catch (ToolsException) { } } @@ -384,7 +521,7 @@ private function getMetadataForModels(array $models): array function (string $className): ClassMetadata { return $this->_em->getClassMetadata($className); }, - $models + $models, ); } @@ -477,7 +614,7 @@ protected function tearDown(): void } if (isset($this->_usedModelSets['directorytree'])) { - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('file')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('file')); // MySQL doesn't know deferred deletions therefore only executing the second query gives errors. $conn->executeStatement('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL'); $conn->executeStatement('DELETE FROM Directory'); @@ -570,17 +707,17 @@ protected function tearDown(): void $conn->executeStatement( sprintf( 'UPDATE %s SET %s = NULL', - $platform->quoteIdentifier('quote-address'), - $platform->quoteIdentifier('user-id') - ) + $platform->quoteSingleIdentifier('quote-address'), + $platform->quoteSingleIdentifier('user-id'), + ), ); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-users-groups')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-group')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-phone')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-user')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-address')); - $conn->executeStatement('DELETE FROM ' . $platform->quoteIdentifier('quote-city')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-users-groups')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-group')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-phone')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-user')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-address')); + $conn->executeStatement('DELETE FROM ' . $platform->quoteSingleIdentifier('quote-city')); } if (isset($this->_usedModelSets['vct_onetoone'])) { @@ -756,8 +893,8 @@ protected function setUp(): void * @throws ORMException */ protected function getEntityManager( - ?DbalExtensions\Connection $connection = null, - ?MappingDriver $mappingDriver = null + Connection|null $connection = null, + MappingDriver|null $mappingDriver = null, ): EntityManagerInterface { // NOTE: Functional tests use their own shared metadata cache, because // the actual database platform used during execution has effect on some @@ -787,7 +924,7 @@ protected function getEntityManager( $cacheConfig = new CacheConfiguration(); $factory = new DefaultCacheFactory( $cacheConfig->getRegionsConfiguration(), - $this->getSharedSecondLevelCache() + $this->getSharedSecondLevelCache(), ); $this->secondLevelCacheFactory = $factory; @@ -805,10 +942,10 @@ protected function getEntityManager( } $config->setMetadataDriverImpl( - $mappingDriver ?? ORMSetup::createDefaultAnnotationDriver([ + $mappingDriver ?? new AttributeDriver([ realpath(__DIR__ . '/Models/Cache'), realpath(__DIR__ . '/Models/GeoNames'), - ], null, true) + ], true), ); $conn = $connection ?: static::$sharedConn; @@ -816,11 +953,15 @@ protected function getEntityManager( $conn->queryLog->reset(); // get rid of more global state - $evm = $conn->getEventManager(); - foreach ($evm->getAllListeners() as $event => $listeners) { - foreach ($listeners as $listener) { - $evm->removeEventListener([$event], $listener); + if (method_exists($conn, 'getEventManager')) { + $evm = $conn->getEventManager(); + foreach ($evm->getAllListeners() as $event => $listeners) { + foreach ($listeners as $listener) { + $evm->removeEventListener([$event], $listener); + } } + } else { + $evm = new EventManager(); } if ($enableSecondLevelCache) { @@ -838,20 +979,18 @@ protected function getEntityManager( $evm->addEventListener(['onFlush'], new DebugUnitOfWorkListener()); } - return new EntityManager($conn, $config); + return new EntityManager($conn, $config, $evm); } final protected function createSchemaManager(): AbstractSchemaManager { - return method_exists(Connection::class, 'createSchemaManager') - ? $this->_em->getConnection()->createSchemaManager() - : $this->_em->getConnection()->getSchemaManager(); + return $this->_em->getConnection()->createSchemaManager(); } /** @throws Throwable */ - protected function onNotSuccessfulTest(Throwable $e): void + protected function onNotSuccessfulTest(Throwable $e): never { - if ($e instanceof AssertionFailedError || $e instanceof Warning) { + if ($e instanceof AssertionFailedError) { throw $e; } @@ -859,9 +998,7 @@ protected function onNotSuccessfulTest(Throwable $e): void $queries = ''; $last25queries = array_slice(array_reverse($this->getQueryLog()->queries, true), 0, 25, true); foreach ($last25queries as $i => $query) { - $params = array_map(static function ($p) { - return is_object($p) ? get_debug_type($p) : var_export($p, true); - }, $query['params'] ?: []); + $params = array_map(static fn ($p) => is_object($p) ? get_debug_type($p) : var_export($p, true), $query['params'] ?: []); $queries .= $i . ". SQL: '" . $query['sql'] . "' Params: " . implode(', ', $params) . PHP_EOL; } @@ -891,7 +1028,7 @@ public function assertSQLEquals(string $expectedSql, string $actualSql): void self::assertEquals( strtolower($expectedSql), strtolower($actualSql), - 'Lowercase comparison of SQL statements failed.' + 'Lowercase comparison of SQL statements failed.', ); } @@ -909,17 +1046,17 @@ protected function setUpDBALTypes(): void final protected function isQueryLogAvailable(): bool { - return $this->_em && $this->_em->getConnection() instanceof DbalExtensions\Connection; + return $this->_em?->getConnection() instanceof Connection; } final protected function getQueryLog(): QueryLog { $connection = $this->_em->getConnection(); - if (! $connection instanceof DbalExtensions\Connection) { + if (! $connection instanceof Connection) { throw new RuntimeException(sprintf( 'The query log is only available if %s is used as wrapper class. Got %s.', - DbalExtensions\Connection::class, - get_debug_type($connection) + Connection::class, + get_debug_type($connection), )); } @@ -958,7 +1095,7 @@ protected function dropTableIfExists(string $name): void try { $schemaManager->dropTable($name); - } catch (DatabaseObjectNotFoundException $e) { + } catch (DatabaseObjectNotFoundException) { } } @@ -977,8 +1114,7 @@ protected function dropAndCreateTable(Table $table): void $schemaManager->createTable($table); } - /** @param object $entity */ - final protected function isUninitializedObject($entity): bool + final protected function isUninitializedObject(object $entity): bool { return $this->_em->getUnitOfWork()->isUninitializedObject($entity); } diff --git a/tests/Tests/OrmTestCase.php b/tests/Tests/OrmTestCase.php index 2ac45922ca7..52d0a4d2781 100644 --- a/tests/Tests/OrmTestCase.php +++ b/tests/Tests/OrmTestCase.php @@ -4,40 +4,41 @@ namespace Doctrine\Tests; -use Doctrine\Common\Annotations; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\Result; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\SchemaConfig; use Doctrine\ORM\Cache\CacheConfiguration; use Doctrine\ORM\Cache\CacheFactory; use Doctrine\ORM\Cache\DefaultCacheFactory; use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger; use Doctrine\ORM\Configuration; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; -use Doctrine\ORM\ORMSetup; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\Tests\Mocks\EntityManagerMock; +use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; +use function method_exists; use function realpath; +use function sprintf; /** * Base testcase class for all ORM testcases. */ -abstract class OrmTestCase extends DoctrineTestCase +abstract class OrmTestCase extends TestCase { /** * The metadata cache that is shared between all ORM tests (except functional tests). - * - * @var CacheItemPoolInterface|null */ - private static $metadataCache = null; + private static CacheItemPoolInterface|null $metadataCache = null; /** * The query cache that is shared between all ORM tests (except functional tests). - * - * @var CacheItemPoolInterface|null */ - private static $queryCache = null; + private static CacheItemPoolInterface|null $queryCache = null; /** @var bool */ protected $isSecondLevelCacheEnabled = false; @@ -51,15 +52,11 @@ abstract class OrmTestCase extends DoctrineTestCase /** @var StatisticsCacheLogger */ protected $secondLevelCacheLogger; - /** @var CacheItemPoolInterface|null */ - private $secondLevelCache = null; + private CacheItemPoolInterface|null $secondLevelCache = null; - protected function createAnnotationDriver(array $paths = []): AnnotationDriver + protected function createAttributeDriver(array $paths = []): AttributeDriver { - return new AnnotationDriver( - new Annotations\PsrCachedReader(new Annotations\AnnotationReader(), new ArrayAdapter()), - $paths - ); + return new AttributeDriver($paths); } /** @@ -70,7 +67,26 @@ protected function createAnnotationDriver(array $paths = []): AnnotationDriver * be configured in the tests to simulate the DBAL behavior that is desired * for a particular test, */ - protected function getTestEntityManager(?Connection $connection = null): EntityManagerMock + protected function getTestEntityManager(): EntityManagerMock + { + return $this->buildTestEntityManagerWithPlatform( + $this->createConnectionMock($this->createPlatformMock()), + ); + } + + protected function createTestEntityManagerWithConnection(Connection $connection): EntityManagerMock + { + return $this->buildTestEntityManagerWithPlatform($connection); + } + + protected function createTestEntityManagerWithPlatform(AbstractPlatform $platform): EntityManagerMock + { + return $this->buildTestEntityManagerWithPlatform( + $this->createConnectionMock($platform), + ); + } + + private function buildTestEntityManagerWithPlatform(Connection $connection): EntityManagerMock { $metadataCache = self::getSharedMetadataCacheImpl(); @@ -79,15 +95,15 @@ protected function getTestEntityManager(?Connection $connection = null): EntityM TestUtil::configureProxies($config); $config->setMetadataCache($metadataCache); $config->setQueryCache(self::getSharedQueryCache()); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver([ + $config->setMetadataDriverImpl(new AttributeDriver([ realpath(__DIR__ . '/Models/Cache'), - ], null, true)); + ], true)); if ($this->isSecondLevelCacheEnabled) { $cacheConfig = new CacheConfiguration(); $factory = new DefaultCacheFactory( $cacheConfig->getRegionsConfiguration(), - $this->getSharedSecondLevelCache() + $this->getSharedSecondLevelCache(), ); $this->secondLevelCacheFactory = $factory; @@ -97,15 +113,6 @@ protected function getTestEntityManager(?Connection $connection = null): EntityM $config->setSecondLevelCacheConfiguration($cacheConfig); } - if ($connection === null) { - $connection = DriverManager::getConnection([ - 'driverClass' => Mocks\DriverMock::class, - 'wrapperClass' => Mocks\ConnectionMock::class, - 'user' => 'john', - 'password' => 'wayne', - ], $config); - } - return new EntityManagerMock($connection, $config); } @@ -132,4 +139,56 @@ protected function getSharedSecondLevelCache(): CacheItemPoolInterface return $this->secondLevelCache ?? $this->secondLevelCache = new ArrayAdapter(); } + + private function createConnectionMock(AbstractPlatform $platform): Connection + { + $connection = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([[], $this->createDriverMock($platform)]) + ->onlyMethods(['quote']) + ->getMockForAbstractClass(); + $connection->method('quote')->willReturnCallback(static fn (string $input) => sprintf("'%s'", $input)); + + return $connection; + } + + private function createPlatformMock(): AbstractPlatform + { + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->method('createSchemaConfig') + ->willReturn(new SchemaConfig()); + + $platform = $this->getMockBuilder(AbstractPlatform::class) + ->onlyMethods(['supportsIdentityColumns', 'createSchemaManager']) + ->getMockForAbstractClass(); + $platform->method('supportsIdentityColumns') + ->willReturn(true); + $platform->method('createSchemaManager') + ->willReturn($schemaManager); + + return $platform; + } + + private function createDriverMock(AbstractPlatform $platform): Driver + { + $result = $this->createMock(Result::class); + $result->method('fetchAssociative') + ->willReturn(false); + + $connection = $this->createMock(Driver\Connection::class); + $connection->method('query') + ->willReturn($result); + + $driver = $this->createMock(Driver::class); + $driver->method('connect') + ->willReturn($connection); + $driver->method('getDatabasePlatform') + ->willReturn($platform); + + if (method_exists(Driver::class, 'getSchemaManager')) { + $driver->method('getSchemaManager') + ->willReturnCallback([$platform, 'createSchemaManager']); + } + + return $driver; + } } diff --git a/tests/Tests/PHPUnitCompatibility/MockBuilderCompatibilityTools.php b/tests/Tests/PHPUnitCompatibility/MockBuilderCompatibilityTools.php deleted file mode 100644 index d19d7011f96..00000000000 --- a/tests/Tests/PHPUnitCompatibility/MockBuilderCompatibilityTools.php +++ /dev/null @@ -1,29 +0,0 @@ - $className - * @param list $onlyMethods - * - * @return MockBuilder - * - * @template TMockedType of object - */ - private function getMockBuilderWithOnlyMethods(string $className, array $onlyMethods): MockBuilder - { - $builder = $this->getMockBuilder($className); - - return method_exists($builder, 'onlyMethods') - ? $builder->onlyMethods($onlyMethods) // PHPUnit 8+ - : $builder->setMethods($onlyMethods); // PHPUnit 7 - } -} diff --git a/tests/Tests/Proxy/AutoloaderTest.php b/tests/Tests/Proxy/AutoloaderTest.php new file mode 100644 index 00000000000..1bcf37910a2 --- /dev/null +++ b/tests/Tests/Proxy/AutoloaderTest.php @@ -0,0 +1,57 @@ + */ + public static function dataResolveFile(): iterable + { + return [ + ['/tmp', 'MyProxy', 'MyProxy\RealClass', '/tmp' . DIRECTORY_SEPARATOR . 'RealClass.php'], + ['/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'], + ['/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'], + ['/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__OtherRealClass.php'], + ]; + } + + /** @param class-string $className */ + #[DataProvider('dataResolveFile')] + public function testResolveFile( + string $proxyDir, + string $proxyNamespace, + string $className, + string $expectedProxyFile, + ): void { + $actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + self::assertEquals($expectedProxyFile, $actualProxyFile); + } + + public function testAutoload(): void + { + if (file_exists(sys_get_temp_dir() . '/AutoloaderTestClass.php')) { + unlink(sys_get_temp_dir() . '/AutoloaderTestClass.php'); + } + + $autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', static function ($proxyDir, $proxyNamespace, $className): void { + file_put_contents(sys_get_temp_dir() . '/AutoloaderTestClass.php', 'real database connection using the following parameters @@ -59,7 +54,7 @@ class TestUtil * 1) Each invocation of this method returns a NEW database connection. * 2) The database is dropped and recreated to ensure it's clean. */ - public static function getConnection(): DbalExtensions\Connection + public static function getConnection(DbalConfiguration|null $config = null): DbalExtensions\Connection { if (! self::$initialized) { self::initializeDatabase(); @@ -67,12 +62,16 @@ public static function getConnection(): DbalExtensions\Connection } $connectionParameters = self::getTestConnectionParameters(); - $configuration = new Configuration(); + if (in_array($connectionParameters['driver'], ['pdo_sqlite', 'sqlite3'], true) && class_exists(EnableForeignKeys::class)) { - $configuration->setMiddlewares([new EnableForeignKeys()]); + if ($config === null) { + $config = new DbalConfiguration(); + } + + $config->setMiddlewares([...$config->getMiddlewares(), new EnableForeignKeys()]); } - $connection = DriverManager::getConnection($connectionParameters, $configuration); + $connection = DriverManager::getConnection($connectionParameters, $config); assert($connection instanceof DbalExtensions\Connection); self::addDbEventSubscribers($connection); @@ -92,23 +91,6 @@ public static function configureProxies(Configuration $configuration): void { $configuration->setProxyDir(__DIR__ . '/Proxies'); $configuration->setProxyNamespace('Doctrine\Tests\Proxies'); - - $proxyImplementation = getenv('ORM_PROXY_IMPLEMENTATION') - ?: (trait_exists(LazyGhostTrait::class) ? 'lazy-ghost' : 'common'); - - switch ($proxyImplementation) { - case 'lazy-ghost': - $configuration->setLazyGhostObjectEnabled(true); - - return; - - case 'common': - $configuration->setLazyGhostObjectEnabled(false); - - return; - } - - throw new LogicException(sprintf('Unknown proxy implementation: %s.', $proxyImplementation)); } private static function initializeDatabase(): void @@ -127,11 +109,8 @@ private static function initializeDatabase(): void $platform = $privConn->getDatabasePlatform(); - if ($platform instanceof SqlitePlatform) { - $method = method_exists(AbstractSchemaManager::class, 'introspectSchema') ? - 'introspectSchema' : - 'createSchema'; - $schema = self::createSchemaManager($testConn)->$method(); + if ($platform instanceof SQLitePlatform) { + $schema = $testConn->createSchemaManager()->introspectSchema(); $stmts = $schema->toDropSql($testConn->getDatabasePlatform()); foreach ($stmts as $stmt) { @@ -141,28 +120,19 @@ private static function initializeDatabase(): void $dbname = $testConnParams['dbname'] ?? $testConn->getDatabase(); $testConn->close(); - if ($dbname !== null) { - $schemaManager = self::createSchemaManager($privConn); + $schemaManager = $privConn->createSchemaManager(); - try { - $schemaManager->dropDatabase($dbname); - } catch (DriverException $e) { - } + try { + $schemaManager->dropDatabase($dbname); + } catch (DatabaseObjectNotFoundException) { + } - $schemaManager->createDatabase($dbname); + $schemaManager->createDatabase($dbname); - $privConn->close(); - } + $privConn->close(); } } - private static function createSchemaManager(Connection $connection): AbstractSchemaManager - { - return method_exists(Connection::class, 'createSchemaManager') - ? $connection->createSchemaManager() - : $connection->getSchemaManager(); - } - private static function addDbEventSubscribers(Connection $conn): void { if (! isset($GLOBALS['db_event_subscribers'])) { @@ -195,7 +165,7 @@ private static function getTestConnectionParameters(): array { if (! isset($GLOBALS['db_driver'])) { throw new UnexpectedValueException( - 'You must provide database connection params including a db_driver value. See phpunit.xml.dist for details' + 'You must provide database connection params including a db_driver value. See phpunit.xml.dist for details', ); } @@ -237,12 +207,20 @@ private static function mapConnectionParameters(array $configuration, string $pr $parameters[$parameter] = $configuration[$prefix . $parameter]; } + if (isset($parameters['port'])) { + $parameters['port'] = (int) $parameters['port']; + } + foreach ($configuration as $param => $value) { - if (! str_starts_with($param, $prefix . 'driver_option_')) { + if (str_starts_with($param, $prefix . 'driver_option_')) { + $parameters['driverOptions'][substr($param, strlen($prefix . 'driver_option_'))] = $value; + } + + if (! str_starts_with($param, $prefix . 'default_table_option_')) { continue; } - $parameters['driverOptions'][substr($param, strlen($prefix . 'driver_option_'))] = $value; + $parameters['defaultTableOptions'][substr($param, strlen($prefix . 'default_table_option_'))] = $value; } $parameters['wrapperClass'] = DbalExtensions\Connection::class;