禁用Doctrine Timestampable自动更新某个更新的`updatedAt`字段

VMC*_*VMC 7 symfony doctrine-orm

在Symfony2项目中,我有一个Doctrine实体,它有一个datetime叫做的字段lastAccessed.此外,实体在字段上使用TimestampableupdatedAt.

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;

}
Run Code Online (Sandbox Code Playgroud)

我需要在不更新字段的lastAccessed情况下更新updatedAt字段.我怎样才能做到这一点?

Luk*_*ous 6

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

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

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


zaj*_*jca 5

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

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

interface TimestampableCancelInterface
{

   public function isTimestampableCanceled(): bool;

}
Run Code Online (Sandbox Code Playgroud)

比扩展 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();
    }

}
Run Code Online (Sandbox Code Playgroud)

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

private $isTimestampableCanceled = false;

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

public function isTimestampableCanceled():bool {
    return $this->isTimestampableCanceled;
}
Run Code Online (Sandbox Code Playgroud)

或者定义你想要的规则

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

stof_doctrine_extensions:
    orm:
        default:
            timestampable: true
    class:
      timestampable:  <Namespace>\TimestampableListener
Run Code Online (Sandbox Code Playgroud)

比你能做的

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

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


小智 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);
Run Code Online (Sandbox Code Playgroud)

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

  • 有没有更清洁的解决方案?如果能够轻松禁用自动更新功能就好了。 (2认同)