diff --git a/doc/timestampable.md b/doc/timestampable.md
index b20a903577..79b3638233 100644
--- a/doc/timestampable.md
+++ b/doc/timestampable.md
@@ -1,674 +1,273 @@
-# Timestampable behavior extension for Doctrine
+# Timestampable Behavior Extension for Doctrine
-**Timestampable** behavior will automate the update of date fields
-on your Entities or Documents. It works through annotations and can update
-fields on creation, update, property subset update, or even on specific property value change.
+The **Timestampable** behavior automates the update of timestamps on your Doctrine objects.
-Features:
+## Index
-- Automatic predefined date field update on creation, update, property subset update, and even on record property changes
-- ORM and ODM support using same listener
-- Specific annotations for properties, and no interface required
-- Can react to specific property or relation changes to specific value
-- Can be nested with other behaviors
-- Attribute, Annotation and Xml mapping support for extensions
+- [Getting Started](#getting-started)
+- [Configuring Timestampable Objects](#configuring-timestampable-objects)
+- [Using Traits](#using-traits)
+- [Logging Changes For Specific Actions](#logging-changes-for-specific-actions)
-This article will cover the basic installation and functionality of **Timestampable** behavior
+## Getting Started
-Content:
+The timestampable behavior can be added to a supported Doctrine object manager by registering its event subscriber
+when creating the manager.
-- [Including](#including-extension) the extension
-- Entity [example](#entity-mapping)
-- Document [example](#document-mapping)
-- [Xml](#xml-mapping) mapping example
-- Advanced usage [examples](#advanced-examples)
-- Using [Traits](#traits)
-
-
+```php
+use Gedmo\Timestampable\TimestampableListener;
-## Setup and autoloading
+$listener = new TimestampableListener();
-Read the [documentation](./annotations.md#em-setup)
-or check the [example code](../example)
-on how to setup and use the extensions in most optimized way.
+// The $om is either an instance of the ORM's entity manager or the MongoDB ODM's document manager
+$om->getEventManager()->addEventSubscriber($listener);
+```
-
+### Using a Clock
-## Timestampable Entity example:
+The timestampable extension supports using a [PSR-20 Clock](https://www.php-fig.org/psr/psr-20/) as the provider for its
+timestamps, falling back to creating a new `DateTime` instance when not available.
-### Timestampable annotations:
-- **@Gedmo\Mapping\Annotation\Timestampable** this annotation tells that this column is timestampable.
-By default it updates this column on update. If column is not date, datetime or time
-type it will trigger an exception.
+To use a clock in the timestampable extension, you can provide one by calling the listener's `setClock` method.
-### Timestampable attributes:
-- **#[Gedmo\Mapping\Annotation\Timestampable]** this attribute tells that this column is timestampable.
- By default it updates this column on update. If column is not date, datetime or time
- type it will trigger an exception.
+```php
+$listener->setClock($clock);
+```
-Available configuration options:
+## Configuring Timestampable Objects
-- **on** - is main option and can be **create, update, change** this tells when it
-should be updated
-- **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes
-- **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value**
+The Itimestampable extension can be configured with [annotations](./annotations.md#timestampable-extension),
+[attributes](./attributes.md#timestampable-extension), or XML configuration (matching the mapping of
+your domain models). The full configuration for annotations and attributes can be reviewed in
+the linked documentation.
-**Note:** that Timestampable interface is not necessary, except in cases where
-you need to identify entity as being Timestampable. The metadata is loaded only once then
-cache is activated
+The below examples show the simplest and default configuration for the extension, setting a field
+when the model is updated.
-### Annotations
+### Attribute Configuration
```php
id;
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-
- public function setBody($body)
- {
- $this->body = $body;
- }
-
- public function getBody()
- {
- return $this->body;
- }
-
- public function getCreated()
- {
- return $this->created;
- }
-
- public function getUpdated()
- {
- return $this->updated;
- }
-
- public function getContentChanged()
- {
- return $this->contentChanged;
- }
-}
-```
-
-### Attributes
-
-```php
#[ORM\Entity]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
- private $id;
-
- #[ORM\Column(name: 'title', type: Types::STRING, length: 128)]
- private $title;
-
- #[ORM\Column(name: 'body', type: Types::STRING)]
- private $body;
+ public ?int $id = null;
- /**
- * @var \DateTime
- */
- #[Gedmo\Timestampable(on: 'create')]
- #[ORM\Column(name: 'created', type: Types::DATE_MUTABLE)]
- private $created;
-
- /**
- * @var \DateTime
- */
- #[ORM\Column(name: 'updated', type: Types::DATETIME_MUTABLE)]
+ #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
#[Gedmo\Timestampable]
- private $updated;
-
- /**
- * @var \DateTime
- */
- #[ORM\Column(name: 'content_changed', type: Types::DATETIME_MUTABLE, nullable: true)]
- #[Gedmo\Timestampable(on: 'change', field: ['title', 'body'])]
- private $contentChanged;
-
- public function getId()
- {
- return $this->id;
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-
- public function setBody($body)
- {
- $this->body = $body;
- }
-
- public function getBody()
- {
- return $this->body;
- }
-
- public function getCreated()
- {
- return $this->created;
- }
-
- public function getUpdated()
- {
- return $this->updated;
- }
-
- public function getContentChanged()
- {
- return $this->contentChanged;
- }
+ public ?\DateTimeImmutable $updatedAt = null;
}
```
-
-
-## Timestampable Document example:
-
-**Note:** this example is using annotations and attributes for mapping, you should use
-one of them, not both.
-
-```php
-id;
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-
- public function setBody($body)
- {
- $this->body = $body;
- }
-
- public function getBody()
- {
- return $this->body;
- }
-
- public function getCreated()
- {
- return $this->created;
- }
-
- public function getUpdated()
- {
- return $this->updated;
- }
-
- public function getContentChanged()
- {
- return $this->contentChanged;
- }
-}
-```
-
-Now on update and creation these annotated fields will be automatically updated
-
-
-
-## Xml mapping example
+### XML Configuration
```xml
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
```
-
-
-## Advanced examples:
-
-### Using dependency of property changes
+### Annotation Configuration
-Add another entity which would represent Article Type:
+> [!NOTE]
+> Support for annotations is deprecated and will be removed in 4.0.
```php
id;
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-}
-```
-
-Now update the Article Entity to reflect published date on Type change:
-
-```php
-type = $type;
- }
-
- public function getId()
- {
- return $this->id;
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-
- public function getCreated()
- {
- return $this->created;
- }
-
- public function getUpdated()
- {
- return $this->updated;
- }
-
- public function getPublished()
- {
- return $this->published;
- }
+ public ?\DateTimeImmutable $updatedAt = null;
}
```
-Now few operations to get it all done:
+### Supported Field Types
-```php
-setTitle('My Article');
+The timestampable extension supports the following field types for the timestamp field:
-$em->persist($article);
-$em->flush();
-// article: $created, $updated were set
+- Date (`date` and `date_immutable`)
+- Time (`time` and `time_immutable`)
+- Date/Time (`datetime` and `datetime_immutable`)
+- Date/Time with timezone (`datetimetz` and `datetimetz_immutable`)
+- Timestamp (`timestamp`)
+- Variable Date/Time (`vardatetime`) (Supported by the ORM and DBAL only)
+- Integer (`integer` only)
-$type = new Type;
-$type->setTitle('Published');
+## Using Traits
-$article = $em->getRepository('Entity\Article')->findByTitle('My Article');
-$article->setType($type);
+The timestampable extension provides traits which can be used to quickly add fields, and optionally the mapping configuration,
+for a created at and updated at timestamp to be updated for the **create** and **update** actions. These traits are
+provided as a convenience for a common configuration, for other use cases it is suggested you add your own fields and configurations.
-$em->persist($article);
-$em->persist($type);
-$em->flush();
-// article: $published, $updated were set
+- `Gedmo\Timestampable\Traits\Timestampable` adds a `$createdAt` and `$updatedAt` property, with getters and setters
+- `Gedmo\Timestampable\Traits\TimestampableDocument` adds a `$createdAt` and `$updatedAt` property, with getters and setters
+ and mapping annotations and attributes for the MongoDB ODM
+- `Gedmo\Timestampable\Traits\TimestampableEntity` adds a `$createdAt` and `$updatedAt` property, with getters and setters
+ and mapping annotations and attributes for the ORM
-$article->getPublished()->format('Y-m-d'); // the date article type changed to published
-```
+## Logging Changes For Specific Actions
-Easy like that, any suggestions on improvements are very welcome
+In addition to supporting logging the timestamp for general create and update actions, the extension can also be configured to
+log the timestamp for a change for specific fields or values.
-### Creating a UTC DateTime type that stores your datetimes in UTC
+### Single Field Changed To Specific Value
-First, we define our custom data type (note the type name is datetime and the type extends DateTimeType which simply overrides the default Doctrine type):
+For example, we want to record the timestamp of when an article is published on a news site. To do this, we add a field to our object
+and configure it using the **change** action, specifying the field and value we want it to match.
```php
setTimeZone(self::$utc);
-
- return $value->format($platform->getDateTimeFormatString());
- }
-
- public function convertToPHPValue($value, AbstractPlatform $platform)
- {
- if ($value === null) {
- return null;
- }
-
- if (is_null(self::$utc)) {
- self::$utc = new \DateTimeZone('UTC');
- }
+ #[ORM\Id]
+ #[ORM\GeneratedValue]
+ #[ORM\Column(type: Types::INTEGER)]
+ public ?int $id = null;
- $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, self::$utc);
+ #[ORM\Column(type: Types::BOOLEAN)]
+ public bool $published = false;
- if (!$val) {
- throw ConversionException::conversionFailed($value, $this->getName());
- }
+ /**
+ * Field to track the timestamp for the last change made to this article.
+ */
+ #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
+ #[Gedmo\Timestampable]
+ public ?\DateTimeImmutable $updatedAt = null;
- return $val;
- }
+ /**
+ * Field to track the timestamp for when this article was published.
+ */
+ #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
+ #[Gedmo\Timestampable(on: 'change', field: 'published', value: true)]
+ public ?\DateTimeImmutable $updatedAt = null;
}
```
-Now in Symfony, we register and override the **datetime** type. **WARNING:** this will override the **datetime** type for all your entities and for all entities in external bundles or extensions, so if you have some entities that require the standard **datetime** type from Doctrine, you must modify the above type and use a different name (such as **utcdatetime**). Additionally, you'll need to modify **Timestampable** so that it includes **utcdatetime** as a valid type.
-
-```yaml
-doctrine:
- dbal:
- types:
- datetime: Acme\DoctrineExtensions\DBAL\Types\UTCDateTimeType
-```
-
-And our Entity properties look as expected:
+The change action can also be configured to watch for changes on related objects using a dot notation path. In this example,
+we log the timestamp for when the article was moved into an archived category.
```php
+ /**
+ * Field to track the timestamp for the last change made to this article.
+ */
+ #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
+ #[Gedmo\Timestampable]
+ public ?\DateTimeImmutable $updatedAt = null;
-## Traits
+ /**
+ * Field to track the timestamp for when this article was archived.
+ */
+ #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
+ #[Gedmo\Timestampable(on: 'change', field: 'category.archived', value: true)]
+ public ?\DateTimeImmutable $updatedAt = null;
+}
+```
-You can use timestampable traits for quick **createdAt** **updatedAt** timestamp definitions
-when using annotation mapping.
-There is also a trait without annotations for easy integration purposes.
+### One of Many Fields Changed
-**Note:** this feature is only available since php **5.4.0**. And you are not required
-to use the Traits provided by extensions.
+The extension can also update a timestampable field when using the **change** action by specifying a list of fields to watch.
+This also supports the dotted path notation, allowing you to watch changes on the model itself as well as related data.
```php