Failed Conditions
CANCELLED  
Pull Request — master (#7095)
by Benjamin
10:13
created

EntityRepository::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\Collections\Collection;
8
use Doctrine\Common\Collections\Criteria;
9
use Doctrine\Common\Collections\Selectable;
10
use Doctrine\Common\Persistence\ObjectRepository;
11
use Doctrine\Common\Util\Inflector;
12
use Doctrine\ORM\Mapping\ClassMetadata;
13
use Doctrine\ORM\Query\ResultSetMappingBuilder;
14
use function array_slice;
15
use function lcfirst;
16
use function sprintf;
17
use function strpos;
18
use function substr;
19
20
/**
21
 * An EntityRepository serves as a repository for entities with generic as well as
22
 * business specific methods for retrieving entities.
23
 *
24
 * This class is designed for inheritance and users can subclass this class to
25
 * write their own repositories with business-specific methods to locate entities.
26
 */
27
class EntityRepository implements ObjectRepository, Selectable
28
{
29
    /**
30
     * @var string
31
     */
32
    protected $entityName;
33
34
    /**
35
     * @var EntityManagerInterface
36
     */
37
    protected $em;
38
39
    /**
40
     * @var ClassMetadata
41
     */
42
    protected $class;
43
44
    /**
45
     * Initializes a new <tt>EntityRepository</tt>.
46
     *
47
     * @param EntityManagerInterface $em    The EntityManager to use.
48
     * @param Mapping\ClassMetadata  $class The class descriptor.
49
     */
50 137
    public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
51
    {
52 137
        $this->entityName = $class->getClassName();
53 137
        $this->em         = $em;
54 137
        $this->class      = $class;
55 137
    }
56
57
    /**
58
     * Creates a new QueryBuilder instance that is prepopulated for this entity name.
59
     *
60
     * @param string $alias
61
     * @param string $indexBy The index for the from.
62
     *
63
     * @return QueryBuilder
64
     */
65 8
    public function createQueryBuilder($alias, $indexBy = null): QueryBuilder
66
    {
67 8
        return $this->em->createQueryBuilder()
68 8
            ->select($alias)
69 8
            ->from($this->entityName, $alias, $indexBy);
70
    }
71
72
    /**
73
     * Creates a new result set mapping builder for this entity.
74
     *
75
     * The column naming strategy is "INCREMENT".
76
     *
77
     * @param string $alias
78
     *
79
     * @return ResultSetMappingBuilder
80
     */
81 1
    public function createResultSetMappingBuilder($alias): ResultSetMappingBuilder
82
    {
83 1
        $rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
84 1
        $rsm->addRootEntityFromClassMetadata($this->entityName, $alias);
85
86 1
        return $rsm;
87
    }
88
89
    /**
90
     * Clears the repository, causing all managed entities to become detached.
91
     */
92
    public function clear()
93
    {
94
        $this->em->clear($this->class->getRootClassName());
95
    }
96
97
    /**
98
     * Finds an entity by its primary key / identifier.
99
     *
100
     * @param mixed    $id          The identifier.
101
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
102
     *                              or NULL if no specific lock mode should be used
103
     *                              during the search.
104
     * @param int|null $lockVersion The lock version.
105
     *
106
     * @return object|null The entity instance or NULL if the entity can not be found.
107
     */
108 15
    public function find($id, $lockMode = null, $lockVersion = null): ?object
109
    {
110 15
        return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\Common\Persistence\ObjectManager::find() has too many arguments starting with $lockMode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
        return $this->em->/** @scrutinizer ignore-call */ find($this->entityName, $id, $lockMode, $lockVersion);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
111
    }
112
113
    /**
114
     * Finds all entities in the repository.
115
     *
116
     * @return object[] The entities.
117
     */
118 33
    public function findAll(): array
119
    {
120 33
        return $this->findBy([]);
121
    }
122
123
    /**
124
     * Finds entities by a set of criteria.
125
     *
126
     * @param mixed[]  $criteria
127
     * @param mixed[]  $orderBy
128
     * @param int|null $limit
129
     * @param int|null $offset
130
     *
131
     * @return object[] The objects.
132
     *
133
     * @todo guilhermeblanco Change orderBy to use a blank array by default (requires Common\Persistence change).
134
     */
135 64
    public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
136
    {
137 64
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
138
139 64
        return $persister->loadAll($criteria, $orderBy !== null ? $orderBy : [], $limit, $offset);
140
    }
141
142
    /**
143
     * Finds a single entity by a set of criteria.
144
     *
145
     * @param mixed[] $criteria
146
     * @param mixed[] $orderBy
147
     *
148
     * @return object|null The entity instance or NULL if the entity can not be found.
149
     */
150 20
    public function findOneBy(array $criteria, array $orderBy = []): ?object
151
    {
152 20
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
153
154 20
        return $persister->load($criteria, null, null, [], null, 1, $orderBy);
155
    }
156
157
    /**
158
     * Counts entities by a set of criteria.
159
     *
160
     * @todo Add this method to `ObjectRepository` interface in the next major release
161
     *
162
     * @param Criteria[] $criteria
163
     *
164
     * @return int The cardinality of the objects that match the given criteria.
165
     */
166 2
    public function count(array $criteria): int
167
    {
168 2
        return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);
169
    }
170
171
    /**
172
     * Adds support for magic method calls.
173
     *
174
     * @param string  $method
175
     * @param mixed[] $arguments
176
     *
177
     * @return mixed The returned value from the resolved method.
178
     *
179
     * @throws ORMException
180
     * @throws \BadMethodCallException If the method called is invalid.
181
     */
182 13
    public function __call($method, $arguments)
183
    {
184 13
        if (strpos($method, 'findBy') === 0) {
185 8
            return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
186
        }
187
188 5
        if (strpos($method, 'findOneBy') === 0) {
189 3
            return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
190
        }
191
192 2
        if (strpos($method, 'countBy') === 0) {
193 1
            return $this->resolveMagicCall('count', substr($method, 7), $arguments);
194
        }
195
196 1
        throw new \BadMethodCallException(
197 1
            sprintf(
198 1
                "Undefined method '%s'. The method name must start with either findBy, findOneBy or countBy!",
199 1
                $method
200
            )
201
        );
202
    }
203
204
    /**
205
     * @return string
206
     */
207
    protected function getEntityName(): string
208
    {
209
        return $this->entityName;
210
    }
211
212
    /**
213
     * @todo do we still need this one or {@getEntityName} both? we could clean up
214
     * @return string
215
     */
216
    public function getClassName(): string
217
    {
218
        return $this->getEntityName();
219
    }
220
221
    /**
222
     * @return EntityManagerInterface
223
     */
224
    protected function getEntityManager(): EntityManagerInterface
225
    {
226
        return $this->em;
227
    }
228
229
    /**
230
     * @return Mapping\ClassMetadata
231
     */
232
    protected function getClassMetadata(): ClassMetadata
233
    {
234
        return $this->class;
235
    }
236
237
    /**
238
     * Select all elements from a selectable that match the expression and
239
     * return a new collection containing these elements.
240
     *
241
     * @return Collection|object[]
242
     */
243 26
    public function matching(Criteria $criteria)
244
    {
245 26
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
246
247 26
        return new LazyCriteriaCollection($persister, $criteria);
248
    }
249
250
    /**
251
     * Resolves a magic method call to the proper existent method at `EntityRepository`.
252
     *
253
     * @param string  $method    The method to call
254
     * @param string  $by        The property name used as condition
255
     * @param mixed[] $arguments The arguments to pass at method call
256
     *
257
     * @throws ORMException If the method called is invalid or the requested field/association does not exist.
258
     *
259
     * @return mixed
260
     */
261 12
    private function resolveMagicCall($method, $by, array $arguments)
262
    {
263 12
        if (! $arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array<mixed,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
264 1
            throw ORMException::findByRequiresParameter($method . $by);
265
        }
266
267 11
        $fieldName = lcfirst(Inflector::classify($by));
268
269 11
        if ($this->class->getProperty($fieldName) === null) {
270 1
            throw ORMException::invalidMagicCall($this->entityName, $fieldName, $method . $by);
271
        }
272
273 10
        return $this->{$method}([$fieldName => $arguments[0]], ...array_slice($arguments, 1));
274
    }
275
}
276