diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index b352a21538b..73bec1912e1 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -24,4 +24,4 @@ on: jobs: coding-standards: - uses: "doctrine/.github/.github/workflows/coding-standards.yml@v12.2.0" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@13.0.0" diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml index 2b141c2b23f..9290eafb3fb 100644 --- a/.github/workflows/composer-lint.yml +++ b/.github/workflows/composer-lint.yml @@ -17,4 +17,4 @@ on: jobs: composer-lint: name: "Composer Lint" - uses: "doctrine/.github/.github/workflows/composer-lint.yml@v12.2.0" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@13.0.0" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index cec65243d48..c70beb6769f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -65,7 +65,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 @@ -149,7 +149,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 @@ -221,7 +221,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 @@ -293,7 +293,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 @@ -345,7 +345,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 @@ -377,7 +377,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f99319c4b49..6825494ccff 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,4 +17,4 @@ on: jobs: documentation: name: "Documentation" - uses: "doctrine/.github/.github/workflows/documentation.yml@v12.2.0" + uses: "doctrine/.github/.github/workflows/documentation.yml@13.0.0" diff --git a/.github/workflows/phpbench.yml b/.github/workflows/phpbench.yml index af699ff5644..aff9a88c999 100644 --- a/.github/workflows/phpbench.yml +++ b/.github/workflows/phpbench.yml @@ -36,7 +36,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" with: fetch-depth: 2 diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 4e1786168e6..ad5c658d9c7 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -7,7 +7,7 @@ on: jobs: release: - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@v12.2.0" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@13.0.0" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 1462df991ba..e949b69d27a 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: "Checkout code" - uses: "actions/checkout@v5" + uses: "actions/checkout@v6" - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/composer.json b/composer.json index ccf22b1f1a7..15c95c45219 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "doctrine/lexer": "^2 || ^3", "doctrine/persistence": "^2.4 || ^3", "psr/cache": "^1 || ^2 || ^3", - "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0 || ^8.0", "symfony/polyfill-php72": "^1.23", "symfony/polyfill-php80": "^1.16" }, @@ -60,7 +60,7 @@ "psr/log": "^1 || ^2 || ^3", "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" + "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0" }, "conflict": { "doctrine/annotations": "<1.13 || >= 3.0" diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 0070c271762..ca0d7b45d9c 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -254,6 +254,21 @@ Here is a complete list of ``Column``s attributes (all optional): - ``options``: Key-value pairs of options that get passed to the underlying database platform when generating DDL statements. +Specifying default values +~~~~~~~~~~~~~~~~~~~~~~~~~ + +While it is possible to specify default values for properties in your +PHP class, Doctrine also allows you to specify default values for +database columns using the ``default`` key in the ``options`` array of +the ``Column`` attribute. + +.. configuration-block:: + .. literalinclude:: basic-mapping/DefaultValues.php + :language: attribute + + .. literalinclude:: basic-mapping/default-values.xml + :language: xml + .. _reference-php-mapping-types: PHP Types Mapping diff --git a/docs/en/reference/basic-mapping/DefaultValues.php b/docs/en/reference/basic-mapping/DefaultValues.php new file mode 100644 index 00000000000..e702f5bdd14 --- /dev/null +++ b/docs/en/reference/basic-mapping/DefaultValues.php @@ -0,0 +1,15 @@ + 'Hello World!'])] + private string $text; +} diff --git a/docs/en/reference/basic-mapping/default-values.xml b/docs/en/reference/basic-mapping/default-values.xml new file mode 100644 index 00000000000..2671080a9e4 --- /dev/null +++ b/docs/en/reference/basic-mapping/default-values.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/en/reference/faq.rst b/docs/en/reference/faq.rst index 85b6e35d851..2fa1b88c1c3 100644 --- a/docs/en/reference/faq.rst +++ b/docs/en/reference/faq.rst @@ -18,30 +18,6 @@ In your mapping configuration, the column definition (for example, the the ``charset`` and ``collation``. The default values are ``utf8`` and ``utf8_unicode_ci``, respectively. -Entity Classes --------------- - -How can I add default values to a column? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL. -This is not necessary however, you can just use your class properties as default values. These are then used -upon insert: - -.. code-block:: php - - class User - { - private const STATUS_DISABLED = 0; - private const STATUS_ENABLED = 1; - - private string $algorithm = "sha1"; - /** @var self::STATUS_* */ - private int $status = self::STATUS_DISABLED; - } - -. - Mapping ------- diff --git a/src/Configuration.php b/src/Configuration.php index 4fb237260ce..2aa86b4ba33 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -1113,7 +1113,7 @@ 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".' + . ' version 6.2 or 7 is not installed. Please run "composer require symfony/var-exporter:^6.4".' ); } diff --git a/src/Tools/Console/ApplicationCompatibility.php b/src/Tools/Console/ApplicationCompatibility.php index 71b2ecf10fd..3f7f90bc617 100644 --- a/src/Tools/Console/ApplicationCompatibility.php +++ b/src/Tools/Console/ApplicationCompatibility.php @@ -18,11 +18,12 @@ trait ApplicationCompatibility { private static function addCommandToApplication(Application $application, Command $command): ?Command { + // @phpstan-ignore function.alreadyNarrowedType (This method did not exist before Symfony 7.4) if (method_exists(Application::class, 'addCommand')) { - // @phpstan-ignore method.notFound (This method will be added in Symfony 7.4) return $application->addCommand($command); } + // @phpstan-ignore method.notFound return $application->add($command); } } diff --git a/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php index 84e911f171b..70fa324a317 100644 --- a/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -23,8 +23,7 @@ class CollectionRegionCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:region:collection') ->setDescription('Clear a second-level cache collection region') diff --git a/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php b/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php index 51d08946521..e55f1c310b2 100644 --- a/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -23,8 +23,7 @@ class EntityRegionCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:region:entity') ->setDescription('Clear a second-level cache entity region') diff --git a/src/Tools/Console/Command/ClearCache/MetadataCommand.php b/src/Tools/Console/Command/ClearCache/MetadataCommand.php index 96decbc50a9..bb365bc3c04 100644 --- a/src/Tools/Console/Command/ClearCache/MetadataCommand.php +++ b/src/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -21,8 +21,7 @@ class MetadataCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:metadata') ->setDescription('Clear all metadata cache of the various cache drivers') diff --git a/src/Tools/Console/Command/ClearCache/QueryCommand.php b/src/Tools/Console/Command/ClearCache/QueryCommand.php index 06f76dcfaa4..7fc579be598 100644 --- a/src/Tools/Console/Command/ClearCache/QueryCommand.php +++ b/src/Tools/Console/Command/ClearCache/QueryCommand.php @@ -30,8 +30,7 @@ class QueryCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:query') ->setDescription('Clear all query cache of the various cache drivers') diff --git a/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php b/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php index faca872e0f1..d9bfa9be5b8 100644 --- a/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php +++ b/src/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -23,8 +23,7 @@ class QueryRegionCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:region:query') ->setDescription('Clear a second-level cache query region') diff --git a/src/Tools/Console/Command/ClearCache/ResultCommand.php b/src/Tools/Console/Command/ClearCache/ResultCommand.php index b61a55e0fe7..73c60802d32 100644 --- a/src/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/src/Tools/Console/Command/ClearCache/ResultCommand.php @@ -32,8 +32,7 @@ class ResultCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:clear-cache:result') ->setDescription('Clear all result cache of the various cache drivers') diff --git a/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php index df0d42dc702..7aa09db3cca 100644 --- a/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ b/src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -75,8 +75,7 @@ public function setMetadataExporter(ClassMetadataExporter $metadataExporter) $this->metadataExporter = $metadataExporter; } - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:convert-d1-schema') ->setAliases(['orm:convert:d1-schema']) diff --git a/src/Tools/Console/Command/ConvertMappingCommand.php b/src/Tools/Console/Command/ConvertMappingCommand.php index f3bdea25f14..a95ca547fad 100644 --- a/src/Tools/Console/Command/ConvertMappingCommand.php +++ b/src/Tools/Console/Command/ConvertMappingCommand.php @@ -40,8 +40,7 @@ class ConvertMappingCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:convert-mapping') ->setAliases(['orm:convert:mapping']) diff --git a/src/Tools/Console/Command/EnsureProductionSettingsCommand.php b/src/Tools/Console/Command/EnsureProductionSettingsCommand.php index bb5df1637a4..2d8ecfe5ddb 100644 --- a/src/Tools/Console/Command/EnsureProductionSettingsCommand.php +++ b/src/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -22,8 +22,7 @@ class EnsureProductionSettingsCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:ensure-production-settings') ->setDescription('Verify that Doctrine is properly configured for a production environment') diff --git a/src/Tools/Console/Command/GenerateEntitiesCommand.php b/src/Tools/Console/Command/GenerateEntitiesCommand.php index 7fdcb98bc8e..c7f2eb44505 100644 --- a/src/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/src/Tools/Console/Command/GenerateEntitiesCommand.php @@ -31,8 +31,7 @@ class GenerateEntitiesCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:generate-entities') ->setAliases(['orm:generate:entities']) diff --git a/src/Tools/Console/Command/GenerateProxiesCommand.php b/src/Tools/Console/Command/GenerateProxiesCommand.php index 6a602869857..da7ee8f69df 100644 --- a/src/Tools/Console/Command/GenerateProxiesCommand.php +++ b/src/Tools/Console/Command/GenerateProxiesCommand.php @@ -29,8 +29,7 @@ class GenerateProxiesCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:generate-proxies') ->setAliases(['orm:generate:proxies']) diff --git a/src/Tools/Console/Command/GenerateRepositoriesCommand.php b/src/Tools/Console/Command/GenerateRepositoriesCommand.php index 54379d5f6b7..539f4e8073a 100644 --- a/src/Tools/Console/Command/GenerateRepositoriesCommand.php +++ b/src/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -30,8 +30,7 @@ class GenerateRepositoriesCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:generate-repositories') ->setAliases(['orm:generate:repositories']) diff --git a/src/Tools/Console/Command/InfoCommand.php b/src/Tools/Console/Command/InfoCommand.php index 9b232efbb50..2be5c1da147 100644 --- a/src/Tools/Console/Command/InfoCommand.php +++ b/src/Tools/Console/Command/InfoCommand.php @@ -23,8 +23,7 @@ class InfoCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:info') ->setDescription('Show basic information about all mapped entities') diff --git a/src/Tools/Console/Command/RunDqlCommand.php b/src/Tools/Console/Command/RunDqlCommand.php index 61f1eef78f1..9bc79d684a6 100644 --- a/src/Tools/Console/Command/RunDqlCommand.php +++ b/src/Tools/Console/Command/RunDqlCommand.php @@ -30,8 +30,7 @@ class RunDqlCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:run-dql') ->setDescription('Executes arbitrary DQL directly from the command line') diff --git a/src/Tools/Console/Command/SchemaTool/AbstractCommand.php b/src/Tools/Console/Command/SchemaTool/AbstractCommand.php index cabf90759a7..c269068089f 100644 --- a/src/Tools/Console/Command/SchemaTool/AbstractCommand.php +++ b/src/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -27,6 +27,10 @@ abstract class AbstractCommand extends AbstractEntityManagerCommand */ abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui); + private function doConfigure(): void + { + } + private function doExecute(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..85e0562246f 100644 --- a/src/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/src/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; +use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\SchemaTool; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -19,8 +20,9 @@ */ class CreateCommand extends AbstractCommand { - /** @return void */ - protected function configure() + use CommandCompatibility; + + private function doConfigure(): 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') @@ -44,6 +46,11 @@ protected function configure() ); } + private function doExecute(InputInterface $input, OutputInterface $output): int + { + return parent::execute($input, $output); + } + /** * {@inheritDoc} */ diff --git a/src/Tools/Console/Command/SchemaTool/DropCommand.php b/src/Tools/Console/Command/SchemaTool/DropCommand.php index c312675392d..531fde66e8c 100644 --- a/src/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/src/Tools/Console/Command/SchemaTool/DropCommand.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; +use Doctrine\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\SchemaTool; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -20,8 +21,9 @@ */ class DropCommand extends AbstractCommand { - /** @return void */ - protected function configure() + use CommandCompatibility; + + private function doConfigure(): void { $this->setName('orm:schema-tool:drop') ->setDescription('Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output') @@ -48,6 +50,11 @@ protected function configure() ); } + private function doExecute(InputInterface $input, OutputInterface $output): int + { + return parent::execute($input, $output); + } + /** * {@inheritDoc} */ diff --git a/src/Tools/Console/Command/SchemaTool/UpdateCommand.php b/src/Tools/Console/Command/SchemaTool/UpdateCommand.php index 3d2631ec0f7..15667bc299c 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\ORM\Tools\Console\CommandCompatibility; use Doctrine\ORM\Tools\SchemaTool; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -21,11 +22,12 @@ */ class UpdateCommand extends AbstractCommand { + use CommandCompatibility; + /** @var string */ protected $name = 'orm:schema-tool:update'; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName($this->name) ->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata') @@ -72,6 +74,11 @@ protected function configure() ); } + private function doExecute(InputInterface $input, OutputInterface $output): int + { + return parent::execute($input, $output); + } + /** * {@inheritDoc} */ diff --git a/src/Tools/Console/Command/ValidateSchemaCommand.php b/src/Tools/Console/Command/ValidateSchemaCommand.php index 3807a81d58d..78feaec2ab8 100644 --- a/src/Tools/Console/Command/ValidateSchemaCommand.php +++ b/src/Tools/Console/Command/ValidateSchemaCommand.php @@ -23,8 +23,7 @@ class ValidateSchemaCommand extends AbstractEntityManagerCommand { use CommandCompatibility; - /** @return void */ - protected function configure() + private function doConfigure(): void { $this->setName('orm:validate-schema') ->setDescription('Validate the mapping files') diff --git a/src/Tools/Console/CommandCompatibility.php b/src/Tools/Console/CommandCompatibility.php index 4e16698f5d2..e13cb79cfed 100644 --- a/src/Tools/Console/CommandCompatibility.php +++ b/src/Tools/Console/CommandCompatibility.php @@ -9,10 +9,32 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -if ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) { +// Symfony 8 +if ((new ReflectionMethod(Command::class, 'configure'))->hasReturnType()) { /** @internal */ trait CommandCompatibility { + protected function configure(): void + { + $this->doConfigure(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + return $this->doExecute($input, $output); + } + } +// Symfony 7 +} elseif ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) { + /** @internal */ + trait CommandCompatibility + { + /** @return void */ + protected function configure() + { + $this->doConfigure(); + } + protected function execute(InputInterface $input, OutputInterface $output): int { return $this->doExecute($input, $output); @@ -22,6 +44,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** @internal */ trait CommandCompatibility { + /** @return void */ + protected function configure() + { + $this->doConfigure(); + } + /** * {@inheritDoc} * diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index 0b80702a80e..bf84ca01f93 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -62,6 +62,7 @@ use function array_sum; use function array_values; use function assert; +use function count; use function current; use function func_get_arg; use function func_num_args; @@ -3172,8 +3173,14 @@ public function createEntity($className, array $data, &$hints = []) $reflField->setValue($entity, $pColl); 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 ( + $assoc['type'] === ClassMetadata::ONE_TO_MANY + // is iteration + && ! (isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION]) + // is foreign key composite + && ! ($targetClass->hasAssociation($assoc['mappedBy']) && count($targetClass->getAssociationMapping($assoc['mappedBy'])['joinColumns'] ?? []) > 1) + && ! isset($assoc['indexBy']) + ) { $this->scheduleCollectionForBatchLoading($pColl, $class); } else { $this->loadCollection($pColl); diff --git a/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php b/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php index af16c686970..c804c90369c 100644 --- a/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php +++ b/tests/Tests/Models/EagerFetchedCompositeOneToMany/RootEntity.php @@ -37,11 +37,19 @@ class RootEntity */ private $secondLevel; + /** + * @ORM\OneToMany(mappedBy="root", targetEntity=SecondLevelWithoutCompositePrimaryKey::class, fetch="EAGER") + * + * @var Collection + */ + private $anotherSecondLevel; + public function __construct(int $id, string $other) { - $this->otherKey = $other; - $this->secondLevel = new ArrayCollection(); - $this->id = $id; + $this->otherKey = $other; + $this->secondLevel = new ArrayCollection(); + $this->anotherSecondLevel = new ArrayCollection(); + $this->id = $id; } public function getId(): ?int diff --git a/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevelWithoutCompositePrimaryKey.php b/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevelWithoutCompositePrimaryKey.php new file mode 100644 index 00000000000..323ac52060a --- /dev/null +++ b/tests/Tests/Models/EagerFetchedCompositeOneToMany/SecondLevelWithoutCompositePrimaryKey.php @@ -0,0 +1,43 @@ +root = $upper; + } + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/tests/Tests/ORM/Functional/EagerFetchOneToManyWithCompositeKeyTest.php b/tests/Tests/ORM/Functional/EagerFetchOneToManyWithCompositeKeyTest.php index 82b9d0b8acb..4a3645f5e3e 100644 --- a/tests/Tests/ORM/Functional/EagerFetchOneToManyWithCompositeKeyTest.php +++ b/tests/Tests/ORM/Functional/EagerFetchOneToManyWithCompositeKeyTest.php @@ -6,6 +6,7 @@ use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\RootEntity; use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\SecondLevel; +use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\SecondLevelWithoutCompositePrimaryKey; use Doctrine\Tests\OrmFunctionalTestCase; final class EagerFetchOneToManyWithCompositeKeyTest extends OrmFunctionalTestCase @@ -13,7 +14,7 @@ final class EagerFetchOneToManyWithCompositeKeyTest extends OrmFunctionalTestCas /** @ticket 11154 */ public function testItDoesNotThrowAnExceptionWhenTriggeringALoad(): void { - $this->setUpEntitySchema([RootEntity::class, SecondLevel::class]); + $this->setUpEntitySchema([RootEntity::class, SecondLevel::class, SecondLevelWithoutCompositePrimaryKey::class]); $a1 = new RootEntity(1, 'A');