Skip to content

Commit

Permalink
Review custom collections and repository docs
Browse files Browse the repository at this point in the history
  • Loading branch information
GromNaN committed Jun 24, 2024
1 parent 8654054 commit 3c79416
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 53 deletions.
80 changes: 48 additions & 32 deletions docs/en/reference/custom-collections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
{
Expand All @@ -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:
Expand All @@ -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()
{
Expand Down Expand Up @@ -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
}
Expand All @@ -120,36 +111,61 @@ 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``:

.. code-block:: php
<?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(),
};
}
}
Expand Down
44 changes: 23 additions & 21 deletions docs/en/reference/document-repositories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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),
};
}
}
Expand Down

0 comments on commit 3c79416

Please sign in to comment.