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

3
我希望在我的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;
        });
    }
}

还有用户类,它是一个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;
    }
}

然后在帮助器或控制器中有一种方法,可以像下面这样访问用户的数据:
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中使用Doctrine集合的可能性。

你尝试过使用 Repository 而不是扩展 ArrayCollection 吗?例如 $userRepository->getUserPostsFrom(User $user, DateTime $from),它可以获取一个用户创建并在某个日期之后发布的帖子。这样做可以通过单个请求完成你的操作,而无需事后过滤... - rkeet
3个回答

4
您找到的是Doctrine Collections包的文档。
Doctrine ORM正在使用Doctrine Collections包,但它们是彼此独立的包。因此,您可以创建自定义集合并不意味着ORM会将其视为有效。
目前,您想要做的是不可能的,无法在Doctrine ORM中实现。
自定义集合的功能计划在下一个主要版本的Doctrine ORM中提供。详情请参见自定义集合特性Doctrine ORM项目计划

2

你的自定义集合必须充当装饰器,并在getPosts()方法中检查它是否是你的自定义集合。

PostArrayCollection应该长这样

最初的回答:

你的自定义集合应该作为一个装饰器,然后在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;
    }
}

1
优秀的解决方案。在我的情况下,这不是一个完整的解决方案,因为我正在使用DDD,并且希望使域与Doctrine无关。我正在使用自定义集合,但我不知道如何将Doctrine Collection映射到我的自定义集合中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接