Completed
Push — develop ( 6bb9f8...c1e8ff )
by Narcotic
41:48 queued 41:41
created

ReadOnlyFieldConstraint   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 3
dl 0
loc 107
rs 10
c 0
b 0
f 0
ccs 0
cts 62
cp 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C checkReadOnlyFields() 0 52 13
B propertyExists() 0 19 5
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)
0 ignored issues
show
Coding Style introduced by
function propertyExists() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

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.

Loading history...
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