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
Push — master ( 69b831...bd71a5 )
by joseph
20:49 queued 13s
created

AbstractEntityRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1.0876

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 3
dl 0
loc 9
ccs 5
cts 9
cp 0.5556
crap 1.0876
rs 10
c 0
b 0
f 0
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 11
    public function __construct(
81
        EntityManagerInterface $entityManager,
82
        EntityFactoryInterface $entityFactory,
83
        NamespaceHelper $namespaceHelper
84
    ) {
85 11
        $this->entityManager   = $entityManager;
86 11
        $this->namespaceHelper = $namespaceHelper;
87 11
        $this->entityFactory   = $entityFactory;
88 11
        $this->initRepository();
89 11
    }
90
91 11
    protected function initRepository(): void
92
    {
93 11
        if (null === $this->metaData) {
94 11
            $entityFqn      = $this->getEntityFqn();
95 11
            $this->metaData = $this->entityManager->getClassMetadata($entityFqn);
96
        }
97
98 11
        $this->entityRepository = new EntityRepository($this->entityManager, $this->metaData);
99 11
    }
100
101 11
    protected function getEntityFqn(): string
102
    {
103 11
        return '\\' . str_replace(
104
            [
105 11
                    'Entity\\Repositories',
106
                ],
107
            [
108 11
                    'Entities',
109
                ],
110 11
            $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 3
    public function initialiseEntity(EntityInterface $entity)
144
    {
145 3
        $this->entityFactory->initialiseEntity($entity);
146
147 3
        return $entity;
148
    }
149
150
    /**
151
     * @return array|EntityInterface[]
152
     */
153 2
    public function findAll(): array
154
    {
155 2
        return $this->initialiseEntities($this->entityRepository->findAll());
156
    }
157
158 4
    public function initialiseEntities($entities)
159
    {
160 4
        foreach ($entities as $entity) {
161 3
            $this->initialiseEntity($entity);
162
        }
163
164 4
        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 2
    public function get($id, ?int $lockMode = null, ?int $lockVersion = null)
176
    {
177
        try {
178 2
            $entity = $this->find($id, $lockMode, $lockVersion);
179 1
        } catch (ConversionException $e) {
180 1
            $error = 'Failed getting by id ' . $id
181 1
                     . ', unless configured as an int ID entity, this should be a valid UUID';
182 1
            throw new DoctrineStaticMetaException($error, $e->getCode(), $e);
183
        }
184 1
        if ($entity === null) {
185
            throw new DoctrineStaticMetaException('Could not find the entity with id ' . $id);
186
        }
187
188 1
        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 2
    public function find($id, ?int $lockMode = null, ?int $lockVersion = null)
199
    {
200 2
        $entity = $this->entityRepository->find($id, $lockMode, $lockVersion);
201 1
        if (null === $entity) {
202
            return null;
203
        }
204 1
        if ($entity instanceof EntityInterface) {
205 1
            $this->initialiseEntity($entity);
206
207 1
            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 2
    public function findOneBy(array $criteria, ?array $orderBy = null)
234
    {
235 2
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
236 2
        $entity   = $this->entityRepository->findOneBy($criteria, $orderBy);
237 2
        if (null === $entity) {
238 1
            return null;
239
        }
240 1
        if ($entity instanceof EntityInterface) {
241 1
            $this->initialiseEntity($entity);
242
243 1
            return $entity;
244
        }
245
    }
246
247 5
    public function mapCriteriaSetUuidsToStrings(array $criteria): array
248
    {
249 5
        foreach ($criteria as $property => $value) {
250 2
            if ($value instanceof EntityInterface) {
251
                $criteria[$property] = $value->getId();
252
            }
253 2
            if ($value instanceof UuidInterface) {
254 1
                $criteria[$property] = $value->toString();
255
            }
256
        }
257
258 5
        return $criteria;
259
    }
260
261
    /**
262
     * @param array $criteria
263
     *
264
     * @return EntityInterface|null
265
     */
266 1
    public function getRandomOneBy(array $criteria)
267
    {
268 1
        $found = $this->getRandomBy($criteria);
269 1
        if ([] === $found) {
270
            throw new RuntimeException('Failed finding any Entities with this criteria');
271
        }
272 1
        $entity = current($found);
273 1
        if ($entity instanceof EntityInterface) {
274 1
            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 2
    public function getRandomBy(array $criteria, int $numToGet = 1): array
287
    {
288 2
        $count = $this->count($criteria);
289 2
        if (0 === $count) {
290
            return [];
291
        }
292 2
        $randOffset = rand(0, $count - $numToGet);
293
294 2
        return $this->findBy($criteria, null, $numToGet, $randOffset);
295
    }
296
297 3
    public function count(array $criteria = []): int
298
    {
299 3
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
300
301 3
        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 3
    public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
313
    {
314 3
        $criteria = $this->mapCriteriaSetUuidsToStrings($criteria);
315
316 3
        return $this->initialiseEntities($this->entityRepository->findBy($criteria, $orderBy, $limit, $offset));
317
    }
318
319 1
    public function matching(Criteria $criteria): LazyCriteriaCollection
320
    {
321 1
        $collection = $this->entityRepository->matching($criteria);
322 1
        if ($collection instanceof LazyCriteriaCollection) {
0 ignored issues
show
introduced by
$collection is always a sub-type of Doctrine\ORM\LazyCriteriaCollection.
Loading history...
323 1
            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 1
    public function createResultSetMappingBuilder(string $alias): Query\ResultSetMappingBuilder
328
    {
329 1
        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 1
    public function clear(): void
343
    {
344 1
        $this->entityRepository->clear();
345 1
    }
346
347
    /**
348
     * Create a query builder with the alias preset
349
     *
350
     * @param string|null $indexBy
351
     *
352
     * @return QueryBuilder
353
     */
354 1
    public function createQueryBuilderWithAlias(string $indexBy = null): QueryBuilder
355
    {
356 1
        return $this->createQueryBuilder($this->getAlias(), $indexBy);
357
    }
358
359 3
    public function createQueryBuilder(string $alias, string $indexBy = null): QueryBuilder
360
    {
361 3
        return (new UuidQueryBuilder($this->entityManager))
362 3
            ->select($alias)
363 3
            ->from($this->getClassName(), $alias, $indexBy);
364
    }
365
366 5
    public function getClassName(): string
367
    {
368 5
        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 2
    public function getAlias(): string
383
    {
384 2
        if (null !== static::$alias) {
0 ignored issues
show
introduced by
The condition null !== static::alias is always true.
Loading history...
385 2
            return static::$alias;
386
        }
387 1
        $class           = $this->namespaceHelper->getClassShortName($this->getClassName());
388 1
        $removeStopWords = str_ireplace(['entity', 'repository'], '', $class);
389 1
        $ucOnly          = preg_replace('%[^A-Z]%', '', $removeStopWords);
390
391 1
        static::$alias = strtolower($ucOnly);
392
393 1
        return static::$alias;
394
    }
395
396 1
    public function createDeletionQueryBuilderWithAlias(): QueryBuilder
397
    {
398 1
        return $this->createDeletionQueryBuilder($this->getAlias());
399
    }
400
401 1
    public function createDeletionQueryBuilder(string $alias): QueryBuilder
402
    {
403 1
        return (new UuidQueryBuilder($this->entityManager))
404 1
            ->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 1
    public function aliasPrefix(string $property): string
415
    {
416 1
        return $this->getAlias() . '.' . $property;
417
    }
418
}
419