Passed
Pull Request — 2.1 (#66)
by Vincent
22:57 queued 15:10
created

Mapper   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 880
Duplicated Lines 0 %

Test Coverage

Coverage 97.48%

Importance

Changes 0
Metric Value
wmc 67
eloc 121
c 0
b 0
f 0
dl 0
loc 880
ccs 155
cts 159
cp 0.9748
rs 3.04

49 Methods

Rating   Name   Duplication   Size   Complexity  
A relation() 0 9 2
A customConstraints() 0 3 1
A queries() 0 3 1
A entity() 0 13 2
A constraints() 0 9 2
A customEvents() 0 2 1
A buildFields() 0 3 1
A events() 0 6 2
A setGenerator() 0 7 3
A getId() 0 3 1
A getDefinedBehaviors() 0 3 1
A getRepositoryClass() 0 3 1
A setCriteriaClass() 0 3 1
A indexes() 0 7 1
A destroy() 0 6 1
A relations() 0 8 2
A getEntityClass() 0 3 1
A info() 0 5 1
A __construct() 0 12 3
A setQuoteIdentifier() 0 3 1
A filters() 0 3 1
A primaryCriteria() 0 3 1
A fields() 0 10 2
A buildIndexes() 0 2 1
A extractOne() 0 3 1
A getPropertyAccessorClass() 0 3 1
A prepareFromRepository() 0 7 1
A metadata() 0 3 1
A setId() 0 3 1
A disableSchemaManager() 0 3 1
A setPropertyAccessorClass() 0 3 1
A instantiate() 0 5 1
A isReadOnly() 0 3 1
A hasSchemaManager() 0 3 1
A repository() 0 5 2
A setHydrator() 0 7 1
A criteria() 0 10 3
A buildRelations() 0 2 1
A hydrateOne() 0 3 1
A sequence() 0 7 1
A behaviors() 0 7 2
A prepareToRepository() 0 3 1
A generator() 0 16 5
A configure() 0 2 1
A scopes() 0 3 1
A setRepositoryClass() 0 3 1
A setReadOnly() 0 3 1
A hydrator() 0 3 1
A hasQuoteIdentifier() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Mapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Mapper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Bdf\Prime\Mapper;
4
5
use Bdf\Prime\Behaviors\BehaviorInterface;
6
use Bdf\Prime\Cache\CacheInterface;
7
use Bdf\Prime\Entity\Criteria;
8
use Bdf\Prime\Entity\Hydrator\MapperHydrator;
9
use Bdf\Prime\Entity\Hydrator\MapperHydratorInterface;
10
use Bdf\Prime\Entity\ImportableInterface;
11
use Bdf\Prime\Exception\PrimeException;
12
use Bdf\Prime\IdGenerators\AutoIncrementGenerator;
13
use Bdf\Prime\IdGenerators\GeneratorInterface;
14
use Bdf\Prime\IdGenerators\NullGenerator;
15
use Bdf\Prime\IdGenerators\TableGenerator;
16
use Bdf\Prime\Mapper\Builder\FieldBuilder;
17
use Bdf\Prime\Mapper\Builder\IndexBuilder;
18
use Bdf\Prime\Mapper\Info\MapperInfo;
19
use Bdf\Prime\Platform\PlatformInterface;
20
use Bdf\Prime\Relations\Builder\RelationBuilder;
21
use Bdf\Prime\Relations\Exceptions\RelationNotFoundException;
22
use Bdf\Prime\Repository\EntityRepository;
23
use Bdf\Prime\Repository\RepositoryEventsSubscriberInterface;
24
use Bdf\Prime\Repository\RepositoryInterface;
25
use Bdf\Prime\ServiceLocator;
26
use Bdf\Serializer\PropertyAccessor\PropertyAccessorInterface;
27
use Bdf\Serializer\PropertyAccessor\ReflectionAccessor;
28
use LogicException;
29
30
use function class_exists;
31
32
/**
33
 * Mapper
34
 *
35
 * Contient les méta données de la table.
36
 *
37
 * @todo Convertir la donnée avec le type approprié sur les methodes setId, hydrateOne
38
 *
39
 * @template E as object
40
 *
41
 * @psalm-import-type FieldDefinition from FieldBuilder
42
 * @psalm-import-type RelationDefinition from RelationBuilder
43
 */
44
abstract class Mapper
45
{
46
    /**
47
     * Enable/Disable query result cache on repository
48
     * If null global cache will be set.
49
     * Set it to false to deactivate cache on this repository
50
     * Set the cache instance in configure method
51
     *
52
     * @var false|CacheInterface
53
     */
54
    protected $resultCache;
55
56
    /**
57
     * @var Metadata
58
     */
59
    private $metadata;
60
61
    /**
62
     * Id generator
63
     *
64
     * Could be defined as string (generator class name). It would be instantiated
65
     * by mapper on generator() method
66
     *
67
     * @var \Bdf\Prime\IdGenerators\GeneratorInterface|null
68
     */
69
    protected $generator;
70
71
    /**
72
     * @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...
73
     */
74
    private $repositoryClass = EntityRepository::class;
75
76
    /**
77
     * The real name of entity class. Could be an none existing class
78
     *
79
     * @var class-string<E>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<E> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<E>.
Loading history...
80
     */
81
    private $entityClass;
82
83
    /**
84
     * The property accessor class name to use by default
85
     *
86
     * @var class-string<PropertyAccessorInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<PropertyAccessorInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<PropertyAccessorInterface>.
Loading history...
87
     */
88
    private $propertyAccessorClass = ReflectionAccessor::class;
89
90
    /**
91
     * Class of the criteria to use
92
     * If null, the class will be resolved from the entity class with the suffix "Criteria" if exists
93
     *
94
     * @var class-string<Criteria>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Criteria>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Criteria>|null.
Loading history...
95
     */
96
    private ?string $criteriaClass = null;
97
98
    /**
99
     * Set repository read only.
100
     *
101
     * @var bool
102
     */
103
    private $readOnly = false;
104
105
    /**
106
     * Use schema resolver
107
     * Disable if schema has not to be manage by this app
108
     *
109
     * @var bool
110
     */
111
    private $useSchemaManager = true;
112
113
    /**
114
     * Use quote identifier
115
     * Allows query builder to use quote identifier
116
     *
117
     * @var bool
118
     */
119
    private $useQuoteIdentifier = false;
120
121
    /**
122
     * The relation builder
123
     *
124
     * @var RelationBuilder
125
     */
126
    private $relationBuilder;
127
128
    /**
129
     * The collection of behaviors
130
     *
131
     * @var BehaviorInterface<E>[]
132
     */
133
    private $behaviors;
134
135
    /**
136
     * The service locator
137
     *
138
     * @var ServiceLocator
139
     */
140
    protected $serviceLocator;
141
142
    /**
143
     * @var MapperHydratorInterface<E>
144
     */
145
    protected $hydrator;
146
147
148
    /**
149
     * Mapper constructor
150
     *
151
     * @param ServiceLocator $serviceLocator
152
     * @param class-string<E> $entityClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<E> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<E>.
Loading history...
153
     * @param Metadata|null $metadata
154
     * @param MapperHydratorInterface<E>|null $hydrator
155
     * @param CacheInterface|null $resultCache
156
     */
157 538
    public function __construct(ServiceLocator $serviceLocator, string $entityClass, ?Metadata $metadata = null, MapperHydratorInterface $hydrator = null, CacheInterface $resultCache = null)
158
    {
159 538
        $this->entityClass = $entityClass;
160 538
        $this->metadata = $metadata ?: new Metadata();
161 538
        $this->serviceLocator = $serviceLocator;
162 538
        $this->resultCache = $resultCache;
163
164 538
        $this->configure();
165
166 538
        $this->metadata->build($this);
167
168 538
        $this->setHydrator($hydrator ?: new MapperHydrator());
169
    }
170
171
    /**
172
     * Custom configuration
173
     */
174 536
    public function configure(): void
175
    {
176
        // to overwrite
177 536
    }
178
179
    /**
180
     * Get entity class
181
     *
182
     * @return class-string<E>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<E> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<E>.
Loading history...
183
     * @final
184
     */
185 553
    public function getEntityClass(): string
186
    {
187 553
        return $this->entityClass;
188
    }
189
190
    /**
191
     * Get metadata
192
     *
193
     * @return Metadata
194
     * @final
195
     */
196 1274
    public function metadata(): Metadata
197
    {
198 1274
        return $this->metadata;
199
    }
200
201
    /**
202
     * Set property accessor class name
203
     *
204
     * @param class-string<PropertyAccessorInterface> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<PropertyAccessorInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<PropertyAccessorInterface>.
Loading history...
205
     * @final
206
     */
207 1
    public function setPropertyAccessorClass(string $className): void
208
    {
209 1
        $this->propertyAccessorClass = $className;
210
    }
211
212
    /**
213
     * Get property accessor class name
214
     *
215
     * @return class-string<PropertyAccessorInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<PropertyAccessorInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<PropertyAccessorInterface>.
Loading history...
216
     * @final
217
     */
218 535
    public function getPropertyAccessorClass(): string
219
    {
220 535
        return $this->propertyAccessorClass;
221
    }
222
223
    /**
224
     * Set repository class name
225
     *
226
     * @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...
227
     * @final
228
     */
229 1
    public function setRepositoryClass(string $className): void
230
    {
231 1
        $this->repositoryClass = $className;
232
    }
233
234
    /**
235
     * Get repository class name
236
     *
237
     * @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...
238
     * @final
239
     */
240 2
    public function getRepositoryClass(): string
241
    {
242 2
        return $this->repositoryClass;
243
    }
244
245
    /**
246
     * Set the repository read only
247
     *
248
     * @param bool $flag
249
     * @final
250
     */
251 558
    public function setReadOnly(bool $flag): void
252
    {
253 558
        $this->readOnly = $flag;
254
    }
255
256
    /**
257
     * Get repository read only state
258
     *
259
     * @return bool
260
     * @final
261
     */
262 668
    public function isReadOnly(): bool
263
    {
264 668
        return $this->readOnly;
265
    }
266
267
    /**
268
     * Disable schema manager on repository
269
     * @final
270
     */
271 1
    public function disableSchemaManager(): void
272
    {
273 1
        $this->useSchemaManager = false;
274
    }
275
276
    /**
277
     * Does repository have a schema manager
278
     *
279
     * @return bool
280
     * @final
281
     */
282 878
    public function hasSchemaManager(): bool
283
    {
284 878
        return $this->useSchemaManager;
285
    }
286
287
    /**
288
     * Set the query builder quote identifier
289
     *
290
     * @param bool $flag
291
     * @final
292
     */
293 3
    public function setQuoteIdentifier(bool $flag): void
294
    {
295 3
        $this->useQuoteIdentifier = $flag;
296
    }
297
298
    /**
299
     * Does query builder use quote identifier
300
     *
301
     * @return bool
302
     * @final
303
     */
304 535
    public function hasQuoteIdentifier(): bool
305
    {
306 535
        return $this->useQuoteIdentifier;
307
    }
308
309
    /**
310
     * Set generator ID
311
     *
312
     * @param string|GeneratorInterface $generator
313
     * @final
314
     */
315 3
    public function setGenerator($generator): void
316
    {
317 3
        if (!is_string($generator) && !$generator instanceof GeneratorInterface) {
0 ignored issues
show
introduced by
$generator is always a sub-type of Bdf\Prime\IdGenerators\GeneratorInterface.
Loading history...
318 1
            throw new LogicException('Trying to set an invalid generator in "' . get_class($this) . '"');
319
        }
320
321 2
        $this->generator = $generator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $generator can also be of type string. However, the property $generator is declared as type Bdf\Prime\IdGenerators\GeneratorInterface|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
322
    }
323
324
    /**
325
     * Get generator ID
326
     *
327
     * @return GeneratorInterface
328
     * @final
329
     */
330 650
    public function generator(): GeneratorInterface
331
    {
332 650
        if ($this->generator === null) {
333 308
            if ($this->metadata->isAutoIncrementPrimaryKey()) {
334 210
                $this->generator = new AutoIncrementGenerator($this);
335 262
            } elseif ($this->metadata->isSequencePrimaryKey()) {
336 229
                $this->generator = new TableGenerator($this);
337
            } else {
338 308
                $this->generator = new NullGenerator();
339
            }
340 604
        } elseif (is_string($this->generator)) {
0 ignored issues
show
introduced by
The condition is_string($this->generator) is always false.
Loading history...
341 1
            $className = $this->generator;
342 1
            $this->generator = new $className($this);
343
        }
344
345 650
        return $this->generator;
346
    }
347
348
    /**
349
     * @return MapperHydratorInterface<E>
350
     * @final
351
     */
352 4
    public function hydrator(): MapperHydratorInterface
353
    {
354 4
        return $this->hydrator;
355
    }
356
357
    /**
358
     * @param MapperHydratorInterface<E> $hydrator
359
     *
360
     * @return $this
361
     * @final
362
     */
363 538
    public function setHydrator(MapperHydratorInterface $hydrator)
364
    {
365 538
        $this->hydrator = $hydrator;
366 538
        $this->hydrator->setPrimeInstantiator($this->serviceLocator->instantiator());
367 538
        $this->hydrator->setPrimeMetadata($this->metadata);
368
369 538
        return $this;
370
    }
371
372
    /**
373
     * Define the criteria class
374
     * By default, it is the entity class with "Criteria" suffix if exists, else base Criteria class
375
     *
376
     * @param class-string<Criteria> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Criteria> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Criteria>.
Loading history...
377
     */
378 1
    final public function setCriteriaClass(string $className): void
379
    {
380 1
        $this->criteriaClass = $className;
381
    }
382
383
    /**
384
     * Set ID value en entity
385
     * Only sequenceable attribute is set (the first one)
386
     *
387
     * @param E $entity
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\Mapper\E 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...
388
     * @param mixed $value
389
     *
390
     * @return void
391
     * @final
392
     */
393 4
    public function setId($entity, $value): void
394
    {
395 4
        $this->hydrateOne($entity, $this->metadata->primary['attributes'][0], $value);
396
    }
397
398
    /**
399
     * Get ID value of an entity
400
     * Only sequenceable attribute is get (the first one)
401
     *
402
     * @param E $entity
403
     *
404
     * @return mixed
405
     * @final
406
     */
407 108
    public function getId($entity)
408
    {
409 108
        return $this->extractOne($entity, $this->metadata->primary['attributes'][0]);
410
    }
411
412
    /**
413
     * Get attribute value of an entity
414
     *
415
     * @param E $entity
416
     * @param string $attribute
417
     *
418
     * @return mixed
419
     * @final
420
     */
421 451
    public function extractOne($entity, string $attribute)
422
    {
423 451
        return $this->hydrator->extractOne($entity, $attribute);
424
    }
425
426
    /**
427
     * Hydrate on property value of an entity
428
     *
429
     * @param E $entity
430
     * @param string $attribute
431
     * @param mixed  $value
432
     *
433
     * @return void
434
     * @final
435
     */
436 375
    public function hydrateOne($entity, string $attribute, $value): void
437
    {
438 375
        $this->hydrator->hydrateOne($entity, $attribute, $value);
439
    }
440
441
    /**
442
     * Get primary key criteria
443
     *
444
     * @param E $entity
445
     *
446
     * @return array
447
     * @final
448
     */
449 173
    public function primaryCriteria($entity): array
450
    {
451 173
        return $this->hydrator->flatExtract($entity, array_flip($this->metadata->primary['attributes']));
452
    }
453
454
    /**
455
     * Instanciate the related class entity
456
     *
457
     * @return E
458
     * @final
459
     */
460 530
    public function instantiate()
461
    {
462
        /** @var E */
463 530
        return $this->serviceLocator->instantiator()
464 530
            ->instantiate($this->metadata->entityClass, $this->metadata->instantiatorHint);
465
    }
466
467
    /**
468
     * User api to instantiate related entity
469
     *
470
     * @param array $data
471
     *
472
     * @return E
473
     * @final
474
     */
475 97
    public function entity(array $data)
476
    {
477 97
        $entity = $this->instantiate();
478
479
        // Allows custom import from developpers.
480 97
        if ($entity instanceof ImportableInterface) {
481 96
            $entity->import($data);
482
        } else {
483 1
            $this->serviceLocator->hydrator($this->metadata->entityClass)
484 1
                ->hydrate($entity, $data);
485
        }
486
487 97
        return $entity;
488
    }
489
490
    /**
491
     * Transform entity to db one dimension array
492
     *
493
     * @param E $entity Entity object
494
     * @param array|null $attributes  Attribute should be flipped as ['key' => true]
495
     *
496
     * @return array
497
     * @final
498
     */
499 661
    public function prepareToRepository($entity, array $attributes = null): array
500
    {
501 661
        return $this->hydrator->flatExtract($entity, $attributes);
502
    }
503
504
    /**
505
     * Get valid array for entity
506
     *
507
     * Inject one dimension array (db field) into entity
508
     * Map attribute and cast value
509
     *
510
     * $optimisation est un tableau donné par le query builder dans le but
511
     * d'optimiser le chargement des relations et des tableaux associatifs. Il contient les entités regroupés par
512
     * la valeur du champs demandé
513
     *
514
     * @param array             $data  Db data
515
     * @param PlatformInterface $platform
516
     *
517
     * @return E
518
     */
519 473
    public function prepareFromRepository(array $data, PlatformInterface $platform)
520
    {
521 473
        $entity = $this->instantiate();
522
523 473
        $this->hydrator->flatHydrate($entity, $data, $platform->types());
524
525 473
        return $entity;
526
    }
527
528
    /**
529
     * Get the repository
530
     *
531
     * @return RepositoryInterface<E>
532
     * @final
533
     */
534 424
    public function repository(): RepositoryInterface
535
    {
536 424
        $className = $this->repositoryClass;
537
538 424
        return new $className($this, $this->serviceLocator, $this->resultCache === false ? null : $this->resultCache);
539
    }
540
541
    /**
542
     * Create a criteria object for this entity
543
     *
544
     * @param array<string, mixed> $filters
545
     *
546
     * @return Criteria
547
     */
548 6
    public function criteria(array $filters = []): Criteria
549
    {
550 6
        $class = $this->criteriaClass;
551
552 6
        if (!$class) {
553 5
            $class = $this->entityClass . 'Criteria';
554 5
            $this->criteriaClass = $class = class_exists($class) ? $class : Criteria::class;
555
        }
556
557 6
        return new $class($filters);
558
    }
559
560
    /**
561
     * Get the mapper info
562
     *
563
     * @return MapperInfo
564
     * @throws PrimeException
565
     * @final
566
     */
567 77
    public function info(): MapperInfo
568
    {
569 77
        $platform = $this->serviceLocator->connection($this->metadata()->connection)->platform();
570
571 77
        return new MapperInfo($this, $platform->types());
572
    }
573
574
    /**
575
     * Get defined relation
576
     *
577
     * Build object relation defined by user
578
     *
579
     * @param string $relationName
580
     *
581
     * @return array  Metadata for relation definition
582
     *
583
     * @throws \RuntimeException  If relation or type does not exist
584
     */
585 266
    public function relation(string $relationName): array
586
    {
587 266
        $relations = $this->relations();
588
589 266
        if (!isset($relations[$relationName])) {
590 7
            throw new RelationNotFoundException('Relation "' . $relationName . '" is not set in ' . $this->metadata->entityName);
591
        }
592
593 261
        return $relations[$relationName];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $relations[$relationName] returns the type Bdf\Prime\Relations\Builder\RelationDefinition which is incompatible with the type-hinted return array.
Loading history...
594
    }
595
596
    //
597
    //------------ API configuration du mapping
598
    //
599
600
    /**
601
     * Definition du schema
602
     *
603
     * Definition
604
     *  - connection         : The connection name declare in connection manager (mandatory).
605
     *  - database           : The database name.
606
     *  - table              : The table name (mandatory).
607
     *  - tableOptions       : The table options (ex: engine => myisam).
608
     *
609
     * <code>
610
     *  return [
611
     *     'connection'   => (string),
612
     *     'database'     => (string),
613
     *     'table'        => (string),
614
     *     'tableOptions' => (array),
615
     *  ];
616
     * </code>
617
     *
618
     * @return array
619
     */
620
    abstract public function schema(): array;
621
622
    /**
623
     * Gets repository fields builder
624
     *
625
     * @return iterable<string, FieldDefinition>
626
     * @final
627
     *
628
     * @todo should be final
629
     */
630 533
    public function fields(): iterable
631
    {
632 533
        $builder = new FieldBuilder();
633 533
        $this->buildFields($builder);
634
635 533
        foreach ($this->behaviors() as $behavior) {
636 4
            $behavior->changeSchema($builder);
637
        }
638
639 533
        return $builder;
640
    }
641
642
    /**
643
     * Build fields from this mapper.
644
     *
645
     * To overwrite.
646
     *
647
     * @param FieldBuilder $builder
648
     */
649
    public function buildFields(FieldBuilder $builder): void
0 ignored issues
show
Unused Code introduced by
The parameter $builder 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

649
    public function buildFields(/** @scrutinizer ignore-unused */ FieldBuilder $builder): 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...
650
    {
651
        throw new LogicException('Fields must be defined in mapper '.__CLASS__);
652
    }
653
654
    /**
655
     * Sequence definition.
656
     *
657
     * The metadata will build the sequence info using this method if the primary key is defined as sequence (Metadata::PK_SEQUENCE).
658
     * Definition:
659
     *  - connection         : The connection name declare in connection manager. The table connection will be used by default.
660
     *  - table              : The table sequence name.
661
     *                         The table name with suffix '_seq' will be used by default.
662
     *  - column             : The sequence column name. Default 'id'.
663
     *  - tableOptions       : The sequence table options (ex: engine => myisam).
664
     *
665
     * <code>
666
     *  return [
667
     *     'connection'   => (string),
668
     *     'table'        => (string),
669
     *     'column'       => (string),
670
     *     'tableOptions' => (array),
671
     *  ];
672
     * </code>
673
     *
674
     * @return 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...
675
     *     connection?: string|null,
676
     *     table?: string|null,
677
     *     column?: string|null,
678
     *     tableOptions?: array,
679
     * }
680
     */
681 527
    public function sequence(): array
682
    {
683 527
        return [
684 527
            'connection'   => null,
685 527
            'table'        => null,
686 527
            'column'       => null,
687 527
            'tableOptions' => [],
688 527
        ];
689
    }
690
691
    /**
692
     * Gets custom filters
693
     * To overwrite
694
     *
695
     * <code>
696
     *  return [
697
     *      'customFilterName' => function(<Bdf\Prime\Query\QueryInterface> $query, <mixed> $value) {
698
     *          return <void>
699
     *      },
700
     *  ];
701
     * </code>
702
     *
703
     * @return array<string, callable>
704
     */
705 468
    public function filters(): array
706
    {
707 468
        return [];
708
    }
709
710
    /**
711
     * Array of index
712
     *
713
     * <code>
714
     *  return [
715
     *      ['attribute1', 'attribute2']
716
     *  ];
717
     * </code>
718
     *
719
     * @return array
720
     * @final
721
     *
722
     * @todo Make final
723
     */
724 531
    public function indexes(): array
725
    {
726 531
        $builder = new IndexBuilder();
727
728 531
        $this->buildIndexes($builder);
729
730 531
        return $builder->build();
731
    }
732
733
    /**
734
     * Build the table indexes
735
     * Note: Indexes can be added on undeclared fields
736
     *
737
     * <code>
738
     * public function buildIndexes(IndexBuilder $builder)
739
     * {
740
     *     $builder
741
     *         ->add()->on('name')->unique()
742
     *         ->add()->on('reference', ['length' => 12])
743
     *         ->add()->on(['type', 'date'])
744
     * }
745
     * </code>
746
     *
747
     * @param IndexBuilder $builder
748
     */
749 521
    public function buildIndexes(IndexBuilder $builder): void
0 ignored issues
show
Unused Code introduced by
The parameter $builder 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

749
    public function buildIndexes(/** @scrutinizer ignore-unused */ IndexBuilder $builder): 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...
750
    {
751 521
    }
752
753
    /**
754
     * Repository extension
755
     * returns additional methods in repository
756
     *
757
     * <code>
758
     * return [
759
     *     'customMethod' => function($query, $test) {
760
     *
761
     *     },
762
     * ];
763
     *
764
     * $repository->customMethod('test');
765
     * </code>
766
     *
767
     * @return array<string, callable>
768
     */
769
    public function scopes(): array
770
    {
771
        throw new LogicException('No scopes have been defined in "' . get_class($this) . '"');
772
    }
773
774
    /**
775
     * Get custom queries for repository
776
     * A custom query works mostly like scopes, but with some differences :
777
     * - Cannot be called using a query (i.e. $query->where(...)->myScope())
778
     * - The function has responsability of creating the query instance
779
     * - The first argument is the repository
780
     *
781
     * <code>
782
     * return [
783
     *     'findByCustom' => function (EntityRepository $repository, $search) {
784
     *         return $repository->make(MyCustomQuery::class)->where('first', $search)->first();
785
     *     }
786
     * ];
787
     * </code>
788
     *
789
     * @return array<string, callable(\Bdf\Prime\Repository\RepositoryInterface<E>,mixed...):mixed>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, callable(\...ace<E>,mixed...):mixed> at position 4 could not be parsed: Expected '>' at position 4, but found 'callable'.
Loading history...
790
     */
791 401
    public function queries(): array
792
    {
793 401
        return [];
794
    }
795
796
    /**
797
     * Register event on notifier
798
     *
799
     * @param RepositoryEventsSubscriberInterface<E> $notifier
800
     * @final
801
     */
802 424
    public function events(RepositoryEventsSubscriberInterface $notifier): void
803
    {
804 424
        $this->customEvents($notifier);
805
806 424
        foreach ($this->behaviors() as $behavior) {
807 4
            $behavior->subscribe($notifier);
808
        }
809
    }
810
811
    /**
812
     * Register custom event on notifier
813
     *
814
     * To overwrite.
815
     *
816
     * @param RepositoryEventsSubscriberInterface<E> $notifier
817
     */
818 384
    public function customEvents(RepositoryEventsSubscriberInterface $notifier): void
819
    {
820
        // To overwrite
821 384
    }
822
823
    /**
824
     * Get all behaviors
825
     *
826
     * @return BehaviorInterface<E>[]
827
     */
828 535
    final public function behaviors(): array
829
    {
830 535
        if ($this->behaviors === null) {
831 535
            $this->behaviors = $this->getDefinedBehaviors();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getDefinedBehaviors() of type array is incompatible with the declared type Bdf\Prime\Behaviors\BehaviorInterface of property $behaviors.

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...
832
        }
833
834 535
        return $this->behaviors;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->behaviors could return the type Bdf\Prime\Behaviors\BehaviorInterface which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
835
    }
836
837
    /**
838
     * Custom definition of behaviors
839
     *
840
     * To overwrite.
841
     *
842
     * @return BehaviorInterface<E>[]
843
     */
844 532
    public function getDefinedBehaviors(): array
845
    {
846 532
        return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type Bdf\Prime\Behaviors\BehaviorInterface.
Loading history...
847
    }
848
849
    /**
850
     * Get all relations
851
     *
852
     * @return array<string, RelationDefinition>
853
     * @final
854
     *
855
     * @todo should be final
856
     */
857 669
    public function relations(): array
858
    {
859 669
        if ($this->relationBuilder === null) {
860 534
            $this->relationBuilder = new RelationBuilder();
861 534
            $this->buildRelations($this->relationBuilder);
862
        }
863
864 669
        return $this->relationBuilder->relations();
865
    }
866
867
    /**
868
     * Build relations from this mapper.
869
     *
870
     * To overwrite.
871
     *
872
     * @param RelationBuilder $builder
873
     */
874 215
    public function buildRelations(RelationBuilder $builder): void
0 ignored issues
show
Unused Code introduced by
The parameter $builder 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

874
    public function buildRelations(/** @scrutinizer ignore-unused */ RelationBuilder $builder): 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...
875
    {
876
        // to overwrite
877 215
    }
878
879
    /**
880
     * Get all constraints
881
     *
882
     * @return array
883
     */
884 535
    final public function constraints(): array
885
    {
886 535
        $constraints = $this->customConstraints();
887
888 535
        foreach ($this->behaviors() as $behavior) {
889 4
            $constraints += $behavior->constraints();
890
        }
891
892 535
        return $constraints;
893
    }
894
895
    /**
896
     * Register custom event on notifier
897
     *
898
     * To overwrite.
899
     *
900
     * <code>
901
     * return [
902
     *     'attribute' => 'value'
903
     * ]
904
     * </code>
905
     *
906
     * @return array
907
     */
908 513
    public function customConstraints(): array
909
    {
910 513
        return [];
911
    }
912
913
    /**
914
     * Clear dependencies for break cyclic references
915
     *
916
     * @internal
917
     */
918 1
    public function destroy(): void
919
    {
920 1
        $this->serviceLocator = null;
921 1
        $this->generator = null;
922 1
        $this->hydrator = null;
923 1
        $this->metadata = null;
924
    }
925
}
926