ReadOnlyFieldConstraint::checkReadOnlyFields()   C
last analyzed

Complexity

Conditions 13
Paths 14

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
dl 0
loc 52
ccs 0
cts 39
cp 0
rs 6.6166
c 0
b 0
f 0
cc 13
nc 14
nop 1
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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  https://opensource.org/licenses/MIT MIT 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