1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
declare(strict_types=1); |
5
|
|
|
|
6
|
|
|
/* |
7
|
|
|
* This file is part of Biurad opensource projects. |
8
|
|
|
* |
9
|
|
|
* PHP version 7.2 and above required |
10
|
|
|
* |
11
|
|
|
* @author Divine Niiquaye Ibok <[email protected]> |
12
|
|
|
* @copyright 2019 Biurad Group (https://biurad.com/) |
13
|
|
|
* @license https://opensource.org/licenses/BSD-3-Clause License |
14
|
|
|
* |
15
|
|
|
* For the full copyright and license information, please view the LICENSE |
16
|
|
|
* file that was distributed with this source code. |
17
|
|
|
*/ |
18
|
|
|
|
19
|
|
|
namespace Biurad\Cycle; |
20
|
|
|
|
21
|
|
|
use Cycle\ORM\Mapper\Mapper; |
22
|
|
|
use Cycle\ORM\Schema; |
23
|
|
|
use Cycle\ORM\Select\Repository; |
24
|
|
|
use Cycle\ORM\Select\Source; |
25
|
|
|
use Cycle\Schema\Definition\Entity; |
26
|
|
|
use Cycle\Schema\GeneratorInterface; |
27
|
|
|
use Cycle\Schema\Registry; |
28
|
|
|
use ReflectionClass; |
29
|
|
|
use Spiral\Database\Exception\CompilerException; |
30
|
|
|
|
31
|
|
|
final class Compiler |
32
|
|
|
{ |
33
|
|
|
/** @var array */ |
34
|
|
|
private $result = []; |
35
|
|
|
|
36
|
|
|
/** @var GeneratorInterface[] */ |
37
|
|
|
private $generators; |
38
|
|
|
|
39
|
|
|
/** @var array */ |
40
|
|
|
private $defaults = [ |
41
|
|
|
Schema::MAPPER => Mapper::class, |
42
|
|
|
Schema::REPOSITORY => Repository::class, |
43
|
|
|
Schema::SOURCE => Source::class, |
44
|
|
|
Schema::CONSTRAIN => null, |
45
|
|
|
]; |
46
|
|
|
|
47
|
|
|
/** @var \Doctrine\Inflector\Inflector */ |
48
|
|
|
private $inflector; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param GeneratorInterface[] $generators |
52
|
|
|
* @param array $defaults |
53
|
|
|
*/ |
54
|
|
|
public function __construct(array $generators = [], array $defaults = []) |
55
|
|
|
{ |
56
|
|
|
$this->generators = $generators; |
57
|
|
|
$this->defaults = $defaults + $this->defaults; |
58
|
|
|
|
59
|
|
|
$this->inflector = (new \Doctrine\Inflector\Rules\English\InflectorFactory())->build(); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Add a schema generator |
64
|
|
|
* |
65
|
|
|
* @param GeneratorInterface $generator |
66
|
|
|
*/ |
67
|
|
|
public function addGenerator(GeneratorInterface $generator): void |
68
|
|
|
{ |
69
|
|
|
$this->generators[] = $generator; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Compile the registry schema. |
74
|
|
|
* |
75
|
|
|
* @param Registry $registry |
76
|
|
|
* |
77
|
|
|
* @return array |
78
|
|
|
*/ |
79
|
|
|
public function compile(Registry $registry): array |
80
|
|
|
{ |
81
|
|
|
foreach ($this->generators as $generator) { |
82
|
|
|
if (!$generator instanceof GeneratorInterface) { |
83
|
|
|
throw new CompilerException(\sprintf( |
84
|
|
|
'Invalid generator `%s`', |
85
|
|
|
\is_object($generator) ? \get_class($generator) : \gettype($generator) |
86
|
|
|
)); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$registry = $generator->run($registry); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
foreach ($registry->getIterator() as $entity) { |
93
|
|
|
if ($this->getPrimary($entity) === null) { |
|
|
|
|
94
|
|
|
// incomplete entity, skip |
95
|
|
|
continue; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
$this->compute($registry, $entity); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
return $this->result; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Get compiled schema result. |
106
|
|
|
* |
107
|
|
|
* @return array |
108
|
|
|
*/ |
109
|
|
|
public function getSchema(): array |
110
|
|
|
{ |
111
|
|
|
return $this->result; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Compile entity and relation definitions into packed ORM schema. |
116
|
|
|
* |
117
|
|
|
* @param Registry $registry |
118
|
|
|
* @param Entity $entity |
119
|
|
|
*/ |
120
|
|
|
protected function compute(Registry $registry, Entity $entity): void |
121
|
|
|
{ |
122
|
|
|
$schema = [ |
123
|
|
|
Schema::ENTITY => $entity->getClass(), |
124
|
|
|
Schema::SOURCE => $entity->getSource() ?? $this->defaults[Schema::SOURCE], |
125
|
|
|
Schema::MAPPER => $entity->getMapper() ?? $this->defaults[Schema::MAPPER], |
126
|
|
|
Schema::REPOSITORY => $entity->getRepository() ?? $this->defaults[Schema::REPOSITORY], |
127
|
|
|
Schema::CONSTRAIN => $entity->getConstrain() ?? $this->defaults[Schema::CONSTRAIN], |
128
|
|
|
Schema::SCHEMA => $entity->getSchema(), |
129
|
|
|
Schema::PRIMARY_KEY => $this->getPrimary($entity), |
|
|
|
|
130
|
|
|
Schema::COLUMNS => $this->renderColumns($entity), |
131
|
|
|
Schema::FIND_BY_KEYS => $this->renderReferences($entity), |
132
|
|
|
Schema::TYPECAST => $this->renderTypecast($entity), |
133
|
|
|
Schema::RELATIONS => $this->renderRelations($registry, $entity), |
134
|
|
|
]; |
135
|
|
|
|
136
|
|
|
if ($registry->hasTable($entity)) { |
137
|
|
|
$schema[Schema::DATABASE] = $registry->getDatabase($entity); |
138
|
|
|
$schema[Schema::TABLE] = $registry->getTable($entity); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
// table inheritance |
142
|
|
|
foreach ($registry->getChildren($entity) as $child) { |
143
|
|
|
$this->result[$child->getClass()] = [Schema::ROLE => $entity->getRole()]; |
144
|
|
|
$schema[Schema::CHILDREN][$this->childAlias($child)] = $child->getClass(); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
\ksort($schema); |
148
|
|
|
$this->result[$entity->getRole()] = $schema; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @param Entity $entity |
153
|
|
|
* |
154
|
|
|
* @return array |
155
|
|
|
*/ |
156
|
|
|
protected function renderColumns(Entity $entity): array |
157
|
|
|
{ |
158
|
|
|
$schema = []; |
159
|
|
|
|
160
|
|
|
foreach ($entity->getFields() as $name => $field) { |
161
|
|
|
$schema[$name] = $field->getColumn(); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
return $schema; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* @param Entity $entity |
169
|
|
|
* |
170
|
|
|
* @return array |
171
|
|
|
*/ |
172
|
|
|
protected function renderTypecast(Entity $entity): array |
173
|
|
|
{ |
174
|
|
|
$schema = []; |
175
|
|
|
|
176
|
|
|
foreach ($entity->getFields() as $name => $field) { |
177
|
|
|
if ($field->hasTypecast()) { |
178
|
|
|
$schema[$name] = $field->getTypecast(); |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $schema; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param Entity $entity |
187
|
|
|
* |
188
|
|
|
* @return array |
189
|
|
|
*/ |
190
|
|
|
protected function renderReferences(Entity $entity): array |
191
|
|
|
{ |
192
|
|
|
$schema = [$this->getPrimary($entity)]; |
|
|
|
|
193
|
|
|
|
194
|
|
|
foreach ($entity->getFields() as $name => $field) { |
195
|
|
|
if ($field->isReferenced()) { |
196
|
|
|
$schema[] = $name; |
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
return \array_unique($schema); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @param Registry $registry |
205
|
|
|
* @param Entity $entity |
206
|
|
|
* |
207
|
|
|
* @return array |
208
|
|
|
*/ |
209
|
|
|
protected function renderRelations(Registry $registry, Entity $entity): array |
210
|
|
|
{ |
211
|
|
|
$result = []; |
212
|
|
|
|
213
|
|
|
foreach ($registry->getRelations($entity) as $name => $relation) { |
214
|
|
|
$result[$name] = $relation->packSchema(); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return $result; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @param Entity $entity |
222
|
|
|
* |
223
|
|
|
* @return null|string |
224
|
|
|
*/ |
225
|
|
|
protected function getPrimary(Entity $entity): ?string |
226
|
|
|
{ |
227
|
|
|
foreach ($entity->getFields() as $name => $field) { |
228
|
|
|
if ($field->isPrimary()) { |
229
|
|
|
return $name; |
230
|
|
|
} |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return null; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Return the unique alias for the child entity. |
238
|
|
|
* |
239
|
|
|
* @param Entity $entity |
240
|
|
|
* |
241
|
|
|
* @return string |
242
|
|
|
*/ |
243
|
|
|
protected function childAlias(Entity $entity): string |
244
|
|
|
{ |
245
|
|
|
$r = new ReflectionClass($entity->getClass()); |
246
|
|
|
|
247
|
|
|
return $this->inflector->classify($r->getShortName()); |
248
|
|
|
} |
249
|
|
|
} |
250
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.