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

时间:2014-08-13 10:42:13

标签: php symfony doctrine event-listener

我们已经设置了一个事件监听器,可以在实体更新,删除或保留时触发Flash消息,但我们无法管理的是如何处理错误。

以下是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())
            )
        );
    }
}

我们要做的是清理控制器并将所有错误消息移动到事件监听器中。例如,这是我们的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);
}

1 个答案:

答案 0 :(得分:2)

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

AcmeBundle /资源/配置/ services.yml

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

AcmeBundle /事件监听/ 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)函数来处理未找到的实体上的消息,但是为了使其工作,我们仍然必须从控制器抛出异常,传递已翻译的消息...基本上我们仍然需要重复一大堆代码,所以我们只回到最初的解决方案;也就是说,直接从控制器的操作管理Flash消息:

...

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

...