【问题标题】:Disable Doctrine Timestampable auto-updating the `updatedAt` field on certain update在某些更新时禁用 Doctrine Timestampable 自动更新 `updatedAt` 字段
【发布时间】:2018-12-07 17:50:07
【问题描述】:

在 Symfony2 项目中,我有一个 Doctrine 实体,它有一个 datetime 字段,称为 lastAccessed。此外,实体在updatedAt 字段上使用Timestampable

namespace AppBundle\Entity;

use
    Doctrine\ORM\Mapping as ORM,
    Gedmo\Mapping\Annotation as Gedmo
;

class MyEntity {

    /**
     * @ORM\Column(type="datetime")
     */
    private $lastAccessed;

    /**
     * @ORM\Column(type="datetime")
     * @Gedmo\Timestampable(on="update")
     */
    private $updatedAt;

}

我需要更新字段 lastAccessed 而不更新 updatedAt 字段。我该怎么做?

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    还有一种简单的方法(或更正确的方法)如何做到这一点,只需覆盖侦听器。

    首先创建将由实体实现的接口

    interface TimestampableCancelInterface
    {
    
       public function isTimestampableCanceled(): bool;
    
    }
    

    扩展 Timestampable 侦听器并覆盖 updateField。 这样我们就可以禁用所有事件,或者使用cancelTimestampable根据实体状态定义自定义取消规则。

    class TimestampableListener extends \Gedmo\Timestampable\TimestampableListener
    {
    
        protected function updateField($object, $eventAdapter, $meta, $field)
        {
            /** @var \Doctrine\Orm\Mapping\ClassMetadata $meta */
            $property = $meta->getReflectionProperty($field);
            $newValue = $this->getFieldValue($meta, $field, $eventAdapter);
    
            if (!$this->isTimestampableCanceled($object)) {
                $property->setValue($object, $newValue);
            }
        }
    
        private function isTimestampableCanceled($object): bool
        {
            if(!$object instanceof TimestampableCancelInterface){
                return false;
            }
    
            return $object->isTimestampableCanceled();
        }
    
    }
    

    实现接口。最简单的方法是为此设置属性

    private $isTimestampableCanceled = false;
    
    public function cancelTimestampable(bool $cancel = true): void
    {
        $this->isTimestampableCanceled = $cancel;
    }
    
    public function isTimestampableCanceled():bool {
        return $this->isTimestampableCanceled;
    }
    

    或根据需要定义规则

    最后一件事是不设置默认侦听器,而是设置我们的侦听器。 我正在使用 symfony:

    stof_doctrine_extensions:
        orm:
            default:
                timestampable: true
        class:
          timestampable:  <Namespace>\TimestampableListener
    

    你可以做到的

    $entity = new Entity;
    $entity->cancelTimestampable(true)
    $em->persist($entity);
    $em->flush(); // and you will get constraint violation since createdAt is not null :D
    

    这种方式可以禁用单个实体的时间戳,而不是整个 onFlush。此外,自定义行为也很容易根据实体状态应用。

    【讨论】:

      【解决方案2】:

      我也偶然发现了这个并想出了这个解决方案:

      public function disableTimestampable()
      {
          $eventManager = $this->getEntityManager()->getEventManager();
          foreach ($eventManager->getListeners('onFlush') as $listener) {
              if ($listener instanceof \Gedmo\Timestampable\TimestampableListener) {
                  $eventManager->removeEventSubscriber($listener);
                  break;
              }
          }
      }
      

      当然,也可以使用非常相似的东西来禁用可指责的行为。

      【讨论】:

        【解决方案3】:

        Timestampable 只是教义行为,因此每次使用 ORM 时都会执行。 在我看来,最简单的方法就是使用 DBAL 层和原始 sql 查询。

        例如:

        $sql = "UPDATE my_table set last_accessed = :lastAccess where id = :id";
        //set parameters 
        $params['lastAccess'] = new \DateTime();
        $params['id'] = $some_id;
        $stmt = $this->entityManager->getConnection()->prepare($sql);
        $stmt->execute($params);
        

        您当然可以将其放入适当的存储库类中

        【讨论】:

        • 没有更清洁的解决方案吗?能够轻松禁用自动更新功能会很好..
        猜你喜欢
        • 1970-01-01
        • 2011-03-26
        • 2015-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-09
        • 1970-01-01
        相关资源
        最近更新 更多