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

时间:2015-04-09 08:44:53

标签: symfony doctrine-orm

在Symfony2项目中,我有一个具有datetime字段的Doctrine实体,名为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字段。我怎么能这样做?

3 个答案:

答案 0 :(得分:2)

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

您当然可以将其放入正确的存储库类

答案 1 :(得分:2)

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

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

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

答案 2 :(得分: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禁用。另外,自定义行为很容易根据实体状态应用。