Skip to content

Commit cdc52b2

Browse files
authored
Merge pull request #12154 from greg0ire/4.0.x
Merge 3.6.x up into 4.0.x
2 parents c91c3a3 + c5e2f3f commit cdc52b2

File tree

14 files changed

+166
-54
lines changed

14 files changed

+166
-54
lines changed

docs/en/reference/advanced-configuration.rst

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ steps of configuration.
2929
3030
$config = new Configuration;
3131
$config->setMetadataCache($metadataCache);
32-
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities'], true);
32+
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);
3333
$config->setMetadataDriverImpl($driverImpl);
3434
$config->setQueryCache($queryCache);
3535
@@ -96,15 +96,59 @@ The attribute driver can be injected in the ``Doctrine\ORM\Configuration``:
9696
<?php
9797
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
9898
99-
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities'], true);
99+
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);
100100
$config->setMetadataDriverImpl($driverImpl);
101101
102102
The path information to the entities is required for the attribute
103103
driver, because otherwise mass-operations on all entities through
104-
the console could not work correctly. All of metadata drivers
105-
accept either a single directory as a string or an array of
106-
directories. With this feature a single driver can support multiple
107-
directories of Entities.
104+
the console could not work correctly. Metadata drivers can accept either
105+
a single directory as a string or an array of directories.
106+
107+
AttributeDriver also accepts ``Doctrine\Persistence\Mapping\Driver\ClassLocator``,
108+
allowing one to customize file discovery logic. You may choose to use Symfony Finder, or
109+
utilize directory scan with ``FileClassLocator::createFromDirectories()``:
110+
111+
.. code-block:: php
112+
113+
<?php
114+
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
115+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
116+
117+
$paths = ['/path/to/lib/MyProject/Entities'];
118+
$classLocator = FileClassLocator::createFromDirectories($paths);
119+
120+
$driverImpl = new AttributeDriver($classLocator);
121+
$config->setMetadataDriverImpl($driverImpl);
122+
123+
With this feature, you're empowered to provide a fine-grained iterator of only necessary
124+
files to the Driver. For example, if you are using Vertical Slice architecture, you can
125+
exclude ``*Test.php``, ``*Controller.php``, ``*Service.php``, etc.:
126+
127+
.. code-block:: php
128+
129+
<?php
130+
use Symfony\Component\Finder\Finder;
131+
132+
$finder = new Finder()->files()->in($paths)
133+
->name('*.php')
134+
->notName(['*Test.php', '*Controller.php', '*Service.php']);
135+
136+
$classLocator = new FileClassLocator($finder);
137+
138+
If you know the list of class names you want to track, use
139+
``Doctrine\Persistence\Mapping\Driver\ClassNames``:
140+
141+
.. code-block:: php
142+
143+
<?php
144+
use Doctrine\Persistence\Mapping\Driver\ClassNames;
145+
use App\Entity\{Article, Book};
146+
147+
$entityClasses = [Article::class, Book::class];
148+
$classLocator = new ClassNames($entityClasses);
149+
150+
$driverImpl = new AttributeDriver($classLocator);
151+
$config->setMetadataDriverImpl($driverImpl);
108152
109153
Metadata Cache (**RECOMMENDED**)
110154
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/Mapping/Driver/AttributeDriver.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\ORM\Mapping\ClassMetadata;
1111
use Doctrine\ORM\Mapping\MappingException;
1212
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
13+
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
1314
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
1415
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
1516
use ReflectionClass;
@@ -32,11 +33,16 @@ class AttributeDriver implements MappingDriver
3233

3334
private readonly AttributeReader $reader;
3435

35-
/** @param array<string> $paths */
36-
public function __construct(array $paths)
36+
/** @param string[]|ClassLocator $paths a ClassLocator, or an array of directories. */
37+
public function __construct(array|ClassLocator $paths)
3738
{
3839
$this->reader = new AttributeReader();
39-
$this->addPaths($paths);
40+
41+
if ($paths instanceof ClassLocator) {
42+
$this->classLocator = $paths;
43+
} else {
44+
$this->addPaths($paths);
45+
}
4046
}
4147

4248
public function isTransient(string $className): bool

src/ORMSetup.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
88
use Doctrine\ORM\Mapping\Driver\XmlDriver;
9+
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
910
use Psr\Cache\CacheItemPoolInterface;
1011
use Redis;
1112
use RuntimeException;
@@ -24,10 +25,10 @@ final class ORMSetup
2425
/**
2526
* Creates a configuration with an attribute metadata driver.
2627
*
27-
* @param string[] $paths
28+
* @param string[]|ClassLocator $paths
2829
*/
2930
public static function createAttributeMetadataConfig(
30-
array $paths,
31+
array|ClassLocator $paths,
3132
bool $isDevMode = false,
3233
string|null $cacheNamespaceSeed = null,
3334
CacheItemPoolInterface|null $cache = null,

tests/Performance/EntityManagerFactory.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,21 @@
1313
use Doctrine\ORM\Configuration;
1414
use Doctrine\ORM\EntityManager;
1515
use Doctrine\ORM\EntityManagerInterface;
16-
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
1716
use Doctrine\ORM\Tools\SchemaTool;
1817
use Doctrine\Tests\Mocks\ArrayResultFactory;
18+
use Doctrine\Tests\Mocks\AttributeDriverFactory;
1919

2020
use function array_map;
21-
use function realpath;
2221

2322
final class EntityManagerFactory
2423
{
2524
public static function getEntityManager(array $schemaClassNames): EntityManagerInterface
2625
{
2726
$config = new Configuration();
2827

29-
$config->setMetadataDriverImpl(new AttributeDriver([
30-
realpath(__DIR__ . '/Models/Cache'),
31-
realpath(__DIR__ . '/Models/GeoNames'),
28+
$config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver([
29+
__DIR__ . '/../Tests/Models/Cache',
30+
__DIR__ . '/../Tests/Models/GeoNames',
3231
]));
3332

3433
$entityManager = new EntityManager(
@@ -49,10 +48,10 @@ public static function makeEntityManagerWithNoResultsConnection(): EntityManager
4948
{
5049
$config = new Configuration();
5150

52-
$config->setMetadataDriverImpl(new AttributeDriver([
53-
realpath(__DIR__ . '/Models/Cache'),
54-
realpath(__DIR__ . '/Models/Generic'),
55-
realpath(__DIR__ . '/Models/GeoNames'),
51+
$config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver([
52+
__DIR__ . '/../Tests/Models/Cache',
53+
__DIR__ . '/../Tests/Models/Generic',
54+
__DIR__ . '/../Tests/Models/GeoNames',
5655
]));
5756

5857
// A connection that doesn't really do anything
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Mocks;
6+
7+
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
8+
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
9+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
10+
11+
use function interface_exists;
12+
13+
final class AttributeDriverFactory
14+
{
15+
/** @param list<string> $paths */
16+
public static function createAttributeDriver(array $paths = []): AttributeDriver
17+
{
18+
if (! self::isClassLocatorSupported()) {
19+
// Persistence < 4.1
20+
return new AttributeDriver($paths);
21+
}
22+
23+
// Persistence >= 4.1
24+
$classLocator = FileClassLocator::createFromDirectories($paths);
25+
26+
return new AttributeDriver($classLocator);
27+
}
28+
29+
/** Supported since doctrine/persistence >= 4.1 */
30+
public static function isClassLocatorSupported(): bool
31+
{
32+
return interface_exists(ClassLocator::class);
33+
}
34+
}

tests/Tests/Mocks/EntityManagerMock.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Doctrine\DBAL\Connection;
99
use Doctrine\ORM\Configuration;
1010
use Doctrine\ORM\EntityManager;
11-
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
1211
use Doctrine\ORM\Proxy\ProxyFactory;
1312
use Doctrine\ORM\UnitOfWork;
1413

@@ -24,7 +23,7 @@ public function __construct(Connection $conn, Configuration|null $config = null,
2423
{
2524
if ($config === null) {
2625
$config = new Configuration();
27-
$config->setMetadataDriverImpl(new AttributeDriver([]));
26+
$config->setMetadataDriverImpl(AttributeDriverFactory::createAttributeDriver());
2827
}
2928

3029
parent::__construct($conn, $config, $eventManager);

tests/Tests/ORM/Functional/EnumTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
use Doctrine\ORM\AbstractQuery;
88
use Doctrine\ORM\Mapping\Column;
9-
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
109
use Doctrine\ORM\Mapping\MappingException;
1110
use Doctrine\ORM\Query\Expr\Func;
1211
use Doctrine\ORM\Tools\SchemaTool;
12+
use Doctrine\Tests\Mocks\AttributeDriverFactory;
1313
use Doctrine\Tests\Models\DataTransferObjects\DtoWithArrayOfEnums;
1414
use Doctrine\Tests\Models\DataTransferObjects\DtoWithEnum;
1515
use Doctrine\Tests\Models\Enums\Card;
@@ -26,7 +26,6 @@
2626
use Doctrine\Tests\OrmFunctionalTestCase;
2727
use PHPUnit\Framework\Attributes\DataProvider;
2828

29-
use function dirname;
3029
use function sprintf;
3130
use function uniqid;
3231

@@ -36,7 +35,9 @@ public function setUp(): void
3635
{
3736
parent::setUp();
3837

39-
$this->_em = $this->getEntityManager(null, new AttributeDriver([dirname(__DIR__, 2) . '/Models/Enums'], true));
38+
$mappingDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../Models/Enums']);
39+
40+
$this->_em = $this->getEntityManager(null, $mappingDriver);
4041
$this->_schemaTool = new SchemaTool($this->_em);
4142

4243
if ($this->isSecondLevelCacheEnabled) {

tests/Tests/ORM/Functional/Locking/LockAgentWorker.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\ORM\Configuration;
1010
use Doctrine\ORM\EntityManager;
1111
use Doctrine\ORM\EntityManagerInterface;
12+
use Doctrine\Tests\Mocks\AttributeDriverFactory;
1213
use Doctrine\Tests\ORM\Functional\Locking\Doctrine\ORM\Query;
1314
use GearmanWorker;
1415
use InvalidArgumentException;
@@ -113,8 +114,8 @@ protected function createEntityManager(Connection $conn): EntityManagerInterface
113114
{
114115
$config = new Configuration();
115116

116-
$annotDriver = new AttributeDriver([__DIR__ . '/../../../Models/']);
117-
$config->setMetadataDriverImpl($annotDriver);
117+
$attributeDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../../Models']);
118+
$config->setMetadataDriverImpl($attributeDriver);
118119
$config->setMetadataCache(new ArrayAdapter());
119120

120121
$config->setQueryCache(new ArrayAdapter());

tests/Tests/ORM/Functional/ReadonlyPropertiesTest.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44

55
namespace Doctrine\Tests\ORM\Functional;
66

7-
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
87
use Doctrine\ORM\Tools\SchemaTool;
8+
use Doctrine\Tests\Mocks\AttributeDriverFactory;
99
use Doctrine\Tests\Models\ReadonlyProperties\Author;
1010
use Doctrine\Tests\Models\ReadonlyProperties\Book;
1111
use Doctrine\Tests\Models\ReadonlyProperties\SimpleBook;
1212
use Doctrine\Tests\OrmFunctionalTestCase;
1313
use Doctrine\Tests\TestUtil;
1414

15-
use function dirname;
16-
1715
class ReadonlyPropertiesTest extends OrmFunctionalTestCase
1816
{
1917
protected function setUp(): void
@@ -22,10 +20,9 @@ protected function setUp(): void
2220
static::$sharedConn = TestUtil::getConnection();
2321
}
2422

25-
$this->_em = $this->getEntityManager(null, new AttributeDriver(
26-
[dirname(__DIR__, 2) . '/Models/ReadonlyProperties'],
27-
true,
28-
));
23+
$attributeDriver = AttributeDriverFactory::createAttributeDriver([__DIR__ . '/../../Models/ReadonlyProperties']);
24+
25+
$this->_em = $this->getEntityManager(null, $attributeDriver);
2926
$this->_schemaTool = new SchemaTool($this->_em);
3027

3128
parent::setUp();

tests/Tests/ORM/Mapping/AttributeDriverTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,32 @@
1010
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
1111
use Doctrine\ORM\Mapping\JoinColumnMapping;
1212
use Doctrine\ORM\Mapping\MappingAttribute;
13+
use Doctrine\Persistence\Mapping\Driver\ClassNames;
1314
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
15+
use Doctrine\Tests\Mocks\AttributeDriverFactory;
16+
use Doctrine\Tests\Models\Cache\City;
1417
use Doctrine\Tests\ORM\Mapping\Fixtures\AttributeEntityWithNestedJoinColumns;
1518
use stdClass;
1619

1720
class AttributeDriverTest extends MappingDriverTestCase
1821
{
1922
protected function loadDriver(): MappingDriver
2023
{
21-
$paths = [];
24+
return AttributeDriverFactory::createAttributeDriver();
25+
}
26+
27+
public function testDriverCanAcceptClassLocator(): void
28+
{
29+
if (! AttributeDriverFactory::isClassLocatorSupported()) {
30+
self::markTestSkipped('This test is only relevant for versions of doctrine/persistence >= 4.1');
31+
}
32+
33+
$classLocator = new ClassNames([City::class]);
34+
35+
$driver = new AttributeDriver($classLocator);
2236

23-
return new AttributeDriver($paths, true);
37+
self::assertSame([], $driver->getPaths(), 'Directory paths must be empty, since file paths are used');
38+
self::assertSame([City::class], $driver->getAllClassNames());
2439
}
2540

2641
public function testOriginallyNestedAttributesDeclaredWithoutOriginalParent(): void

0 commit comments

Comments
 (0)