Last modification timestamp

PHPCR provides mixin types with special meaning. This cookbook is about mix:created and mix:lastModified.

Using the build-in support

If the PHPCR implementation you use supports the mixins automatically, you can get timestamps on your documents by simply adding the mixins:

  • PHP
    1use Doctrine\ODM\PHPCR\Mapping\Attributes as PHPCR; #[PHPCR\Document(mixins: ["mix:created", "mix:lastModified"])] class SomeDocument { #[PHPCR\Field(type: 'date', property: 'jcr:created')] private ?\DateTimeInterface $created; #[PHPCR\Field(type: 'string', property: 'jcr:createdBy')] private ?string $createdBy; #[PHPCR\Field(type: 'date', property: 'jcr:lastModified')] private ?\DateTimeInterface $lastModified; #[PHPCR\Field(type: 'string', property: 'jcr:lastModifiedBy')] private ?string $lastModifiedBy; }
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • XML
    1<doctrine-mapping> <document name="SomeDocument"> <field fieldName="created" property="jcr:created" /> <field fieldName="createdBy" property="jcr:createdBy" /> <field fieldName="lastModified" type="jcr:lastModified" /> <field fieldName="lastModifiedBy" type="jcr:lastModifiedBy" /> </document> </doctrine-mapping>
    2
    3
    4
    5
    6
    7
    8
  • YAML
    1SomeDocument: fields: created: property: "jcr:created" createdBy: property: "jcr:createdBy" lastModified: property: "jcr:lastModified" lastModifiedBy: property: "jcr:lastModifiedBy"
    2
    3
    4
    5
    6
    7
    8
    9
    10

After flushing this document, these fields will be populated. The creation date of course will never change, but the last modified date will be updated whenever you change properties on the document.

The createdBy and lastModifiedBy fields contain the user name used for the PHPCR backend connection. This is most likely a static name coming from your configuration.

You can also set lastModified or lastModifiedBy manually with your update to get your custom values.

Support for automatically updating mix:lastModified documents was added to Jackalope in version 1.1. If you are using 1.0, you need to use the manual method explained in the next section to get automated last modification updates.

A lastModified listener for custom behaviour

To customize the lastModified logic, you can build a listener to manually set the properties as needed. If you do this, you want to disable automated lastModified support in your PHPCR implementation.

For Jackalope, you can set the jackalope.auto_lastmodified option to false in the parameters to RepositoryFactory::getRepository. This will only affect mix:lastModified but not mix:created.

The document looks exactly the same as above. To update the modification date, write an event listener as follows and register it with the EventManager:

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ODM\PHPCR\DocumentManager;

/**
 * This listener ensures that the jcr:lastModified property is updated
 * on prePersist and preUpdate events.
 *
 * @author Thierry Marianne <[email protected]>
 */
class LastModified
{
    public function prePersist(LifecycleEventArgs $e): void
    {
        $this->updateLastModifiedProperty($e);
    }

    public function preUpdate(LifecycleEventArgs $e): void
    {
        $this->updateLastModifiedProperty($e);
    }

    /**
     * If the document has the mixin mix:lastModified then update the field
     * that is mapped to jcr:lastModified.
     */
    private function updateLastModifiedProperty(LifecycleEventArgs $e): void
    {
        $document = $e->getObject();

        /**
         * @var DocumentManager $dm
         */
        $dm = $e->getObjectManager();

        $metadata = $dm->getClassMetadata(get_class($document));
        $mixins = $metadata->getMixins();

        if (!in_array('mix:lastModified', $mixins)) {
            return;
        }

        // custom logic to determine if we need to update the lastModified date goes here.
        // ...

        // look for the field mapped to jcr:lastModified and update
        foreach ($metadata->getFieldNames() as $fieldName) {
            $field = $metadata->getField($fieldName);
            if ('jcr:lastModified' == $field['property']) {
                $metadata->setFieldValue($document, $fieldName, new \DateTime());
                break;
            }
        }
    }
}

If you need to update lastModifiedBy, follow the same pattern.