Skip to content

Commit b7c93f3

Browse files
committed
Use real object managers to know repository type
Based on the class metadata we can easily fetch data from any configuration format.
1 parent af7bb42 commit b7c93f3

11 files changed

+249
-9
lines changed

extension.neon

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
parameters:
22
doctrine:
3-
repositoryClass: Doctrine\ORM\EntityRepository
3+
objectManagerLoader: ~
44

55
services:
66
-
@@ -17,8 +17,6 @@ services:
1717
- phpstan.broker.dynamicMethodReturnTypeExtension
1818
-
1919
class: PHPStan\Type\Doctrine\ObjectManagerGetRepositoryDynamicReturnTypeExtension
20-
arguments:
21-
repositoryClass: %doctrine.repositoryClass%
2220
tags:
2321
- phpstan.broker.dynamicMethodReturnTypeExtension
2422
-
@@ -29,3 +27,7 @@ services:
2927
class: PHPStan\Type\Doctrine\ObjectRepositoryDynamicReturnTypeExtension
3028
tags:
3129
- phpstan.broker.dynamicMethodReturnTypeExtension
30+
-
31+
class: PHPStan\Type\Doctrine\ObjectMetadataResolver
32+
arguments:
33+
objectManagerLoader: %doctrine.objectManagerLoader%

src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
class ObjectManagerGetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension
1414
{
1515

16-
/** @var string */
17-
private $repositoryClass;
16+
/** @var ObjectMetadataResolver */
17+
private $metadataResolver;
1818

19-
public function __construct(string $repositoryClass)
19+
public function __construct(ObjectMetadataResolver $metadataResolver)
2020
{
21-
$this->repositoryClass = $repositoryClass;
21+
$this->metadataResolver = $metadataResolver;
2222
}
2323

2424
public function getClass(): string
@@ -47,7 +47,14 @@ public function getTypeFromMethodCall(
4747
return new MixedType();
4848
}
4949

50-
return new ObjectRepositoryType($argType->getValue(), $this->repositoryClass);
50+
$objectName = $argType->getValue();
51+
$repositoryClass = $this->metadataResolver->getRepositoryClass($objectName);
52+
53+
if ($repositoryClass === null) {
54+
return new MixedType();
55+
}
56+
57+
return new ObjectRepositoryType($objectName, $repositoryClass);
5158
}
5259

5360
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Doctrine;
4+
5+
use Doctrine\Common\Persistence\ObjectManager;
6+
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as ODMMetadata;
7+
use Doctrine\ORM\Mapping\ClassMetadata as ORMMetadata;
8+
use RuntimeException;
9+
use function file_exists;
10+
use function is_readable;
11+
12+
final class ObjectMetadataResolver
13+
{
14+
15+
/** @var ObjectManager */
16+
private $objectManager;
17+
18+
public function __construct(string $objectManagerLoader)
19+
{
20+
$this->objectManager = $this->getObjectManager($objectManagerLoader);
21+
}
22+
23+
private function getObjectManager(string $objectManagerLoader): ObjectManager
24+
{
25+
if (! file_exists($objectManagerLoader) && ! is_readable($objectManagerLoader)) {
26+
throw new RuntimeException('Object manager could not be loaded');
27+
}
28+
29+
return require $objectManagerLoader;
30+
}
31+
32+
public function getRepositoryClass(string $className): ?string
33+
{
34+
$metatada = $this->objectManager->getClassMetadata($className);
35+
36+
if ($metatada instanceof ORMMetadata) {
37+
return $metatada->customRepositoryClassName ?? 'Doctrine\ORM\EntityRepository';
38+
}
39+
40+
if ($metatada instanceof ODMMetadata) {
41+
return $metatada->customRepositoryClassName ?? 'Doctrine\ODM\MongoDB\DocumentRepository';
42+
}
43+
44+
return null;
45+
}
46+
47+
}

tests/DoctrineIntegration/ODM/DocumentManagerIntegrationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public function dataTopics(): array
1616
['documentManagerDynamicReturn'],
1717
['documentRepositoryDynamicReturn'],
1818
['documentManagerMergeReturn'],
19+
['customRepositoryUsage'],
1920
];
2021
}
2122

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DoctrineIntegration\ODM\CustomRepositoryUsage;
4+
5+
use Doctrine\ODM\MongoDB\DocumentManager;
6+
use Doctrine\ODM\MongoDB\DocumentRepository;
7+
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
8+
use Doctrine\ODM\MongoDB\Mapping\Annotations\Id;
9+
use RuntimeException;
10+
11+
class Example
12+
{
13+
/**
14+
* @var MyRepository
15+
*/
16+
private $repository;
17+
18+
public function __construct(DocumentManager $documentManager)
19+
{
20+
$this->repository = $documentManager->getRepository(MyDocument::class);
21+
}
22+
23+
public function get(): void
24+
{
25+
$test = $this->repository->get('testing');
26+
$test->doSomethingElse();
27+
}
28+
}
29+
30+
/**
31+
* @Document(repositoryClass=MyRepository::class)
32+
*/
33+
class MyDocument
34+
{
35+
/**
36+
* @Id(strategy="NONE", type="string")
37+
*
38+
* @var string
39+
*/
40+
private $id;
41+
42+
public function doSomethingElse(): void
43+
{
44+
}
45+
}
46+
47+
class MyRepository extends DocumentRepository
48+
{
49+
public function get(string $id): MyDocument
50+
{
51+
$document = $this->find($id);
52+
53+
if ($document === null) {
54+
throw new RuntimeException('Not found...');
55+
}
56+
57+
return $document;
58+
}
59+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
use Doctrine\Common\Annotations\AnnotationReader;
4+
use Doctrine\Common\Annotations\AnnotationRegistry;
5+
use Doctrine\Common\Cache\ArrayCache;
6+
use Doctrine\ODM\MongoDB\Configuration;
7+
use Doctrine\ODM\MongoDB\DocumentManager;
8+
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
9+
10+
AnnotationRegistry::registerUniqueLoader('class_exists');
11+
12+
$config = new Configuration();
13+
$config->setProxyDir(__DIR__);
14+
$config->setProxyNamespace('PHPstan\Doctrine\OdmProxies');
15+
$config->setMetadataCacheImpl(new ArrayCache());
16+
$config->setHydratorDir(__DIR__);
17+
$config->setHydratorNamespace('PHPstan\Doctrine\OdmHydrators');
18+
19+
$config->setMetadataDriverImpl(
20+
new AnnotationDriver(
21+
new AnnotationReader(),
22+
[__DIR__ . '/data']
23+
)
24+
);
25+
26+
return DocumentManager::create(
27+
null,
28+
$config
29+
);

tests/DoctrineIntegration/ODM/phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ includes:
33

44
parameters:
55
doctrine:
6-
repositoryClass: Doctrine\ODM\MongoDB\DocumentRepository
6+
objectManagerLoader: tests/DoctrineIntegration/ODM/document-manager.php

tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public function dataTopics(): array
1616
['entityManagerDynamicReturn'],
1717
['entityRepositoryDynamicReturn'],
1818
['entityManagerMergeReturn'],
19+
['customRepositoryUsage'],
1920
];
2021
}
2122

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DoctrineIntegration\ORM\CustomRepositoryUsage;
4+
5+
use Doctrine\ORM\EntityManagerInterface;
6+
use Doctrine\ORM\EntityRepository;
7+
use Doctrine\ORM\Mapping as ORM;
8+
use RuntimeException;
9+
10+
class Example
11+
{
12+
/**
13+
* @var MyRepository
14+
*/
15+
private $repository;
16+
17+
public function __construct(EntityManagerInterface $entityManager)
18+
{
19+
$this->repository = $entityManager->getRepository(MyEntity::class);
20+
}
21+
22+
public function get(): void
23+
{
24+
$test = $this->repository->get(1);
25+
$test->doSomethingElse();
26+
}
27+
}
28+
29+
/**
30+
* @ORM\Entity(repositoryClass=MyRepository::class)
31+
*/
32+
class MyEntity
33+
{
34+
/**
35+
* @ORM\Id()
36+
* @ORM\GeneratedValue()
37+
* @ORM\Column(type="integer")
38+
*
39+
* @var int
40+
*/
41+
private $id;
42+
43+
public function doSomethingElse(): void
44+
{
45+
}
46+
}
47+
48+
class MyRepository extends EntityRepository
49+
{
50+
public function get(int $id): MyEntity
51+
{
52+
$entity = $this->find($id);
53+
54+
if ($entity === null) {
55+
throw new RuntimeException('Not found...');
56+
}
57+
58+
return $entity;
59+
}
60+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
use Doctrine\Common\Annotations\AnnotationReader;
4+
use Doctrine\Common\Annotations\AnnotationRegistry;
5+
use Doctrine\Common\Cache\ArrayCache;
6+
use Doctrine\ORM\Configuration;
7+
use Doctrine\ORM\EntityManager;
8+
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
9+
10+
AnnotationRegistry::registerUniqueLoader('class_exists');
11+
12+
$config = new Configuration();
13+
$config->setProxyDir(__DIR__);
14+
$config->setProxyNamespace('PHPstan\Doctrine\OrmProxies');
15+
$config->setMetadataCacheImpl(new ArrayCache());
16+
17+
$config->setMetadataDriverImpl(
18+
new AnnotationDriver(
19+
new AnnotationReader(),
20+
[__DIR__ . '/data']
21+
)
22+
);
23+
24+
return EntityManager::create(
25+
[
26+
'driver' => 'pdo_sqlite',
27+
'memory' => true,
28+
],
29+
$config
30+
);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
includes:
22
- ../../../extension.neon
3+
4+
parameters:
5+
doctrine:
6+
objectManagerLoader: tests/DoctrineIntegration/ORM/entity-manager.php

0 commit comments

Comments
 (0)