2
votes

Comment utiliser une collection personnalisée dans Symfony 4

Je souhaite utiliser une classe de collection personnalisée pour mon application Symfony 4. Le scénario suivant est un exemple de ce que j'essaie de réaliser:

J'ai une classe de collecte d'articles qui a un utilitaire pour filtrer et mapper les données.

public function showPublishedPostTitlesForUser(User $user)
{
    $publishedPostTitles = $user->getPosts()
        ->filterPublishedAfterDate(new DateTime())
        ->mapTitles();

    // Render the titles...
}

Aussi le classe d'utilisateur qui est une entité Doctrine.

class User
{
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Post", mappedBy="post", cascade={"persist"})
     */
    private $posts;

    public function __construct()
    {
        $this->posts = new PostArrayCollection();
    }

    public function getPosts(): PostCollectionInterface
    {
        return $this->posts;
    }
}

Ensuite, il y a une méthode dans un assistant ou un contrôleur qui accédera aux données de l'utilisateur comme suit:

class PostArrayCollection extends ArrayCollection implements PostCollectionInterface
{
    public function mapTitles()
    {
        return $this->map(function (Post $post) {
            return $post->getTitle();
        });
    }

    public function filterPublishedAfterDate(DateTime $from)
    {
        return $this->filter(function (Post $post) use ($from) {
            return $post->publishedDate() > $from;
        });
    }
}

Ce qui précède fonctionne lors de la création manuelle d'un nouvel objet, par exemple dans un test unitaire. Mais cela ne fonctionnera pas lors du chargement de l'entité à partir du référentiel, car alors les collections seront remplies d'objets Doctrine \ ORM \ PersistentCollection .

Ma question est maintenant, comment puis-je configurer mon application pour que je puisse utiliser une collection persistante personnalisée (par exemple PersistentPostCollection ) lors du chargement d'entités?

J'ai trouvé cette page https://www.doctrine-project.org/projects/doctrine-collections /en/latest/lazy-collections.html#lazy-collections , mais je ne trouve pas comment l'intégrer dans Symfony 4.

Remarque: Le scénario ci-dessus est un exemple simple dans le but de garder cette question courte et simple à aborder. Je suis conscient que tout ce problème peut être évité en utilisant un référentiel pour obtenir les données correctes. Mais ce n'est pas ce que je recherche ici. Cette question concerne la compréhension de ce qui est possible avec les collections Doctrine dans Symfony 4.


1 commentaires

Avez-vous essayé d'utiliser un référentiel au lieu d'étendre ArrayCollection? Par exemple. $ userRepository-> getUserPostsFrom (User $ user, DateTime $ from) qui obtient les messages "créés par" un utilisateur et "publié" arter Date? Pourrait terminer votre action avec une seule demande au lieu de filtrer par la suite ...


3 Réponses :


3
votes

Ce que vous avez trouvé est la documentation du paquet Doctrine Collections.

Doctrine ORM utilise le package Doctrine Collections, mais ce sont des packages séparés les uns des autres. Par conséquent, le fait que vous puissiez créer une collection personnalisée ne signifie pas que ORM respectera cela.

Ce que vous voulez faire n'est pas possible pour le moment avec Doctrine ORM.

La fonctionnalité des collections personnalisées est prévue pour prochaine version majeure de Doctrine ORM .


0 commentaires

2
votes

Votre collection personnalisée doit jouer le rôle de décorateur et vérifier dans votre méthode getPosts () s'il s'agit de votre collection personnalisée.

La PostArrayCollection ressemblera à ceci

class User
{
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Post", mappedBy="post", cascade={"persist"})
     */
    private $posts;

    public function __construct()
    {
        $this->posts = PostArrayCollection::fromItems();
    }

    public function getPosts(): PostCollectionInterface
    {
        if ($this->posts instanceof PersistentCollection) {
            $this->posts = PostArrayCollection::fromDoctrine($this->posts);
        }
        return $this->posts;
    }
}

La classe User ressemblera à ceci.

class PostArrayCollection implements Collection, Selectable, PostCollectionInterface
{
    /**
     * @var ArrayCollection|PersistentCollection
     */
    private $collection;

    public static function fromItems(Post ...$elements)
    {
        return new self(new ArrayCollection($elements));
    }

    public static function fromDoctrine(PersistentCollection $collection)
    {
        return new self($collection);
    }

    private function __construct(Collection $collection)
    {
        $this->collection = $collection;
    }

    public function mapTitles()
    {
        return $this->collection->map(function (Post $post) {
            return $post->getTitle();
        });
    }

    public function filterPublishedAfterDate(DateTime $from)
    {
        return $this->collection->filter(function (Post $post) use ($from) {
            return $post->publishedDate() > $from;
        });
    }

    // TODO implement all other methods and delegate it to $this->collection
}


0 commentaires

0
votes

Excellente solution. Dans mon cas, ce n'est pas une solution complète car j'utilise DDD et je veux rendre le domaine indépendant de Doctrine. J'utilise des collections personnalisées et je ne sais pas comment mapper Doctrine Collection sur mes collections personnalisées


0 commentaires