Issues (590)

src/Mapper/Mapper.php (22 issues)

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\Hydrator\MapperHydrator;
8
use Bdf\Prime\Entity\Hydrator\MapperHydratorInterface;
9
use Bdf\Prime\Entity\ImportableInterface;
10
use Bdf\Prime\Exception\PrimeException;
11
use Bdf\Prime\IdGenerators\AutoIncrementGenerator;
12
use Bdf\Prime\IdGenerators\GeneratorInterface;
13
use Bdf\Prime\IdGenerators\NullGenerator;
14
use Bdf\Prime\IdGenerators\TableGenerator;
15
use Bdf\Prime\Mapper\Builder\FieldBuilder;
16
use Bdf\Prime\Mapper\Builder\IndexBuilder;
17
use Bdf\Prime\Mapper\Info\MapperInfo;
18
use Bdf\Prime\Platform\PlatformInterface;
19
use Bdf\Prime\Relations\Builder\RelationBuilder;
20
use Bdf\Prime\Relations\Exceptions\RelationNotFoundException;
21
use Bdf\Prime\Repository\EntityRepository;
22
use Bdf\Prime\Repository\RepositoryEventsSubscriberInterface;
23
use Bdf\Prime\Repository\RepositoryInterface;
24
use Bdf\Prime\ServiceLocator;
25
use Bdf\Serializer\PropertyAccessor\PropertyAccessorInterface;
26
use Bdf\Serializer\PropertyAccessor\ReflectionAccessor;
27
use LogicException;
28
29
/**
30
 * Mapper
31
 *
32
 * Contient les méta données de la table.
33
 *
34
 * @todo Convertir la donnée avec le type approprié sur les methodes setId, hydrateOne
35
 *
36
 * @template E as object
37
 *
38
 * @psalm-import-type FieldDefinition from FieldBuilder
39
 * @psalm-import-type RelationDefinition from RelationBuilder
40
 */
41
abstract class Mapper
42
{
43
    /**
44
     * Enable/Disable query result cache on repository
45
     * If null global cache will be set.
46
     * Set it to false to deactivate cache on this repository
47
     * Set the cache instance in configure method
48
     *
49
     * @var false|CacheInterface
50
     */
51
    protected $resultCache;
52
53
    /**
54
     * @var Metadata
55
     */
56
    private $metadata;
57
58
    /**
59
     * Id generator
60
     *
61
     * Could be defined as string (generator class name). It would be instantiated
62
     * by mapper on generator() method
63
     *
64
     * @var \Bdf\Prime\IdGenerators\GeneratorInterface|null
65
     */
66
    protected $generator;
67
68
    /**
69
     * @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...
70
     */
71
    private $repositoryClass = EntityRepository::class;
72
73
    /**
74
     * The real name of entity class. Could be an none existing class
75
     *
76
     * @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...
77
     */
78
    private $entityClass;
79
80
    /**
81
     * The property accessor class name to use by default
82
     *
83
     * @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...
84
     */
85
    private $propertyAccessorClass = ReflectionAccessor::class;
86
87
    /**
88
     * Set repository read only.
89
     *
90
     * @var bool
91
     */
92
    private $readOnly = false;
93
94
    /**
95
     * Use schema resolver
96
     * Disable if schema has not to be manage by this app
97
     *
98
     * @var bool
99
     */
100
    private $useSchemaManager = true;
101
102
    /**
103
     * Use quote identifier
104
     * Allows query builder to use quote identifier
105
     *
106
     * @var bool
107
     */
108
    private $useQuoteIdentifier = false;
109
110
    /**
111
     * The relation builder
112
     *
113
     * @var RelationBuilder
114
     */
115
    private $relationBuilder;
116
117
    /**
118
     * The collection of behaviors
119
     *
120
     * @var BehaviorInterface<E>[]
121
     */
122
    private $behaviors;
123
124
    /**
125
     * The service locator
126
     *
127
     * @var ServiceLocator
128
     */
129
    protected $serviceLocator;
130
131
    /**
132
     * @var MapperHydratorInterface<E>
133
     */
134
    protected $hydrator;
135
136
137
    /**
138
     * Mapper constructor
139
     *
140
     * @param ServiceLocator $serviceLocator
141
     * @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...
142
     * @param Metadata|null $metadata
143
     * @param MapperHydratorInterface<E>|null $hydrator
144
     * @param CacheInterface|null $resultCache
145
     */
146 516
    public function __construct(ServiceLocator $serviceLocator, string $entityClass, ?Metadata $metadata = null, MapperHydratorInterface $hydrator = null, CacheInterface $resultCache = null)
147
    {
148 516
        $this->entityClass = $entityClass;
149 516
        $this->metadata = $metadata ?: new Metadata();
150 516
        $this->serviceLocator = $serviceLocator;
151 516
        $this->resultCache = $resultCache;
152
153 516
        $this->configure();
154
155 516
        $this->metadata->build($this);
156
157 516
        $this->setHydrator($hydrator ?: new MapperHydrator());
158
    }
159
160
    /**
161
     * Custom configuration
162
     */
163 514
    public function configure(): void
164
    {
165
        // to overwrite
166 514
    }
167
168
    /**
169
     * Get entity class
170
     *
171
     * @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...
172
     * @final
173
     */
174 531
    public function getEntityClass(): string
175
    {
176 531
        return $this->entityClass;
177
    }
178
179
    /**
180
     * Get metadata
181
     *
182
     * @return Metadata
183
     * @final
184
     */
185 1253
    public function metadata(): Metadata
186
    {
187 1253
        return $this->metadata;
188
    }
189
190
    /**
191
     * Set property accessor class name
192
     *
193
     * @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...
194
     * @final
195
     */
196 1
    public function setPropertyAccessorClass(string $className): void
197
    {
198 1
        $this->propertyAccessorClass = $className;
199
    }
200
201
    /**
202
     * Get property accessor class name
203
     *
204
     * @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...
205
     * @final
206
     */
207 513
    public function getPropertyAccessorClass(): string
208
    {
209 513
        return $this->propertyAccessorClass;
210
    }
211
212
    /**
213
     * Set repository class name
214
     *
215
     * @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...
216
     * @final
217
     */
218 1
    public function setRepositoryClass(string $className): void
219
    {
220 1
        $this->repositoryClass = $className;
221
    }
222
223
    /**
224
     * Get repository class name
225
     *
226
     * @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...
227
     * @final
228
     */
229 2
    public function getRepositoryClass(): string
230
    {
231 2
        return $this->repositoryClass;
232
    }
233
234
    /**
235
     * Set the repository read only
236
     *
237
     * @param bool $flag
238
     * @final
239
     */
240 557
    public function setReadOnly(bool $flag): void
241
    {
242 557
        $this->readOnly = $flag;
243
    }
244
245
    /**
246
     * Get repository read only state
247
     *
248
     * @return bool
249
     * @final
250
     */
251 666
    public function isReadOnly(): bool
252
    {
253 666
        return $this->readOnly;
254
    }
255
256
    /**
257
     * Disable schema manager on repository
258
     * @final
259
     */
260 1
    public function disableSchemaManager(): void
261
    {
262 1
        $this->useSchemaManager = false;
263
    }
264
265
    /**
266
     * Does repository have a schema manager
267
     *
268
     * @return bool
269
     * @final
270
     */
271 873
    public function hasSchemaManager(): bool
272
    {
273 873
        return $this->useSchemaManager;
274
    }
275
276
    /**
277
     * Set the query builder quote identifier
278
     *
279
     * @param bool $flag
280
     * @final
281
     */
282 3
    public function setQuoteIdentifier(bool $flag): void
283
    {
284 3
        $this->useQuoteIdentifier = $flag;
285
    }
286
287
    /**
288
     * Does query builder use quote identifier
289
     *
290
     * @return bool
291
     * @final
292
     */
293 513
    public function hasQuoteIdentifier(): bool
294
    {
295 513
        return $this->useQuoteIdentifier;
296
    }
297
298
    /**
299
     * Set generator ID
300
     *
301
     * @param string|GeneratorInterface $generator
302
     * @final
303
     */
304 3
    public function setGenerator($generator): void
305
    {
306 3
        if (!is_string($generator) && !$generator instanceof GeneratorInterface) {
0 ignored issues
show
$generator is always a sub-type of Bdf\Prime\IdGenerators\GeneratorInterface.
Loading history...
307 1
            throw new LogicException('Trying to set an invalid generator in "' . get_class($this) . '"');
308
        }
309
310 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...
311
    }
312
313
    /**
314
     * Get generator ID
315
     *
316
     * @return GeneratorInterface
317
     * @final
318
     */
319 648
    public function generator(): GeneratorInterface
320
    {
321 648
        if ($this->generator === null) {
322 305
            if ($this->metadata->isAutoIncrementPrimaryKey()) {
323 208
                $this->generator = new AutoIncrementGenerator($this);
324 261
            } elseif ($this->metadata->isSequencePrimaryKey()) {
325 229
                $this->generator = new TableGenerator($this);
326
            } else {
327 305
                $this->generator = new NullGenerator();
328
            }
329 605
        } elseif (is_string($this->generator)) {
0 ignored issues
show
The condition is_string($this->generator) is always false.
Loading history...
330 1
            $className = $this->generator;
331 1
            $this->generator = new $className($this);
332
        }
333
334 648
        return $this->generator;
335
    }
336
337
    /**
338
     * @return MapperHydratorInterface<E>
339
     * @final
340
     */
341 4
    public function hydrator(): MapperHydratorInterface
342
    {
343 4
        return $this->hydrator;
344
    }
345
346
    /**
347
     * @param MapperHydratorInterface<E> $hydrator
348
     *
349
     * @return $this
350
     * @final
351
     */
352 516
    public function setHydrator(MapperHydratorInterface $hydrator)
353
    {
354 516
        $this->hydrator = $hydrator;
355 516
        $this->hydrator->setPrimeInstantiator($this->serviceLocator->instantiator());
356 516
        $this->hydrator->setPrimeMetadata($this->metadata);
357
358 516
        return $this;
359
    }
360
361
    /**
362
     * Set ID value en entity
363
     * Only sequenceable attribute is set (the first one)
364
     *
365
     * @param E $entity
0 ignored issues
show
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...
366
     * @param mixed $value
367
     *
368
     * @return void
369
     * @final
370
     */
371 4
    public function setId($entity, $value): void
372
    {
373 4
        $this->hydrateOne($entity, $this->metadata->primary['attributes'][0], $value);
374
    }
375
376
    /**
377
     * Get ID value of an entity
378
     * Only sequenceable attribute is get (the first one)
379
     *
380
     * @param E $entity
381
     *
382
     * @return mixed
383
     * @final
384
     */
385 108
    public function getId($entity)
386
    {
387 108
        return $this->extractOne($entity, $this->metadata->primary['attributes'][0]);
388
    }
389
390
    /**
391
     * Get attribute value of an entity
392
     *
393
     * @param E $entity
394
     * @param string $attribute
395
     *
396
     * @return mixed
397
     * @final
398
     */
399 451
    public function extractOne($entity, string $attribute)
400
    {
401 451
        return $this->hydrator->extractOne($entity, $attribute);
402
    }
403
404
    /**
405
     * Hydrate on property value of an entity
406
     *
407
     * @param E $entity
408
     * @param string $attribute
409
     * @param mixed  $value
410
     *
411
     * @return void
412
     * @final
413
     */
414 374
    public function hydrateOne($entity, string $attribute, $value): void
415
    {
416 374
        $this->hydrator->hydrateOne($entity, $attribute, $value);
417
    }
418
419
    /**
420
     * Get primary key criteria
421
     *
422
     * @param E $entity
423
     *
424
     * @return array
425
     * @final
426
     */
427 172
    public function primaryCriteria($entity): array
428
    {
429 172
        return $this->hydrator->flatExtract($entity, array_flip($this->metadata->primary['attributes']));
430
    }
431
432
    /**
433
     * Instanciate the related class entity
434
     *
435
     * @return E
436
     * @final
437
     */
438 529
    public function instantiate()
439
    {
440
        /** @var E */
441 529
        return $this->serviceLocator->instantiator()
442 529
            ->instantiate($this->metadata->entityClass, $this->metadata->instantiatorHint);
443
    }
444
445
    /**
446
     * User api to instantiate related entity
447
     *
448
     * @param array $data
449
     *
450
     * @return E
451
     * @final
452
     */
453 97
    public function entity(array $data)
454
    {
455 97
        $entity = $this->instantiate();
456
457
        // Allows custom import from developpers.
458 97
        if ($entity instanceof ImportableInterface) {
459 96
            $entity->import($data);
460
        } else {
461 1
            $this->serviceLocator->hydrator($this->metadata->entityClass)
462 1
                ->hydrate($entity, $data);
463
        }
464
465 97
        return $entity;
466
    }
467
468
    /**
469
     * Transform entity to db one dimension array
470
     *
471
     * @param E $entity Entity object
472
     * @param array|null $attributes  Attribute should be flipped as ['key' => true]
473
     *
474
     * @return array
475
     * @final
476
     */
477 659
    public function prepareToRepository($entity, array $attributes = null): array
478
    {
479 659
        return $this->hydrator->flatExtract($entity, $attributes);
480
    }
481
482
    /**
483
     * Get valid array for entity
484
     *
485
     * Inject one dimension array (db field) into entity
486
     * Map attribute and cast value
487
     *
488
     * $optimisation est un tableau donné par le query builder dans le but
489
     * d'optimiser le chargement des relations et des tableaux associatifs. Il contient les entités regroupés par
490
     * la valeur du champs demandé
491
     *
492
     * @param array             $data  Db data
493
     * @param PlatformInterface $platform
494
     *
495
     * @return E
496
     */
497 472
    public function prepareFromRepository(array $data, PlatformInterface $platform)
498
    {
499 472
        $entity = $this->instantiate();
500
501 472
        $this->hydrator->flatHydrate($entity, $data, $platform->types());
502
503 472
        return $entity;
504
    }
505
506
    /**
507
     * Get the repository
508
     *
509
     * @return RepositoryInterface<E>
510
     * @final
511
     */
512 411
    public function repository(): RepositoryInterface
513
    {
514 411
        $className = $this->repositoryClass;
515
516 411
        return new $className($this, $this->serviceLocator, $this->resultCache === false ? null : $this->resultCache);
517
    }
518
519
    /**
520
     * Get the mapper info
521
     *
522
     * @return MapperInfo
523
     * @throws PrimeException
524
     * @final
525
     */
526 73
    public function info(): MapperInfo
527
    {
528 73
        $platform = $this->serviceLocator->connection($this->metadata()->connection)->platform();
529
530 73
        return new MapperInfo($this, $platform->types());
531
    }
532
533
    /**
534
     * Get defined relation
535
     *
536
     * Build object relation defined by user
537
     *
538
     * @param string $relationName
539
     *
540
     * @return array  Metadata for relation definition
541
     *
542
     * @throws \RuntimeException  If relation or type does not exist
543
     */
544 266
    public function relation(string $relationName): array
545
    {
546 266
        $relations = $this->relations();
547
548 266
        if (!isset($relations[$relationName])) {
549 7
            throw new RelationNotFoundException('Relation "' . $relationName . '" is not set in ' . $this->metadata->entityName);
550
        }
551
552 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...
553
    }
554
555
    //
556
    //------------ API configuration du mapping
557
    //
558
559
    /**
560
     * Definition du schema
561
     *
562
     * Definition
563
     *  - connection         : The connection name declare in connection manager (mandatory).
564
     *  - database           : The database name.
565
     *  - table              : The table name (mandatory).
566
     *  - tableOptions       : The table options (ex: engine => myisam).
567
     *
568
     * <code>
569
     *  return [
570
     *     'connection'   => (string),
571
     *     'database'     => (string),
572
     *     'table'        => (string),
573
     *     'tableOptions' => (array),
574
     *  ];
575
     * </code>
576
     *
577
     * @return array
578
     */
579
    abstract public function schema(): array;
580
581
    /**
582
     * Gets repository fields builder
583
     *
584
     * @return iterable<string, FieldDefinition>
585
     * @final
586
     *
587
     * @todo should be final
588
     */
589 511
    public function fields(): iterable
590
    {
591 511
        $builder = new FieldBuilder();
592 511
        $this->buildFields($builder);
593
594 511
        foreach ($this->behaviors() as $behavior) {
595 4
            $behavior->changeSchema($builder);
596
        }
597
598 511
        return $builder;
599
    }
600
601
    /**
602
     * Build fields from this mapper.
603
     *
604
     * To overwrite.
605
     *
606
     * @param FieldBuilder $builder
607
     */
608
    public function buildFields(FieldBuilder $builder): void
0 ignored issues
show
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

608
    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...
609
    {
610
        throw new LogicException('Fields must be defined in mapper '.__CLASS__);
611
    }
612
613
    /**
614
     * Sequence definition.
615
     *
616
     * The metadata will build the sequence info using this method if the primary key is defined as sequence (Metadata::PK_SEQUENCE).
617
     * Definition:
618
     *  - connection         : The connection name declare in connection manager. The table connection will be used by default.
619
     *  - table              : The table sequence name.
620
     *                         The table name with suffix '_seq' will be used by default.
621
     *  - column             : The sequence column name. Default 'id'.
622
     *  - tableOptions       : The sequence table options (ex: engine => myisam).
623
     *
624
     * <code>
625
     *  return [
626
     *     'connection'   => (string),
627
     *     'table'        => (string),
628
     *     'column'       => (string),
629
     *     'tableOptions' => (array),
630
     *  ];
631
     * </code>
632
     *
633
     * @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...
634
     *     connection?: string|null,
635
     *     table?: string|null,
636
     *     column?: string|null,
637
     *     tableOptions?: array,
638
     * }
639
     */
640 506
    public function sequence(): array
641
    {
642 506
        return [
643 506
            'connection'   => null,
644 506
            'table'        => null,
645 506
            'column'       => null,
646 506
            'tableOptions' => [],
647 506
        ];
648
    }
649
650
    /**
651
     * Gets custom filters
652
     * To overwrite
653
     *
654
     * <code>
655
     *  return [
656
     *      'customFilterName' => function(<Bdf\Prime\Query\QueryInterface> $query, <mixed> $value) {
657
     *          return <void>
658
     *      },
659
     *  ];
660
     * </code>
661
     *
662
     * @return array<string, callable>
663
     */
664 459
    public function filters(): array
665
    {
666 459
        return [];
667
    }
668
669
    /**
670
     * Array of index
671
     *
672
     * <code>
673
     *  return [
674
     *      ['attribute1', 'attribute2']
675
     *  ];
676
     * </code>
677
     *
678
     * @return array
679
     * @final
680
     *
681
     * @todo Make final
682
     */
683 509
    public function indexes(): array
684
    {
685 509
        $builder = new IndexBuilder();
686
687 509
        $this->buildIndexes($builder);
688
689 509
        return $builder->build();
690
    }
691
692
    /**
693
     * Build the table indexes
694
     * Note: Indexes can be added on undeclared fields
695
     *
696
     * <code>
697
     * public function buildIndexes(IndexBuilder $builder)
698
     * {
699
     *     $builder
700
     *         ->add()->on('name')->unique()
701
     *         ->add()->on('reference', ['length' => 12])
702
     *         ->add()->on(['type', 'date'])
703
     * }
704
     * </code>
705
     *
706
     * @param IndexBuilder $builder
707
     */
708 499
    public function buildIndexes(IndexBuilder $builder): void
0 ignored issues
show
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

708
    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...
709
    {
710 499
    }
711
712
    /**
713
     * Repository extension
714
     * returns additional methods in repository
715
     *
716
     * <code>
717
     * return [
718
     *     'customMethod' => function($query, $test) {
719
     *
720
     *     },
721
     * ];
722
     *
723
     * $repository->customMethod('test');
724
     * </code>
725
     *
726
     * @return array<string, callable>
727
     */
728
    public function scopes(): array
729
    {
730
        throw new LogicException('No scopes have been defined in "' . get_class($this) . '"');
731
    }
732
733
    /**
734
     * Get custom queries for repository
735
     * A custom query works mostly like scopes, but with some differences :
736
     * - Cannot be called using a query (i.e. $query->where(...)->myScope())
737
     * - The function has responsability of creating the query instance
738
     * - The first argument is the repository
739
     *
740
     * <code>
741
     * return [
742
     *     'findByCustom' => function (EntityRepository $repository, $search) {
743
     *         return $repository->make(MyCustomQuery::class)->where('first', $search)->first();
744
     *     }
745
     * ];
746
     * </code>
747
     *
748
     * @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...
749
     */
750 394
    public function queries(): array
751
    {
752 394
        return [];
753
    }
754
755
    /**
756
     * Register event on notifier
757
     *
758
     * @param RepositoryEventsSubscriberInterface<E> $notifier
759
     * @final
760
     */
761 411
    public function events(RepositoryEventsSubscriberInterface $notifier): void
762
    {
763 411
        $this->customEvents($notifier);
764
765 411
        foreach ($this->behaviors() as $behavior) {
766 4
            $behavior->subscribe($notifier);
767
        }
768
    }
769
770
    /**
771
     * Register custom event on notifier
772
     *
773
     * To overwrite.
774
     *
775
     * @param RepositoryEventsSubscriberInterface<E> $notifier
776
     */
777 379
    public function customEvents(RepositoryEventsSubscriberInterface $notifier): void
778
    {
779
        // To overwrite
780 379
    }
781
782
    /**
783
     * Get all behaviors
784
     *
785
     * @return BehaviorInterface<E>[]
786
     */
787 513
    final public function behaviors(): array
788
    {
789 513
        if ($this->behaviors === null) {
790 513
            $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...
791
        }
792
793 513
        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...
794
    }
795
796
    /**
797
     * Custom definition of behaviors
798
     *
799
     * To overwrite.
800
     *
801
     * @return BehaviorInterface<E>[]
802
     */
803 510
    public function getDefinedBehaviors(): array
804
    {
805 510
        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...
806
    }
807
808
    /**
809
     * Get all relations
810
     *
811
     * @return array<string, RelationDefinition>
812
     * @final
813
     *
814
     * @todo should be final
815
     */
816 648
    public function relations(): array
817
    {
818 648
        if ($this->relationBuilder === null) {
819 512
            $this->relationBuilder = new RelationBuilder();
820 512
            $this->buildRelations($this->relationBuilder);
821
        }
822
823 648
        return $this->relationBuilder->relations();
824
    }
825
826
    /**
827
     * Build relations from this mapper.
828
     *
829
     * To overwrite.
830
     *
831
     * @param RelationBuilder $builder
832
     */
833 207
    public function buildRelations(RelationBuilder $builder): void
0 ignored issues
show
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

833
    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...
834
    {
835
        // to overwrite
836 207
    }
837
838
    /**
839
     * Get all constraints
840
     *
841
     * @return array
842
     */
843 513
    final public function constraints(): array
844
    {
845 513
        $constraints = $this->customConstraints();
846
847 513
        foreach ($this->behaviors() as $behavior) {
848 4
            $constraints += $behavior->constraints();
849
        }
850
851 513
        return $constraints;
852
    }
853
854
    /**
855
     * Register custom event on notifier
856
     *
857
     * To overwrite.
858
     *
859
     * <code>
860
     * return [
861
     *     'attribute' => 'value'
862
     * ]
863
     * </code>
864
     *
865
     * @return array
866
     */
867 491
    public function customConstraints(): array
868
    {
869 491
        return [];
870
    }
871
872
    /**
873
     * Clear dependencies for break cyclic references
874
     *
875
     * @internal
876
     */
877 1
    public function destroy(): void
878
    {
879 1
        $this->serviceLocator = null;
880 1
        $this->generator = null;
881 1
        $this->hydrator = null;
882 1
        $this->metadata = null;
883
    }
884
}
885