Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions docs/en/reference/metadata-drivers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,55 @@ namespace:
$driver = new \Doctrine\ODM\MongoDB\Mapping\Driver\XmlDriver('/path/to/mapping/files');
$em->getConfiguration()->setMetadataDriverImpl($driver);

The ``AttributeDriver`` can be initialized from a list of directories that
contain the PHP classes with ODM attributes, or from an instance of a class
locator that will find the classes:

From a list of directories:

.. code-block:: php

<?php
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;

$driver = new AttributeDriver([__DIR__ . '/src/Document']);
$em->getConfiguration()->setMetadataDriverImpl($driver);

Using Symfony Finder to locate classes. For example, if you are using Vertical
Slice architecture, you can exclude ``*Test.php``, ``*Controller.php``,
``*Service.php``, etc.:

.. code-block:: php
<?php
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
use Symfony\Component\Finder\Finder;

$finder = new Finder()->files()->in([__DIR__ . '/src/'])
->name('*.php')
->notName(['*Test.php', '*Controller.php', '*Service.php']);

$classLocator = new FileClassLocator($finder);

$driver = new AttributeDriver($classLocator);
$em->getConfiguration()->setMetadataDriverImpl($driver);

If you know the list of class names you want to track, use
``Doctrine\Persistence\Mapping\Driver\ClassNames``:

.. code-block:: php
<?php
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
use Doctrine\Persistence\Mapping\Driver\ClassNames;
use App\Document\{Article, Book};

$entityClasses = [Article::class, Book::class];

$classLocator = new ClassNames($entityClasses);

$driver = new AttributeDriver($classLocator);
$em->getConfiguration()->setMetadataDriverImpl($driver);

Implementing Metadata Drivers
-----------------------------

Expand Down
12 changes: 7 additions & 5 deletions lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\Reader;
use Doctrine\Persistence\Mapping\Driver\ClassLocator;

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

$this->addPaths((array) $paths);
// Setting the reader in the parent constructor is deprecated.
$this->reader = $reader;
}

/**
* Factory method for the Annotation Driver
*
* @param string[]|string $paths
* @param string|string[]|ClassLocator $paths
*/
public static function create($paths = [], ?Reader $reader = null): AnnotationDriver
{
Expand Down
13 changes: 9 additions & 4 deletions lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use MongoDB\BSON\Document;
Expand All @@ -32,7 +33,7 @@
use function trigger_deprecation;

/**
* The AtttributeDriver reads the mapping metadata from attributes.
* The AttributeDriver reads the mapping metadata from attributes.
*/
class AttributeDriver implements MappingDriver
{
Expand All @@ -45,7 +46,7 @@ class AttributeDriver implements MappingDriver
*/
protected $reader;

/** @param string|string[]|null $paths */
/** @param string|string[]|ClassLocator|null $paths */
public function __construct($paths = null, ?Reader $reader = null)
{
if ($reader !== null) {
Expand All @@ -59,7 +60,11 @@ public function __construct($paths = null, ?Reader $reader = null)

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

$this->addPaths((array) $paths);
if ($paths instanceof ClassLocator) {
$this->classLocator = $paths;
} else {
$this->addPaths((array) $paths);
}
}

public function isTransient($className): bool
Expand Down Expand Up @@ -416,7 +421,7 @@ public function getReader()
/**
* Factory method for the Attribute Driver
*
* @param string[]|string $paths
* @param string|string[]|ClassLocator $paths
*
* @return AttributeDriver
*/
Expand Down
11 changes: 10 additions & 1 deletion tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Doctrine\ODM\MongoDB\Proxy\InternalProxy;
use Doctrine\ODM\MongoDB\Tests\Query\Filter\Filter;
use Doctrine\ODM\MongoDB\UnitOfWork;
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use MongoDB\Client;
use MongoDB\Driver\Manager;
Expand All @@ -20,6 +21,7 @@

use function array_key_exists;
use function array_map;
use function class_exists;
use function count;
use function explode;
use function getenv;
Expand Down Expand Up @@ -124,7 +126,14 @@ public static function isLazyObject(object $document): bool

protected static function createMetadataDriverImpl(): MappingDriver
{
return AttributeDriver::create(__DIR__ . '/../../../../Documents');
$paths = [__DIR__ . '/../../../../Documents'];

// Available in Doctrine Persistence 4.1+
if (class_exists(FileClassLocator::class)) {
$paths = FileClassLocator::createFromDirectories($paths);
}

return AttributeDriver::create($paths);
}

protected static function createTestDocumentManager(): DocumentManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Doctrine\ODM\MongoDB\Tests\Mapping;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
Expand Down Expand Up @@ -76,8 +75,7 @@ public function testFieldInheritance(): void
public function testLoadMetadataForNonDocumentThrowsException(): void
{
$cm = new ClassMetadata('stdClass');
$reader = new AnnotationReader();
$annotationDriver = new AnnotationDriver($reader);
$annotationDriver = static::loadDriver();

$this->expectException(MappingException::class);
$annotationDriver->loadMetadataForClass('stdClass', $cm);
Expand All @@ -87,8 +85,7 @@ public function testLoadMetadataForNonDocumentThrowsException(): void
public function testColumnWithMissingTypeDefaultsToString(): void
{
$cm = new ClassMetadata(ColumnWithoutType::class);
$reader = new AnnotationReader();
$annotationDriver = new AnnotationDriver($reader);
$annotationDriver = static::loadDriver();

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

protected function loadDriverForCMSDocuments(): MappingDriver
{
$annotationDriver = static::loadDriver();
$annotationDriver = static::loadDriver([__DIR__ . '/../../../../../Documents']);
assert($annotationDriver instanceof AnnotationDriver || $annotationDriver instanceof AttributeDriver);
$annotationDriver->addPaths([__DIR__ . '/../../../../../Documents']);

return $annotationDriver;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@

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

protected static function createMetadataDriverImpl(): MappingDriver
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

namespace Doctrine\ODM\MongoDB\Tests\Mapping;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;

use function call_user_func;
use function class_exists;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
Expand All @@ -20,11 +21,13 @@

class AnnotationDriverTest extends AbstractAnnotationDriverTestCase
{
protected static function loadDriver(): MappingDriver
protected static function loadDriver(array $paths = []): MappingDriver
{
$reader = new AnnotationReader();
if (class_exists(FileClassLocator::class)) {
$paths = FileClassLocator::createFromDirectories($paths);
}

return new AnnotationDriver($reader);
return AnnotationDriver::create($paths);
}

public function testIndexesClassAnnotationEmitsDeprecationMessage(): void
Expand Down
11 changes: 9 additions & 2 deletions tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
namespace Doctrine\ODM\MongoDB\Tests\Mapping;

use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;

use function class_exists;

class AttributeDriverTest extends AbstractAnnotationDriverTestCase
{
protected static function loadDriver(): MappingDriver
protected static function loadDriver(array $paths = []): MappingDriver
{
return new AttributeDriver();
if (class_exists(FileClassLocator::class)) {
$paths = FileClassLocator::createFromDirectories($paths);
}

return AttributeDriver::create($paths);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
use SimpleXMLElement;
use stdClass;

use function assert;

use const DIRECTORY_SEPARATOR;

class XmlMappingDriverTest extends AbstractMappingDriverTestCase
{
protected static function loadDriver(): MappingDriver
protected static function loadDriver(array $paths = []): MappingDriver
{
assert($paths === []);

return new XmlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'xml');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@

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

use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver;
use Doctrine\ODM\MongoDB\Tests\Tools\Console\Command\AbstractCommandTestCase;
use Doctrine\ODM\MongoDB\Tools\Console\Command\Schema\UpdateCommand;
use Doctrine\Persistence\Mapping\Driver\ClassNames;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Documents\Ecommerce;
use Documents\SchemaValidated;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

use function class_exists;

class UpdateCommandTest extends AbstractCommandTestCase
{
protected ?Command $command;
Expand Down Expand Up @@ -67,7 +72,7 @@ public function testDisabledValidatorProcessing(): void
public function testProcessValidators(): void
{
// Only load a subset of documents with legit annotations
$annotationDriver = AnnotationDriver::create(__DIR__ . '/../../../../../../../../Documents/Ecommerce');
$annotationDriver = $this->createDriver();
$this->dm->getConfiguration()->setMetadataDriverImpl($annotationDriver);
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
Expand All @@ -77,10 +82,29 @@ public function testProcessValidators(): void
public function testDisabledValidatorsProcessing(): void
{
// Only load a subset of documents with legit annotations
$annotationDriver = AnnotationDriver::create(__DIR__ . '/../../../../../../../../Documents/Ecommerce');
$annotationDriver = $this->createDriver();
$this->dm->getConfiguration()->setMetadataDriverImpl($annotationDriver);
$this->commandTester->execute(['--disable-validators' => true]);
$output = $this->commandTester->getDisplay();
self::assertStringNotContainsString('Updated validation for all classes', $output);
}

private function createDriver(): MappingDriver
{
$paths = [__DIR__ . '/../../../../../../../../Documents/Ecommerce'];
// Available in Doctrine Persistence 4.1+
if (class_exists(ClassNames::class)) {
$paths = new ClassNames([
Ecommerce\Basket::class,
Ecommerce\ConfigurableProduct::class,
Ecommerce\Currency::class,
Ecommerce\Money::class,
Ecommerce\Option::class,
Ecommerce\Order::class,
Ecommerce\StockItem::class,
]);
}

return AttributeDriver::create($paths);
}
}