Completed
Push — master ( 99d827...855666 )
by Nurlan
01:45
created

Mapper::getEmbedded()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/********************************************************************************
3
 *   Apache License, Version 2.0                                                *
4
 *                                                                              *
5
 *   Copyright [2020] [Nurlan Mukhanov <[email protected]>]                      *
6
 *                                                                              *
7
 *   Licensed under the Apache License, Version 2.0 (the "License");            *
8
 *   you may not use this file except in compliance with the License.           *
9
 *   You may obtain a copy of the License at                                    *
10
 *                                                                              *
11
 *       http://www.apache.org/licenses/LICENSE-2.0                             *
12
 *                                                                              *
13
 *   Unless required by applicable law or agreed to in writing, software        *
14
 *   distributed under the License is distributed on an "AS IS" BASIS,          *
15
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
16
 *   See the License for the specific language governing permissions and        *
17
 *   limitations under the License.                                             *
18
 *                                                                              *
19
 ********************************************************************************/
20
21
declare(strict_types=1);
22
23
namespace DBD\Entity;
24
25
use DBD\Common\Instantiatable;
26
use DBD\Common\Singleton;
27
use DBD\Entity\Common\Enforcer;
28
use DBD\Entity\Common\EntityException;
29
use DBD\Entity\Common\Utils;
30
31
/**
32
 * Class Mapper
33
 * @todo Check child classes for methods
34
 * @todo check for private vars
35
 *
36
 * @package DBD\Entity
37
 */
38
abstract class Mapper extends Singleton
39
{
40
    const ANNOTATION = "abstract";
41
    const POSTFIX = "Map";
42
43
    /**
44
     * Used for quick access to the mapper without instantiating it and have only one instance
45
     *
46
     * @return Mapper|static
47
     * @throws EntityException
48
     */
49
    public static function me(): Instantiatable
50
    {
51
        return self::instantiate();
52
    }
53
54
    /**
55
     * @param bool $callEnforcer
56
     * @return Mapper|static
57
     * @throws Common\EntityException
58
     * @throws EntityException
59
     */
60
    private static function instantiate(bool $callEnforcer = true): Mapper
61
    {
62
        /** @var static $self */
63
        $self = parent::me();
64
65
        $class = get_class($self);
66
67
        if (!isset(MapperCache::me()->fullyInstantiated[$class])) {
68
69
            // Check we set ANNOTATION properly in Mapper instance
70
            if ($callEnforcer) {
71
                Enforcer::__add(__CLASS__, $class);
72
            }
73
            $self->getAllVariables();
74
75
            MapperCache::me()->fullyInstantiated[$class] = true;
76
        }
77
        return $self;
78
    }
79
80
    /**
81
     * Read all public, private and protected variable names and their values.
82
     * Used when we need convert Mapper to Table instance
83
     *
84
     * @return MapperVariables
85
     * @throws EntityException
86
     */
87
    public function getAllVariables(): MapperVariables
88
    {
89
        $thisName = $this->name();
90
91
        if (!isset(MapperCache::me()->allVariables[$thisName])) {
92
93
            /**
94
             * All available variables
95
             * Columns are always PUBLIC
96
             * Complex, Constraints and Embedded are always PROTECTED
97
             */
98
            $allVars = get_object_vars($this);
99
            $publicVars = Utils::getObjectVars($this);
100
            $protectedVars = Utils::arrayDiff($allVars, $publicVars);
101
102
            $constraints = [];
103
            $embedded = [];
104
            $complex = [];
105
            $columns = [];
106
107
            foreach ($publicVars as $varName => $varValue) {
108
                $this->checkProperty($varValue, $varName);
109
                $columns[$varName] = $varValue;
110
            }
111
112
            foreach ($protectedVars as $varName => $varValue) {
113
                $this->checkProperty($varValue, $varName);
114
115
                if (isset($varValue[Constraint::LOCAL_COLUMN])) {
116
                    $constraints[$varName] = $varValue;
117
                } else {
118
                    if (isset($varValue[Embedded::NAME])) {
119
                        $embedded[$varName] = $varValue;
120
                    } else if (isset($varValue[Complex::TYPE])) {
121
                        $complex[$varName] = $varValue;
122
                    }
123
                }
124
            }
125
126
            $this->processComplexes($complex, $thisName);
127
128
            $this->processEmbedded($embedded, $thisName);
129
130
            $this->processColumns($thisName, $columns);
131
132
            $this->processConstraints($thisName, $constraints, $columns, $embedded, $complex);
133
134
        }
135
136
        return MapperCache::me()->allVariables[$thisName];
137
    }
138
139
    /**
140
     * Get simple Mapper class name without namespace
141
     */
142
    public function name()
143
    {
144
        $name = get_class($this);
145
146
        return substr($name, strrpos($name, '\\') + 1);
147
    }
148
149
    /**
150
     * @param $varValue
151
     * @param string $varName
152
     * @throws EntityException
153
     */
154
    private function checkProperty($varValue, string $varName): void
155
    {
156
        if (is_null($varValue)) {
157
            throw new EntityException(sprintf("property '\$%s' of %s is null", $varName, get_class($this)));
158
        }
159
        if (!is_array($varValue)) {
160
            throw new EntityException(sprintf("property '\$%s' of %s is not array", $varName, get_class($this)));
161
        }
162
        if (count($varValue) == 0) {
163
            throw new EntityException(sprintf("property '\$%s' of %s does not have definitions", $varName, get_class($this)));
164
        }
165
    }
166
167
    /**
168
     * @param array $complex
169
     * @param $thisName
170
     * @throws EntityException
171
     */
172
    private function processComplexes(array $complex, $thisName): void
173
    {
174
        /** ----------------------COMPLEX------------------------ */
175
        foreach ($complex as $complexName => $complexValue) {
176
            $this->$complexName = new Complex($complexValue);
177
            MapperCache::me()->complex[$thisName][$complexName] = $this->$complexName;
178
        }
179
        // У нас может не быть комплексов
180
        if (!isset(MapperCache::me()->complex[$thisName])) {
181
            MapperCache::me()->complex[$thisName] = [];
182
        }
183
    }
184
185
    /**
186
     * @param array $embedded
187
     * @param $thisName
188
     * @throws EntityException
189
     */
190
    private function processEmbedded(array $embedded, $thisName): void
191
    {
192
        /** ----------------------EMBEDDED------------------------ */
193
        foreach ($embedded as $embeddedName => $embeddedValue) {
194
            $this->$embeddedName = new Embedded($embeddedValue);
195
            MapperCache::me()->embedded[$thisName][$embeddedName] = $this->$embeddedName;
196
        }
197
        // У нас может не быть эмбедов
198
        if (!isset(MapperCache::me()->embedded[$thisName])) {
199
            MapperCache::me()->embedded[$thisName] = [];
200
        }
201
    }
202
203
    /**
204
     * @param $thisName
205
     * @param array $columns
206
     * @throws EntityException
207
     */
208
    private function processColumns($thisName, array $columns): void
209
    {
210
        /** ----------------------COLUMNS------------------------ */
211
        if (!isset(MapperCache::me()->columns[$thisName])) {
212
            foreach ($columns as $columnName => $columnValue) {
213
                $this->$columnName = new Column($columnValue);
214
                MapperCache::me()->columns[$thisName][$columnName] = $this->$columnName;
215
            }
216
        }
217
        // У нас может не быть колонок
218
        if (!isset(MapperCache::me()->columns[$thisName])) {
219
            MapperCache::me()->columns[$thisName] = [];
220
        }
221
    }
222
223
    /**
224
     * @param $thisName
225
     * @param array $constraints
226
     * @param array $columns
227
     * @param array $embedded
228
     * @param array $complex
229
     * @throws EntityException
230
     */
231
    private function processConstraints($thisName, array $constraints, array $columns, array $embedded, array $complex): void
232
    {
233
        /** ----------------------CONSTRAINTS------------------------ */
234
        $temporaryConstraints = [];
235
        if (!isset(MapperCache::me()->constraints[$thisName])) {
236
            $entityClass = get_parent_class($this);
237
238
            foreach ($constraints as $constraintName => $constraintValue) {
239
                $temporaryConstraint = new Constraint($constraintValue);
240
                // we asking provide self instance while table still not ready
241
                //$temporaryConstraint->localTable = $this->getTable();
242
243
                // If we use View - we do not always need to define constraint fields
244
                if ($entityClass !== View::class && is_string($temporaryConstraint->localColumn)) {
245
                    $temporaryConstraint->localColumn = $this->findColumnByOriginName($temporaryConstraint->localColumn);
246
                }
247
                $temporaryConstraints[$constraintName] = $temporaryConstraint;
248
            }
249
        }
250
251
        // У нас может не быть ограничений
252
        if (!isset(MapperCache::me()->constraints[$thisName])) {
253
            MapperCache::me()->constraints[$thisName] = [];
254
        }
255
        MapperCache::me()->allVariables[$thisName] = new MapperVariables($columns, $constraints, $embedded, $complex);
256
257
        // Now fill constraint as map is ready
258
        foreach ($temporaryConstraints as $constraintName => $temporaryConstraint) {
259
            $temporaryConstraint->localTable = $this->getTable();
260
            $this->$constraintName = $temporaryConstraint;
261
            MapperCache::me()->constraints[$thisName][$constraintName] = $this->$constraintName;
262
        }
263
    }
264
265
    /**
266
     * @param string $originName
267
     *
268
     * @return Column
269
     * @throws EntityException
270
     */
271
    public function findColumnByOriginName(string $originName): Column
272
    {
273
        foreach ($this->getColumns() as $column) {
274
            if ($column->name == $originName) {
275
                return $column;
276
            }
277
        }
278
        throw new EntityException(sprintf("Can't find origin column '%s' in %s", $originName, get_class($this)));
279
    }
280
281
    /**
282
     * @return Column[]
283
     */
284
    public function getColumns(): array
285
    {
286
        return MapperCache::me()->columns[$this->name()];
287
    }
288
289
    /**
290
     * @return mixed
291
     */
292
    public function getTable()
293
    {
294
        $thisName = $this->name();
295
296
        if (!isset(MapperCache::me()->table[$thisName])) {
297
            $parentClass = $this->getEntityClass();
298
            $table = new Table();
299
            /** @var Entity $parentClass */
300
            $table->name = $parentClass::TABLE;
301
            $table->scheme = $parentClass::SCHEME;
302
            $table->columns = $this->getColumns();
303
            $table->constraints = $this->getConstraints();
304
            $table->keys = $this->getPrimaryKey();
305
            $table->annotation = $this->getAnnotation();
306
307
            MapperCache::me()->table[$thisName] = $table;
308
        }
309
310
        return MapperCache::me()->table[$thisName];
311
    }
312
313
    /**
314
     * Returns Entity class name which uses this Mapper
315
     *
316
     * @return string
317
     */
318
    public function getEntityClass(): string
319
    {
320
        return substr(get_class($this), 0, strlen(self::POSTFIX) * -1);
321
    }
322
323
    /**
324
     * @return Constraint[]
325
     */
326
    public function getConstraints(): array
327
    {
328
        return MapperCache::me()->constraints[$this->name()];
329
    }
330
331
    /**
332
     * @return Column[] that is associative array where key is property name
333
     */
334
    public function getPrimaryKey(): array
335
    {
336
        $keys = [];
337
        foreach (MapperCache::me()->columns[$this->name()] as $columnName => $column) {
338
            if (isset($column->key) and $column->key === true) {
339
                $keys[$columnName] = $column;
340
            }
341
        }
342
343
        return $keys;
344
    }
345
346
    /**
347
     * Returns table comment
348
     *
349
     * @return string
350
     */
351
    public function getAnnotation(): string
352
    {
353
        return $this::ANNOTATION;
354
    }
355
356
    /**
357
     * @return Mapper|static
358
     * @throws EntityException
359
     */
360
    public static function meWithoutEnforcer(): Mapper
361
    {
362
        return self::instantiate(false);
363
    }
364
365
    /**
366
     * @return mixed
367
     * @deprecated
368
     * @see Mapper::getColumns()
369
     * @todo Remove in 2.4 version
370
     */
371
    public function getBaseColumns()
372
    {
373
        return MapperCache::me()->baseColumns[$this->name()];
374
    }
375
376
    /**
377
     * Special getter to access protected and private properties
378
     * @param string $property
379
     *
380
     * @return mixed
381
     * @throws EntityException
382
     */
383
    public function __get(string $property)
384
    {
385
        if (!property_exists($this, $property)) {
386
            throw new EntityException(sprintf("Can't find property '\$%s' of '%s'", $property, get_class($this)));
387
        }
388
389
        return $this->$property;
390
    }
391
392
    /**
393
     * @return Complex[]
394
     */
395
    public function getComplex(): array
396
    {
397
        return MapperCache::me()->complex[$this->name()];
398
    }
399
400
    /**
401
     * @return Embedded[]
402
     */
403
    public function getEmbedded(): array
404
    {
405
        return MapperCache::me()->embedded[$this->name()];
406
    }
407
408
    /**
409
     * @param Column $column
410
     *
411
     * @return int|string
412
     * @throws EntityException
413
     */
414
    public function getVarNameByColumn(Column $column)
415
    {
416
        foreach ($this->getOriginFieldNames() as $varName => $originFieldName) {
417
            if ($originFieldName == $column->name) {
418
                return $varName;
419
            }
420
        }
421
422
        throw new EntityException(sprintf("Seems column '%s' does not belong to this mapper", $column->name));
423
    }
424
425
    /**
426
     * @return array
427
     */
428
    public function getOriginFieldNames(): array
429
    {
430
        $thisName = $this->name();
431
        if (!isset(MapperCache::me()->originFieldNames[$thisName])) {
432
            if (count($this->getColumns())) {
433
                foreach ($this->getColumns() as $columnName => $column) {
434
                    MapperCache::me()->originFieldNames[$thisName][$columnName] = $column->name;
435
                }
436
            } else {
437
                MapperCache::me()->originFieldNames[$thisName] = [];
438
            }
439
        }
440
441
        return MapperCache::me()->originFieldNames[$thisName];
442
    }
443
}
444