Passed
Pull Request — master (#26)
by butschster
02:38
created

Entity::setPrimaryKeys()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 6
nop 1
dl 0
loc 20
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Cycle ORM Schema Builder.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Schema\Definition;
13
14
use Cycle\Schema\Definition\Map\FieldMap;
15
use Cycle\Schema\Definition\Map\OptionMap;
16
use Cycle\Schema\Definition\Map\RelationMap;
17
use Cycle\Schema\Exception\EntityException;
18
19
/**
20
 * Contains information about specific entity definition.
21
 */
22
final class Entity
23
{
24
    /** @var OptionMap */
25
    private $options;
26
27
    /** @var string */
28
    private $role;
29
30
    /** @var string|null */
31
    private $class;
32
33
    /** @var string|null */
34
    private $mapper;
35
36
    /** @var string|null */
37
    private $source;
38
39
    /** @var string|null */
40
    private $constrain;
41
42
    /** @var string|null */
43
    private $repository;
44
45
    /** @var FieldMap */
46
    private $fields;
47
48
    /** @var RelationMap */
49
    private $relations;
50
51
    /** @var array */
52
    private $schema = [];
53
54
    /** @var array */
55
    private $primaryKeys = [];
56
57
    /**
58
     * Entity constructor.
59
     */
60
    public function __construct()
61
    {
62
        $this->options = new OptionMap();
63
        $this->fields = new FieldMap();
64
        $this->relations = new RelationMap();
65
    }
66
67
    /**
68
     * Full entity copy.
69
     */
70
    public function __clone()
71
    {
72
        $this->options = clone $this->options;
73
        $this->fields = clone $this->fields;
74
        $this->relations = clone $this->relations;
75
    }
76
77
    /**
78
     * @return OptionMap
79
     */
80
    public function getOptions(): OptionMap
81
    {
82
        return $this->options;
83
    }
84
85
    /**
86
     * @param string $role
87
     * @return Entity
88
     */
89
    public function setRole(string $role): self
90
    {
91
        $this->role = $role;
92
93
        return $this;
94
    }
95
96
    /**
97
     * @return string|null
98
     */
99
    public function getRole(): ?string
100
    {
101
        return $this->role;
102
    }
103
104
    /***
105
     * @param string $class
106
     * @return Entity
107
     */
108
    public function setClass(string $class): self
109
    {
110
        $this->class = $class;
111
112
        return $this;
113
    }
114
115
    /**
116
     * @return string|null
117
     */
118
    public function getClass(): ?string
119
    {
120
        return $this->class;
121
    }
122
123
    /**
124
     * @param string|null $mapper
125
     * @return Entity
126
     */
127
    public function setMapper(?string $mapper): self
128
    {
129
        $this->mapper = $mapper;
130
131
        return $this;
132
    }
133
134
    /**
135
     * @return string
136
     */
137
    public function getMapper(): ?string
138
    {
139
        return $this->normalizeClass($this->mapper);
140
    }
141
142
    /**
143
     * @param string|null $source
144
     * @return Entity
145
     */
146
    public function setSource(?string $source): self
147
    {
148
        $this->source = $source;
149
150
        return $this;
151
    }
152
153
    /**
154
     * @return string
155
     */
156
    public function getSource(): ?string
157
    {
158
        return $this->normalizeClass($this->source);
159
    }
160
161
    /**
162
     * @param string|null $constrain
163
     * @return Entity
164
     */
165
    public function setConstrain(?string $constrain): self
166
    {
167
        $this->constrain = $constrain;
168
169
        return $this;
170
    }
171
172
    /**
173
     * @return string|null
174
     */
175
    public function getConstrain(): ?string
176
    {
177
        return $this->normalizeClass($this->constrain);
178
    }
179
180
    /**
181
     * @param string|null $repository
182
     * @return Entity
183
     */
184
    public function setRepository(?string $repository): self
185
    {
186
        $this->repository = $repository;
187
188
        return $this;
189
    }
190
191
    /**
192
     * @return string
193
     */
194
    public function getRepository(): ?string
195
    {
196
        return $this->normalizeClass($this->repository);
197
    }
198
199
    /**
200
     * @return FieldMap
201
     */
202
    public function getFields(): FieldMap
203
    {
204
        return $this->fields;
205
    }
206
207
    /**
208
     * @return RelationMap
209
     */
210
    public function getRelations(): RelationMap
211
    {
212
        return $this->relations;
213
    }
214
215
    /**
216
     * @param array $schema
217
     * @return Entity
218
     */
219
    public function setSchema(array $schema): Entity
220
    {
221
        $this->schema = $schema;
222
223
        return $this;
224
    }
225
226
    /**
227
     * @return array
228
     */
229
    public function getSchema(): array
230
    {
231
        return $this->schema;
232
    }
233
234
    /**
235
     * Merge entity relations and fields.
236
     *
237
     * @param Entity $entity
238
     */
239
    public function merge(Entity $entity): void
240
    {
241
        foreach ($entity->getRelations() as $name => $relation) {
242
            if (!$this->relations->has($name)) {
243
                $this->relations->set($name, $relation);
244
            }
245
        }
246
247
        foreach ($entity->getFields() as $name => $field) {
248
            if (!$this->fields->has($name)) {
249
                $this->fields->set($name, $field);
250
            }
251
        }
252
    }
253
254
    /**
255
     * Check if entity has primary key
256
     */
257
    public function hasPrimaryKey(): bool
258
    {
259
        if ($this->primaryKeys !== []) {
260
            return true;
261
        }
262
263
        foreach ($this->getFields() as $name => $field) {
264
            if ($field->isPrimary()) {
265
                return true;
266
            }
267
        }
268
269
        return false;
270
    }
271
272
    /**
273
     * Set primary key columns
274
     * Column names will be converted into property names
275
     */
276
    public function setPrimaryKeys(array $columns): void
277
    {
278
        $propertyNames = [];
279
        $totalColumns = count($columns);
280
281
        foreach ($this->getFields() as $name => $field) {
282
            if (($i = array_search($field->getColumn(), $columns)) !== false) {
283
                $propertyNames[] = $name;
284
                unset($columns[$i]);
285
            }
286
        }
287
288
        if (count($propertyNames) !== $totalColumns) {
289
            throw new EntityException(sprintf(
290
                'Invalid primary keys for `%s`. Columns `%s` not found.',
291
                $this->getRole(), implode('`, `', $columns)
292
            ));
293
        }
294
295
        $this->primaryKeys = $propertyNames;
296
    }
297
298
    /**
299
     * Get entity primary key property names
300
     * @return string[]
301
     */
302
    public function getPrimaryKeys(): array
303
    {
304
        $keys = [];
305
306
        foreach ($this->getFields() as $name => $field) {
307
            if ($field->isPrimary()) {
308
                $keys[] = $name;
309
            }
310
        }
311
312
        if ($this->primaryKeys !== [] && $keys === []) {
313
            return $this->primaryKeys;
314
        }
315
316
        if ($this->primaryKeys === [] && $keys !== []) {
317
            return $keys;
318
        }
319
320
        if (
321
            count($keys) !== count($this->primaryKeys)
322
            || array_diff($keys, $this->primaryKeys) != []
323
        ) {
324
            throw new EntityException("Ambiguous primary key definition for `{$this->getRole()}`.");
325
        }
326
327
        return $this->primaryKeys;
328
    }
329
330
    /**
331
     * @param string|null $class
332
     * @return string|null
333
     */
334
    private function normalizeClass(string $class = null): ?string
335
    {
336
        if ($class === null) {
337
            return null;
338
        }
339
340
        return ltrim($class, '\\');
341
    }
342
}
343