Skip to content

Commit 1c53f21

Browse files
bug #44987 [DoctrineBridge] [5.4] Fix automapping (mbabker)
This PR was merged into the 5.4 branch. Discussion ---------- [DoctrineBridge] [5.4] Fix automapping | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #44355 | License | MIT | Doc PR | N/A This should fix the broken automapping configuration for bundles that use annotations and attributes, see #44355 for additional details. When using the newer bundle directory structure conventions, especially with the `Bundle::getPath()` method overridden to point the bundle root path to the Bundle class' parent directory, the issue comes up because the bundle filesystem root path does not match the root path for the actual bundle code (effectively the PSR-4 root path). To fix automapping, the `AbstractDoctrineExtension::detectMetadataDriver()` method will be called a second time with the Bundle classpath if the first call (which is effectively the same as `Bundle::getPath()`) can't resolve the appropriate mapping type. This will also update setting `$bundleConfig['dir']` for drivers that are based on PHP classes so that the root path is based on the bundle classpath (AKA the PSR-4 root path) instead of the bundle path. The bulk of this patch is mainly from adding test fixture bundles to be able to test the `AbstractDoctrineExtension::getMappingDriverBundleConfigDefaults()` method in this repo and ensuring that the metadata driver detection works right for all of the supported drivers. Commits ------- 0aaa2fdca7 Fix bundle automapping detection for annotations and attributes
2 parents 9e769ac + 28c0909 commit 1c53f21

File tree

23 files changed

+500
-4
lines changed

23 files changed

+500
-4
lines changed

DependencyInjection/AbstractDoctrineExtension.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,15 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re
148148
$bundleDir = func_get_arg(3);
149149
}
150150

151-
$bundleDir ?? $bundleDir = \dirname($bundle->getFileName());
151+
$bundleClassDir = \dirname($bundle->getFileName());
152+
$bundleDir ?? $bundleDir = $bundleClassDir;
152153

153154
if (!$bundleConfig['type']) {
154155
$bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container);
156+
157+
if (!$bundleConfig['type'] && $bundleDir !== $bundleClassDir) {
158+
$bundleConfig['type'] = $this->detectMetadataDriver($bundleClassDir, $container);
159+
}
155160
}
156161

157162
if (!$bundleConfig['type']) {
@@ -161,7 +166,7 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re
161166

162167
if (!$bundleConfig['dir']) {
163168
if (\in_array($bundleConfig['type'], ['annotation', 'staticphp', 'attribute'])) {
164-
$bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName();
169+
$bundleConfig['dir'] = $bundleClassDir.'/'.$this->getMappingObjectDefaultName();
165170
} else {
166171
$bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory($bundleDir);
167172
}

Tests/DependencyInjection/DoctrineExtensionTest.php

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Definition;
1818
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
19+
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
1920

2021
/**
2122
* @author Fabio B. Silva <[email protected]>
@@ -53,6 +54,10 @@ protected function setUp(): void
5354
$this->extension
5455
->method('getMappingObjectDefaultName')
5556
->willReturn('Entity');
57+
58+
$this->extension
59+
->method('getMappingResourceExtension')
60+
->willReturn('orm');
5661
}
5762

5863
public function testFixManagersAutoMappingsWithTwoAutomappings()
@@ -271,6 +276,75 @@ public function testUnrecognizedCacheDriverException()
271276
$this->invokeLoadCacheDriver($objectManager, $container, $cacheName);
272277
}
273278

279+
public function providerBundles()
280+
{
281+
yield ['AnnotationsBundle', 'annotation', '/Entity'];
282+
if (\PHP_VERSION_ID >= 80000) {
283+
yield ['AttributesBundle', 'attribute', '/Entity'];
284+
}
285+
yield ['XmlBundle', 'xml', '/Resources/config/doctrine'];
286+
yield ['PhpBundle', 'php', '/Resources/config/doctrine'];
287+
yield ['YamlBundle', 'yml', '/Resources/config/doctrine'];
288+
289+
yield ['SrcXmlBundle', 'xml', '/Resources/config/doctrine'];
290+
291+
yield ['NewAnnotationsBundle', 'annotation', '/src/Entity'];
292+
yield ['NewXmlBundle', 'xml', '/config/doctrine'];
293+
}
294+
295+
/**
296+
* @dataProvider providerBundles
297+
*/
298+
public function testBundleAutoMapping(string $bundle, string $expectedType, string $dirSuffix)
299+
{
300+
$bundleDir = __DIR__.'/../Fixtures/Bundles/'.$bundle;
301+
$bundleClassName = 'Fixtures\\Bundles\\'.$bundle.'\\'.$bundle;
302+
303+
if (is_dir($bundleDir.'/src')) {
304+
require_once $bundleDir.'/src/'.$bundle.'.php';
305+
} else {
306+
require_once $bundleDir.'/'.$bundle.'.php';
307+
}
308+
309+
/** @var BundleInterface $bundleClass */
310+
$bundleClass = new $bundleClassName();
311+
312+
$mappingConfig = [
313+
'dir' => false,
314+
'type' => false,
315+
'prefix' => false,
316+
'mapping' => true,
317+
'is_bundle' => true,
318+
];
319+
320+
$this->extension
321+
->method('getMappingResourceConfigDirectory')
322+
->willReturnCallback(function ($bundleDir) {
323+
if (null !== $bundleDir && is_dir($bundleDir.'/config/doctrine')) {
324+
return 'config/doctrine';
325+
}
326+
327+
return 'Resources/config/doctrine';
328+
});
329+
330+
$container = $this->createContainer([], [$bundle => $bundleClassName]);
331+
332+
$reflection = new \ReflectionClass(\get_class($this->extension));
333+
$method = $reflection->getMethod('getMappingDriverBundleConfigDefaults');
334+
$method->setAccessible(true);
335+
336+
$this->assertSame(
337+
[
338+
'dir' => $bundleClass->getPath().$dirSuffix,
339+
'type' => $expectedType,
340+
'prefix' => $bundleClass->getNamespace().'\\Entity',
341+
'mapping' => true,
342+
'is_bundle' => true,
343+
],
344+
$method->invoke($this->extension, $mappingConfig, new \ReflectionClass($bundleClass), $container, $bundleClass->getPath())
345+
);
346+
}
347+
274348
protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName)
275349
{
276350
$method = new \ReflectionMethod($this->extension, 'loadObjectManagerCacheDriver');
@@ -280,10 +354,10 @@ protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder
280354
$method->invokeArgs($this->extension, [$objectManager, $container, $cacheName]);
281355
}
282356

283-
protected function createContainer(array $data = []): ContainerBuilder
357+
protected function createContainer(array $data = [], array $extraBundles = []): ContainerBuilder
284358
{
285359
return new ContainerBuilder(new ParameterBag(array_merge([
286-
'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
360+
'kernel.bundles' => array_merge(['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], $extraBundles),
287361
'kernel.cache_dir' => __DIR__,
288362
'kernel.build_dir' => __DIR__,
289363
'kernel.container_class' => 'kernel',
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\AnnotationsBundle;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
class AnnotationsBundle extends Bundle
17+
{
18+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\AnnotationsBundle\Entity;
13+
14+
use Doctrine\ORM\Mapping\Column;
15+
use Doctrine\ORM\Mapping\Entity;
16+
use Doctrine\ORM\Mapping\Id;
17+
18+
/**
19+
* @Entity
20+
*/
21+
class Person
22+
{
23+
/** @Id @Column(type="integer") */
24+
protected $id;
25+
26+
/** @Column(type="string") */
27+
public $name;
28+
29+
public function __construct($id, $name)
30+
{
31+
$this->id = $id;
32+
$this->name = $name;
33+
}
34+
35+
public function __toString(): string
36+
{
37+
return (string) $this->name;
38+
}
39+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\AttributesBundle;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
class AttributesBundle extends Bundle
17+
{
18+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\AttributesBundle\Entity;
13+
14+
use Doctrine\ORM\Mapping\Column;
15+
use Doctrine\ORM\Mapping\Entity;
16+
use Doctrine\ORM\Mapping\Id;
17+
18+
#[Entity]
19+
class Person
20+
{
21+
#[Id, Column(type: 'integer')]
22+
protected $id;
23+
24+
#[Column(type: 'string')]
25+
public $name;
26+
27+
public function __construct($id, $name)
28+
{
29+
$this->id = $id;
30+
$this->name = $name;
31+
}
32+
33+
public function __toString(): string
34+
{
35+
return (string) $this->name;
36+
}
37+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\NewAnnotationsBundle\Entity;
13+
14+
use Doctrine\ORM\Mapping\Column;
15+
use Doctrine\ORM\Mapping\Entity;
16+
use Doctrine\ORM\Mapping\Id;
17+
18+
/**
19+
* @Entity
20+
*/
21+
class Person
22+
{
23+
/** @Id @Column(type="integer") */
24+
protected $id;
25+
26+
/** @Column(type="string") */
27+
public $name;
28+
29+
public function __construct($id, $name)
30+
{
31+
$this->id = $id;
32+
$this->name = $name;
33+
}
34+
35+
public function __toString(): string
36+
{
37+
return (string) $this->name;
38+
}
39+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\NewAnnotationsBundle;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
class NewAnnotationsBundle extends Bundle
17+
{
18+
public function getPath(): string
19+
{
20+
return \dirname(__DIR__);
21+
}
22+
}

Tests/Fixtures/Bundles/NewXmlBundle/config/doctrine/Person.orm.xml

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Fixtures\Bundles\NewXmlBundle\Entity;
13+
14+
class Person
15+
{
16+
protected $id;
17+
18+
public $name;
19+
20+
public function __construct($id, $name)
21+
{
22+
$this->id = $id;
23+
$this->name = $name;
24+
}
25+
26+
public function __toString(): string
27+
{
28+
return (string) $this->name;
29+
}
30+
}

0 commit comments

Comments
 (0)