如何自动保护学说实体上的“索引”列的完整性?

时间:2019-05-06 15:34:53

标签: doctrine-orm

我知道Doctrine的扩展可以管理树/嵌套集的行为,但这似乎对我想要的功能来说是非常过分的。

我只是有一个名为Faq的模型,其中包含字段questionanswernumbercreatedAtupdatedAtnumber列用于编辑问题在页面上的显示顺序。

我正在使用EasyAdminBundle提供一个简单的管理面板,供我的客户编辑FAQ。

现在是这样,假设有5个问题,客户希望将第5个问题设为第3个问题。我想要的是,他只需用值number编辑第五个问题的3字段,其他实体的所有其他number字段都会自动适应此更改。所以3和4现在分别变成4和5。

我假设我需要某种事件侦听器,但我不太清楚哪种类型。

到目前为止,我只知道一旦有了正确的事件侦听器,我就应该在执行它时执行此操作:

function updateNumbers(EntityManagerInterface $em)
{
    $faqRepo = $em->getRepository(Faq::class);
    $faqs = $faqRepo->findAll();
    // ^ that is already correctly sorted, based on number and updatedAt

    foreach($faqs as $i => $faq) {
        $faq->setNumber($i+1);
    }

    $em->flush();
}

现在,我只需要知道如何确保在正确的时刻触发该功能即可。有帮助吗?

2 个答案:

答案 0 :(得分:0)

使用您编写的代码,您甚至可能甚至应该使用postFlush,但是请注意,从事件监听器调用$em->flush()会再次触发事件,因此您需要一个递归保护器。您可能还想编写其他逻辑,仅当某些职位的位置确实发生变化时才运行重新索引逻辑。

对于稍微简单的方法,您可以加入preUpdate事件。当某个条目的number发生更改时,您将触发一个数据库查询,该查询将更新number大于或等于新值的所有条目,并将它们的number增加1。

答案 1 :(得分:0)

好的,我通过执行以下操作解决了该问题:

// src/EventListener/FaqSorter.php

namespace App\EventListener;

use App\Entity\Faq;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\FaqRepository;
use Doctrine\ORM\Event\LifecycleEventArgs;

class FaqSorter
{
    /**
     * @ORM\PrePersist
     * @ORM\PostUpdate
     * @ORM\PostRemove
     */
    function updateNumbers(Faq $faq, LifecycleEventArgs $event)
    {
        static $hasRun = false;

        // This is not the prettiest solution to prevent recurrence,
        // but AFAIK I never need to run this more than once per request.
        if ($hasRun) return;

        $em = $event->getEntityManager();

        /** @var FaqRepository $faqRepo */
        $faqRepo = $em->getRepository(get_class($faq));
        $faqs = $faqRepo->findAll();
        // ^ that is already correctly sorted, based on number and updatedAt

        if ($faq->getNumber() === null) {
            // If the number was null then this was triggered
            // by a PrePersist event and then I'll just
            // put it at the end of the list.
            return $faq->setNumber(count($faqs) + 1);
        }

        foreach($faqs as $i => $faq) {
            $faq->setNumber($i+1);
        }

        $hasRun = true;

        $em->flush();
    }
}

然后在config/services.yaml中输入:

services:
    # ...

    App\EventListener\FaqSorter:
        tags:
            - { name: doctrine.orm.entity_listener }

src/Entity/Faq.php中:

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\FaqRepository")
 * @ORM\EntityListeners({"App\EventListener\FaqSorter"})
 */
class Faq {
    // ...
}