diff --git a/doc/tokenizer.md b/doc/tokenizer.md
new file mode 100644
index 0000000000..d2eb6f8fde
--- /dev/null
+++ b/doc/tokenizer.md
@@ -0,0 +1,609 @@
+# Tokenizer behavior extension for Doctrine 2
+
+**Tokenizer** behavior will automate the update of token field
+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.
+
+Features:
+
+- Automatic predefined token 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
+- Annotation, Yaml and Xml mapping support for extensions
+
+[blog_reference]: http://gediminasm.org/article/tokenizer-behavior-extension-for-doctrine-2 "Tokenizer extension for Doctrine 2 helps automate update of token"
+[blog_test]: http://gediminasm.org/test "Test extensions on this blog"
+
+**Note:**
+
+- You can [test live][blog_test] on this blog
+- Public [Timestampable repository](http://github.com/l3pp4rd/DoctrineExtensions "Tokenizer extension on Github") is available on github
+
+**Portability:**
+
+- **Tokenizer** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
+ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
+
+This article will cover the basic installation and functionality of **Tokenizer** behavior
+
+Content:
+
+- [Including](#including-extension) the extension
+- Entity [example](#entity-mapping)
+- Document [example](#document-mapping)
+- [Yaml](#yaml-mapping) mapping example
+- [Xml](#xml-mapping) mapping example
+- Advanced usage [examples](#advanced-examples)
+- Using [Traits](#traits)
+
+
+
+## Setup and autoloading
+
+Read the [documentation](http://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/annotations.md#em-setup)
+or check the [example code](http://github.com/l3pp4rd/DoctrineExtensions/tree/master/example)
+on how to setup and use the extensions in most optimized way.
+
+
+
+## Tokenizer Entity example:
+
+### Tokenizer annotations:
+- **@Gedmo\Mapping\Annotation\Tokenizer** this annotation tells that this column is Tokenizer
+by default it updates this column on update. If column is not date, datetime or time
+type it will trigger an exception.
+
+Available configuration options:
+
+- **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**
+
+**Note:** that Tokenizer interface is not necessary, except in cases where
+you need to identify entity as being Token. The metadata is loaded only once then
+cache is activated
+
+``` 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 getToken()
+ {
+ return $this->token;
+ }
+}
+```
+
+
+
+## Tokenizer Document example:
+
+``` 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 getToken()
+ {
+ return $this->token;
+ }
+
+}
+```
+
+Now on update and creation these annotated fields will be automatically updated
+
+
+
+## Yaml mapping example:
+
+Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
+
+```
+---
+Entity\Article:
+ type: entity
+ table: articles
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ fields:
+ title:
+ type: string
+ length: 64
+ token:
+ type: date
+ gedmo:
+ tokenizer:
+ on: create
+```
+
+
+
+## Xml mapping example
+
+``` xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## Advanced examples:
+
+### Using dependency of property changes
+
+Add another entity which would represent Article Type:
+
+``` 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;
+ }
+}
+```
+
+Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
+
+```
+---
+Entity\Article:
+ type: entity
+ table: articles
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ fields:
+ title:
+ type: string
+ length: 64
+ created:
+ type: date
+ gedmo:
+ timestampable:
+ on: create
+ updated:
+ type: datetime
+ gedmo:
+ timestampable:
+ on: update
+ published:
+ type: datetime
+ gedmo:
+ timestampable:
+ on: change
+ field: type.title
+ value: Published
+ manyToOne:
+ type:
+ targetEntity: Entity\Type
+ inversedBy: articles
+```
+
+Now few operations to get it all done:
+
+``` php
+setTitle('My Article');
+
+$em->persist($article);
+$em->flush();
+// article: $created, $updated were set
+
+$type = new Type;
+$type->setTitle('Published');
+
+$article = $em->getRepository('Entity\Article')->findByTitle('My Article');
+$article->setType($type);
+
+$em->persist($article);
+$em->persist($type);
+$em->flush();
+// article: $published, $updated were set
+
+$article->getPublished()->format('Y-m-d'); // the date article type changed to published
+```
+
+Easy like that, any suggestions on improvements are very welcome
+
+### Creating a UTC DateTime type that stores your datetimes in UTC
+
+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):
+
+``` 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');
+ }
+
+ $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, self::$utc);
+
+ if (!$val) {
+ throw ConversionException::conversionFailed($value, $this->getName());
+ }
+
+ return $val;
+ }
+}
+```
+
+Now in Symfony2, 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.
+
+```
+doctrine:
+ dbal:
+ types:
+ datetime: Acme\DoctrineExtensions\DBAL\Types\UTCDateTimeType
+```
+
+And our Entity properties look as expected:
+
+``` php
+
+
+## Traits
+
+You can use timestampable traits for quick **createdAt** **updatedAt** timestamp definitions
+when using annotation mapping.
+
+**Note:** this feature is only available since php **5.4.0**. And you are not required
+to use the Traits provided by extensions.
+
+``` php
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+final class Tokenizer extends Annotation
+{
+ /** @var string */
+ public $on = 'update';
+ /** @var string|array */
+ public $field;
+ /** @var mixed */
+ public $value;
+}
+
diff --git a/lib/Gedmo/Tokenizer/Mapping/Driver/Annotation.php b/lib/Gedmo/Tokenizer/Mapping/Driver/Annotation.php
new file mode 100644
index 0000000000..e740b430e7
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Driver/Annotation.php
@@ -0,0 +1,77 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class Annotation extends AbstractAnnotationDriver
+{
+ /**
+ * Annotation field is Tokenizer
+ */
+ const TOKENIZER = 'Gedmo\\Mapping\\Annotation\\Tokenizer';
+
+ /**
+ * List of types which are valid for timestamp
+ *
+ * @var array
+ */
+ protected $validTypes = array(
+ 'string',
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public function readExtendedMetadata($meta, array &$config) {
+ $class = $this->getMetaReflectionClass($meta);
+
+ // property annotations
+ foreach ($class->getProperties() as $property) {
+ if ($meta->isMappedSuperclass && !$property->isPrivate() ||
+ $meta->isInheritedField($property->name) ||
+ isset($meta->associationMappings[$property->name]['inherited'])
+ ) {
+ continue;
+ }
+ if ($tokenizer = $this->reader->getPropertyAnnotation($property, self::TOKENIZER)) {
+ $field = $property->getName();
+ if (!$meta->hasField($field)) {
+ throw new InvalidMappingException("Unable to find timestampable [{$field}] as mapped property in entity - {$meta->name}");
+ }
+ if (!$this->isValidField($meta, $field)) {
+ throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
+ }
+ if (!in_array($tokenizer->on, array('update', 'create', 'change'))) {
+ throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
+ }
+ if ($tokenizer->on == 'change') {
+ if (!isset($tokenizer->field)) {
+ throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
+ }
+ if (is_array($tokenizer->field) && isset($tokenizer->value)) {
+ throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet.");
+ }
+ $field = array(
+ 'field' => $field,
+ 'trackedField' => $tokenizer->field,
+ 'value' => $tokenizer->value,
+ );
+ }
+ // properties are unique and mapper checks that, no risk here
+ $config[$tokenizer->on][] = $field;
+ }
+ }
+ }
+}
diff --git a/lib/Gedmo/Tokenizer/Mapping/Driver/Xml.php b/lib/Gedmo/Tokenizer/Mapping/Driver/Xml.php
new file mode 100644
index 0000000000..1b459eafe5
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Driver/Xml.php
@@ -0,0 +1,93 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class Xml extends BaseXml
+{
+
+ /**
+ * List of types which are valid for timestamp
+ *
+ * @var array
+ */
+ private $validTypes = array(
+ 'string',
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public function readExtendedMetadata($meta, array &$config)
+ {
+ /**
+ * @var \SimpleXmlElement $mapping
+ */
+ $mapping = $this->_getMapping($meta->name);
+
+ if (isset($mapping->field)) {
+ /**
+ * @var \SimpleXmlElement $fieldMapping
+ */
+ foreach ($mapping->field as $fieldMapping) {
+ $fieldMappingDoctrine = $fieldMapping;
+ $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
+ if (isset($fieldMapping->timestampable)) {
+ /**
+ * @var \SimpleXmlElement $data
+ */
+ $data = $fieldMapping->timestampable;
+
+ $field = $this->_getAttribute($fieldMappingDoctrine, 'name');
+ if (!$this->isValidField($meta, $field)) {
+ throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
+ }
+ if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
+ throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
+ }
+
+ if ($this->_getAttribute($data, 'on') == 'change') {
+ if (!$this->_isAttributeSet($data, 'field')) {
+ throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
+ }
+ $trackedFieldAttribute = $this->_getAttribute($data, 'field');
+ $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null;
+ if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
+ throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet.");
+ }
+ $field = array(
+ 'field' => $field,
+ 'trackedField' => $trackedFieldAttribute,
+ 'value' => $valueAttribute,
+ );
+ }
+ $config[$this->_getAttribute($data, 'on')][] = $field;
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if $field type is valid
+ *
+ * @param object $meta
+ * @param string $field
+ * @return boolean
+ */
+ protected function isValidField($meta, $field)
+ {
+ $mapping = $meta->getFieldMapping($field);
+ return $mapping && in_array($mapping['type'], $this->validTypes);
+ }
+}
diff --git a/lib/Gedmo/Tokenizer/Mapping/Driver/Yaml.php b/lib/Gedmo/Tokenizer/Mapping/Driver/Yaml.php
new file mode 100644
index 0000000000..a16a7a1890
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Driver/Yaml.php
@@ -0,0 +1,94 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class Yaml extends File implements Driver
+{
+ /**
+ * File extension
+ * @var string
+ */
+ protected $_extension = '.dcm.yml';
+
+ /**
+ * List of types which are valid for timestamp
+ *
+ * @var array
+ */
+ private $validTypes = array(
+ 'string',
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public function readExtendedMetadata($meta, array &$config)
+ {
+ $mapping = $this->_getMapping($meta->name);
+
+ if (isset($mapping['fields'])) {
+ foreach ($mapping['fields'] as $field => $fieldMapping) {
+ if (isset($fieldMapping['gedmo']['timestampable'])) {
+ $mappingProperty = $fieldMapping['gedmo']['timestampable'];
+ if (!$this->isValidField($meta, $field)) {
+ throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}");
+ }
+ if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
+ throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
+ }
+
+ if ($mappingProperty['on'] == 'change') {
+ if (!isset($mappingProperty['field'])) {
+ throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
+ }
+ $trackedFieldAttribute = $mappingProperty['field'];
+ $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null;
+ if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
+ throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet.");
+ }
+ $field = array(
+ 'field' => $field,
+ 'trackedField' => $trackedFieldAttribute,
+ 'value' => $valueAttribute,
+ );
+ }
+ $config[$mappingProperty['on']][] = $field;
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected function _loadMappingFile($file)
+ {
+ return \Symfony\Component\Yaml\Yaml::parse($file);
+ }
+
+ /**
+ * Checks if $field type is valid
+ *
+ * @param object $meta
+ * @param string $field
+ * @return boolean
+ */
+ protected function isValidField($meta, $field)
+ {
+ $mapping = $meta->getFieldMapping($field);
+ return $mapping && in_array($mapping['type'], $this->validTypes);
+ }
+}
diff --git a/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ODM.php b/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ODM.php
new file mode 100644
index 0000000000..65a02ae502
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ODM.php
@@ -0,0 +1,31 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+final class ODM extends BaseAdapterODM implements TokenizerAdapter
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function getDateValue($meta, $field)
+ {
+ $mapping = $meta->getFieldMapping($field);
+ if (isset($mapping['type']) && $mapping['type'] === 'string') {
+ $date = new \DateTime();
+ $token =base_convert(sha1(uniqid(mt_rand(1, 999) . $date->format('Y-m-d H:i:s'), true)), 16, 36);
+
+ return $token;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ORM.php b/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ORM.php
new file mode 100644
index 0000000000..042b9b4505
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Event/Adapter/ORM.php
@@ -0,0 +1,31 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+final class ORM extends BaseAdapterORM implements TokenizerAdapter
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function getDateValue($meta, $field)
+ {
+ $mapping = $meta->getFieldMapping($field);
+ if (isset($mapping['type']) && $mapping['type'] === 'string') {
+ $date = new \DateTime();
+ $token =base_convert(sha1(uniqid(mt_rand(1, 999) . $date->format('Y-m-d H:i:s'), true)), 16, 36);
+
+ return $token;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/lib/Gedmo/Tokenizer/Mapping/Event/TokenizerAdapter.php b/lib/Gedmo/Tokenizer/Mapping/Event/TokenizerAdapter.php
new file mode 100644
index 0000000000..66aa112aaa
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Mapping/Event/TokenizerAdapter.php
@@ -0,0 +1,24 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+interface TokenizerAdapter extends AdapterInterface
+{
+ /**
+ * Get the date value
+ *
+ * @param object $meta
+ * @param string $field
+ * @return mixed
+ */
+ function getDateValue($meta, $field);
+}
\ No newline at end of file
diff --git a/lib/Gedmo/Tokenizer/Tokenizer.php b/lib/Gedmo/Tokenizer/Tokenizer.php
new file mode 100644
index 0000000000..ae10baad12
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Tokenizer.php
@@ -0,0 +1,50 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+interface Tokenizer
+{
+ // Tokenizer expects annotations on properties
+
+ /**
+ * @gedmo:Tokenizer(on="create")
+ * token which should be updated on insert only
+ */
+
+ /**
+ * @gedmo:Tokenizer(on="update")
+ * token which should be updated on update and insert
+ */
+
+ /**
+ * @gedmo:Tokenizer(on="change", field="field", value="value")
+ * token which should be updated on changed "property"
+ * value and become equal to given "value"
+ */
+
+ /**
+ * @gedmo:Tokenizer(on="change", field="field")
+ * token which should be updated on changed "property"
+ */
+
+ /**
+ * @gedmo:Tokenizer(on="change", fields={"field1", "field2"})
+ * token which should be updated if at least one of the given fields changed
+ */
+
+ /**
+ * example
+ *
+ * @gedmo:Tokenizer(on="create")
+ * @Column(type="string")
+ * $token
+ */
+}
\ No newline at end of file
diff --git a/lib/Gedmo/Tokenizer/TokenizerListener.php b/lib/Gedmo/Tokenizer/TokenizerListener.php
new file mode 100644
index 0000000000..34c440871a
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/TokenizerListener.php
@@ -0,0 +1,191 @@
+
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TokenizerListener extends MappedEventSubscriber
+{
+ /**
+ * Specifies the list of events to listen
+ *
+ * @return array
+ */
+ public function getSubscribedEvents()
+ {
+ return array(
+ 'prePersist',
+ 'onFlush',
+ 'loadClassMetadata'
+ );
+ }
+
+ /**
+ * Maps additional metadata for the Entity
+ *
+ * @param EventArgs $eventArgs
+ * @return void
+ */
+ public function loadClassMetadata(EventArgs $eventArgs)
+ {
+ $ea = $this->getEventAdapter($eventArgs);
+ $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
+ }
+
+ /**
+ * Looks for Tokenizer objects being updated
+ * to update modification date
+ *
+ * @param EventArgs $args
+ * @throws \Gedmo\Exception\UnexpectedValueException
+ * @return void
+ */
+ public function onFlush(EventArgs $args)
+ {
+ $ea = $this->getEventAdapter($args);
+ $om = $ea->getObjectManager();
+ $uow = $om->getUnitOfWork();
+ // check all scheduled updates
+ foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
+ $meta = $om->getClassMetadata(get_class($object));
+ if ($config = $this->getConfiguration($om, $meta->name)) {
+ $changeSet = $ea->getObjectChangeSet($uow, $object);
+ $needChanges = false;
+
+ if (isset($config['update'])) {
+ foreach ($config['update'] as $field) {
+ if (!isset($changeSet[$field])) { // let manual values
+ $needChanges = true;
+ $this->updateField($object, $ea, $meta, $field);
+
+ }
+ }
+ }
+
+ if (isset($config['change'])) {
+ foreach ($config['change'] as $options) {
+ if (isset($changeSet[$options['field']])) {
+ continue; // value was set manually
+ }
+
+ if (!is_array($options['trackedField'])) {
+ $singleField = true;
+ $trackedFields = array($options['trackedField']);
+ } else {
+ $singleField = false;
+ $trackedFields = $options['trackedField'];
+ }
+
+ foreach ($trackedFields as $tracked) {
+ $trackedChild = null;
+ $parts = explode('.', $tracked);
+ if (isset($parts[1])) {
+ $tracked = $parts[0];
+ $trackedChild = $parts[1];
+ }
+
+ if (isset($changeSet[$tracked])) {
+ $changes = $changeSet[$tracked];
+ if (isset($trackedChild)) {
+ $changingObject = $changes[1];
+ if (!is_object($changingObject)) {
+ throw new UnexpectedValueException(
+ "Field - [{$field}] is expected to be object in class - {$meta->name}"
+ );
+ }
+ $objectMeta = $om->getClassMetadata(get_class($changingObject));
+ $om->initializeObject($changingObject);
+ $value = $objectMeta->getReflectionProperty($trackedChild)->getValue($changingObject);
+ } else {
+ $value = $changes[1];
+ }
+
+ if (($singleField && in_array($value, (array)$options['value'])) || $options['value'] === null) {
+ $needChanges = true;
+ $this->updateField($object, $ea, $meta, $options['field']);
+ }
+ }
+ }
+ }
+ }
+
+ if ($needChanges) {
+ $ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks for persisted Timestampable objects
+ * to update creation and modification dates
+ *
+ * @param EventArgs $args
+ * @return void
+ */
+ public function prePersist(EventArgs $args)
+ {
+ $ea = $this->getEventAdapter($args);
+ $om = $ea->getObjectManager();
+ $object = $ea->getObject();
+
+ $meta = $om->getClassMetadata(get_class($object));
+
+ if ($config = $this->getConfiguration($om, $meta->getName())) {
+
+ if (isset($config['update'])) {
+ foreach ($config['update'] as $field) {
+ if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values
+ $this->updateField($object, $ea, $meta, $field);
+ }
+ }
+ }
+
+ if (isset($config['create'])) {
+ foreach ($config['create'] as $field) {
+ if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values
+ $this->updateField($object, $ea, $meta, $field);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected function getNamespace()
+ {
+ return __NAMESPACE__;
+ }
+
+ /**
+ * Updates a field
+ *
+ * @param $object
+ * @param $ea
+ * @param $meta
+ * @param $field
+ */
+ protected function updateField($object, $ea, $meta, $field)
+ {
+ $property = $meta->getReflectionProperty($field);
+ $oldValue = $property->getValue($object);
+ $newValue = $ea->getDateValue($meta, $field);
+ $property->setValue($object, $newValue);
+ if ($object instanceof NotifyPropertyChanged) {
+ $uow = $ea->getObjectManager()->getUnitOfWork();
+ $uow->propertyChanged($object, $field, $oldValue, $newValue);
+ }
+ }
+}
diff --git a/lib/Gedmo/Tokenizer/Traits/TokenizerDocument.php b/lib/Gedmo/Tokenizer/Traits/TokenizerDocument.php
new file mode 100644
index 0000000000..56a3932aeb
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Traits/TokenizerDocument.php
@@ -0,0 +1,42 @@
+= 5.4
+ *
+ * @author Ceif Khedhiri
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+trait TokenizerDocument
+{
+ /**
+ * @var \DateTime
+ * @Gedmo\Tokenizer(on="create")
+ * @ODM\String
+ */
+ private $token;
+
+ /**
+ * Sets token.
+ *
+ * @param $token
+ * @return $this
+ */
+ public function setToken($token)
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ /**
+ * Returns token.
+ *
+ * @return token
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+}
diff --git a/lib/Gedmo/Tokenizer/Traits/TokenizerEntity.php b/lib/Gedmo/Tokenizer/Traits/TokenizerEntity.php
new file mode 100644
index 0000000000..cdbd8acb12
--- /dev/null
+++ b/lib/Gedmo/Tokenizer/Traits/TokenizerEntity.php
@@ -0,0 +1,43 @@
+= 5.4
+ *
+ * @author Ceif Khedhiri
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+trait TokenizerEntity
+{
+ /**
+ * @var token
+ * @Gedmo\Tokenizer(on="create")
+ * @ORM\Column(type="string")
+ */
+ protected $token;
+
+ /**
+ * Sets token.
+ *
+ * @param $token
+ * @return $this
+ */
+ public function setToken( $token)
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ /**
+ * Returns token.
+ *
+ * @return token
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+
+}