【问题标题】:Doctrine blameable extension 'on change' doesn't work教义可责备的扩展“关于改变”不起作用
【发布时间】:2015-04-01 06:00:15
【问题描述】:

我在 symfony 2.6.3 上使用 stof Doctrine 扩展。

TimeStampable 和 SoftDeletable 运行良好。

“on create”和“on update”也可归咎于:

/**
 * @var User $createdBy
 *
 * @Gedmo\Blameable(on="create")
 * @ORM\ManyToOne(targetEntity="my\TestBundle\Entity\User")
 * @ORM\JoinColumn(name="createdBy", referencedColumnName="id")
 */
protected $createdBy;

/**
 * @var User $updatedBy
 *
 * @Gedmo\Blameable(on="update")
 * @ORM\ManyToOne(targetEntity="my\TestBundle\Entity\User")
 * @ORM\JoinColumn(name="updatedBy", referencedColumnName="id")
 */
protected $updatedBy;

但是“on change”似乎不起作用。

/**
 * @var User $deletedBy
 *
 * @Gedmo\Blameable(on="change", field="deletedAt")
 * @ORM\ManyToOne(targetEntity="my\UserBundle\Entity\User")
 * @ORM\JoinColumn(name="deletedBy", referencedColumnName="id")
 */
protected $deletedBy;

我在“deletedAt”字段上配置了 SoftDeletable。 SoftDeletable 工作正常,但 deletedBy 永远不会被填充。

我怎样才能让它发挥作用?我只想设置删除实体的用户ID。

【问题讨论】:

    标签: php symfony onchange stofdoctrineextensions


    【解决方案1】:

    这是我的解决方案,我使用 preSoftDelete 事件:

    app.event.entity_delete:
        class: AppBundle\EventListener\EntityDeleteListener
        arguments:
            - @security.token_storage
        tags:
            - { name: doctrine.event_listener, event: preSoftDelete, connection: default }
    

    和服务:

    <?php
    
    namespace AppBundle\EventListener;
    
    use Doctrine\ORM\Event\LifecycleEventArgs;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
    use Symfony\Component\Security\Core\Exception\AccessDeniedException;
    
    class EntityDeleteListener
    {
        /**
         * @var TokenStorageInterface
         */
        private $tokenStorage;
    
        public function __construct(TokenStorageInterface $tokenStorage)
        {
            $this->tokenStorage = $tokenStorage;
        }
    
        public function preSoftDelete(LifecycleEventArgs $args)
        {
            $token  = $this->tokenStorage->getToken();
            $object = $args->getEntity();
            $om     = $args->getEntityManager();
            $uow    = $om->getUnitOfWork();
    
            if (!method_exists($object, 'setDeletedBy')) {
                return;
            }
    
            if (null == $token) {
                throw new AccessDeniedException('Only authorized users can delete entities');
            }
    
            $meta = $om->getClassMetadata(get_class($object));
            $reflProp = $meta->getReflectionProperty('deletedBy');
            $oldValue = $reflProp->getValue($object);
            $reflProp->setValue($object, $token->getUser()->getUsername());
    
            $om->persist($object);
            $uow->propertyChanged($object, 'deletedBy', $oldValue, $token->getUser()->getUsername());
            $uow->scheduleExtraUpdate($object, array(
                'deletedBy' => array($oldValue, $token->getUser()->getUsername()),
            ));
        }
    }
    

    这不是一致性,因为我检查了 setDeletedBy 方法是否存在并设置了 deletedBy 属性,但它对我有用,您可以根据需要升级此代码

    【讨论】:

      【解决方案2】:

      这是我找到的另一个解决方案:

      注册服务:

      softdeleteable.listener:
        class: AppBundle\EventListener\SoftDeleteableListener
        arguments:
          - '@security.token_storage'
        tags:
          - { name: doctrine.event_listener, event: preFlush, method: preFlush }
      

      SoftDeleteableListener:

      /**
       * @var TokenStorageInterface|null
       */
      private $tokenStorage;
      
      /**
       * DoctrineListener constructor.
       *
       * @param TokenStorageInterface|null $tokenStorage
       */
      public function __construct(TokenStorageInterface $tokenStorage)
      {
          $this->tokenStorage = $tokenStorage;
      }
      
      /**
       * @param PreFlushEventArgs $event
       */
      public function preFlush(PreFlushEventArgs $event)
      {
          $user = $this->getUser();
          $em   = $event->getEntityManager();
      
          foreach ($em->getUnitOfWork()->getScheduledEntityDeletions() as $object) {
              /** @var SoftDeleteableEntity|BlameableEntity $object */
              if (method_exists($object, 'getDeletedBy') && $user instanceof User) {
                  $object->setDeletedBy($user);
                  $em->merge($object);
      
                  // Persist and Flush allready managed by other doctrine extensions.
              }
          }
      }
      
      /**
       * @return User|void
       */
      public function getUser()
      {
          if (!$this->tokenStorage || !$this->tokenStorage instanceof TokenStorageInterface) {
              throw new \LogicException('The SecurityBundle is not registered in your application.');
          }
      
          $token = $this->tokenStorage->getToken();
          if (!$token) {
              /** @noinspection PhpInconsistentReturnPointsInspection */
              return;
          }
      
          $user = $token->getUser();
          if (!$user instanceof User) {
              /** @noinspection PhpInconsistentReturnPointsInspection */
              return;
          }
      
          return $user;
      }
      

      【讨论】:

        【解决方案3】:

        这是我的解决方案:

        mybundle.soft_delete:
         class: Listener\SoftDeleteListener
         arguments:
            - @security.token_storage
         tags:
            - { name: doctrine_mongodb.odm.event_listener, event: preSoftDelete }
        
        class SoftDeleteListener
        {
        /**
         * @var TokenStorageInterface
         */
        private $tokenStorage;
        
        public function __construct(TokenStorageInterface $tokenStorage)
        {
            $this->tokenStorage = $tokenStorage;
        }
        
        /**
         * Method called before "soft delete" system happened.
         *
         * @param LifecycleEventArgs $lifeCycleEvent Event details.
         */
        public function preSoftDelete(LifecycleEventArgs $lifeCycleEvent)
        {
            $document = $lifeCycleEvent->getDocument();
            if ($document instanceof SoftDeletedByInterface) {
                $token = $this->tokenStorage->getToken();
        
                if (is_object($token)) {
                    $oldValue = $document->getDeletedBy();
                    $user = $token->getUser();
        
                    $document->setDeletedBy($user);
                    $uow = $lifeCycleEvent->getObjectManager()->getUnitOfWork();
                    $uow->propertyChanged($document, 'deletedBy', $oldValue, $user);
                    $uow->scheduleExtraUpdate($document, array('deletedBy' => array($oldValue, $user)));
                }
            }
        }
        

        }

        【讨论】:

          【解决方案4】:

          问题是你想在调用 remove 方法时更新实体(设置用户)。

          目前可能没有完美的解决方案来注册使用 Softdeleteable + Blameable 扩展程序软删除对象的用户。

          有些想法可能是覆盖 SoftDeleteableListener (https://github.com/Atlantic18/DoctrineExtensions/blob/master/lib/Gedmo/SoftDeleteable/SoftDeleteableListener.php),但我在这样做时遇到了问题。

          我目前的工作解决方案是使用实体侦听器解析器。

          MyEntity.php

          /**
          * @ORM\EntityListeners({„Acme\MyBundle\Entity\Listener\MyEntityListener" })
          */
          
          class MyEntity {
          
          /**
          * @ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User")
          * @ORM\JoinColumn(name="deleted_by", referencedColumnName="id")
          */
          private $deletedBy;
          
          public function getDeletedBy()
          {
              return $this->deletedBy;
          }
          
          public function setDeletedBy($deletedBy)
          {
              $this->deletedBy = $deletedBy;
          }
          

          MyEntityListener.php

          use Doctrine\ORM\Event\LifecycleEventArgs;
          use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
          use Acme\MyBundle\Entity\MyEntity;
          
          class MyEntityListener
          {
          /**
           * @var TokenStorageInterface
           */
          private $token_storage;
          
          public function __construct(TokenStorageInterface $token_storage)
          {
              $this->token_storage = $token_storage;
          }
          
          public function preRemove(MyEntity $myentity, LifecycleEventArgs $event)
          {
              $token = $this->token_storage->getToken();
              if (null !== $token) {
                  $entityManager = $event->getObjectManager();
                  $myentity->setDeletedBy($token->getUser());
                  $entityManager->persist($myentity);
                  $entityManager->flush();
              }
          }
          
          }
          

          这里的一个缺陷是调用flush方法。

          注册服务:

          services:
              myentity.listener.resolver:
                  class: Acme\MyBundle\Entity\Listener\MyEntityListener
                  arguments:
                      - @security.token_storage
                  tags:
                      - { name: doctrine.orm.entity_listener, event: preRemove }
          

          更新 composer.json 中的学说/学说包:

          "doctrine/doctrine-bundle": "1.3.x-dev"
          

          如果您有任何其他解决方案,尤其是有关 SoftDeleteableListener 的问题,请在此处发布。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-06
            • 2016-02-28
            • 2018-10-26
            • 2018-10-01
            • 2021-08-02
            • 1970-01-01
            相关资源
            最近更新 更多