Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 2.19.x up into 3.1.x #11363

Merged
merged 9 commits into from
Mar 16, 2024
5 changes: 5 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ jobs:
with:
dependency-versions: "highest"

- name: "Add orphan metadata where needed"
run: |
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/reference/installation.rst)" > docs/en/reference/installation.rst

- name: "Run guides-cli"
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"
2 changes: 0 additions & 2 deletions docs/en/reference/installation.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
:orphan:

Installation
============

Expand Down
2 changes: 0 additions & 2 deletions docs/en/sidebar.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
:orphan:

.. toc::

.. tocheader:: Tutorials
Expand Down
14 changes: 13 additions & 1 deletion src/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -2662,7 +2662,19 @@ private function eagerLoadCollections(array $collections, ToManyInverseSideMappi
foreach ($found as $targetValue) {
$sourceEntity = $targetProperty->getValue($targetValue);

$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
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']];
}
} else {
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
}

$idHash = implode(' ', $id);

if ($mapping->indexBy !== null) {
Expand Down
35 changes: 35 additions & 0 deletions tests/Tests/Models/AbstractFetchEager/AbstractRemoteControl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\AbstractFetchEager;

use Doctrine\Common\Collections\ArrayCollection;
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'])]
abstract class AbstractRemoteControl
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
public int $id;

#[ORM\Column(type: 'string')]
public string $name;

/** @var Collection<User> */
#[ORM\OneToMany(targetEntity: User::class, mappedBy: 'remoteControl', fetch: 'EAGER')]
public Collection $users;

public function __construct(string $name)
{
$this->name = $name;
$this->users = new ArrayCollection();
}
}
12 changes: 12 additions & 0 deletions tests/Tests/Models/AbstractFetchEager/MobileRemoteControl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\AbstractFetchEager;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class MobileRemoteControl extends AbstractRemoteControl
{
}
26 changes: 26 additions & 0 deletions tests/Tests/Models/AbstractFetchEager/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\AbstractFetchEager;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'abstract_fetch_eager_user')]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
public int $id;

#[ORM\ManyToOne(targetEntity: AbstractRemoteControl::class, inversedBy: 'users')]
#[ORM\JoinColumn(nullable: false)]
public AbstractRemoteControl $remoteControl;

public function __construct(AbstractRemoteControl $control)
{
$this->remoteControl = $control;
}
}
37 changes: 37 additions & 0 deletions tests/Tests/ORM/Functional/AbstractFetchEagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional;

use Doctrine\Tests\Models\AbstractFetchEager\AbstractRemoteControl;
use Doctrine\Tests\Models\AbstractFetchEager\MobileRemoteControl;
use Doctrine\Tests\Models\AbstractFetchEager\User;
use Doctrine\Tests\OrmFunctionalTestCase;

final class AbstractFetchEagerTest extends OrmFunctionalTestCase
{
public function testWithAbstractFetchEager(): void
{
$this->createSchemaForModels(
AbstractRemoteControl::class,
User::class,
);

$control = new MobileRemoteControl('smart');
$user = new User($control);

$entityManage = $this->getEntityManager();

$entityManage->persist($control);
$entityManage->persist($user);
$entityManage->flush();
$entityManage->clear();

$user = $entityManage->find(User::class, $user->id);

self::assertNotNull($user);
self::assertEquals('smart', $user->remoteControl->name);
self::assertTrue($user->remoteControl->users->contains($user));
}
}
Loading