我如何将定制的学说存储库绑定到 symfony 中的映射实体?

How can I tie bespoke doctrine repositories to mapped entities in symfony?

我有两个实体。任务和部门。

一个部门可以有很多任务。

/**
 * @ORM\Entity
 * @ORM\Table(name="departments")
 */
class Department
{
    /**
     * @ORM\OneToMany(targetEntity="Task", mappedBy="department")
     */
    public $tasks;

    /**
     * @return ArrayCollection|Task[]
     */
    public function getTasks()
    {
        return $this->tasks;
    }
}

.

/**
 * @ORM\Entity
 * @ORM\Table(name="tasks")
 */
class Task
{
    /**
     * @ORM\ManyToOne(targetEntity="Department", inversedBy="tasks")
     */
    public $department;

    /**
     * @return Department
     */
    public function getDepartment()
    {
        return $this->department;
    }
}

我还有一个显示所有部门及其相关任务的视图。

{% for department in departments %}
    {% for task in department.tasks %}
        ...
    {% endfor %}
{% endfor %}

然后这给了我几列,每列都填满了任务。

我现在想更改此设置,以便每列仅显示按截止日期排序的最紧迫的任务(本周内到期)。

为此,我创建了带有自定义查询的 TaskRepository class:

class TaskRepository extends EntityRepository
{
    /**
     * @param Department $department
     * @return Task[]
     */
    public function findAllRecentTasksForGenus(Department $department)
    {
        return $this->createQueryBuilder('task')
            ->andWhere('task.department = :department')
            ->setParameter('department', $department)
            ->andWhere('task.dueDate> :dueDate')
            ->setParameter('dueDate', new \DateTime('-1 week'))
            ->orderBy('task.dueDate', 'DESC')
            ->getQuery()
            ->execute();
    }
}

现在,如果这是一个只有一个部门的页面,我只需在我的控制器中调用自定义查询,然后将其拉入视图。

然而,我向所有部门展示了他们所有迫在眉睫的任务,这让控制器有点不整洁,因为这会迫使我建立一个部门及其任务的数组。

考虑到目前我只需要在 twig 中循环 department.tasks 并且它知道提取与部门实体相关的任务,这感觉很难看。

我也知道我不应该(也不能轻易)在我的部门实体的存储库中调用自定义查询。

所以我想知道的是 - 我能否以某种方式将定制查询绑定到相关实体,以便原则知道当我向部门请求任务时,它必须去而不是得到除定义的子集?

理想情况下,我希望能够通过调用 tasksDueSoon 之类的东西来绕过如下所示的树枝模板,而不必向控制器(或其他地方)添加额外的逻辑负载.

{% for department in departments %}
    {% for task in department.tasksDueSoon %}
        ...
    {% endfor %}
{% endfor %}

如果那不可能,我必须通过控制器沿着自定义数组的路径走下去,这当然很好,但感觉不对,所以希望在 Syomfony 中有更好的方法来做到这一点,并告诉部门实体使用任务存储库或其他东西。

部门 class 中有一个关于你的任务 属性 的 @OrderBy-annotation,但据我所知,你不能将其设为更复杂的查询。

我通常做的是创建另一个 getter,它使用 Collection Criteria 过滤掉我想要的 children。

当然这有一些开销,因为它对从存储库中获取的所有任务进行操作,但我发现在大多数情况下,性能对于我通常操作的规模来说仍然非常好。当它变慢时我通常会退回到您不喜欢的当前解决方案,甚至更进一步,并在必要时使用 NativeQuery and/or 部分结果。

我只想在 Department 实体中创建一个方法,getTasksOrderedByDueDate() 之类的。可能比 SQL 查询的性能稍差,但对我来说似乎更干净。

public function getTasksOrderedByDueDate(): array
{
    $tasks = $this->getTasks();
    usort($tasks, function (Task $a, Task $b) {
        return $a->getDueDate()->getTimestamp() - $b->getDueDate()->getTimestamp();
    });

    return $tasks;
}

并且只需在 Twig 模板中使用它:

{% for department in departments %}
    {% for task in department.tasksOrderedByDueDate %}
        ...
    {% endfor %}
{% endfor %}

现在除非它成为一个真正的问题,否则不要担心性能。

我总是在不测试实际代码的情况下犹豫 post 一个答案,但这里是。诀窍是在加载部门的同时加载最近的任务。

class DepartmentRepository
{
    findDepartmentsAlongWithRecentTasks()
    {
        $qb = $this->createQueryBuilder('dept');
        $qb->select('dept','task');
        $qb->leftJoin('dept.tasks','task');

        $qb->andWhere('task.dueDate > :dueDate')
           ->setParameter('dueDate', new \DateTime('-1 week'))
           ->orderBy('task.dueDate', 'DESC');

        return $qb->getQuery()->execute();