Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable Doctrine Timestampable auto-updating the `updatedAt` field on certain update

In a Symfony2 project, I have a Doctrine entity that has a datetime field, called lastAccessed. Also, the entity uses Timestampable on updatedAt field.

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;

}

I need to update the field lastAccessed without also updating the updatedAt field. How can I do that?

like image 266
VMC Avatar asked Apr 09 '15 08:04

VMC


Video Answer


2 Answers

There is also an easy way (or more proper way) how to do it, just override listener.

first create interface which will be implemented by entity

interface TimestampableCancelInterface
{

   public function isTimestampableCanceled(): bool;

}

than extend Timestampable listener and override updateField. this way we can disable all all events or with cancelTimestampable define custom rules for cancellation based on entity state.

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

}

implement interface. Most simple way is to just set property for this

private $isTimestampableCanceled = false;

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

public function isTimestampableCanceled():bool {
    return $this->isTimestampableCanceled;
}

or define rules like you want

last thing is to not set default listener but ours. I'm using symfony so:

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

Than you can just do

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

This way can be timestamps disabled per single entity not for whole onFlush. Also custom behavior is easy to apply based on entity state.

like image 130
zajca Avatar answered Oct 27 '22 06:10

zajca


I just stumbled upon this as well and came up with this solution:

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

Something very similar can be used to disable the blamable behavior as well of course.

like image 22
Luksurious Avatar answered Oct 27 '22 06:10

Luksurious