php - symfony2 flash 消息作为事件监听器来处理错误

标签 php symfony doctrine event-listener

我们已经设置了一个事件监听器,当实体更新、删除或持久化时,它会触发闪现消息,但我们无法完全管理的是如何处理错误。

这是来自services.yml的相关代码

flash_messages:
    class: Acme\AcmeBundle\EventListener\FlashMessages
    tags:
        - { name: doctrine.event_listener, event: postUpdate }
        - { name: doctrine.event_listener, event: postRemove }
        - { name: doctrine.event_listener, event: postPersist }
    arguments: [ @session, @translator ]

这是Acme/AcmeBundle/EventListener/FlashMessages.php

中的监听器
namespace Acme\AcmeBundle\EventListener;

use
    Doctrine\ORM\Event\LifecycleEventArgs,
    Symfony\Component\HttpFoundation\Session\Session,
    Symfony\Component\Translation\TranslatorInterface
;

class FlashMessages
{
    private $session;
    protected $translator;

    public function __construct(Session $session, TranslatorInterface $translator)
    {
        $this->session = $session;
        $this->translator = $translator;
    }

    public function postUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        $this->session->getFlashBag()->add(
            'success',
            $this->translator->trans(
                '%name% entity.write.success',
                array('%name%' => $entity->getClassName())
            )
        );
    }

    public function postRemove(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        $this->session->getFlashBag()->add(
            'success',
            $this->translator->trans(
                '%name% entity.delete.success',
                array('%name%' => $entity->getClassName())
            )
        );
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        $this->session->getFlashBag()->add(
            'success',
            $this->translator->trans(
                '%name% entity.create.success',
                array('%name%' => $entity->getClassName())
            )
        );
    }
}

我们正在尝试做的是清理 Controller 并将所有错误消息移至事件监听器中。例如,这是我们的 DeliveryController.php:

/**
 * Finds and displays a Delivery entity.
 *
 * @Route("/{id}", name="delivery_show")
 * @Method("GET")
 * @Template()
 */
public function showAction($id)
{
    $em = $this->getDoctrine()->getManager();

    $entity = $em->getRepository('AcmeBundle:Delivery')->find($id);

    if (!$entity) {

        /**
         * @todo move this code to eventListener
         */
        $entity = new Delivery();

        $this->get('session')->getFlashBag()->add(
            'danger',
            $this->get('translator')->trans(
                '%name% entity.find.fail',
                array('%name%' => $entity->getClassName())
            )
        );
        // end @todo

        return new RedirectResponse($this->generateUrl('delivery'));
    }

    return array(
        'entity'      => $entity,
        'menu_tree' => $this->menu_tree,
    );
}

像这样,理想情况下我们希望在实体无法创建、持久化和删除时也处理错误。

仅供引用,翻译由 Acme/AcmeBundle/Resources/translations/messages.en.yml

%name% entity.create.fail: There wes an error creating the %name%. Please try again later.
%name% entity.create.success: %name% created successfully.
%name% entity.write.fail: There wes an error saving the %name%. Please try again later.
%name% entity.write.success: %name% saved successfully.
%name% entity.delete.fail: There wes an error deleting the %name%. Please try again later.
%name% entity.delete.success: %name% deleted successfully.
%name% entity.find.fail: %name% not found.

并且 $entity->getClassName() 位于每个实体中,如下所示:

private $className;

/**
 * Get class name
 * @return string
 */
public function getClassName()
{
    $entity = explode('\\', get_class($this));
    return end($entity);
}

最佳答案

我们最终改变了 FlashMessages 的工作方式,因为我们遇到了实体链更新和显示太多 Flash 消息的问题。通过以下方式执行 int 可以让我们停止链式效应。我们从 postUpdate()postUpdate()postPersist() 转变为使用单个 onFlush()。请参阅下面的代码:

AcmeBundle/Resources/config/services.yml

flash_messages:
    class: Acme\AcmeBundle\EventListener\FlashMessages
    tags:
        - { name: doctrine.event_listener, event: onFlush }
    arguments: [ @session, @translator, @service_container ]

AcmeBundle/EventListener/FlashMessages.php

<?php
namespace Acme\AcmeBundle\EventListener;

use
    Symfony\Component\HttpFoundation\Session\Session,
    Symfony\Component\Translation\TranslatorInterface,
    Doctrine\ORM\Event\OnFlushEventArgs,
    Acme\AcmeBundle\Entity\DeliveryItem
;

class FlashMessages
{
    private $session;
    protected $translator;

    public function __construct(Session $session, TranslatorInterface $translator)
    {
        $this->session = $session;
        $this->translator = $translator;
    }

    public function onFlush(OnFlushEventArgs $args)
    {
        $this->em = $args->getEntityManager();
        $uow = $this->em->getUnitOfWork();
        $insert = current($uow->getScheduledEntityInsertions());
        $update = current($uow->getScheduledEntityUpdates());
        $delete = current($uow->getScheduledEntityDeletions());

        // Don't show messages when updating individual DeliveryItem and StockHistory
        if ($insert instanceof DeliveryItem ||
            $update instanceof DeliveryItem ||
            $delete instanceof DeliveryItem) {

            return false;
        }

        // Flash message on insert
        if ($uow->getScheduledEntityInsertions()) {
            $this->session->getFlashBag()->add(
                'success',
                $this->translator->trans(
                    '%name% entity.create.success',
                    array('%name%' => $insert->getClassName())
                )
            );
        }

        // Flash message on update
        if ($uow->getScheduledEntityUpdates()) {
            $this->session->getFlashBag()->add(
                'success',
                $this->translator->trans(
                    '%name% entity.write.success',
                    array('%name%' => $update->getClassName())
                )
            );
        }

        // Flash message on delete
        if ($uow->getScheduledEntityDeletions()) {
            $this->session->getFlashBag()->add(
                'success',
                $this->translator->trans(
                    '%name% entity.delete.success',
                    array('%name%' => $delete->getClassName())
                )
            );
        }
    }
}

我们尝试设置一个 onKernelException(GetResponseForExceptionEvent $event) 函数来处理未找到的实体上的消息,但为了使其正常工作,我们仍然必须从 Controller 抛出异常,传递翻译后的消息...基本上我们仍然需要重复一大块代码,所以我们只是回滚到原始解决方案;也就是说,直接从 Controller 的操作中管理闪存消息:

...

if (!$entity) {

    $entity = new Delivery();

    $this->get('session')->getFlashBag()->add(
        'danger',
        $this->get('translator')->trans(
            '%name% entity.find.fail',
            array('%name%' => $entity->getClassName())
        )
    );

    return new RedirectResponse($this->generateUrl('delivery'));
}

...

关于php - symfony2 flash 消息作为事件监听器来处理错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25284069/

相关文章:

php - 扩展Magento的核心类时,我需要更改哪种XML结构才能使更改生效?

php - 没有日期的 jQuery 计时器

php - 如果 id 为 ='3',则始终位于底部(在 MySQL 中使用 `ORDER BY`)

http - 响应中格式错误的 block

symfony - Doctrine Timestampable 扩展不持久

php - 如何使用 Doctrine (Symfony 4)将数据从mysql数据库获取到数据表中?

php - Doctrine GeneratedValue=NONE 不断为 Id 插入 NULL

php - Symfony 2 : INNER JOIN on non related table with doctrine query builder

php - 是否有可以使用 PHP 编写 RFC 4180 CSV 文件的库?

symfony - Sonata Admin 列表自定义查询 ListMapper sonata_type_model 字段