From 3c7941614139b7da09aea98f434b3d841db64839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= <jerome@tamarelle.net> Date: Mon, 24 Jun 2024 14:51:44 +0200 Subject: [PATCH] Review custom collections and repository docs --- docs/en/reference/custom-collections.rst | 80 ++++++++++++--------- docs/en/reference/document-repositories.rst | 44 ++++++------ 2 files changed, 71 insertions(+), 53 deletions(-) diff --git a/docs/en/reference/custom-collections.rst b/docs/en/reference/custom-collections.rst index 29e2db869..fdf1889e5 100644 --- a/docs/en/reference/custom-collections.rst +++ b/docs/en/reference/custom-collections.rst @@ -3,9 +3,6 @@ Custom Collections ================== -.. note:: - This feature was introduced in version 1.1 - By default, Doctrine uses ``ArrayCollection`` implementation of its ``Collection`` interface to hold both embedded and referenced documents. That collection may then be wrapped by a ``PersistentCollection`` to allow for change tracking and other @@ -16,14 +13,16 @@ persistence-related features. <?php use Doctrine\Common\Collections\ArrayCollection; + use Doctrine\Common\Collections\Collection; #[Document] class Application { // ... + /** @var Collection<Section> */ #[EmbedMany(targetDocument: Section::class)] - private $sections; + public Collection $sections; public function __construct() { @@ -41,11 +40,6 @@ owning document's class. Custom Collection Classes ------------------------- -.. note:: - You may want to check `malarzm/collections <https://github.com/malarzm/collections>`_ - which provides alternative implementations of Doctrine's ``Collection`` interface and - aims to kickstart development of your own collections. - Using your own ``Collection`` implementation is as simple as specifying the ``collectionClass`` parameter in the ``#[EmbedMany]`` or ``#[ReferenceMany]`` mapping and ensuring that your custom class is initialized in the owning class' constructor: @@ -65,7 +59,7 @@ and ensuring that your custom class is initialized in the owning class' construc collectionClass: SectionCollection::class, targetDocument: Section::class, )] - private $sections; + private Collection $sections; public function __construct() { @@ -104,12 +98,9 @@ Alternatively, you may want to implement the whole class from scratch: class SectionCollection implements Collection { - private $elements = []; - - public function __construct(array $elements = []) - { - $this->elements = $elements; - } + public function __construct( + private array $elements = [] + ) {} // your implementation of all methods interface requires } @@ -120,9 +111,38 @@ Taking Control of the Collection's Constructor By default, Doctrine assumes that it can instantiate your collections in same manner as an ``ArrayCollection`` (i.e. the only parameter is an optional PHP array); however, you may want to inject additional dependencies into your -custom collection class(es). This will require you to create a -`PersistentCollectionFactory implementation <https://github.com/doctrine/mongodb-odm/blob/2.2.x/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionFactory.php>`_, -which Doctrine will then use to construct its persistent collections. +custom collection class(es). + +For this example, we assume that you want to pass Symfony's event dispatcher +to your custom collection class. To do this, you need to modify the +constructor to accept the event dispatcher. You also need to override the +``createFrom`` method to allow Doctrine to pass the dependencies to the +collection constructor. + +.. code-block:: php + + <?php + + use Doctrine\Common\Collections\ArrayCollection; + use Doctrine\Common\Collections\Collection; + + class SectionCollection extend ArrayCollection + { + public function __construct( + private EventDispatcherInterface $eventDispatcher, + private array $elements = [], + ) {} + + public function createFrom(array $elements): static + { + return new static($this->eventDispatcher, $elements); + } + + // your custom methods + } + +This requires you to create a ``PersistentCollectionFactory`` implementation, +which Doctrine ODM will then use to construct its persistent collections. You may decide to implement this class from scratch or extend our ``AbstractPersistentCollectionFactory``: @@ -130,26 +150,22 @@ You may decide to implement this class from scratch or extend our <?php + use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\PersistentCollection\AbstractPersistentCollectionFactory; use Symfony\Component\EventDispatcher\EventDispatcherInterface; final class YourPersistentCollectionFactory extends AbstractPersistentCollectionFactory { - private $eventDispatcher; - - public function __construct(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } + public function __construct( + private EventDispatcherInterface $eventDispatcher, + ) {} - protected function createCollectionClass(string $collectionClass) + protected function createCollectionClass(string $collectionClass): Collection { - switch ($collectionClass) { - case SectionCollection::class: - return new $collectionClass([], $this->eventDispatcher); - default: - return new $collectionClass(); - } + return match ($collectionClass) { + SectionCollection::class => new SectionCollection([], $this->eventDispatcher), + default => new $collectionClass(), + }; } } diff --git a/docs/en/reference/document-repositories.rst b/docs/en/reference/document-repositories.rst index 99b696786..edce83729 100644 --- a/docs/en/reference/document-repositories.rst +++ b/docs/en/reference/document-repositories.rst @@ -103,11 +103,6 @@ implementation for all documents (unless overridden by the mapping): Repositories with Additional Dependencies ----------------------------------------- -.. note:: - - Implementing your own RepositoryFactory is possible since version 1.0, but the - ``AbstractRepositoryFactory`` class used in this example is only available since 1.2. - By default, Doctrine assumes that it can instantiate your repositories in same manner as its default one: @@ -117,42 +112,49 @@ as its default one: namespace Repositories; + use Doctrine\ODM\MongoDB\DocumentRepository; + use Doctrine\ODM\MongoDB\DocumentManager; + use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + use Doctrine\ODM\MongoDB\UnitOfWork; + class UserRepository extends DocumentRepository { public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $classMetadata) { - /* constructor is inherited from DocumentRepository */ - /* ... */ + // The constructor arguments are inherited from DocumentRepository + parent::__construct($dm, $uow, $classMetadata); } } -In order to change the way Doctrine instantiates repositories, you will need to implement your own -`RepositoryFactory <https://github.com/doctrine/mongodb-odm/blob/2.2.x/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php>`_ +In order to change the way Doctrine instantiates repositories, you will need to +implement your own `RepositoryFactory <https://github.com/doctrine/mongodb-odm/blob/2.9.x/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php>`_ + +In the following example, we create a custom repository factory to pass Symfony's +event dispatcher to the repository constructor. .. code-block:: php <?php + use Doctrine\ODM\MongoDB\DocumentRepository; + use Doctrine\ODM\MongoDB\DocumentManager; + use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Repository\AbstractRepositoryFactory; + use Doctrine\ODM\MongoDB\UnitOfWork; use Symfony\Component\EventDispatcher\EventDispatcherInterface; final class YourRepositoryFactory extends AbstractRepositoryFactory { - private $eventDispatcher; - - public function __construct(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } + public function __construct( + private EventDispatcherInterface $eventDispatcher + ) {} protected function instantiateRepository(string $repositoryClassName, DocumentManager $documentManager, ClassMetadata $metadata) { - switch ($repositoryClassName) { - case UserRepository::class: - return new UserRepository($this->eventDispatcher, $documentManager, $metadata); - default: - return new $repositoryClassName($documentManager, $documentManager->getUnitOfWork(), $metadata); - } + return match ($repositoryClassName) { + UserRepository::class => new UserRepository($this->eventDispatcher, $documentManager, $metadata), + default => new $repositoryClassName($documentManager, $documentManager->getUnitOfWork(), $metadata), + }; } }