Skip to content

Commit 4f3dbd0

Browse files
GromNaNrela589n
andauthored
Enable ClassLocator from Doctrine Persistence 4.1 (#2802)
--------- Co-authored-by: Yevhen Sidelnyk <[email protected]>
1 parent 8030f02 commit 4f3dbd0

File tree

10 files changed

+128
-28
lines changed

10 files changed

+128
-28
lines changed

docs/en/reference/metadata-drivers.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,55 @@ namespace:
4848
$driver = new \Doctrine\ODM\MongoDB\Mapping\Driver\XmlDriver('/path/to/mapping/files');
4949
$em->getConfiguration()->setMetadataDriverImpl($driver);
5050
51+
The ``AttributeDriver`` can be initialized from a list of directories that
52+
contain the PHP classes with ODM attributes, or from an instance of a class
53+
locator that will find the classes:
54+
55+
From a list of directories:
56+
57+
.. code-block:: php
58+
59+
<?php
60+
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
61+
62+
$driver = new AttributeDriver([__DIR__ . '/src/Document']);
63+
$em->getConfiguration()->setMetadataDriverImpl($driver);
64+
65+
Using Symfony Finder to locate classes. For example, if you are using Vertical
66+
Slice architecture, you can exclude ``*Test.php``, ``*Controller.php``,
67+
``*Service.php``, etc.:
68+
69+
.. code-block:: php
70+
<?php
71+
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
72+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
73+
use Symfony\Component\Finder\Finder;
74+
75+
$finder = new Finder()->files()->in([__DIR__ . '/src/'])
76+
->name('*.php')
77+
->notName(['*Test.php', '*Controller.php', '*Service.php']);
78+
79+
$classLocator = new FileClassLocator($finder);
80+
81+
$driver = new AttributeDriver($classLocator);
82+
$em->getConfiguration()->setMetadataDriverImpl($driver);
83+
84+
If you know the list of class names you want to track, use
85+
``Doctrine\Persistence\Mapping\Driver\ClassNames``:
86+
87+
.. code-block:: php
88+
<?php
89+
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
90+
use Doctrine\Persistence\Mapping\Driver\ClassNames;
91+
use App\Document\{Article, Book};
92+
93+
$entityClasses = [Article::class, Book::class];
94+
95+
$classLocator = new ClassNames($entityClasses);
96+
97+
$driver = new AttributeDriver($classLocator);
98+
$em->getConfiguration()->setMetadataDriverImpl($driver);
99+
51100
Implementing Metadata Drivers
52101
-----------------------------
53102

lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php

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

77
use Doctrine\Common\Annotations\AnnotationReader;
88
use Doctrine\Common\Annotations\Reader;
9+
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
910

1011
/**
1112
* The AnnotationDriver reads the mapping metadata from docblock annotations.
@@ -25,20 +26,21 @@ class AnnotationDriver extends AttributeDriver
2526
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
2627
* docblock annotations.
2728
*
28-
* @param Reader $reader The AnnotationReader to use, duck-typed.
29-
* @param string|string[]|null $paths One or multiple paths where mapping classes can be found.
29+
* @param Reader $reader The AnnotationReader to use, duck-typed.
30+
* @param string|string[]|ClassLocator|null $paths One or multiple paths where mapping classes can be found.
3031
*/
3132
public function __construct($reader, $paths = null)
3233
{
33-
$this->reader = $reader;
34+
parent::__construct($paths);
3435

35-
$this->addPaths((array) $paths);
36+
// Setting the reader in the parent constructor is deprecated.
37+
$this->reader = $reader;
3638
}
3739

3840
/**
3941
* Factory method for the Annotation Driver
4042
*
41-
* @param string[]|string $paths
43+
* @param string|string[]|ClassLocator $paths
4244
*/
4345
public static function create($paths = [], ?Reader $reader = null): AnnotationDriver
4446
{

lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
1515
use Doctrine\ODM\MongoDB\Mapping\MappingException;
1616
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
17+
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
1718
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
1819
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
1920
use MongoDB\BSON\Document;
@@ -32,7 +33,7 @@
3233
use function trigger_deprecation;
3334

3435
/**
35-
* The AtttributeDriver reads the mapping metadata from attributes.
36+
* The AttributeDriver reads the mapping metadata from attributes.
3637
*/
3738
class AttributeDriver implements MappingDriver
3839
{
@@ -45,7 +46,7 @@ class AttributeDriver implements MappingDriver
4546
*/
4647
protected $reader;
4748

48-
/** @param string|string[]|null $paths */
49+
/** @param string|string[]|ClassLocator|null $paths */
4950
public function __construct($paths = null, ?Reader $reader = null)
5051
{
5152
if ($reader !== null) {
@@ -59,7 +60,11 @@ public function __construct($paths = null, ?Reader $reader = null)
5960

6061
$this->reader = $reader ?? new AttributeReader();
6162

62-
$this->addPaths((array) $paths);
63+
if ($paths instanceof ClassLocator) {
64+
$this->classLocator = $paths;
65+
} else {
66+
$this->addPaths((array) $paths);
67+
}
6368
}
6469

6570
public function isTransient($className): bool
@@ -416,7 +421,7 @@ public function getReader()
416421
/**
417422
* Factory method for the Attribute Driver
418423
*
419-
* @param string[]|string $paths
424+
* @param string|string[]|ClassLocator $paths
420425
*
421426
* @return AttributeDriver
422427
*/

tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\ODM\MongoDB\Proxy\InternalProxy;
1111
use Doctrine\ODM\MongoDB\Tests\Query\Filter\Filter;
1212
use Doctrine\ODM\MongoDB\UnitOfWork;
13+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
1314
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
1415
use MongoDB\Client;
1516
use MongoDB\Driver\Manager;
@@ -20,6 +21,7 @@
2021

2122
use function array_key_exists;
2223
use function array_map;
24+
use function class_exists;
2325
use function count;
2426
use function explode;
2527
use function getenv;
@@ -124,7 +126,14 @@ public static function isLazyObject(object $document): bool
124126

125127
protected static function createMetadataDriverImpl(): MappingDriver
126128
{
127-
return AttributeDriver::create(__DIR__ . '/../../../../Documents');
129+
$paths = [__DIR__ . '/../../../../Documents'];
130+
131+
// Available in Doctrine Persistence 4.1+
132+
if (class_exists(FileClassLocator::class)) {
133+
$paths = FileClassLocator::createFromDirectories($paths);
134+
}
135+
136+
return AttributeDriver::create($paths);
128137
}
129138

130139
protected static function createTestDocumentManager(): DocumentManager

tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php

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

55
namespace Doctrine\ODM\MongoDB\Tests\Mapping;
66

7-
use Doctrine\Common\Annotations\AnnotationReader;
87
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
98
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
109
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
@@ -76,8 +75,7 @@ public function testFieldInheritance(): void
7675
public function testLoadMetadataForNonDocumentThrowsException(): void
7776
{
7877
$cm = new ClassMetadata('stdClass');
79-
$reader = new AnnotationReader();
80-
$annotationDriver = new AnnotationDriver($reader);
78+
$annotationDriver = static::loadDriver();
8179

8280
$this->expectException(MappingException::class);
8381
$annotationDriver->loadMetadataForClass('stdClass', $cm);
@@ -87,8 +85,7 @@ public function testLoadMetadataForNonDocumentThrowsException(): void
8785
public function testColumnWithMissingTypeDefaultsToString(): void
8886
{
8987
$cm = new ClassMetadata(ColumnWithoutType::class);
90-
$reader = new AnnotationReader();
91-
$annotationDriver = new AnnotationDriver($reader);
88+
$annotationDriver = static::loadDriver();
9289

9390
$annotationDriver->loadMetadataForClass(stdClass::class, $cm);
9491
self::assertEquals('id', $cm->fieldMappings['id']['type']);
@@ -255,9 +252,8 @@ public function testWrongValueForValidationValidatorShouldThrowException(): void
255252

256253
protected function loadDriverForCMSDocuments(): MappingDriver
257254
{
258-
$annotationDriver = static::loadDriver();
255+
$annotationDriver = static::loadDriver([__DIR__ . '/../../../../../Documents']);
259256
assert($annotationDriver instanceof AnnotationDriver || $annotationDriver instanceof AttributeDriver);
260-
$annotationDriver->addPaths([__DIR__ . '/../../../../../Documents']);
261257

262258
return $annotationDriver;
263259
}

tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTestCase.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434

3535
abstract class AbstractMappingDriverTestCase extends BaseTestCase
3636
{
37-
abstract protected static function loadDriver(): MappingDriver;
37+
/** @param list<string> $paths */
38+
abstract protected static function loadDriver(array $paths = []): MappingDriver;
3839

3940
protected static function createMetadataDriverImpl(): MappingDriver
4041
{

tests/Doctrine/ODM/MongoDB/Tests/Mapping/AnnotationDriverTest.php

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

55
namespace Doctrine\ODM\MongoDB\Tests\Mapping;
66

7-
use Doctrine\Common\Annotations\AnnotationReader;
87
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
98
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
109
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
1110
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
11+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
1212
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
1313

1414
use function call_user_func;
15+
use function class_exists;
1516
use function restore_error_handler;
1617
use function set_error_handler;
1718
use function sprintf;
@@ -20,11 +21,13 @@
2021

2122
class AnnotationDriverTest extends AbstractAnnotationDriverTestCase
2223
{
23-
protected static function loadDriver(): MappingDriver
24+
protected static function loadDriver(array $paths = []): MappingDriver
2425
{
25-
$reader = new AnnotationReader();
26+
if (class_exists(FileClassLocator::class)) {
27+
$paths = FileClassLocator::createFromDirectories($paths);
28+
}
2629

27-
return new AnnotationDriver($reader);
30+
return AnnotationDriver::create($paths);
2831
}
2932

3033
public function testIndexesClassAnnotationEmitsDeprecationMessage(): void

tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
namespace Doctrine\ODM\MongoDB\Tests\Mapping;
66

77
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
8+
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
89
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
910

11+
use function class_exists;
12+
1013
class AttributeDriverTest extends AbstractAnnotationDriverTestCase
1114
{
12-
protected static function loadDriver(): MappingDriver
15+
protected static function loadDriver(array $paths = []): MappingDriver
1316
{
14-
return new AttributeDriver();
17+
if (class_exists(FileClassLocator::class)) {
18+
$paths = FileClassLocator::createFromDirectories($paths);
19+
}
20+
21+
return AttributeDriver::create($paths);
1522
}
1623
}

tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212
use SimpleXMLElement;
1313
use stdClass;
1414

15+
use function assert;
16+
1517
use const DIRECTORY_SEPARATOR;
1618

1719
class XmlMappingDriverTest extends AbstractMappingDriverTestCase
1820
{
19-
protected static function loadDriver(): MappingDriver
21+
protected static function loadDriver(array $paths = []): MappingDriver
2022
{
23+
assert($paths === []);
24+
2125
return new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml');
2226
}
2327

tests/Doctrine/ODM/MongoDB/Tests/Tools/Console/Command/Schema/UpdateCommandTest.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44

55
namespace Doctrine\ODM\MongoDB\Tests\Tools\Console\Command\Schema;
66

7-
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
7+
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
88
use Doctrine\ODM\MongoDB\Tests\Tools\Console\Command\AbstractCommandTestCase;
99
use Doctrine\ODM\MongoDB\Tools\Console\Command\Schema\UpdateCommand;
10+
use Doctrine\Persistence\Mapping\Driver\ClassNames;
11+
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
12+
use Documents\Ecommerce;
1013
use Documents\SchemaValidated;
1114
use Symfony\Component\Console\Command\Command;
1215
use Symfony\Component\Console\Tester\CommandTester;
1316

17+
use function class_exists;
18+
1419
class UpdateCommandTest extends AbstractCommandTestCase
1520
{
1621
protected ?Command $command;
@@ -67,7 +72,7 @@ public function testDisabledValidatorProcessing(): void
6772
public function testProcessValidators(): void
6873
{
6974
// Only load a subset of documents with legit annotations
70-
$annotationDriver = AnnotationDriver::create(__DIR__ . '/../../../../../../../../Documents/Ecommerce');
75+
$annotationDriver = $this->createDriver();
7176
$this->dm->getConfiguration()->setMetadataDriverImpl($annotationDriver);
7277
$this->commandTester->execute([]);
7378
$output = $this->commandTester->getDisplay();
@@ -77,10 +82,29 @@ public function testProcessValidators(): void
7782
public function testDisabledValidatorsProcessing(): void
7883
{
7984
// Only load a subset of documents with legit annotations
80-
$annotationDriver = AnnotationDriver::create(__DIR__ . '/../../../../../../../../Documents/Ecommerce');
85+
$annotationDriver = $this->createDriver();
8186
$this->dm->getConfiguration()->setMetadataDriverImpl($annotationDriver);
8287
$this->commandTester->execute(['--disable-validators' => true]);
8388
$output = $this->commandTester->getDisplay();
8489
self::assertStringNotContainsString('Updated validation for all classes', $output);
8590
}
91+
92+
private function createDriver(): MappingDriver
93+
{
94+
$paths = [__DIR__ . '/../../../../../../../../Documents/Ecommerce'];
95+
// Available in Doctrine Persistence 4.1+
96+
if (class_exists(ClassNames::class)) {
97+
$paths = new ClassNames([
98+
Ecommerce\Basket::class,
99+
Ecommerce\ConfigurableProduct::class,
100+
Ecommerce\Currency::class,
101+
Ecommerce\Money::class,
102+
Ecommerce\Option::class,
103+
Ecommerce\Order::class,
104+
Ecommerce\StockItem::class,
105+
]);
106+
}
107+
108+
return AttributeDriver::create($paths);
109+
}
86110
}

0 commit comments

Comments
 (0)