1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Schema constraint that validates if readOnly: true fields are manipulated and rejects changes on those. |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace Graviton\SchemaBundle\Constraint; |
7
|
|
|
|
8
|
|
|
use Graviton\JsonSchemaBundle\Validator\Constraint\Event\ConstraintEventSchema; |
9
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccess; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* @author List of contributors <https://github.com/libgraviton/graviton/graphs/contributors> |
13
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GNU Public License |
14
|
|
|
* @link http://swisscom.ch |
15
|
|
|
*/ |
16
|
|
|
class ReadOnlyFieldConstraint |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var array |
21
|
|
|
*/ |
22
|
|
|
private $fieldMap; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* ReadOnlyFieldConstraint constructor. |
26
|
|
|
* |
27
|
|
|
* @param ConstraintUtils $utils Utils |
28
|
|
|
* @param array $readOnlyFieldsMap field map from compiler pass |
29
|
|
|
*/ |
30
|
|
|
public function __construct(ConstraintUtils $utils, array $readOnlyFieldsMap) |
31
|
|
|
{ |
32
|
|
|
$this->utils = $utils; |
33
|
|
|
$this->fieldMap = $readOnlyFieldsMap; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Checks the readOnly fields and sets error in event if needed |
38
|
|
|
* |
39
|
|
|
* @param ConstraintEventSchema $event event class |
40
|
|
|
* |
41
|
|
|
* @return void |
42
|
|
|
*/ |
43
|
|
|
public function checkReadOnlyFields(ConstraintEventSchema $event) |
44
|
|
|
{ |
45
|
|
|
$schema = $event->getSchema(); |
46
|
|
|
$data = $event->getElement(); |
47
|
|
|
|
48
|
|
|
if (!isset($schema->{'x-documentClass'}) || !isset($data->id)) { |
49
|
|
|
return; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
$documentClass = $schema->{'x-documentClass'}; |
53
|
|
|
|
54
|
|
|
if (!isset($this->fieldMap[$documentClass])) { |
55
|
|
|
return; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$readOnlyFields = $this->fieldMap[$documentClass]; |
59
|
|
|
|
60
|
|
|
// get the current record |
61
|
|
|
$currentRecord = $this->utils->getCurrentEntity(); |
62
|
|
|
|
63
|
|
|
if (is_null($currentRecord)) { |
64
|
|
|
return; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
// compare fields in both objects, if it doesn't exists in DB it can be updated. |
68
|
|
|
$accessor = PropertyAccess::createPropertyAccessor(); |
69
|
|
|
foreach ($readOnlyFields as $fieldName) { |
70
|
|
|
$storedValue = null; |
71
|
|
|
if ($this->propertyExists($currentRecord, $fieldName) && |
72
|
|
|
$accessor->isReadable($currentRecord, $fieldName)) { |
73
|
|
|
$storedValue = $accessor->getValue($currentRecord, $fieldName); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
if (is_object($storedValue)) { |
77
|
|
|
// skip objects as a whole, we will test their readOnly properties instead |
78
|
|
|
continue; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$setValue = null; |
82
|
|
|
if ($this->propertyExists($data, $fieldName) && |
83
|
|
|
$accessor->isReadable($data, $fieldName)) { |
84
|
|
|
$setValue = $accessor->getValue($data, $fieldName); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
if ($storedValue && ($storedValue != $setValue)) { |
88
|
|
|
$event->addError( |
89
|
|
|
sprintf('The value %s is read only.', json_encode($storedValue)), |
90
|
|
|
$fieldName |
91
|
|
|
); |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* To validate before accessor brakes with not found field |
98
|
|
|
* |
99
|
|
|
* @param object $object To be parsed |
100
|
|
|
* @param string $fieldName Field name, dot chained. |
101
|
|
|
* @return bool |
102
|
|
|
*/ |
103
|
|
|
private function propertyExists($object, $fieldName) |
|
|
|
|
104
|
|
|
{ |
105
|
|
|
if (property_exists($object, $fieldName)) { |
106
|
|
|
return true; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
foreach (explode('.', $fieldName) as $field) { |
110
|
|
|
if (property_exists($object, $field)) { |
111
|
|
|
$object = $object->{$field}; |
112
|
|
|
if (!is_object($object)) { |
113
|
|
|
break; |
114
|
|
|
} |
115
|
|
|
continue; |
116
|
|
|
} else { |
117
|
|
|
return false; |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
return true; |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.