Skip to content

Commit 61aa29e

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 c4c6f85 commit 61aa29e

File tree

20 files changed

+161
-27
lines changed

20 files changed

+161
-27
lines changed

CHANGELOG.md

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

2929
## [3.12.0] - 2023-07-08
30+
### Added
31+
- Blameable/IpTraceable/SoftDeletable/Timestampable: Added functionality to use setter method instead of setting property values directly (#2644)
32+
3033
### Added
3134
- Tree: `setSibling()` and `getSibling()` methods in the `Node` interface through the BC `@method` annotation
3235
- 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
@@ -18,6 +18,7 @@
1818
use Doctrine\Persistence\Mapping\ClassMetadata;
1919
use Doctrine\Persistence\NotifyPropertyChanged;
2020
use Doctrine\Persistence\ObjectManager;
21+
use Gedmo\Exception\InvalidMappingException;
2122
use Gedmo\Exception\UnexpectedValueException;
2223
use Gedmo\Mapping\Event\AdapterInterface;
2324
use Gedmo\Mapping\MappedEventSubscriber;
@@ -81,7 +82,7 @@ public function onFlush(EventArgs $args)
8182
$new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false;
8283
if (null === $new) { // let manual values
8384
$needChanges = true;
84-
$this->updateField($object, $ea, $meta, $field);
85+
$this->updateField($object, $ea, $meta, $field, $config);
8586
}
8687
}
8788
}
@@ -93,7 +94,7 @@ public function onFlush(EventArgs $args)
9394
&& null === $changeSet[$field][1];
9495
if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values
9596
$needChanges = true;
96-
$this->updateField($object, $ea, $meta, $field);
97+
$this->updateField($object, $ea, $meta, $field, $config);
9798
}
9899
}
99100
}
@@ -144,7 +145,7 @@ public function onFlush(EventArgs $args)
144145

145146
if (null === $configuredValues || ($singleField && in_array($value, $configuredValues, true))) {
146147
$needChanges = true;
147-
$this->updateField($object, $ea, $meta, $options['field']);
148+
$this->updateField($object, $ea, $meta, $options['field'], $config);
148149
}
149150
}
150151
}
@@ -172,14 +173,14 @@ public function prePersist(EventArgs $args)
172173
if (isset($config['update'])) {
173174
foreach ($config['update'] as $field) {
174175
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
175-
$this->updateField($object, $ea, $meta, $field);
176+
$this->updateField($object, $ea, $meta, $field, $config);
176177
}
177178
}
178179
}
179180
if (isset($config['create'])) {
180181
foreach ($config['create'] as $field) {
181182
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
182-
$this->updateField($object, $ea, $meta, $field);
183+
$this->updateField($object, $ea, $meta, $field, $config);
183184
}
184185
}
185186
}
@@ -204,10 +205,11 @@ abstract protected function getFieldValue($meta, $field, $eventAdapter);
204205
* @param AdapterInterface $eventAdapter
205206
* @param ClassMetadata $meta
206207
* @param string $field
208+
* @param array $config
207209
*
208210
* @return void
209211
*/
210-
protected function updateField($object, $eventAdapter, $meta, $field)
212+
protected function updateField($object, $eventAdapter, $meta, $field, array $config = [])
211213
{
212214
$property = $meta->getReflectionProperty($field);
213215
$oldValue = $property->getValue($object);
@@ -223,7 +225,18 @@ protected function updateField($object, $eventAdapter, $meta, $field)
223225
}
224226
}
225227

226-
$property->setValue($object, $newValue);
228+
if (!empty($config['setterMethod'][$field])) {
229+
$reflectionClass = $meta->getReflectionClass();
230+
$setterName = $config['setterMethod'][$field];
231+
232+
if (!$reflectionClass->hasMethod($setterName)) {
233+
throw new InvalidMappingException("Setter method - [{$setterName}] does not exist in class - {$meta->getName()}");
234+
}
235+
236+
$reflectionClass->getMethod($setterName)->invoke($object, $newValue);
237+
} else {
238+
$property->setValue($object, $newValue);
239+
}
227240

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

src/Blameable/Mapping/Driver/Annotation.php

Lines changed: 8 additions & 3 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() ||
50-
$meta->isInheritedField($property->name) ||
51-
isset($meta->associationMappings[$property->name]['inherited'])
49+
if (
50+
isset($meta->associationMappings[$property->name]['inherited']) ||
51+
($meta->isMappedSuperclass && !$property->isPrivate()) ||
52+
$meta->isInheritedField($property->name)
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
@@ -32,13 +32,20 @@ final class Blameable implements GedmoAnnotation
3232
public $field;
3333
/** @var mixed */
3434
public $value;
35+
/** @var string|null */
36+
public $setterMethod;
3537

3638
/**
3739
* @param string|string[]|null $field
3840
* @param mixed $value
3941
*/
40-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
41-
{
42+
public function __construct(
43+
array $data = [],
44+
string $on = 'update',
45+
$field = null,
46+
$value = null,
47+
string $setterMethod = null
48+
) {
4249
if ([] !== $data) {
4350
@trigger_error(sprintf(
4451
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -50,12 +57,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5057
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5158
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5259
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
60+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5361

5462
return;
5563
}
5664

5765
$this->on = $on;
5866
$this->field = $field;
5967
$this->value = $value;
68+
$this->setterMethod = $setterMethod;
6069
}
6170
}

src/Mapping/Annotation/IpTraceable.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@ final class IpTraceable implements GedmoAnnotation
3232
public $field;
3333
/** @var mixed */
3434
public $value;
35+
/** @var string|null */
36+
public $setterMethod;
3537

3638
/**
3739
* @param string|string[]|null $field
3840
* @param mixed $value
3941
*/
40-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
41-
{
42+
public function __construct(
43+
array $data = [],
44+
string $on = 'update',
45+
$field = null,
46+
$value = null,
47+
string $setterMethod = null
48+
) {
4249
if ([] !== $data) {
4350
@trigger_error(sprintf(
4451
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -50,12 +57,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5057
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5158
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5259
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
60+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5361

5462
return;
5563
}
5664

5765
$this->on = $on;
5866
$this->field = $field;
5967
$this->value = $value;
68+
$this->setterMethod = $setterMethod;
6069
}
6170
}

0 commit comments

Comments
 (0)