Skip to content

Commit 7e9eaed

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 61c77d1 commit 7e9eaed

File tree

20 files changed

+155
-26
lines changed

20 files changed

+155
-26
lines changed

CHANGELOG.md

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

2727
## [3.12.0] - 2023-07-08
28+
### Added
29+
- Blameable/IpTraceable/SoftDeletable/Timestampable: Added functionality to use setter method instead of setting property values directly (#2644)
30+
2831
### Added
2932
- Tree: `setSibling()` and `getSibling()` methods in the `Node` interface through the BC `@method` annotation
3033
- 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;
@@ -82,7 +83,7 @@ public function onFlush(EventArgs $args)
8283
$new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false;
8384
if (null === $new) { // let manual values
8485
$needChanges = true;
85-
$this->updateField($object, $ea, $meta, $field);
86+
$this->updateField($object, $ea, $meta, $field, $config);
8687
}
8788
}
8889
}
@@ -94,7 +95,7 @@ public function onFlush(EventArgs $args)
9495
&& null === $changeSet[$field][1];
9596
if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values
9697
$needChanges = true;
97-
$this->updateField($object, $ea, $meta, $field);
98+
$this->updateField($object, $ea, $meta, $field, $config);
9899
}
99100
}
100101
}
@@ -145,7 +146,7 @@ public function onFlush(EventArgs $args)
145146

146147
if (null === $configuredValues || ($singleField && in_array($value, $configuredValues, true))) {
147148
$needChanges = true;
148-
$this->updateField($object, $ea, $meta, $options['field']);
149+
$this->updateField($object, $ea, $meta, $options['field'], $config);
149150
}
150151
}
151152
}
@@ -173,14 +174,14 @@ public function prePersist(EventArgs $args)
173174
if (isset($config['update'])) {
174175
foreach ($config['update'] as $field) {
175176
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
176-
$this->updateField($object, $ea, $meta, $field);
177+
$this->updateField($object, $ea, $meta, $field, $config);
177178
}
178179
}
179180
}
180181
if (isset($config['create'])) {
181182
foreach ($config['create'] as $field) {
182183
if (null === $meta->getReflectionProperty($field)->getValue($object)) { // let manual values
183-
$this->updateField($object, $ea, $meta, $field);
184+
$this->updateField($object, $ea, $meta, $field, $config);
184185
}
185186
}
186187
}
@@ -205,10 +206,11 @@ abstract protected function getFieldValue($meta, $field, $eventAdapter);
205206
* @param AdapterInterface $eventAdapter
206207
* @param ClassMetadata $meta
207208
* @param string $field
209+
* @param array $config
208210
*
209211
* @return void
210212
*/
211-
protected function updateField($object, $eventAdapter, $meta, $field)
213+
protected function updateField($object, $eventAdapter, $meta, $field, array $config = [])
212214
{
213215
$property = $meta->getReflectionProperty($field);
214216
$oldValue = $property->getValue($object);
@@ -224,7 +226,18 @@ protected function updateField($object, $eventAdapter, $meta, $field)
224226
}
225227
}
226228

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

229242
if ($object instanceof NotifyPropertyChanged) {
230243
$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,14 +32,21 @@ 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 array<string, mixed> $data
3840
* @param string|string[]|null $field
3941
* @param mixed $value
4042
*/
41-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
42-
{
43+
public function __construct(
44+
array $data = [],
45+
string $on = 'update',
46+
$field = null,
47+
$value = null,
48+
string $setterMethod = null
49+
) {
4350
if ([] !== $data) {
4451
@trigger_error(sprintf(
4552
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -51,12 +58,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5158
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5259
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5360
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
61+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5462

5563
return;
5664
}
5765

5866
$this->on = $on;
5967
$this->field = $field;
6068
$this->value = $value;
69+
$this->setterMethod = $setterMethod;
6170
}
6271
}

src/Mapping/Annotation/IpTraceable.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,21 @@ 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 array<string, mixed> $data
3840
* @param string|string[]|null $field
3941
* @param mixed $value
4042
*/
41-
public function __construct(array $data = [], string $on = 'update', $field = null, $value = null)
42-
{
43+
public function __construct(
44+
array $data = [],
45+
string $on = 'update',
46+
$field = null,
47+
$value = null,
48+
string $setterMethod = null
49+
) {
4350
if ([] !== $data) {
4451
@trigger_error(sprintf(
4552
'Passing an array as first argument to "%s()" is deprecated. Use named arguments instead.',
@@ -51,12 +58,14 @@ public function __construct(array $data = [], string $on = 'update', $field = nu
5158
$this->on = $this->getAttributeValue($data, 'on', $args, 1, $on);
5259
$this->field = $this->getAttributeValue($data, 'field', $args, 2, $field);
5360
$this->value = $this->getAttributeValue($data, 'value', $args, 3, $value);
61+
$this->setterMethod = $this->getAttributeValue($data, 'setterMethod', $args, 4, $setterMethod);
5462

5563
return;
5664
}
5765

5866
$this->on = $on;
5967
$this->field = $field;
6068
$this->value = $value;
69+
$this->setterMethod = $setterMethod;
6170
}
6271
}

0 commit comments

Comments
 (0)