Issues (590)

src/Mapper/Metadata.php (26 issues)

1
<?php
2
3
namespace Bdf\Prime\Mapper;
4
5
use Bdf\Prime\Entity\Instantiator\InstantiatorInterface;
6
use Bdf\Prime\Relations\Builder\RelationBuilder;
7
use Bdf\Prime\Relations\Relation;
8
use stdClass;
9
10
/**
11
 * Metadata
12
 *
13
 * @todo gerer le nom de la base de données si non fourni
14
 * @todo exception si aucune primary ou unique n'a été définit ?
15
 * @todo doit on injecter si private ??
16
 *
17
 * @psalm-type FieldMetadata = array{
18
 *     primary: Metadata::PK_*|null,
19
 *     type: string,
20
 *     default: mixed,
21
 *     phpOptions: array<string, mixed>,
22
 *     field: string,
23
 *     attribute: string,
24
 *     embedded: string|null,
25
 *     length?: int,
26
 *     comment?: string,
27
 *     nillable?: bool,
28
 *     unsigned?: bool,
29
 *     unique?: bool|string,
30
 *     class?: class-string
31
 * }
32
 *
33
 * @psalm-type SequenceMetadata = array{
34
 *     connection: string|null,
35
 *     table: string|null,
36
 *     column: string|null,
37
 *     options: array
38
 * }
39
 *
40
 * @psalm-type EmbeddedMetadata = array{
41
 *     path: string,
42
 *     parentPath: string,
43
 *     paths: list<string>,
44
 *     class?: class-string,
45
 *     hint?: int|null,
46
 *     class_map?: array<string, class-string>,
47
 *     hints?: array<class-string, int|null>,
48
 *     discriminator_field?: string,
49
 *     discriminator_attribute?: string
50
 * }
51
 *
52
 * @psalm-type IndexMetadata = array{
53
 *     fields:array<string, array<string, string>>,
54
 *     unique?: bool
55
 * }&array<string, string>
56
 *
57
 * @psalm-import-type FieldDefinition from \Bdf\Prime\Mapper\Builder\FieldBuilder
58
 * @psalm-import-type RelationDefinition from RelationBuilder
59
 */
60
class Metadata
61
{
62
    /* constantes définissant le type de primary key */
63
    public const PK_AUTOINCREMENT = 'autoincrement';
64
    public const PK_AUTO = true;
65
    public const PK_SEQUENCE = 'sequence';
66
67
    /**
68
     * The expected entity classname
69
     *
70
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
71
     */
72
    public $entityName;
73
74
    /**
75
     * The instantiator hint
76
     *
77
     * @var int|null
78
     */
79
    public $instantiatorHint;
80
81
    /**
82
     * The class name to use
83
     *
84
     * if the class name does not exist, a stdClass will be used
85
     *
86
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
87
     */
88
    public $entityClass;
89
90
    /**
91
     * The property accessor class name to use
92
     * Usefull only for building metadata
93
     *
94
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
95
     */
96
    public $propertyAccessorClass;
97
98
    /**
99
     * @var string
100
     */
101
    public $connection;
102
103
    /**
104
     * @var string|null
105
     */
106
    public $database;
107
108
    /**
109
     * @var string
110
     */
111
    public $table;
112
113
    /**
114
     * @var boolean
115
     */
116
    public $useQuoteIdentifier;
117
118
    /**
119
     * @var array<string, string>
120
     */
121
    public $tableOptions = [];
122
123
    /**
124
     * The indexes
125
     * Format :
126
     * [
127
     *    [index_name] => [
128
     *         'fields' => [
129
     *             'field1' => [
130
     *                 'fieldOption' => 'optValue',
131
     *                 ...
132
     *             ],
133
     *             ...
134
     *         ],
135
     *         'unique' => true,
136
     *         'option1' => 'value1',
137
     *         'option2' => 'value2',
138
     *         ...
139
     *    ]
140
     * ]
141
     *
142
     * With :
143
     * - index_name : The index name as string for named index, or integer offset for generated name
144
     * - 'fields' : Array of fields where the index is applied. The fields are the database fields
145
     * - 'unique' : Not set for simple indexes, the value is true for indicate a unique constraint on the index
146
     * - options : Key/value index options, depends of the database platform and driver
147
     * - fieldOption : Option related to the field, like length or sort order
148
     *
149
     * @var IndexMetadata[]
150
     */
151
    public $indexes = [];
152
153
    /**
154
     * @var SequenceMetadata
0 ignored issues
show
The type Bdf\Prime\Mapper\SequenceMetadata was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
155
     */
156
    public $sequence = [
157
        'connection' => null,
158
        'table'      => null,
159
        'column'     => null,
160
        'options'    => [],
161
    ];
162
163
    /**
164
     * List of entity columns, indexed by the database columns name
165
     *
166
     * @var array<string, FieldMetadata>
167
     */
168
    public $fields = [];
169
170
    /**
171
     * List of entity columns, indexed by the entity property name
172
     *
173
     * @var array<string, FieldMetadata>
174
     */
175
    public $attributes = [];
176
177
    /**
178
     * @var array<string, EmbeddedMetadata>
179
     */
180
    public $embeddeds = [];
181
182
    /**
183
     * @var array{
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{ at position 2 could not be parsed: the token is null at position 2.
Loading history...
184
     *     type: Metadata::PK_*,
185
     *     attributes: list<string>,
186
     *     fields: list<string>
187
     * }
188
     */
189
    public $primary = [
190
        'type'          => self::PK_AUTO,
191
        'attributes'    => [],
192
        'fields'        => [],
193
    ];
194
195
    /**
196
     * The repository global constraints
197
     *
198
     * @var array
199
     */
200
    public $constraints = [];
201
202
    /**
203
     * Flag indiquant que le meta a déjà été construit
204
     *
205
     * @var bool
206
     */
207
    protected $built = false;
208
209
    /**
210
     * Relations that must be loaded eagerly
211
     *
212
     * @var array
213
     */
214
    public $eagerRelations = [];
215
216
    /**
217
     * Get entity class name
218
     *
219
     * @return class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
220
     */
221 1
    public function getEntityClass(): string
222
    {
223 1
        return $this->entityClass;
224
    }
225
226
    /**
227
     * Is metadata built
228
     *
229
     * @return bool
230
     */
231 1
    public function isBuilt(): bool
232
    {
233 1
        return $this->built;
234
    }
235
236
    /**
237
     * Get connection identifier from locator
238
     *
239
     * @return string|null
240
     */
241 1
    public function connection(): ?string
242
    {
243 1
        return $this->connection;
244
    }
245
246
    /**
247
     * Get database name
248
     *
249
     * @return string|null
250
     */
251 1
    public function database(): ?string
252
    {
253 1
        return $this->database;
254
    }
255
256
    /**
257
     * Get table name
258
     *
259
     * @return string
260
     */
261 89
    public function table(): string
262
    {
263 89
        return $this->table;
264
    }
265
266
    /**
267
     * Get table options
268
     *
269
     * @return array
270
     */
271 865
    public function tableOptions(): array
272
    {
273 865
        return $this->tableOptions;
274
    }
275
276
    /**
277
     * Get indexes
278
     *
279
     * @return array<string, IndexMetadata>
280
     */
281 2
    public function indexes(): array
282
    {
283 2
        return $this->indexes;
284
    }
285
286
    /**
287
     * Get embedded meta
288
     *
289
     * @return array<string, EmbeddedMetadata>
290
     */
291 251
    public function embeddeds(): array
292
    {
293 251
        return $this->embeddeds;
294
    }
295
296
    /**
297
     * Get attribute embedded meta
298
     *
299
     * @param string $attribute
300
     *
301
     * @return EmbeddedMetadata|null
0 ignored issues
show
The type Bdf\Prime\Mapper\EmbeddedMetadata was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
302
     */
303 2
    public function embedded($attribute): ?array
304
    {
305 2
        return $this->embeddeds[$attribute] ?? null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->embeddeds[$attribute] ?? null could return the type Bdf\Prime\Mapper\EmbeddedMetadata which is incompatible with the type-hinted return array|null. Consider adding an additional type-check to rule them out.
Loading history...
306
    }
307
308
    /**
309
     * Get attribute or field metadata
310
     *
311
     * @param string $key
312
     * @param string $type
313
     *
314
     * @return array|null
315
     */
316 1
    public function meta($key, $type = 'attributes')
317
    {
318 1
        if (isset($this->{$type}[$key])) {
319 1
            return $this->{$type}[$key];
320
        }
321
322 1
        return null;
323
    }
324
325
    /**
326
     * Returns primary attributes | fields | type
327
     *
328
     * @param 'attributes'|'fields'|'type' $type
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'attributes'|'fields'|'type' at position 0 could not be parsed: Unknown type name ''attributes'' at position 0 in 'attributes'|'fields'|'type'.
Loading history...
329
     *
330
     * @return list<string>|Metadata::PK_*
331
     */
332 1
    public function primary($type = 'attributes')
333
    {
334 1
        return $this->primary[$type];
335
    }
336
337
    /**
338
     * Returns metadata for first primary key
339
     *
340
     * @return FieldMetadata
0 ignored issues
show
The type Bdf\Prime\Mapper\FieldMetadata was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
341
     */
342
    public function firstPrimaryMeta(): array
343
    {
344
        list($primary) = $this->primary['attributes'];
345
346
        return $this->attributes[$primary];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->attributes[$primary] returns the type Bdf\Prime\Mapper\FieldMetadata which is incompatible with the type-hinted return array.
Loading history...
347
    }
348
349
    /**
350
     * Returns all metadata for primary key
351
     *
352
     * @return array<string, FieldMetadata>
353
     */
354
    public function primaryMeta(): array
355
    {
356
        $meta = [];
357
358
        foreach ($this->primary['attributes'] as $attribute) {
359
            $meta[$attribute] = $this->attributes[$attribute];
360
        }
361
362
        return $meta;
363
    }
364
365
    /**
366
     * Returns sequence info
367
     *
368
     * @param 'connection'|'table'|'column'|'options'|null $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'connection'|'table'|'column'|'options'|null at position 0 could not be parsed: Unknown type name ''connection'' at position 0 in 'connection'|'table'|'column'|'options'|null.
Loading history...
369
     *
370
     * @return SequenceMetadata|string|array|null
371
     */
372
    public function sequence(?string $key = null)
373
    {
374
        return $key === null
375
            ? $this->sequence
376
            : $this->sequence[$key];
377
    }
378
379
    /**
380
     * Check if key is primary
381
     *
382
     * @param string $key
383
     * @param 'attributes'|'fields' $type
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'attributes'|'fields' at position 0 could not be parsed: Unknown type name ''attributes'' at position 0 in 'attributes'|'fields'.
Loading history...
384
     *
385
     * @return bool
386
     */
387 1
    public function isPrimary($key, $type = 'attributes'): bool
388
    {
389 1
        return in_array($key, $this->primary[$type]);
390
    }
391
392
    /**
393
     * Is primary key an auto increment
394
     *
395
     * @return bool
396
     */
397 303
    public function isAutoIncrementPrimaryKey(): bool
398
    {
399 303
        return $this->primary['type'] === self::PK_AUTOINCREMENT;
400
    }
401
402
    /**
403
     * Is a sequence generated primary key
404
     *
405
     * @return bool
406
     *
407
     * @psalm-assert string $this->sequence['table']
408
     * @psalm-assert string $this->sequence['column']
409
     */
410 1031
    public function isSequencePrimaryKey(): bool
411
    {
412 1031
        return $this->primary['type'] === self::PK_SEQUENCE;
413
    }
414
415
    /**
416
     * Is a foreign key as primary key
417
     *
418
     * @return bool
419
     */
420 67
    public function isForeignPrimaryKey(): bool
421
    {
422 67
        return $this->primary['type'] === self::PK_AUTO;
423
    }
424
425
    /**
426
     * The primary key has multiple fields
427
     *
428
     * @return bool
429
     */
430 125
    public function isCompositePrimaryKey(): bool
431
    {
432 125
        return count($this->primary['attributes']) > 1;
433
    }
434
435
    /**
436
     * Get fields metadata
437
     *
438
     * @return array<string, FieldMetadata>
439
     */
440
    public function fields(): array
441
    {
442
        return $this->fields;
443
    }
444
445 1
    public function eagerRelations(): array
446
    {
447 1
        return $this->eagerRelations;
448
    }
449
450
    /**
451
     * Does field exist
452
     *
453
     * @param string $field
454
     *
455
     * @return bool
456
     */
457
    public function fieldExists($field): bool
458
    {
459
        return isset($this->fields[$field]);
460
    }
461
462
    /**
463
     * Get field type
464
     *
465
     * @param string $field
466
     *
467
     * @return string
468
     */
469
    public function fieldType($field): string
470
    {
471
        return $this->fields[$field]['type'];
472
    }
473
474
    /**
475
     * Get attributes
476
     *
477
     * @return array<string, FieldMetadata>
478
     */
479 251
    public function attributes(): array
480
    {
481 251
        return $this->attributes;
482
    }
483
484
    /**
485
     * Does attribute exist
486
     *
487
     * @param string $attribute
488
     *
489
     * @return bool
490
     */
491
    public function attributeExists($attribute): bool
492
    {
493
        return isset($this->attributes[$attribute]);
494
    }
495
496
    /**
497
     * Get attribute type
498
     *
499
     * @param string $attribute
500
     *
501
     * @return string
502
     */
503
    public function attributeType($attribute): string
504
    {
505
        return $this->attributes[$attribute]['type'];
506
    }
507
508
    /**
509
     * Get field from attribute alias if exists
510
     *
511
     * @param string $attribute
512
     *
513
     * @return string
514
     */
515
    public function fieldFrom($attribute): string
516
    {
517
        return $this->attributes[$attribute]['field'];
518
    }
519
520
    /**
521
     * Get attribute from field alias if exists
522
     *
523
     * @param string $field
524
     *
525
     * @return string
526
     */
527
    public function attributeFrom($field): string
528
    {
529
        return $this->fields[$field]['attribute'];
530
    }
531
532
    /**
533
     * @param Mapper $mapper
534
     * @psalm-param Mapper<E> $mapper
535
     * @template E as object
536
     */
537 513
    public function build(Mapper $mapper): void
538
    {
539 513
        if (!$this->built) {
540 513
            $this->entityName = $mapper->getEntityClass();
541 513
            $this->useQuoteIdentifier = $mapper->hasQuoteIdentifier();
542 513
            $this->constraints = $mapper->constraints();
543 513
            $this->propertyAccessorClass = $mapper->getPropertyAccessorClass();
544 513
            $this->entityClass = $this->getExistingClassName($this->entityName);
545 513
            $this->instantiatorHint = $this->getInstantiatorHint($this->entityClass);
546
547 513
            $this->buildSchema($mapper->schema());
548 513
            $this->buildFields($mapper->fields());
549 513
            $this->buildSequence($mapper->sequence());
550 513
            $this->buildIndexes($mapper->indexes());
551 513
            $this->buildRelations($mapper->relations());
552
553 513
            $this->built = true;
554
        }
555
    }
556
557
    /**
558
     * Get the classname if exists
559
     *
560
     * @param class-string $entityClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
561
     *
562
     * @return class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
563
     */
564 513
    private function getExistingClassName($entityClass): string
565
    {
566 513
        if ($entityClass === stdClass::class || !class_exists($entityClass)) {
567 5
            return stdClass::class;
568
        }
569
570 510
        return $entityClass;
571
    }
572
573
    /**
574
     * Guess the instantiator hint
575
     *
576
     * This method will check the class constructor. If it has one non optional parameter
577
     * it will return no hint. Otherwise the default constructor hiint will be returned.
578
     *
579
     * @param class-string $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
580
     *
581
     * @return null|int
582
     */
583 513
    private function getInstantiatorHint($className): ?int
584
    {
585 513
        if ($className === stdClass::class) {
586 5
            return InstantiatorInterface::USE_CONSTRUCTOR_HINT;
587
        }
588
589
        try {
590 512
            $constructor = (new \ReflectionClass($className))->getConstructor();
591
592 510
            if ($constructor !== null) {
593 448
                foreach ($constructor->getParameters() as $parameter) {
594 447
                    if (!$parameter->isOptional()) {
595 7
                        return null;
596
                    }
597
                }
598
            }
599
600 505
            return InstantiatorInterface::USE_CONSTRUCTOR_HINT;
601 2
        } catch (\ReflectionException $exception) {
602 2
            return null;
603
        }
604
    }
605
606
    /**
607
     * Build schema metadata
608
     *
609
     * @param array{connection: string, database?: string, table: string, tableOptions?: array} $schema
610
     */
611 513
    private function buildSchema(array $schema): void
612
    {
613 513
        $schema += [
614 513
            'connection'   => null,
615 513
            'database'     => null,
616 513
            'table'        => null,
617 513
            'tableOptions' => [],
618 513
        ];
619
620
        //TODO Comment recuperer la database si non fournie
621
        //$service->connection($this->connection)->getDatabase();
622
623 513
        $this->connection   = $schema['connection'];
624 513
        $this->database     = $schema['database'];
625 513
        $this->table        = $schema['table'];
626 513
        $this->tableOptions = $schema['tableOptions'];
627
    }
628
629
    /**
630
     * Builds fields metadata
631
     *
632
     * @param iterable<string, FieldDefinition> $fields
633
     * @param EmbeddedMetadata|null $embeddedMeta
634
     */
635 513
    private function buildFields(iterable $fields, $embeddedMeta = null): void
636
    {
637 513
        foreach ($fields as $attribute => $meta) {
638 513
            if (isset($meta['embedded'])) {
639 390
                $this->buildFields(
640 390
                    $meta['embedded'],
641 390
                    empty($meta['polymorph'])
642 390
                        ? $this->buildEmbedded($attribute, $meta['class'], $embeddedMeta)
0 ignored issues
show
It seems like $embeddedMeta can also be of type Bdf\Prime\Mapper\EmbeddedMetadata; however, parameter $embeddedMeta of Bdf\Prime\Mapper\Metadata::buildEmbedded() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

642
                        ? $this->buildEmbedded($attribute, $meta['class'], /** @scrutinizer ignore-type */ $embeddedMeta)
Loading history...
643 390
                        : $this->buildPolymorph($attribute, $meta, $embeddedMeta)
0 ignored issues
show
It seems like $embeddedMeta can also be of type Bdf\Prime\Mapper\EmbeddedMetadata; however, parameter $embeddedMeta of Bdf\Prime\Mapper\Metadata::buildPolymorph() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

643
                        : $this->buildPolymorph($attribute, $meta, /** @scrutinizer ignore-type */ $embeddedMeta)
Loading history...
644 390
                );
645
            } else {
646 513
                $this->buildField($attribute, $meta, $embeddedMeta);
0 ignored issues
show
It seems like $embeddedMeta can also be of type Bdf\Prime\Mapper\EmbeddedMetadata; however, parameter $embeddedMeta of Bdf\Prime\Mapper\Metadata::buildField() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

646
                $this->buildField($attribute, $meta, /** @scrutinizer ignore-type */ $embeddedMeta);
Loading history...
647
            }
648
        }
649
    }
650
651
    /**
652
     * Builds indexes metadata
653
     *
654
     * @param array $indexes
655
     */
656 513
    private function buildIndexes(array $indexes): void
657
    {
658 513
        $this->buildSimpleIndexes($indexes);
659 513
        $this->buildIndexesFromFields();
660
    }
661
662
    /**
663
     * Build simple indexes declared on mapper
664
     * The field names will be mapped with DB attributes if exists, and will be normalized into the new format
665
     *
666
     * Supports old format :
667
     * [
668
     *     'index_name' => ['field1', 'field2', ...],
669
     *     ...
670
     * ]
671
     *
672
     * And new format :
673
     * [
674
     *     'index_name' => [
675
     *         'fields' => [
676
     *             'field1' => [fieldOptions],
677
     *             'field2' => [fieldOptions],
678
     *             ...
679
     *         ],
680
     *         'unique' => true,
681
     *         'option' => 'value',
682
     *         // More options
683
     *     ],
684
     *     ...
685
     * ]
686
     *
687
     * @param array $indexes
688
     */
689 513
    private function buildSimpleIndexes(array $indexes): void
690
    {
691 513
        foreach ($indexes as $name => $index) {
692
            // Legacy format compatibility
693 15
            if (!isset($index['fields'])) {
694 5
                $fields = [];
695
696 5
                foreach ($index as $field) {
697 5
                    $fields[$field] = [];
698
                }
699
700 5
                $index = ['fields' => $fields];
701
            }
702
703 15
            $fields = [];
704
705
            // Map the field name if exists
706 15
            foreach ($index['fields'] as $field => $options) {
707 15
                if (isset($this->attributes[$field])) {
708 15
                    $field = $this->attributes[$field]['field'];
709
                }
710
711 15
                $fields[$field] = $options;
712
            }
713
714 15
            $index['fields'] = $fields;
715
716 15
            $this->indexes[$name] = $index;
717
        }
718
    }
719
720
    /**
721
     * Extract unique indexes from field declaration
722
     * Must be called after @see Metadata::buildFields()
723
     *
724
     * The unique indexes will follow same format as simple indexes, but with the option 'unique' set as true
725
     */
726 513
    private function buildIndexesFromFields(): void
727
    {
728 513
        foreach ($this->fields as $field => $meta) {
729 513
            if (empty($meta['unique'])) {
730 513
                continue;
731
            }
732
733 9
            if (is_string($meta['unique'])) {
734 1
                if (isset($this->indexes[$meta['unique']])) {
735 1
                    $this->indexes[$meta['unique']]['fields'][$field] = [];
736
                } else {
737 1
                    $this->indexes[$meta['unique']] = [
738 1
                        'fields' => [$field => []],
739 1
                        'unique' => true,
740 1
                    ];
741
                }
742
            } else {
743 9
                $this->indexes[] = [
744 9
                    'fields' => [$field => []],
745 9
                    'unique' => true,
746 9
                ];
747
            }
748
        }
749
    }
750
751
    /**
752
     * Build Embedded meta
753
     *
754
     * @param string $attribute
755
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
756
     * @param array|null $embeddedMeta
757
     *
758
     * @return EmbeddedMetadata
759
     */
760 446
    private function buildEmbedded(string $attribute, string $class, ?array $embeddedMeta): array
761
    {
762 446
        if ($embeddedMeta === null) {
0 ignored issues
show
The condition $embeddedMeta === null is always false.
Loading history...
763 444
            $attributePath = $attribute;
764 444
            $path          = 'root';
765 444
            $paths         = [$attributePath];
766
        } else {
767 185
            $attributePath = $embeddedMeta['path'].'.'.$attribute;
768 185
            $path          = $embeddedMeta['path'];
769 185
            $paths         = array_merge($embeddedMeta['paths'], [$attributePath]);
770
        }
771
772 446
        return $this->embeddeds[$attributePath] = [
773 446
            'class'           => $this->getExistingClassName($class),
774 446
            'hint'            => $this->getInstantiatorHint($class),
775 446
            'path'            => $attributePath,
776 446
            'parentPath'      => $path,
777 446
            'paths'           => $paths,
778 446
        ];
779
    }
780
781
    /**
782
     * Build a polymorph embedded
783
     *
784
     * @param string $attribute
785
     * @param array $meta
786
     * @param array|null $embeddedMeta
787
     *
788
     * @return EmbeddedMetadata
789
     */
790 2
    private function buildPolymorph($attribute, array $meta, ?array $embeddedMeta): array
791
    {
792 2
        if ($embeddedMeta === null) {
0 ignored issues
show
The condition $embeddedMeta === null is always false.
Loading history...
793 2
            $attributePath = $attribute;
794 2
            $path          = 'root';
795 2
            $paths         = [$attributePath];
796
        } else {
797
            $attributePath = $embeddedMeta['path'].'.'.$attribute;
798
            $path          = $embeddedMeta['path'];
799
            $paths         = array_merge($embeddedMeta['paths'], [$attributePath]);
800
        }
801
802 2
        $hints = [];
803
804 2
        foreach ($meta['class_map'] as $class) {
805 2
            $hints[$class] = $this->getInstantiatorHint($class);
806
        }
807
808 2
        return $this->embeddeds[$attributePath] = [
809 2
            'path'       => $attributePath,
810 2
            'parentPath' => $path,
811 2
            'paths'      => $paths,
812 2
            'hints'      => $hints
813 2
        ] + $meta;
814
    }
815
816
    /**
817
     * Build mapped embedded meta
818
     *
819
     * @todo  parcourir la map pour construire les differents embeddeds 'attribute-discriminatorValue'
820
     *
821
     * @param string $attribute
822
     * @param string[] $map
823
     * @param string $discriminator
824
     * @param array  $embeddedMeta
825
     */
826 214
    private function buildMappedEmbedded($attribute, $map, $discriminator, $embeddedMeta = null): void
0 ignored issues
show
The parameter $discriminator is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

826
    private function buildMappedEmbedded($attribute, $map, /** @scrutinizer ignore-unused */ $discriminator, $embeddedMeta = null): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
827
    {
828 214
        $entity = reset($map);
829
830 214
        if (is_string($entity)) {
831 214
            list($entity) = Relation::parseEntity($entity);
832
        } else {
833
            $entity = $entity['entity'];
834
        }
835
836 214
        $this->buildEmbedded($attribute, $entity, $embeddedMeta);
837
    }
838
839
    /**
840
     * Build embedded relations missing in embedded
841
     *
842
     * @param iterable<string, RelationDefinition> $relations
843
     */
844 513
    private function buildRelations(iterable $relations): void
845
    {
846 513
        foreach ($relations as $attribute => $relation) {
847
            // il est possible de déclarer des relations sans attribut sur l'entity (cas de grosse collection)
848 423
            if (!empty($relation['detached'])) {
849 283
                continue;
850
            }
851
852 417
            if (isset($relation['mode']) && $relation['mode'] === RelationBuilder::MODE_EAGER) {
853 143
                $this->eagerRelations[$attribute] = [];
854
            }
855
856
            // si l'attribut est déjà définit, car embedded
857 417
            if (isset($this->embeddeds[$attribute])) {
858 338
                continue;
859
            }
860
861 329
            if (isset($relation['map'])) {
862 214
                $this->buildMappedEmbedded($attribute, $relation['map'], $relation['discriminator']);
863 214
                continue;
864
            }
865
866
            // si entity n'est pas definit
867 324
            if (!isset($relation['entity'])) {
868 49
                continue;
869
            }
870
871
            // Attention: un embedded relation doit appartenir à l'entity.
872
            // Ne pas pas etre dans un object de entity
873
            // C'est pour cette raison que le 3eme parametre est à null
874 314
            $this->buildEmbedded($attribute, $relation['entity'], null);
875
        }
876
877
        // Preformatage des eager relations
878 513
        $this->eagerRelations = Relation::sanitizeRelations($this->eagerRelations);
879
    }
880
881
    /**
882
     * Build field meta
883
     *
884
     * @param string $attribute
885
     * @param FieldDefinition $meta
0 ignored issues
show
The type Bdf\Prime\Mapper\FieldDefinition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
886
     * @param EmbeddedMetadata|null $embeddedMeta
887
     */
888 513
    private function buildField($attribute, $meta, ?array $embeddedMeta): void
889
    {
890
        //concatenation de l'attribut parent
891 513
        if ($embeddedMeta === null) {
0 ignored issues
show
The condition $embeddedMeta === null is always false.
Loading history...
892 512
            $attributePath = $attribute;
893 512
            $path          = null;
894
        } else {
895 390
            $attributePath = $embeddedMeta['path'] . '.' . $attribute;
896 390
            $path          = $embeddedMeta['path'];
897
        }
898
899
        // TODO call 'Inflector::tableize($attributePath) ?'
900 513
        $field = isset($meta['alias']) ? $meta['alias'] : $attributePath;
901 513
        unset($meta['alias']);
902
903
        // TODO ne pas gerer les defaults
904 513
        $meta += [
905 513
            'primary'    => null,
906 513
            'default'    => null,
907 513
            'phpOptions' => [],
908 513
        ];
909
910 513
        $this->fields[$field] = $this->attributes[$attributePath] = [
911 513
            'field'             => $field,
912 513
            'attribute'         => $attributePath,
913 513
            'embedded'          => $path,
914 513
        ] + $meta;
915
916
        /*
917
         * Construction des meta des primary keys.
918
         * Si un champs est en auto-increment, celui-ci doit etre placé en début de tableau.
919
         */
920 513
        if ($meta['primary']) {
921 504
            if ($this->primary['type'] !== self::PK_AUTO && $meta['primary'] !== self::PK_AUTO) {
922
                throw new \LogicException('Trying to set a primary key');
923
            }
924
925 504
            if ($meta['primary'] === self::PK_AUTO) {
926 288
                $this->primary['attributes'][] = $attributePath;
927 288
                $this->primary['fields'][]     = $field;
928
            } else {
929 484
                $this->primary['type'] = $meta['primary'];
930 484
                array_unshift($this->primary['attributes'], $attributePath);
931 484
                array_unshift($this->primary['fields'], $field);
932
            }
933
        }
934
    }
935
936
    /**
937
     * Build sequence info if primary is a sequence
938
     *
939
     * @param array{connection?:string,table?:string,column?:string,tableOptions?:array} $sequence
940
     */
941 513
    private function buildSequence($sequence): void
942
    {
943 513
        if (!$this->isSequencePrimaryKey()) {
944 503
            return;
945
        }
946
947 255
        $this->sequence = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('connection' => $s...leOptions'] ?? array()) of type array<string,array|mixed|string> is incompatible with the declared type Bdf\Prime\Mapper\SequenceMetadata of property $sequence.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
948 255
            'connection' => $sequence['connection'] ?? $this->connection,
949 255
            'table'      => $sequence['table'] ?? $this->table . '_seq',
950 255
            'column'     => $sequence['column'] ?? 'id',
951 255
            'options'    => $sequence['tableOptions'] ?? [],
952 255
        ];
953
    }
954
}
955