Skip to content

Commit f8ac148

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 9d5c15d commit f8ac148

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
@@ -36,6 +36,9 @@ a release.
3636
- Fix bug collecting metadata for inherited mapped classes
3737

3838
## [3.12.0] - 2023-07-08
39+
### Added
40+
- Blameable/IpTraceable/SoftDeletable/Timestampable: Added functionality to use setter method instead of setting property values directly (#2644)
41+
3942
### Added
4043
- Tree: `setSibling()` and `getSibling()` methods in the `Node` interface through the BC `@method` annotation
4144
- 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;
@@ -83,7 +84,7 @@ public function onFlush(EventArgs $args)
8384
$new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false;
8485
if (null === $new) { // let manual values
8586
$needChanges = true;
86-
$this->updateField($object, $ea, $meta, $field);
87+
$this->updateField($object, $ea, $meta, $field, $config);
8788
}
8889
}
8990
}
@@ -95,7 +96,7 @@ public function onFlush(EventArgs $args)
9596
&& null === $changeSet[$field][1];
9697
if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values
9798
$needChanges = true;
98-
$this->updateField($object, $ea, $meta, $field);
99+
$this->updateField($object, $ea, $meta, $field, $config);
99100
}
100101
}
101102
}
@@ -146,7 +147,7 @@ public function onFlush(EventArgs $args)
146147

147148
if (null === $configuredValues || ($singleField && in_array($value, $configuredValues, true))) {
148149
$needChanges = true;
149-
$this->updateField($object, $ea, $meta, $options['field']);
150+
$this->updateField($object, $ea, $meta, $options['field'], $config);
150151
}
151152
}
152153
}
@@ -174,14 +175,14 @@ public function prePersist(EventArgs $args)
174175
if (isset($config['update'])) {
175176
foreach ($config['update'] as $field) {
176177
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
177-
$this->updateField($object, $ea, $meta, $field);
178+
$this->updateField($object, $ea, $meta, $field, $config);
178179
}
179180
}
180181
}
181182
if (isset($config['create'])) {
182183
foreach ($config['create'] as $field) {
183184
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
184-
$this->updateField($object, $ea, $meta, $field);
185+
$this->updateField($object, $ea, $meta, $field, $config);
185186
}
186187
}
187188
}
@@ -206,10 +207,11 @@ abstract protected function getFieldValue($meta, $field, $eventAdapter);
206207
* @param AdapterInterface $eventAdapter
207208
* @param ClassMetadata $meta
208209
* @param string $field
210+
* @param array $config
209211
*
210212
* @return void
211213
*/
212-
protected function updateField($object, $eventAdapter, $meta, $field)
214+
protected function updateField($object, $eventAdapter, $meta, $field, array $config = [])
213215
{
214216
$property = $meta->getReflectionProperty($field);
215217
$oldValue = $property->getValue($object);
@@ -225,7 +227,18 @@ protected function updateField($object, $eventAdapter, $meta, $field)
225227
}
226228
}
227229

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

230243
if ($object instanceof NotifyPropertyChanged) {
231244
$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
@@ -33,14 +33,21 @@ final class Blameable implements GedmoAnnotation
3333
public $field;
3434
/** @var mixed */
3535
public $value;
36+
/** @var string|null */
37+
public $setterMethod;
3638

3739
/**
3840
* @param array<string, mixed> $data
3941
* @param string|string[]|null $field
4042
* @param mixed $value
4143
*/
42-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
43-
{
44+
public function __construct(
45+
array $data = [],
46+
string $on = 'update',
47+
$field = null,
48+
$value = null,
49+
string $setterMethod = null
50+
) {
4451
if ([] !== $data) {
4552
@trigger_error(sprintf(
4653
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -52,12 +59,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5259
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5360
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5461
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
62+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5563

5664
return;
5765
}
5866

5967
$this->on = $on;
6068
$this->field = $field;
6169
$this->value = $value;
70+
$this->setterMethod = $setterMethod;
6271
}
6372
}

src/Mapping/Annotation/IpTraceable.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,21 @@ final class IpTraceable 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
@trigger_error(sprintf(
4754
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -53,12 +60,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5360
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5461
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5562
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
63+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5664

5765
return;
5866
}
5967

6068
$this->on = $on;
6169
$this->field = $field;
6270
$this->value = $value;
71+
$this->setterMethod = $setterMethod;
6372
}
6473
}

0 commit comments

Comments
 (0)