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),
+            };
         }
     }