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 ( 2b80ff...4a8ee3 )
by joseph
21s queued 13s
created

AbstractEntityRepository   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Test Coverage

Coverage 44.74%

Importance

Changes 0
Metric Value
eloc 100
dl 0
loc 307
ccs 85
cts 190
cp 0.4474
rs 9.0399
c 0
b 0
f 0
wmc 42

24 Methods

Rating   Name   Duplication   Size   Complexity  
A getRandomResultFromQueryBuilder() 0 19 3
A getOneBy() 0 8 2
A getCountForQueryBuilder() 0 6 1
A createNamedQuery() 0 3 1
A createNativeNamedQuery() 0 3 1
A find() 0 10 3
A mapCriteriaSetUuidsToStrings() 0 12 4
A __construct() 0 9 1
A initialiseEntities() 0 7 2
A get() 0 14 3
A getRandomBy() 0 9 2
A getClassName() 0 3 1
A createResultSetMappingBuilder() 0 3 1
A findAll() 0 3 1
A clear() 0 3 1
A createQueryBuilder() 0 6 1
A initRepository() 0 8 2
A getEntityFqn() 0 10 1
A initialiseEntity() 0 5 1
A count() 0 5 1
A findOneBy() 0 11 3
A findBy() 0 5 1
A getRandomOneBy() 0 11 3
A matching() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like AbstractEntityRepository 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 AbstractEntityRepository, and based on these observations, apply Extract Interface, too.

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