Completed
Push — 2.x ( db763c...24284c )
by Aleksei
05:34 queued 05:33
created

Entity::merge()   B

Complexity

Conditions 7
Paths 27

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 9
nc 27
nop 1
dl 0
loc 17
ccs 8
cts 8
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Definition;
6
7
use Cycle\ORM\MapperInterface;
8
use Cycle\ORM\RepositoryInterface;
9
use Cycle\ORM\Select\ScopeInterface;
10
use Cycle\ORM\Select\SourceInterface;
11
use Cycle\Schema\Definition\Map\FieldMap;
12
use Cycle\Schema\Definition\Map\ForeignKeyMap;
13
use Cycle\Schema\Definition\Map\OptionMap;
14
use Cycle\Schema\Definition\Map\RelationMap;
15
use Cycle\Schema\Exception\EntityException;
16
use Cycle\Schema\SchemaModifierInterface;
17
18
/**
19
 * Contains information about specific entity definition.
20
 *
21
 * @template TEntity of object
22
 */
23
final class Entity
24
{
25
    private OptionMap $options;
26
27
    /**
28
     * @var non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
29
     */
30
    private ?string $role = null;
31
32
    /**
33
     * @var class-string<TEntity>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TEntity>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TEntity>|null.
Loading history...
34
     */
35
    private ?string $class = null;
36
37
    /**
38
     * @var non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
39
     */
40
    private ?string $database = null;
41
42
    /**
43
     * @var non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
44
     */
45
    private ?string $tableName = null;
46
47
    /**
48
     * @var class-string<MapperInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<MapperInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<MapperInterface>|null.
Loading history...
49 1438
     */
50
    private ?string $mapper = null;
51 1438
52 1438
    /**
53 1438
     * @var class-string<SourceInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SourceInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SourceInterface>|null.
Loading history...
54 1438
     */
55 1438
    private ?string $source = null;
56
57
    /**
58
     * @var class-string<ScopeInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ScopeInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ScopeInterface>|null.
Loading history...
59
     */
60 56
    private ?string $scope = null;
61
62 56
    /**
63 56
     * @var class-string<RepositoryInterface<TEntity>>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<RepositoryInterface<TEntity>>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<RepositoryInterface<TEntity>>|null.
Loading history...
64 56
     */
65 56
    private ?string $repository = null;
66 56
67
    /**
68 16
     * @var class-string|class-string[]|non-empty-string|non-empty-string[]|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|class-strin...non-empty-string[]|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|class-string[]|non-empty-string|non-empty-string[]|null.
Loading history...
69
     */
70 16
    private array|string|null $typecast = null;
71
72
    private array $schema = [];
73 1356
74
    private FieldMap $fields;
75 1356
76
    private RelationMap $relations;
77 1356
    private FieldMap $primaryFields;
78
    private array $schemaModifiers = [];
79
    private ?Inheritance $inheritance = null;
80 1364
    /** @var class-string|null */
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
81
    private ?string $stiParent = null;
82 1364
    private ForeignKeyMap $foreignKeys;
83
84
    public function __construct()
85 1318
    {
86
        $this->options = new OptionMap();
87 1318
        $this->fields = new FieldMap();
88
        $this->primaryFields = new FieldMap();
89 1318
        $this->relations = new RelationMap();
90
        $this->foreignKeys = new ForeignKeyMap();
91
    }
92 1250
93
    /**
94 1250
     * Full entity copy.
95
     */
96
    public function __clone()
97 2
    {
98
        $this->options = clone $this->options;
99 2
        $this->fields = clone $this->fields;
100
        $this->primaryFields = clone $this->primaryFields;
101 2
        $this->relations = clone $this->relations;
102
        $this->foreignKeys = clone $this->foreignKeys;
103
    }
104 790
105
    public function getOptions(): OptionMap
106 790
    {
107
        return $this->options;
108
    }
109 2
110
    /**
111 2
     * @param non-empty-string $role
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
112
     */
113 2
    public function setRole(string $role): self
114
    {
115
        $this->role = $role;
116 790
117
        return $this;
118 790
    }
119
120
    /**
121 2
     * @return non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
122
     */
123 2
    public function getRole(): ?string
124
    {
125 2
        return $this->role;
126
    }
127
128 790
    /**
129
     * @param class-string<TEntity> $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TEntity> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TEntity>.
Loading history...
130 790
     */
131
    public function setClass(string $class): self
132
    {
133 2
        $this->class = $class;
134
135 2
        return $this;
136
    }
137 2
138
    /**
139
     * @return class-string<TEntity>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TEntity>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TEntity>|null.
Loading history...
140 790
     */
141
    public function getClass(): ?string
142 790
    {
143
        return $this->class;
144
    }
145
146
    /**
147
     * @param class-string<MapperInterface>|null $mapper
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<MapperInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<MapperInterface>|null.
Loading history...
148
     */
149
    public function setMapper(?string $mapper): self
150 598
    {
151
        $this->mapper = $mapper;
152 598
153
        return $this;
154 598
    }
155
156
    /**
157
     * @return class-string<MapperInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<MapperInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<MapperInterface>|null.
Loading history...
158
     */
159
    public function getMapper(): ?string
160 794
    {
161
        return $this->normalizeClass($this->mapper);
162 794
    }
163
164
    /**
165 1328
     * @param class-string<SourceInterface>|null $source
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SourceInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SourceInterface>|null.
Loading history...
166
     */
167 1328
    public function setSource(?string $source): self
168
    {
169
        $this->source = $source;
170 1252
171
        return $this;
172 1252
    }
173
174
    /**
175 16
     * @return class-string<SourceInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SourceInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SourceInterface>|null.
Loading history...
176
     */
177 16
    public function getSource(): ?string
178 16
    {
179
        return $this->normalizeClass($this->source);
180
    }
181
182
    /**
183
     * @param class-string<ScopeInterface>|null $scope
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ScopeInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ScopeInterface>|null.
Loading history...
184 794
     */
185
    public function setScope(?string $scope): self
186
    {
187 794
        $this->scope = $scope;
188 784
189
        return $this;
190 2
    }
191
192 2
    /**
193
     * @return class-string<ScopeInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ScopeInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ScopeInterface>|null.
Loading history...
194 2
     */
195
    public function getScope(): ?string
196
    {
197 790
        return $this->normalizeClass($this->scope);
198
    }
199 790
200
    /**
201
     * @param class-string<RepositoryInterface<TEntity>>|null $repository
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<RepositoryInterface<TEntity>>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<RepositoryInterface<TEntity>>|null.
Loading history...
202
     */
203
    public function setRepository(?string $repository): self
204
    {
205 10
        $this->repository = $repository;
206
207 10
        return $this;
208 2
    }
209 2
210
    /**
211
     * @return class-string<RepositoryInterface<TEntity>>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<RepositoryInterface<TEntity>>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<RepositoryInterface<TEntity>>|null.
Loading history...
212
     */
213 10
    public function getRepository(): ?string
214 10
    {
215 10
        return $this->normalizeClass($this->repository);
216
    }
217
218 10
    /**
219
     * @param class-string|class-string[]|non-empty-string|non-empty-string[]|null $typecast
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|class-strin...non-empty-string[]|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|class-string[]|non-empty-string|non-empty-string[]|null.
Loading history...
220
     *
221
     * @return $this
222
     */
223 794
    public function setTypecast(array|string|null $typecast): self
224
    {
225 794
        $this->typecast = $typecast;
226 2
227
        return $this;
228
    }
229 792
230 792
    /**
231 790
     * @return class-string|class-string[]|non-empty-string|non-empty-string[]|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|class-strin...non-empty-string[]|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|class-string[]|non-empty-string|non-empty-string[]|null.
Loading history...
232
     */
233
    public function getTypecast(): array|string|null
234
    {
235 36
        return $this->typecast;
236
    }
237
238
    public function getFields(): FieldMap
239
    {
240
        return $this->fields;
241
    }
242
243 6
    public function getRelations(): RelationMap
244
    {
245 6
        return $this->relations;
246
    }
247 6
248 6
    public function getForeignKeys(): ForeignKeyMap
249 4
    {
250
        return $this->foreignKeys;
251 4
    }
252
253
    public function addSchemaModifier(SchemaModifierInterface $modifier): self
254
    {
255
        $this->schemaModifiers[] = $modifier;
256 1236
        return $this;
257
    }
258 1236
259
    /**
260 1236
     * @return \Traversable<array-key, SchemaModifierInterface>
261 1236
     */
262 1184
    public function getSchemaModifiers(): \Traversable
263
    {
264
        // yield from $this->getRelations();
265
        yield from $this->schemaModifiers;
266 1236
    }
267 1184
268
    public function setSchema(array $schema): self
269
    {
270
        $this->schema = $schema;
271 94
272 94
        return $this;
273
    }
274
275 2
    public function getSchema(): array
276
    {
277
        return $this->schema;
278 92
    }
279
280
    /**
281 796
     * Merge entity relations and fields.
282
     */
283 796
    public function merge(self $entity): void
284 788
    {
285
        foreach ($entity->getRelations() as $name => $relation) {
286
            if (!$this->relations->has($name)) {
287 8
                $this->relations->set($name, $relation);
288
            }
289
        }
290 18
291
        foreach ($entity->getFields() as $name => $field) {
292 18
            if (!$this->fields->has($name)) {
293 18
                $this->fields->set($name, $field);
294
            }
295 792
        }
296
297 792
        foreach ($entity->getForeignKeys() as $foreignKey) {
298
            if (!$this->foreignKeys->has($foreignKey)) {
299
                $this->foreignKeys->set($foreignKey);
300
            }
301
        }
302
    }
303 782
304
    /**
305 782
     * Check if entity has primary key
306
     */
307
    public function hasPrimaryKey(): bool
308
    {
309
        if ($this->primaryFields->count() > 0) {
310
            return true;
311 4
        }
312
313 4
        foreach ($this->getFields() as $field) {
314 4
            if ($field->isPrimary()) {
315
                return true;
316 2
            }
317
        }
318 2
319
        return false;
320
    }
321 2
322
    /**
323 2
     * Set primary key using column list
324 2
     *
325
     * @param string[] $columns
326 2
     */
327
    public function setPrimaryColumns(array $columns): void
328 2
    {
329
        $this->primaryFields = new FieldMap();
330
331 2
        foreach ($columns as $column) {
332
            $name = $this->fields->getKeyByColumnName($column);
333 2
            $this->primaryFields->set($name, $this->fields->get($name));
334 2
        }
335
    }
336
337
    /**
338
     * Get entity primary key property names
339
     */
340
    public function getPrimaryFields(): FieldMap
341
    {
342
        $map = new FieldMap();
343
344
        foreach ($this->getFields() as $name => $field) {
345
            if ($field->isPrimary()) {
346
                $map->set($name, $field);
347
            }
348
        }
349
350
        if ($this->primaryFields->count() === 0 xor $map->count() === 0) {
351
            return $map->count() === 0 ? $this->primaryFields : $map;
352
        }
353
354
        if (
355
            $this->primaryFields->count() !== $map->count()
356
            || array_diff($map->getColumnNames(), $this->primaryFields->getColumnNames()) !== []
357
        ) {
358
            // todo make friendly exception
359
            throw new EntityException("Ambiguous primary key definition for `{$this->getRole()}`.");
360
        }
361
362
        return $this->primaryFields;
363
    }
364
365
    /**
366
     * @template T of object
367
     *
368
     * @param class-string<T>|null $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>|null.
Loading history...
369
     *
370
     * @return ($class is class-string<T> ? class-string<T> : null)
0 ignored issues
show
Documentation Bug introduced by
The doc comment ($class at position 1 could not be parsed: Unknown type name '$class' at position 1 in ($class.
Loading history...
371
     */
372
    private function normalizeClass(string $class = null): ?string
373
    {
374
        if ($class === null) {
375
            return null;
376
        }
377
378
        /** @var class-string<T> $class */
379
        $class = \ltrim($class, '\\');
380
381
        return $class;
382
    }
383
384
    public function setInheritance(Inheritance $inheritance): void
385
    {
386
        $this->inheritance = $inheritance;
387
    }
388
389
    public function getInheritance(): ?Inheritance
390
    {
391
        return $this->inheritance;
392
    }
393
394
    /**
395
     * Check if entity is a child of STI
396
     */
397
    public function isChildOfSingleTableInheritance(): bool
398
    {
399
        return $this->stiParent !== null;
400
    }
401
402
    /**
403
     * @param class-string|null $parentClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
404
     */
405
    public function markAsChildOfSingleTableInheritance(?string $parentClass): void
406
    {
407
        $this->stiParent = $parentClass;
408
    }
409
410
    public function getDatabase(): ?string
411
    {
412
        return $this->database;
413
    }
414
415
    /**
416
     * @param non-empty-string|null $database
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
417
     */
418
    public function setDatabase(?string $database): void
419
    {
420
        $this->database = $database;
421
    }
422
423
    public function getTableName(): ?string
424
    {
425
        return $this->tableName;
426
    }
427
428
    /**
429
     * @param non-empty-string $tableName
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
430
     */
431
    public function setTableName(string $tableName): void
432
    {
433
        $this->tableName = $tableName;
434
    }
435
}
436