如何在Symfony 4中使用自定义集合

时间:2019-02-25 13:10:38

标签: php symfony doctrine-orm doctrine symfony4

我想为Symfony 4应用程序使用自定义集合类。以下方案是我要实现的目标的一个示例:

我有一个帖子收集类,该类具有一些用于过滤和映射数据的实用程序。

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

还有一个用户类,它是一个教义实体。

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;
    }
}

然后,在助手或控制器中有一个方法可以访问用户的数据,如下所示:

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

    // Render the titles...
}

例如,在单元测试中,手动创建新对象时,上述方法有效。但是,当从存储库加载实体时,它将不起作用,因为这样集合中将填充Doctrine\ORM\PersistentCollection个对象。

现在的问题是,如何配置应用程序,以便在加载实体时可以使用自定义的持久性集合(例如PersistentPostCollection)?

我确实找到了此页面https://www.doctrine-project.org/projects/doctrine-collections/en/latest/lazy-collections.html#lazy-collections,但找不到如何将其集成到Symfony 4中。

  

注意:上面的场景是一个简单的示例,目的是使这个问题简短易懂。我知道,使用存储库获取正确的数据可以避免整个问题。但这不是我在这里寻找的。这个问题是关于了解Symfony 4中的教义集合的可能性。

3 个答案:

答案 0 :(得分:3)

您找到的是Doctrine Collections软件包的文档。

Doctrine ORM正在使用“ Doctrine Collections”软件包,但它们是彼此独立的软件包。因此,您可以创建自定义集合的事实并不意味着ORM会尊重这一点。

Doctrine ORM现在无法实现您想做的事情。

feature of custom collections计划于next major Doctrine ORM version使用。

答案 1 :(得分:1)

您的自定义集合必须充当装饰者,并检查您的getPosts()方法是否是您的自定义集合。

PostArrayCollection看起来像这样

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
}

User类将如下所示。

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;
    }
}

答案 2 :(得分:0)

优秀的解决方案。就我而言,这不是一个完整的解决方案,因为我使用的是DDD,并且我想使域与Doctrine无关。我正在使用自定义集合,但我不知道如何在我的自定义集合中映射Doctrine Collection