Skip to content

Commit d950838

Browse files
committed
Add native types to EntityRepository
1 parent 4158915 commit d950838

File tree

4 files changed

+55
-107
lines changed

4 files changed

+55
-107
lines changed

lib/Doctrine/ORM/EntityRepository.php

Lines changed: 42 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -35,93 +35,65 @@
3535
*/
3636
class EntityRepository implements ObjectRepository, Selectable
3737
{
38-
/**
39-
* @internal This property will be private in 3.0, call {@see getEntityName()} instead.
40-
*
41-
* @var string
42-
*/
43-
protected $_entityName;
44-
45-
/**
46-
* @internal This property will be private in 3.0, call {@see getEntityManager()} instead.
47-
*
48-
* @var EntityManagerInterface
49-
*/
50-
protected $_em;
38+
/** @psalm-var class-string<T> */
39+
private string $entityName;
40+
private static ?Inflector $inflector = null;
5141

5242
/**
53-
* @internal This property will be private in 3.0, call {@see getClassMetadata()} instead.
54-
*
55-
* @var ClassMetadata
43+
* @psalm-param ClassMetadata<T> $class
5644
*/
57-
protected $_class;
58-
59-
/** @var Inflector|null */
60-
private static $inflector;
61-
62-
public function __construct(EntityManagerInterface $em, ClassMetadata $class)
63-
{
64-
$this->_entityName = $class->name;
65-
$this->_em = $em;
66-
$this->_class = $class;
45+
public function __construct(
46+
private EntityManagerInterface $em,
47+
private ClassMetadata $class
48+
) {
49+
$this->entityName = $class->name;
6750
}
6851

6952
/**
7053
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
71-
*
72-
* @param string $alias
73-
* @param string|null $indexBy The index for the from.
74-
*
75-
* @return QueryBuilder
7654
*/
77-
public function createQueryBuilder($alias, $indexBy = null)
55+
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
7856
{
79-
return $this->_em->createQueryBuilder()
57+
return $this->em->createQueryBuilder()
8058
->select($alias)
81-
->from($this->_entityName, $alias, $indexBy);
59+
->from($this->entityName, $alias, $indexBy);
8260
}
8361

8462
/**
8563
* Creates a new result set mapping builder for this entity.
8664
*
8765
* The column naming strategy is "INCREMENT".
88-
*
89-
* @param string $alias
90-
*
91-
* @return ResultSetMappingBuilder
9266
*/
93-
public function createResultSetMappingBuilder($alias)
67+
public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder
9468
{
95-
$rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
96-
$rsm->addRootEntityFromClassMetadata($this->_entityName, $alias);
69+
$rsm = new ResultSetMappingBuilder($this->em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
70+
$rsm->addRootEntityFromClassMetadata($this->entityName, $alias);
9771

9872
return $rsm;
9973
}
10074

10175
/**
10276
* Finds an entity by its primary key / identifier.
10377
*
104-
* @param mixed $id The identifier.
105-
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
106-
* or NULL if no specific lock mode should be used
107-
* during the search.
108-
* @param int|null $lockVersion The lock version.
78+
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
79+
* or NULL if no specific lock mode should be used
80+
* during the search.
10981
* @psalm-param LockMode::*|null $lockMode
11082
*
11183
* @return object|null The entity instance or NULL if the entity can not be found.
11284
* @psalm-return ?T
11385
*/
114-
public function find($id, $lockMode = null, $lockVersion = null)
86+
public function find(mixed $id, ?int $lockMode = null, ?int $lockVersion = null): ?object
11587
{
116-
return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion);
88+
return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
11789
}
11890

11991
/**
12092
* Finds all entities in the repository.
12193
*
12294
* @psalm-return list<T> The entities.
12395
*/
124-
public function findAll()
96+
public function findAll(): array
12597
{
12698
return $this->findBy([]);
12799
}
@@ -137,9 +109,9 @@ public function findAll()
137109
* @return object[] The objects.
138110
* @psalm-return list<T>
139111
*/
140-
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
112+
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
141113
{
142-
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
114+
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
143115

144116
return $persister->loadAll($criteria, $orderBy, $limit, $offset);
145117
}
@@ -153,9 +125,9 @@ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $
153125
* @return object|null The entity instance or NULL if the entity can not be found.
154126
* @psalm-return ?T
155127
*/
156-
public function findOneBy(array $criteria, ?array $orderBy = null)
128+
public function findOneBy(array $criteria, ?array $orderBy = null): ?object
157129
{
158-
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
130+
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
159131

160132
return $persister->load($criteria, null, null, [], null, 1, $orderBy);
161133
}
@@ -169,23 +141,20 @@ public function findOneBy(array $criteria, ?array $orderBy = null)
169141
*
170142
* @todo Add this method to `ObjectRepository` interface in the next major release
171143
*/
172-
public function count(array $criteria = [])
144+
public function count(array $criteria = []): int
173145
{
174-
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->count($criteria);
146+
return $this->em->getUnitOfWork()->getEntityPersister($this->entityName)->count($criteria);
175147
}
176148

177149
/**
178150
* Adds support for magic method calls.
179151
*
180-
* @param string $method
181152
* @param mixed[] $arguments
182153
* @psalm-param list<mixed> $arguments
183154
*
184-
* @return mixed The returned value from the resolved method.
185-
*
186155
* @throws BadMethodCallException If the method called is invalid.
187156
*/
188-
public function __call($method, $arguments)
157+
public function __call(string $method, array $arguments): mixed
189158
{
190159
if (str_starts_with($method, 'findBy')) {
191160
return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
@@ -207,47 +176,37 @@ public function __call($method, $arguments)
207176
}
208177

209178
/**
210-
* @return string
179+
* @psalm-return class-string<T>
211180
*/
212-
protected function getEntityName()
181+
protected function getEntityName(): string
213182
{
214-
return $this->_entityName;
183+
return $this->entityName;
215184
}
216185

217-
/**
218-
* @return string
219-
*/
220-
public function getClassName()
186+
public function getClassName(): string
221187
{
222188
return $this->getEntityName();
223189
}
224190

225-
/**
226-
* @return EntityManagerInterface
227-
*/
228-
protected function getEntityManager()
191+
protected function getEntityManager(): EntityManagerInterface
229192
{
230-
return $this->_em;
193+
return $this->em;
231194
}
232195

233-
/**
234-
* @return ClassMetadata
235-
*/
236-
protected function getClassMetadata()
196+
protected function getClassMetadata(): ClassMetadata
237197
{
238-
return $this->_class;
198+
return $this->class;
239199
}
240200

241201
/**
242202
* Select all elements from a selectable that match the expression and
243203
* return a new collection containing these elements.
244204
*
245-
* @return AbstractLazyCollection
246205
* @psalm-return AbstractLazyCollection<int, T>&Selectable<int, T>
247206
*/
248-
public function matching(Criteria $criteria)
207+
public function matching(Criteria $criteria): AbstractLazyCollection
249208
{
250-
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
209+
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
251210

252211
return new LazyCriteriaCollection($persister, $criteria);
253212
}
@@ -259,26 +218,22 @@ public function matching(Criteria $criteria)
259218
* @param string $by The property name used as condition
260219
* @psalm-param list<mixed> $arguments The arguments to pass at method call
261220
*
262-
* @return mixed
263-
*
264221
* @throws InvalidMagicMethodCall If the method called is invalid or the
265222
* requested field/association does not exist.
266223
*/
267-
private function resolveMagicCall(string $method, string $by, array $arguments)
224+
private function resolveMagicCall(string $method, string $by, array $arguments): mixed
268225
{
269226
if (! $arguments) {
270227
throw InvalidMagicMethodCall::onMissingParameter($method . $by);
271228
}
272229

273-
if (self::$inflector === null) {
274-
self::$inflector = InflectorFactory::create()->build();
275-
}
230+
self::$inflector ??= InflectorFactory::create()->build();
276231

277232
$fieldName = lcfirst(self::$inflector->classify($by));
278233

279-
if (! ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName))) {
234+
if (! ($this->class->hasField($fieldName) || $this->class->hasAssociation($fieldName))) {
280235
throw InvalidMagicMethodCall::becauseFieldNotFoundIn(
281-
$this->_entityName,
236+
$this->entityName,
282237
$fieldName,
283238
$method . $by
284239
);

phpstan-baseline.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,4 +1374,3 @@ parameters:
13741374
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$subClasses\\.$#"
13751375
count: 1
13761376
path: lib/Doctrine/ORM/Utility/HierarchyDiscriminatorResolver.php
1377-

psalm-baseline.xml

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -288,26 +288,14 @@
288288
</DeprecatedClass>
289289
</file>
290290
<file src="lib/Doctrine/ORM/EntityRepository.php">
291-
<ArgumentTypeCoercion occurrences="5">
292-
<code>$this-&gt;_entityName</code>
293-
<code>$this-&gt;_entityName</code>
294-
<code>$this-&gt;_entityName</code>
295-
<code>$this-&gt;_entityName</code>
296-
<code>$this-&gt;_entityName</code>
297-
</ArgumentTypeCoercion>
298-
<InvalidReturnStatement occurrences="3">
291+
<InvalidReturnStatement occurrences="2">
299292
<code>$persister-&gt;load($criteria, null, null, [], null, 1, $orderBy)</code>
300-
<code>$this-&gt;_em-&gt;find($this-&gt;_entityName, $id, $lockMode, $lockVersion)</code>
301293
<code>new LazyCriteriaCollection($persister, $criteria)</code>
302294
</InvalidReturnStatement>
303-
<InvalidReturnType occurrences="3">
304-
<code>?T</code>
295+
<InvalidReturnType occurrences="2">
305296
<code>?T</code>
306297
<code>AbstractLazyCollection&lt;int, T&gt;&amp;Selectable&lt;int, T&gt;</code>
307298
</InvalidReturnType>
308-
<LessSpecificImplementedReturnType occurrences="1">
309-
<code>string</code>
310-
</LessSpecificImplementedReturnType>
311299
</file>
312300
<file src="lib/Doctrine/ORM/Event/LifecycleEventArgs.php">
313301
<LessSpecificReturnStatement occurrences="1">

tests/Doctrine/Tests/ORM/Repository/DefaultRepositoryFactoryTest.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\ORM\Mapping\ClassMetadata;
1111
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
1212
use Doctrine\Tests\Models\DDC753\DDC753DefaultRepository;
13+
use Doctrine\Tests\Models\DDC753\DDC753EntityWithDefaultCustomRepository;
1314
use Doctrine\Tests\Models\DDC869\DDC869PaymentRepository;
1415
use PHPUnit\Framework\MockObject\MockObject;
1516
use PHPUnit\Framework\TestCase;
@@ -22,13 +23,12 @@
2223
class DefaultRepositoryFactoryTest extends TestCase
2324
{
2425
/** @var EntityManagerInterface&MockObject */
25-
private $entityManager;
26+
private EntityManagerInterface $entityManager;
2627

2728
/** @var Configuration&MockObject */
28-
private $configuration;
29+
private Configuration $configuration;
2930

30-
/** @var DefaultRepositoryFactory */
31-
private $repositoryFactory;
31+
private DefaultRepositoryFactory $repositoryFactory;
3232

3333
protected function setUp(): void
3434
{
@@ -70,7 +70,7 @@ public function testCreatedRepositoriesAreCached(): void
7070

7171
public function testCreatesRepositoryFromCustomClassMetadata(): void
7272
{
73-
$customMetadata = $this->buildClassMetadata(__DIR__);
73+
$customMetadata = $this->buildClassMetadata(DDC753EntityWithDefaultCustomRepository::class);
7474
$customMetadata->customRepositoryClassName = DDC753DefaultRepository::class;
7575

7676
$this->entityManager
@@ -107,12 +107,18 @@ public function testCachesDistinctRepositoriesPerDistinctEntityManager(): void
107107
}
108108

109109
/**
110+
* @psalm-param class-string<TEntity> $className
111+
*
110112
* @return ClassMetadata&MockObject
113+
* @psalm-return ClassMetadata<TEntity>&MockObject
114+
*
115+
* @template TEntity of object
111116
*/
112117
private function buildClassMetadata(string $className): ClassMetadata
113118
{
114119
$metadata = $this->createMock(ClassMetadata::class);
115120
$metadata->expects(self::any())->method('getName')->will(self::returnValue($className));
121+
$metadata->name = $className;
116122

117123
$metadata->customRepositoryClassName = null;
118124

0 commit comments

Comments
 (0)