Passed
Pull Request — master (#3)
by
unknown
01:41
created

Mapper::getComplex()   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
use DBD\Entity\Interfaces\EntityMapper;
31
32
33
/**
34
 * Class Mapper
35
 * @todo Check child classes for methods
36
 * @todo check for private vars
37
 *
38
 * @package DBD\Entity
39
 */
40
abstract class Mapper extends Singleton implements EntityMapper
41
{
42
    use MapperTrait;
0 ignored issues
show
Bug introduced by
The trait DBD\Entity\MapperTrait requires the property $tables which is not provided by DBD\Entity\Mapper.
Loading history...
43
44
    const ANNOTATION = "abstract";
45
    const POSTFIX = "Map";
46
47
    /**
48
     * Used for quick access to the mapper without instantiating it and have only one instance
49
     *
50
     * @return Mapper|static
51
     * @throws EntityException
52
     */
53
    public static function me(): Instantiatable
54
    {
55
        return self::instantiate();
56
    }
57
58
    /**
59
     * @param bool $callEnforcer
60
     * @return Mapper|static
61
     * @throws Common\EntityException
62
     * @throws EntityException
63
     */
64
    private static function instantiate(bool $callEnforcer = true): Mapper
65
    {
66
        /** @var static $self */
67
        $self = parent::me();
68
69
        $class = get_class($self);
70
71
        if (!isset(MapperCache::me()->fullyInstantiated[$class])) {
72
73
            // Check we set ANNOTATION properly in Mapper instance
74
            if ($callEnforcer) {
75
                Enforcer::__add(__CLASS__, $class);
76
            }
77
            $self->getAllVariables();
78
79
            MapperCache::me()->fullyInstantiated[$class] = true;
80
        }
81
        return $self;
82
    }
83
84
    /**
85
     * Read all public, private and protected variable names and their values.
86
     * Used when we need convert Mapper to Table instance
87
     *
88
     * @return MapperVariables
89
     * @throws EntityException
90
     */
91
    public function getAllVariables(): MapperVariables
92
    {
93
        $thisName = $this->name();
94
95
        if (!isset(MapperCache::me()->allVariables[$thisName])) {
96
97
            /**
98
             * All available variables
99
             * Columns are always PUBLIC
100
             * Complex, Constraints and Embedded are always PROTECTED
101
             */
102
            $allVars = get_object_vars($this);
103
            $publicVars = Utils::getObjectVars($this);
104
            $protectedVars = Utils::arrayDiff($allVars, $publicVars);
105
106
            $constraints = $embedded = $complex = $columns = [];
107
108
            foreach ($publicVars as $varName => $varValue) {
109
                $this->checkProperty($varValue, $varName);
110
                $columns[$varName] = $varValue;
111
            }
112
113
            foreach ($protectedVars as $varName => $varValue) {
114
                $this->checkProperty($varValue, $varName);
115
116
                if (isset($varValue[Constraint::LOCAL_COLUMN])) {
117
                    $constraints[$varName] = $varValue;
118
                } else {
119
                    if (isset($varValue[Embedded::NAME])) {
120
                        $embedded[$varName] = $varValue;
121
                    } else if (isset($varValue[Complex::TYPE])) {
122
                        $complex[$varName] = $varValue;
123
                    }
124
                }
125
            }
126
127
            $this->processComplexes($complex);
128
129
            $this->processEmbedded($embedded);
130
131
            $this->processColumns($columns);
132
133
            $this->processConstraints($constraints, $columns, $embedded, $complex);
134
135
        }
136
137
        return MapperCache::me()->allVariables[$thisName];
138
    }
139
140
    /**
141
     * Get simple Mapper class name without namespace
142
     */
143
    public function name(): string
144
    {
145
        $name = get_class($this);
146
147
        return substr($name, strrpos($name, '\\') + 1);
148
    }
149
150
    /**
151
     * Returns Entity class name which uses this Mapper
152
     *
153
     * @return string
154
     */
155
    public function getEntityClass(): string
156
    {
157
        return substr(get_class($this), 0, strlen(self::POSTFIX) * -1);
158
    }
159
160
    public function getScheme(): string
161
    {
162
        return $this->getEntityClass()::SCHEME;
163
    }
164
165
    public function getTableName(): string
166
    {
167
        return $this->getEntityClass()::TABLE;
168
    }
169
170
    /**
171
     * @param $varValue
172
     * @param string $varName
173
     * @throws EntityException
174
     */
175
    private function checkProperty($varValue, string $varName): void
176
    {
177
        if (is_null($varValue)) {
178
            throw new EntityException(sprintf("property '\$%s' of %s is null", $varName, get_class($this)));
179
        }
180
        if (!is_array($varValue)) {
181
            throw new EntityException(sprintf("property '\$%s' of %s is not array", $varName, get_class($this)));
182
        }
183
        if (count($varValue) == 0) {
184
            throw new EntityException(sprintf("property '\$%s' of %s does not have definitions", $varName, get_class($this)));
185
        }
186
    }
187
188
    /**
189
     * @param array $complex
190
     * @throws EntityException
191
     */
192
    private function processComplexes(array $complex): void
193
    {
194
        $thisName = $this->name();
195
196
        /** ----------------------COMPLEX------------------------ */
197
        foreach ($complex as $complexName => $complexValue) {
198
            $this->$complexName = new Complex($complexValue);
199
            MapperCache::me()->complex[$thisName][$complexName] = $this->$complexName;
200
        }
201
        // У нас может не быть комплексов
202
        if (!isset(MapperCache::me()->complex[$thisName])) {
203
            MapperCache::me()->complex[$thisName] = [];
204
        }
205
    }
206
207
    /**
208
     * @param array $embedded
209
     * @throws EntityException
210
     */
211
    private function processEmbedded(array $embedded): void
212
    {
213
        $thisName = $this->name();
214
        /** ----------------------EMBEDDED------------------------ */
215
        foreach ($embedded as $embeddedName => $embeddedValue) {
216
            $this->$embeddedName = new Embedded($embeddedValue);
217
            MapperCache::me()->embedded[$thisName][$embeddedName] = $this->$embeddedName;
218
        }
219
        // У нас может не быть эмбедов
220
        if (!isset(MapperCache::me()->embedded[$thisName])) {
221
            MapperCache::me()->embedded[$thisName] = [];
222
        }
223
    }
224
225
    /**
226
     * @param array $columns
227
     * @throws EntityException
228
     */
229
    private function processColumns(array $columns): void
230
    {
231
        $thisName = $this->name();
232
233
        /** ----------------------COLUMNS------------------------ */
234
        if (!isset(MapperCache::me()->columns[$thisName])) {
235
            foreach ($columns as $columnName => $columnValue) {
236
                $this->$columnName = new Column($columnValue);
237
                MapperCache::me()->columns[$thisName][$columnName] = $this->$columnName;
238
            }
239
        }
240
        // У нас может не быть колонок
241
        if (!isset(MapperCache::me()->columns[$thisName])) {
242
            MapperCache::me()->columns[$thisName] = [];
243
        }
244
    }
245
246
    /**
247
     * @param array $constraints
248
     * @param array $columns
249
     * @param array $embedded
250
     * @param array $complex
251
     * @throws EntityException
252
     */
253
    private function processConstraints(array $constraints, array $columns, array $embedded, array $complex): void
254
    {
255
        $thisName = $this->name();
256
257
        /** ----------------------CONSTRAINTS------------------------ */
258
        $temporaryConstraints = [];
259
        if (!isset(MapperCache::me()->constraints[$thisName])) {
260
            $entityClass = get_parent_class($this);
261
262
            foreach ($constraints as $constraintName => $constraintValue) {
263
                $temporaryConstraint = new Constraint($constraintValue);
264
                // we asking provide self instance while table still not ready
265
                //$temporaryConstraint->localTable = $this->getTable();
266
267
                // If we use View - we do not always need to define constraint fields
268
                if ($entityClass !== View::class && is_string($temporaryConstraint->localColumn)) {
269
                    $temporaryConstraint->localColumn = $this->findColumnByOriginName($temporaryConstraint->localColumn);
270
                }
271
                $temporaryConstraints[$constraintName] = $temporaryConstraint;
272
            }
273
        }
274
275
        // У нас может не быть ограничений
276
        if (!isset(MapperCache::me()->constraints[$thisName])) {
277
            MapperCache::me()->constraints[$thisName] = [];
278
        }
279
        MapperCache::me()->allVariables[$thisName] = new MapperVariables($columns, $constraints, $embedded, $complex);
280
281
        // Now fill constraint as map is ready
282
        foreach ($temporaryConstraints as $constraintName => $temporaryConstraint) {
283
            $temporaryConstraint->localTable = $this->getTable();
284
            $this->$constraintName = $temporaryConstraint;
285
            MapperCache::me()->constraints[$thisName][$constraintName] = $this->$constraintName;
286
        }
287
    }
288
289
    /**
290
     * Returns table comment
291
     *
292
     * @return string
293
     */
294
    public function getAnnotation(): string
295
    {
296
        return $this::ANNOTATION;
297
    }
298
299
    /**
300
     * @return Mapper|static
301
     * @throws EntityException
302
     */
303
    public static function meWithoutEnforcer(): Mapper
304
    {
305
        return self::instantiate(false);
306
    }
307
308
    /**
309
     * Special getter to access protected and private properties
310
     *
311
     * @param string $property
312
     * @return mixed
313
     * @throws EntityException
314
     */
315
    public function __get(string $property)
316
    {
317
        if (!property_exists($this, $property)) {
318
            throw new EntityException(sprintf("Can't find property '\$%s' of '%s'", $property, get_class($this)));
319
        }
320
321
        return $this->$property;
322
    }
323
}
324