Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.73% covered (success)
92.73%
102 / 110
89.66% covered (warning)
89.66%
26 / 29
CRAP
0.00% covered (danger)
0.00%
0 / 1
Entity
92.73% covered (success)
92.73%
102 / 110
89.66% covered (warning)
89.66%
26 / 29
54.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 exchangeArray
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getUnflattenedArray
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getDefaults
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValidator
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 isValid
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 testValid
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 createExample
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getExample
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 readJsonSchema
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getEntityName
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setJsonCompressLevel
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 jsonSerialize
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __clone
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 addData
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 withData
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 hasId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 toProperty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasProperty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProperty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withProperty
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 withLessData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withCleanedUpFormData
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getResolveLevel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setResolveLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 withResolveLevel
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 withReference
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace BO\Zmsentities\Schema;
4
5use BO\Zmsentities\Helper\Property;
6
7/**
8 * @SuppressWarnings(NumberOfChildren)
9 * @SuppressWarnings(PublicMethod)
10 * @SuppressWarnings(Complexity)
11 */
12class Entity extends \ArrayObject implements \JsonSerializable
13{
14    /**
15     * primary id for entity
16     *
17     */
18    public const PRIMARY = 'id';
19
20    /**
21     * @var String $schema Filename of JSON-Schema file
22     */
23    public static $schema = null;
24
25    /**
26     * @var String $schema Filename of JSON-Schema file
27     */
28    public static $schemaRefPrefix = '';
29
30    /**
31     * @var \ArrayObject $jsonSchema JSON-Schema definition to validate data
32     */
33    protected $jsonSchema = null;
34
35    /**
36     * @var Int $jsonCompressLevel
37     */
38    protected $jsonCompressLevel = 0;
39
40    /**
41     * @var Array $schemaCache for not loading and interpreting a schema twice
42     *
43     */
44    protected static $schemaCache = [];
45
46    /**
47     * @var Int $resolveLevel indicator on data integrity
48     */
49    protected $resolveLevel = null;
50
51    /**
52     * Read the json schema and let array act like an object
53     */
54    public function __construct($input = null, $flags = \ArrayObject::ARRAY_AS_PROPS, $iterator_class = "ArrayIterator")
55    {
56        parent::__construct($this->getDefaults(), $flags, $iterator_class);
57        if ($input) {
58            $input = $this->getUnflattenedArray($input);
59            $this->addData($input);
60        }
61    }
62
63    public function exchangeArray($input)
64    {
65        parent::exchangeArray($this->getDefaults());
66        $input = $this->getUnflattenedArray($input);
67        $this->addData($input);
68    }
69
70    public function getUnflattenedArray($input)
71    {
72        if (!$input instanceof UnflattedArray) {
73            $input = new UnflattedArray($input);
74            $input->getUnflattenedArray();
75        }
76        $input = $input->getValue();
77        return $input;
78    }
79    /**
80     * Set Default values
81     */
82    public function getDefaults()
83    {
84        return [];
85    }
86
87    /**
88     * This method is private, because the used library should not be used outside of this class!
89     */
90    public function getValidator($locale = 'de_DE', $resolveLevel = 0)
91    {
92        $jsonSchema = self::readJsonSchema()->withResolvedReferences($resolveLevel);
93        $data = (new Schema($this))->withoutRefs();
94        if (Property::__keyExists('$schema', $data)) {
95            unset($data['$schema']);
96        }
97        $validator = new Validator($data->toJsonObject(true), $jsonSchema, $locale);
98        return $validator;
99    }
100
101    /**
102     * Check if the given data validates against the given jsonSchema
103     *
104     * @return bool
105     */
106    public function isValid($resolveLevel = 0): bool
107    {
108        $validator = $this->getValidator('de_DE', $resolveLevel = 0);
109        return $validator->isValid();
110    }
111
112    /**
113     * Check if the given data validates against the given jsonSchema
114     *
115     * @throws \BO\Zmsentities\Expcetion\SchemaValidation
116     * @return bool
117     */
118    public function testValid($locale = 'de_DE', $resolveLevel = 0): bool
119    {
120        $validator = $this->getValidator($locale, $resolveLevel);
121        if (!$validator->isValid()) {
122            $exception = new \BO\Zmsentities\Exception\SchemaValidation();
123            $exception->setSchemaName($this->getEntityName());
124            $exception->setValidationError($validator->getErrors());
125            throw $exception;
126        }
127        return true;
128    }
129
130    /**
131     * create an example for testing
132     *
133     * @return self
134     */
135    public static function createExample()
136    {
137        $object = new static();
138        return $object->getExample();
139    }
140
141    /**
142     * return a new object as example
143     *
144     * @return self
145     */
146    public static function getExample()
147    {
148        $class = get_called_class();
149        $jsonSchema = self::readJsonSchema();
150        if ($jsonSchema->offsetExists('example')) {
151            return new $class($jsonSchema['example']);
152        }
153        return new $class();
154    }
155
156    protected static function readJsonSchema()
157    {
158        $class = get_called_class();
159        if (!array_key_exists($class, self::$schemaCache)) {
160            self::$schemaCache[$class] = Loader::asArray($class::$schema);
161        }
162        return self::$schemaCache[$class];
163    }
164
165    public function getEntityName()
166    {
167        $entity = get_class($this);
168        $entity = preg_replace('#.*[\\\]#', '', $entity);
169        $entity = strtolower($entity);
170        return $entity;
171    }
172
173    public function setJsonCompressLevel($jsonCompressLevel)
174    {
175        $this->jsonCompressLevel = $jsonCompressLevel;
176        return $this;
177    }
178
179    public function jsonSerialize()
180    {
181        $schema = array(
182            '$schema' => 'https://schema.berlin.de/queuemanagement/' . $this->getEntityName() . '.json'
183        );
184        $schema = array_merge($schema, $this->getArrayCopy());
185        if ($this instanceof \BO\Zmsentities\Helper\NoSanitize) {
186            $serialize = $schema;
187        } else {
188            $schema = new Schema($schema);
189            $schema->setDefaults($this->getDefaults());
190            $schema->setJsonCompressLevel($this->jsonCompressLevel);
191            $serialize = $schema->toJsonObject();
192        }
193        return $serialize;
194    }
195
196    public function __toString()
197    {
198        return json_encode($this->jsonSerialize(), JSON_HEX_QUOT);
199    }
200
201    public function __clone()
202    {
203        foreach ($this as $key => $property) {
204            if (is_object($property)) {
205                $this[$key] = clone $property;
206            }
207        }
208    }
209
210    /**
211     * Performs a merge with an iterable
212     * Sub-entities are preserved
213     */
214    public function addData($mergeData)
215    {
216        foreach ($mergeData as $key => $item) {
217            if (isset($this[$key])) {
218                if ($this[$key] instanceof Entity) {
219                    $this[$key]->setResolveLevel($this->getResolveLevel() - 1);
220                    $this[$key]->addData($item);
221                } elseif ($this[$key] instanceof \BO\Zmsentities\Collection\Base) {
222                    $this[$key]->exchangeArray([]);
223                    $this[$key]->setResolveLevel($this->getResolveLevel() - 1);
224                    $this[$key]->addData($item);
225                } elseif (is_array($this[$key])) {
226                    $this[$key] = array_replace_recursive($this[$key], $item);
227                } else {
228                    $this[$key] = $item;
229                }
230            } else {
231                $this[$key] = $item;
232            }
233        }
234        return $this;
235    }
236
237    /**
238     * Performs addData on a cloned entity
239     */
240    public function withData($mergeData)
241    {
242        $entity = clone $this;
243        $entity->addData($mergeData);
244        return $entity;
245    }
246
247    public function hasId()
248    {
249        return (false !== $this->getId()) ? true : false;
250    }
251
252    public function getId()
253    {
254        $idName = $this::PRIMARY;
255        return ($this->offsetExists($idName) && $this[$idName]) ? $this[$idName] : false;
256    }
257
258    /**
259     * Allow accessing properties without checking if it exists first
260     *
261     * @return \BO\Zmsentities\Helper\Property
262     */
263    public function toProperty()
264    {
265        return new \BO\Zmsentities\Helper\Property($this);
266    }
267
268    public function hasProperty($propertyName)
269    {
270        return $this->toProperty()->{$propertyName}->isAvailable();
271    }
272
273    public function getProperty($propertyName, $default = '')
274    {
275        return $this->toProperty()->{$propertyName}->get($default);
276    }
277
278    /**
279     * Change property without changing original
280     */
281    public function withProperty($propertyName, $newValue)
282    {
283        $entity = clone $this;
284        $entity[$propertyName] = $newValue;
285        return $entity;
286    }
287
288    /**
289     * Reduce data of dereferenced entities to a required minimum
290     *
291     */
292    public function withLessData()
293    {
294        return clone $this;
295    }
296
297    /**
298     * Reduce data of dereferenced entities to a required minimum
299     *
300     */
301    public function withCleanedUpFormData()
302    {
303        $entity = clone $this;
304        if (isset($entity['save'])) {
305            unset($entity['save']);
306        }
307        if (isset($entity['removeImage'])) {
308            unset($entity['removeImage']);
309        }
310        return $entity;
311    }
312
313    /**
314     * @return Int
315     */
316    public function getResolveLevel()
317    {
318        return $this->resolveLevel;
319    }
320
321    /**
322     * @param Int $resolveLevel
323     * @return self
324     */
325    public function setResolveLevel($resolveLevel)
326    {
327        $this->resolveLevel = $resolveLevel;
328        return $this;
329    }
330
331    /**
332     * Set a very strict resolveLevel to reduce data
333     *
334     * @param Int $resolveLevel
335     * @return self
336     */
337    public function withResolveLevel($resolveLevel)
338    {
339        if ($resolveLevel >= 0) {
340            $entity = clone $this;
341            foreach ($entity as $key => $value) {
342                if ($value instanceof Entity || $value instanceof \BO\Zmsentities\Collection\Base) {
343                    $entity[$key] = $value->withResolveLevel($resolveLevel - 1);
344                } else {
345                    $entity[$key] = $value;
346                }
347            }
348            $entity->setResolveLevel($resolveLevel);
349            return $entity;
350        } else {
351            return $this->withReference();
352        }
353    }
354
355    /**
356     * Replace data with a jsonSchema Reference
357     *
358     * @param Array $additionalData
359     * @return self
360     */
361    public function withReference($additionalData = [])
362    {
363        if (isset($this[$this::PRIMARY])) {
364            $additionalData['$ref'] =
365                $this::$schemaRefPrefix . $this->getEntityName() . '/' . $this[$this::PRIMARY] . '/';
366            return $additionalData;
367        } else {
368            return $this->withResolveLevel(0);
369        }
370    }
371}