GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#214)
by joseph
21:10
created

AbstractEntityRepository::getEntityFqn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 1
eloc 4
c 3
b 0
f 1
nc 1
nop 0
dl 0
loc 10
rs 10
ccs 0
cts 10
cp 0
crap 2
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Repositories;
4
5
use Doctrine\Common\Collections\Criteria;
6
use Doctrine\DBAL\Types\ConversionException;
7
use Doctrine\ORM\EntityManagerInterface;
8
use Doctrine\ORM\EntityRepository;
9
use Doctrine\ORM\LazyCriteriaCollection;
10
use Doctrine\ORM\Mapping\ClassMetadata;
11
use Doctrine\ORM\NativeQuery;
12
use Doctrine\ORM\Query;
13
use Doctrine\ORM\QueryBuilder;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
15
use EdmondsCommerce\DoctrineStaticMeta\Entity\Factory\EntityFactoryInterface;
16
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
17
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
18
use Ramsey\Uuid\UuidInterface;
19
use RuntimeException;
20
use function str_replace;
21
22
/**
23
 * Class AbstractEntityRepository
24
 *
25
 * This provides a base class that handles instantiating the correctly configured EntityRepository and provides an
26
 * extensible baseline for further customisation
27
 *
28
 * We have extracted an interface from the standard Doctrine EntityRepository and implemented that
29
 * However, so we can add type safety, we can't "actually" implement it
30
 *
31
 * We have also deliberately left out the magic calls. Please make real methods in your concrete repository class
32
 *
33
 * Note, there are quite a few PHPMD warnings, however it needs to respect the legacy interface so they are being
34
 * suppressed
35
 *
36
 * @package EdmondsCommerce\DoctrineStaticMeta\Entity\Repositories
37
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
38
 * @SuppressWarnings(PHPMD.ExcessivePublicCount)
39
 * @SuppressWarnings(PHPMD.NumberOfChildren)
40
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
41
 */
42
abstract class AbstractEntityRepository implements EntityRepositoryInterface
43
{
44
    /**
45
     * @var string
46
     */
47
    protected static $alias;
48
    /**
49
     * @var EntityManagerInterface
50
     */
51
    protected $entityManager;
52
    /**
53
     * @var EntityRepository
54
     */
55
    protected $entityRepository;
56
    /**
57
     * @var string
58
     */
59
    protected $repositoryFactoryFqn;
60
    /**
61
     * @var ClassMetadata|null
62
     */
63
    protected $metaData;
64
    /**
65
     * @var NamespaceHelper
66
     */
67
    protected $namespaceHelper;
68
    /**
69
     * @var EntityFactoryInterface
70
     */
71
    private $entityFactory;
72
73
    /**
74
     * AbstractEntityRepositoryFactory constructor.
75
     *
76
     * @param EntityManagerInterface $entityManager
77
     * @param EntityFactoryInterface $entityFactory
78
     * @param NamespaceHelper|null   $namespaceHelper
79
     */
80
    public function __construct(
81
        EntityManagerInterface $entityManager,
82
        EntityFactoryInterface $entityFactory,
83
        NamespaceHelper $namespaceHelper
84
    ) {
85
        $this->entityManager   = $entityManager;
86
        $this->namespaceHelper = $namespaceHelper;
87
        $this->entityFactory   = $entityFactory;
88
        $this->initRepository();
89
    }
90
91
    protected function initRepository(): void
92
    {
93
        if (null === $this->metaData) {
94
            $entityFqn      = $this->getEntityFqn();
95
            $this->metaData = $this->entityManager->getClassMetadata($entityFqn);
96
        }
97
98
        $this->entityRepository = new EntityRepository($this->entityManager, $this->metaData);
99
    }
100
101
    protected function getEntityFqn(): string
102
    {
103
        return '\\' . str_replace(
104
            [
105
                    'Entity\\Repositories',
106
                ],
107
            [
108
                    'Entities',
109
                ],
110
            $this->namespaceHelper->cropSuffix(static::class, 'Repository')
111
        );
112
    }
113
114
    public function getRandomResultFromQueryBuilder(QueryBuilder $queryBuilder, string $entityAlias): ?EntityInterface
115
    {
116
        $count = $this->getCountForQueryBuilder($queryBuilder, $entityAlias);
117
        if (0 === $count) {
118
            return null;
119
        }
120
121
        $queryBuilder->setMaxResults(1);
122
        $limitIndex = random_int(0, $count - 1);
123
        $results    = $queryBuilder->getQuery()
124
                                   ->setFirstResult($limitIndex)
125
                                   ->execute();
126
        $entity     = current($results);
127
        if (null === $entity) {
128
            return null;
129
        }
130
        $this->initialiseEntity($entity);
131
132
        return $entity;
133
    }
134
135
    public function getCountForQueryBuilder(QueryBuilder $queryBuilder, string $aliasToCount): int
136
    {
137
        $clone = clone $queryBuilder;
138
        $clone->select($queryBuilder->expr()->count($aliasToCount));
139
140
        return (int)$clone->getQuery()->getSingleScalarResult();
141
    }
142
143
    public function initialiseEntity(EntityInterface $entity)
144
    {
145
        $this->entityFactory->initialiseEntity($entity);
146
147
        return $entity;
148
    }
149
150
    /**
151
     * @return array|EntityInterface[]
152
     */
153
    public function findAll(): array
154
    {
155
        return $this->initialiseEntities($this->entityRepository->findAll());
156
    }
157
158
    public function initialiseEntities($entities)
159
    {
160
        foreach ($entities as $entity) {
161
            $this->initialiseEntity($entity);
162
        }
163
164
        return $entities;
165
    }
166
167
    /**
168
     * @param mixed    $id
169
     * @param int|null $lockMode
170
     * @param int|null $lockVersion
171
     *
172
     * @return EntityInterface
173
     * @throws DoctrineStaticMetaException
174
     */
175
    public function get($id, ?int $lockMode = null, ?int $lockVersion = null)
176
    {
177
        try {
178
            $entity = $this->find($id, $lockMode, $lockVersion);
179
        } catch (ConversionException $e) {
180
            $error = 'Failed getting by id ' . $id
181
                     . ', unless configured as an int ID entity, this should be a valid UUID';
182
            throw new DoctrineStaticMetaException($error, $e->getCode(), $e);
183
        }
184
        if ($entity === null) {
185
            throw new DoctrineStaticMetaException('Could not find the entity with id ' . $id);
186
        }
187
188
        return $this->initialiseEntity($entity);
189
    }
190
191
    /**
192
     * @param mixed    $id
193
     * @param int|null $lockMode
194
     * @param int|null $lockVersion
195
     *
196
     * @return EntityInterface|null
197
     */
198
    public function find($id, ?int $lockMode = null, ?int $lockVersion = null)
199
    {
200
        $entity = $this->entityRepository->find($id, $lockMode, $lockVersion);
201
        if (null === $entity) {
202
            return null;
203
        }
204
        if ($entity instanceof EntityInterface) {
205
            $this->initialiseEntity($entity);
206
207
            return $entity;
208
        }
209
    }
210
211
    /**
212
     * @param array      $criteria
213
     * @param array|null $orderBy
214
     *
215
     * @return EntityInterface
216
     */
217
    public function getOneBy(array $criteria, ?array $orderBy = null)
218
    {
219
        $result = $this->findOneBy($criteria, $orderBy);
220
        if ($result === null) {
221
            throw new RuntimeException('Could not find the entity');
222
        }
223
224
        return $this->initialiseEntity($result);
225
    }
226
227
    /**
228
     * @param array      $criteria
229
     * @param array|null $orderBy
230
     *
231
     * @return EntityInterface|null
232
     */
233
    public function findOneBy(array $criteria, ?array $orderBy = null)
234
    {
235
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
236
        $entity   = $this->entityRepository->findOneBy($criteria, $orderBy);
237
        if (null === $entity) {
238
            return null;
239
        }
240
        if ($entity instanceof EntityInterface) {
241
            $this->initialiseEntity($entity);
242
243
            return $entity;
244
        }
245
    }
246
247
    public function mapCriteriaSetUuidsToStrings(array $criteria): array
248
    {
249
        foreach ($criteria as $property => $value) {
250
            if ($value instanceof EntityInterface) {
251
                $criteria[$property] = $value->getId();
252
            }
253
            if ($value instanceof UuidInterface) {
254
                $criteria[$property] = $value->toString();
255
            }
256
        }
257
258
        return $criteria;
259
    }
260
261
    /**
262
     * @param array $criteria
263
     *
264
     * @return EntityInterface|null
265
     */
266
    public function getRandomOneBy(array $criteria)
267
    {
268
        $found = $this->getRandomBy($criteria);
269
        if ([] === $found) {
270
            throw new RuntimeException('Failed finding any Entities with this criteria');
271
        }
272
        $entity = current($found);
273
        if ($entity instanceof EntityInterface) {
274
            return $entity;
275
        }
276
        throw new RuntimeException('Unexpected Entity Type ' . get_class($entity));
277
    }
278
279
    /**
280
     * @param array $criteria
281
     *
282
     * @param int   $numToGet
283
     *
284
     * @return EntityInterface[]|array
285
     */
286
    public function getRandomBy(array $criteria, int $numToGet = 1): array
287
    {
288
        $count = $this->count($criteria);
289
        if (0 === $count) {
290
            return [];
291
        }
292
        $randOffset = rand(0, $count - $numToGet);
293
294
        return $this->findBy($criteria, null, $numToGet, $randOffset);
295
    }
296
297
    public function count(array $criteria = []): int
298
    {
299
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
300
301
        return $this->entityRepository->count($criteria);
302
    }
303
304
    /**
305
     * @param array      $criteria
306
     * @param array|null $orderBy
307
     * @param int|null   $limit
308
     * @param int|null   $offset
309
     *
310
     * @return array|EntityInterface[]
311
     */
312
    public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
313
    {
314
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
315
316
        return $this->initialiseEntities($this->entityRepository->findBy($criteria, $orderBy, $limit, $offset));
317
    }
318
319
    public function matching(Criteria $criteria): LazyCriteriaCollection
320
    {
321
        $collection = $this->entityRepository->matching($criteria);
322
        if ($collection instanceof LazyCriteriaCollection) {
0 ignored issues
show
introduced by
$collection is always a sub-type of Doctrine\ORM\LazyCriteriaCollection.
Loading history...
323
            return $this->initialiseEntities($collection);
324
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 322 is false. This is incompatible with the type-hinted return Doctrine\ORM\LazyCriteriaCollection. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
325
    }
326
327
    public function createResultSetMappingBuilder(string $alias): Query\ResultSetMappingBuilder
328
    {
329
        return $this->entityRepository->createResultSetMappingBuilder($alias);
330
    }
331
332
    public function createNamedQuery(string $queryName): Query
333
    {
334
        return $this->entityRepository->createNamedQuery($queryName);
335
    }
336
337
    public function createNativeNamedQuery(string $queryName): NativeQuery
338
    {
339
        return $this->entityRepository->createNativeNamedQuery($queryName);
340
    }
341
342
    public function clear(): void
343
    {
344
        $this->entityRepository->clear();
345
    }
346
347
    /**
348
     * Create a query builder with the alias preset
349
     *
350
     * @param string|null $indexBy
351
     *
352
     * @return QueryBuilder
353
     */
354
    public function createQueryBuilderWithAlias(string $indexBy = null): QueryBuilder
355
    {
356
        return $this->createQueryBuilder($this->getAlias(), $indexBy);
357
    }
358
359
    public function createQueryBuilder(string $alias, string $indexBy = null): QueryBuilder
360
    {
361
        return (new UuidQueryBuilder($this->entityManager))
362
            ->select($alias)
363
            ->from($this->getClassName(), $alias, $indexBy);
364
    }
365
366
    public function getClassName(): string
367
    {
368
        return $this->entityRepository->getClassName();
369
    }
370
371
    /**
372
     * Generate an alias based on the class name
373
     *
374
     * removes the words entity and repository
375
     *
376
     * gets all the upper case letters and returns them as a lower case string
377
     *
378
     * Warning - nothing is done to guarantee uniqueness for now
379
     *
380
     * @return string
381
     */
382
    public function getAlias(): string
383
    {
384
        if (null !== static::$alias) {
0 ignored issues
show
introduced by
The condition null !== static::alias is always true.
Loading history...
385
            return static::$alias;
386
        }
387
        $class           = $this->namespaceHelper->getClassShortName($this->getClassName());
388
        $removeStopWords = str_ireplace(['entity', 'repository'], '', $class);
389
        $ucOnly          = preg_replace('%[^A-Z]%', '', $removeStopWords);
390
391
        static::$alias = strtolower($ucOnly);
392
393
        return static::$alias;
394
    }
395
396
    public function createDeletionQueryBuilderWithAlias(): QueryBuilder
397
    {
398
        return $this->createDeletionQueryBuilder($this->getAlias());
399
    }
400
401
    public function createDeletionQueryBuilder(string $alias): QueryBuilder
402
    {
403
        return (new UuidQueryBuilder($this->entityManager))
404
            ->delete($this->getClassName(), $alias);
405
    }
406
407
    /**
408
     * For use with query builder, auto prefix alias
409
     *
410
     * @param string $property
411
     *
412
     * @return string
413
     */
414
    public function aliasPrefix(string $property): string
415
    {
416
        return $this->getAlias() . '.' . $property;
417
    }
418
}
419