Skip to content

Commit 5f4bcae

Browse files
committed
Add config options to allow fields to be updated using a setter method.
This change is applied in Blameable, IpTraceable, SoftDeleteable & Timestampable annotations. The new attribute "setterMethod" can be passed to specify the setter method to be used for a field. If no setter method is specified, the property value would be set directly as before.
1 parent 8d658b4 commit 5f4bcae

File tree

20 files changed

+154
-25
lines changed

20 files changed

+154
-25
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ a release.
6060
- Fix bug collecting metadata for inherited mapped classes
6161

6262
## [3.12.0] - 2023-07-08
63+
### Added
64+
- Blameable/IpTraceable/SoftDeletable/Timestampable: Added functionality to use setter method instead of setting property values directly (#2644)
65+
6366
### Added
6467
- Tree: `setSibling()` and `getSibling()` methods in the `Node` interface through the BC `@method` annotation
6568
- Tree: Support array of fields and directions in the `$sortByField` and `$direction` parameters at `AbstractTreeRepository::recover()`

src/AbstractTrackingListener.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Doctrine\Persistence\Mapping\ClassMetadata;
2121
use Doctrine\Persistence\NotifyPropertyChanged;
2222
use Doctrine\Persistence\ObjectManager;
23+
use Gedmo\Exception\InvalidMappingException;
2324
use Gedmo\Exception\UnexpectedValueException;
2425
use Gedmo\Mapping\Event\AdapterInterface;
2526
use Gedmo\Mapping\MappedEventSubscriber;
@@ -89,7 +90,7 @@ public function onFlush(EventArgs $args)
8990
$new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false;
9091
if (null === $new) { // let manual values
9192
$needChanges = true;
92-
$this->updateField($object, $ea, $meta, $field);
93+
$this->updateField($object, $ea, $meta, $field, $config);
9394
}
9495
}
9596
}
@@ -101,7 +102,7 @@ public function onFlush(EventArgs $args)
101102
&& null === $changeSet[$field][1];
102103
if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values
103104
$needChanges = true;
104-
$this->updateField($object, $ea, $meta, $field);
105+
$this->updateField($object, $ea, $meta, $field, $config);
105106
}
106107
}
107108
}
@@ -152,7 +153,7 @@ public function onFlush(EventArgs $args)
152153

153154
if (null === $configuredValues || ($singleField && in_array($value, $configuredValues, true))) {
154155
$needChanges = true;
155-
$this->updateField($object, $ea, $meta, $options['field']);
156+
$this->updateField($object, $ea, $meta, $options['field'], $config);
156157
}
157158
}
158159
}
@@ -184,14 +185,14 @@ public function prePersist(EventArgs $args)
184185
if (isset($config['update'])) {
185186
foreach ($config['update'] as $field) {
186187
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
187-
$this->updateField($object, $ea, $meta, $field);
188+
$this->updateField($object, $ea, $meta, $field, $config);
188189
}
189190
}
190191
}
191192
if (isset($config['create'])) {
192193
foreach ($config['create'] as $field) {
193194
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
194-
$this->updateField($object, $ea, $meta, $field);
195+
$this->updateField($object, $ea, $meta, $field, $config);
195196
}
196197
}
197198
}
@@ -216,10 +217,11 @@ abstract protected function getFieldValue($meta, $field, $eventAdapter);
216217
* @param AdapterInterface $eventAdapter
217218
* @param ClassMetadata $meta
218219
* @param string $field
220+
* @param array $config
219221
*
220222
* @return void
221223
*/
222-
protected function updateField($object, $eventAdapter, $meta, $field)
224+
protected function updateField($object, $eventAdapter, $meta, $field, array $config = [])
223225
{
224226
$property = $meta->getReflectionProperty($field);
225227
$oldValue = $property->getValue($object);
@@ -235,7 +237,18 @@ protected function updateField($object, $eventAdapter, $meta, $field)
235237
}
236238
}
237239

238-
$property->setValue($object, $newValue);
240+
if (!empty($config['setterMethod'][$field])) {
241+
$reflectionClass = $meta->getReflectionClass();
242+
$setterName = $config['setterMethod'][$field];
243+
244+
if (!$reflectionClass->hasMethod($setterName)) {
245+
throw new InvalidMappingException("Setter method - [{$setterName}] does not exist in class - {$meta->getName()}");
246+
}
247+
248+
$reflectionClass->getMethod($setterName)->invoke($object, $newValue);
249+
} else {
250+
$property->setValue($object, $newValue);
251+
}
239252

240253
if ($object instanceof NotifyPropertyChanged) {
241254
$uow = $eventAdapter->getObjectManager()->getUnitOfWork();

src/Blameable/Mapping/Driver/Annotation.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ public function readExtendedMetadata($meta, array &$config)
4646
$class = $this->getMetaReflectionClass($meta);
4747
// property annotations
4848
foreach ($class->getProperties() as $property) {
49-
if ($meta->isMappedSuperclass && !$property->isPrivate()
49+
if (
50+
isset($meta->associationMappings[$property->name]['inherited'])
51+
|| ($meta->isMappedSuperclass && !$property->isPrivate())
5052
|| $meta->isInheritedField($property->name)
51-
|| isset($meta->associationMappings[$property->name]['inherited'])
5253
) {
5354
continue;
5455
}
5556
if ($blameable = $this->reader->getPropertyAnnotation($property, self::BLAMEABLE)) {
57+
assert($blameable instanceof Blameable);
58+
5659
$field = $property->getName();
5760

5861
if (!$meta->hasField($field) && !$meta->hasAssociation($field)) {
@@ -84,6 +87,8 @@ public function readExtendedMetadata($meta, array &$config)
8487
'value' => $blameable->value,
8588
];
8689
}
90+
// add the setter method for the field
91+
$this->setSetterMethod($field, $blameable->setterMethod, $config);
8792
// properties are unique and mapper checks that, no risk here
8893
$config[$blameable->on][] = $field;
8994
}

src/Blameable/Mapping/Driver/Xml.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ public function readExtendedMetadata($meta, array &$config)
6363
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), ['update', 'create', 'change'], true)) {
6464
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->getName()}");
6565
}
66-
66+
if ($this->_isAttributeSet($data, 'setterMethod')) {
67+
$setterMethod = $this->_getAttribute($data, 'setterMethod');
68+
$this->setSetterMethod($field, $setterMethod, $config);
69+
}
6770
if ('change' === $this->_getAttribute($data, 'on')) {
6871
if (!$this->_isAttributeSet($data, 'field')) {
6972
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->getName()}");

src/Blameable/Mapping/Driver/Yaml.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public function readExtendedMetadata($meta, array &$config)
6161
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->getName()}");
6262
}
6363

64+
if (isset($mappingProperty['setterMethod'])) {
65+
$this->setSetterMethod($field, $mappingProperty['setterMethod'], $config);
66+
}
67+
6468
if ('change' === $mappingProperty['on']) {
6569
if (!isset($mappingProperty['field'])) {
6670
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->getName()}");

src/IpTraceable/Mapping/Driver/Annotation.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ public function readExtendedMetadata($meta, array &$config)
4444
$class = $this->getMetaReflectionClass($meta);
4545
// property annotations
4646
foreach ($class->getProperties() as $property) {
47-
if ($meta->isMappedSuperclass && !$property->isPrivate()
48-
|| $meta->isInheritedField($property->name)
49-
|| isset($meta->associationMappings[$property->name]['inherited'])
47+
if (
48+
isset($meta->associationMappings[$property->name]['inherited']) ||
49+
($meta->isMappedSuperclass && !$property->isPrivate()) ||
50+
$meta->isInheritedField($property->name)
5051
) {
5152
continue;
5253
}
5354
if ($ipTraceable = $this->reader->getPropertyAnnotation($property, self::IP_TRACEABLE)) {
55+
assert($ipTraceable instanceof IpTraceable);
56+
5457
$field = $property->getName();
5558

5659
if (!$meta->hasField($field)) {
@@ -75,6 +78,8 @@ public function readExtendedMetadata($meta, array &$config)
7578
'value' => $ipTraceable->value,
7679
];
7780
}
81+
// add the setter method for the field
82+
$this->setSetterMethod($field, $ipTraceable->setterMethod, $config);
7883
// properties are unique and mapper checks that, no risk here
7984
$config[$ipTraceable->on][] = $field;
8085
}

src/IpTraceable/Mapping/Driver/Xml.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ public function readExtendedMetadata($meta, array &$config)
6363
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), ['update', 'create', 'change'], true)) {
6464
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->getName()}");
6565
}
66-
66+
if ($this->_isAttributeSet($data, 'setterMethod')) {
67+
$setterMethod = $this->_getAttribute($data, 'setterMethod');
68+
$this->setSetterMethod($field, $setterMethod, $config);
69+
}
6770
if ('change' === $this->_getAttribute($data, 'on')) {
6871
if (!$this->_isAttributeSet($data, 'field')) {
6972
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->getName()}");

src/IpTraceable/Mapping/Driver/Yaml.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public function readExtendedMetadata($meta, array &$config)
5858
if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], ['update', 'create', 'change'], true)) {
5959
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->getName()}");
6060
}
61+
if (isset($mappingProperty['setterMethod'])) {
62+
$this->setSetterMethod($field, $mappingProperty['setterMethod'], $config);
63+
}
6164

6265
if ('change' === $mappingProperty['on']) {
6366
if (!isset($mappingProperty['field'])) {

src/Mapping/Annotation/Blameable.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,21 @@ final class Blameable implements GedmoAnnotation
3434
public $field;
3535
/** @var mixed */
3636
public $value;
37+
/** @var string|null */
38+
public $setterMethod;
3739

3840
/**
3941
* @param array<string, mixed> $data
4042
* @param string|string[]|null $field
4143
* @param mixed $value
4244
*/
43-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
44-
{
45+
public function __construct(
46+
array $data = [],
47+
string $on = 'update',
48+
$field = null,
49+
$value = null,
50+
string $setterMethod = null
51+
) {
4552
if ([] !== $data) {
4653
Deprecation::trigger(
4754
'gedmo/doctrine-extensions',
@@ -55,12 +62,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5562
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5663
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5764
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
65+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5866

5967
return;
6068
}
6169

6270
$this->on = $on;
6371
$this->field = $field;
6472
$this->value = $value;
73+
$this->setterMethod = $setterMethod;
6574
}
6675
}

src/Mapping/Annotation/IpTraceable.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,21 @@ final class IpTraceable implements GedmoAnnotation
3535
public $field;
3636
/** @var mixed */
3737
public $value;
38+
/** @var string|null */
39+
public $setterMethod;
3840

3941
/**
4042
* @param array<string, mixed> $data
4143
* @param string|string[]|null $field
4244
* @param mixed $value
4345
*/
44-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
45-
{
46+
public function __construct(
47+
array $data = [],
48+
string $on = 'update',
49+
$field = null,
50+
$value = null,
51+
string $setterMethod = null
52+
) {
4653
if ([] !== $data) {
4754
Deprecation::trigger(
4855
'gedmo/doctrine-extensions',
@@ -56,12 +63,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5663
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5764
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5865
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
66+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5967

6068
return;
6169
}
6270

6371
$this->on = $on;
6472
$this->field = $field;
6573
$this->value = $value;
74+
$this->setterMethod = $setterMethod;
6675
}
6776
}

0 commit comments

Comments
 (0)