phpstan-doctrine
phpstan-doctrine copied to clipboard
Return value of <MyClass extends EntityRepository>::createQueryBuilder does not resolve to correct type when phpstan-doctrine is used
So we have this case where we override the createQueryBuilder
method in one of our entity repositories to provide a specialised QueryBuilder that has some helper methods to work with the entity in question.
Heres a small test case that represents what we do in way less code:
<?php /** namespaces and uses-statements ... */
/** @extends ServiceEntityRepository<TestEntity> */
class TestEntityRepository extends ServiceEntityRepository
{
/** constructor... */
public function createQueryBuilder($alias, $indexBy = null): TestQueryBuilder
{
return new TestQueryBuilder($this->_em);
}
public function queryAllWithMagic(): array
{
$qb = $this->createQueryBuilder('tst');
\PHPStan\dumpType($qb);
return $qb->addMySpecialMagic()->getQuery()->getResult();
}
}
TestQueryBuilder
is just simply class TestQueryBuilder extends QueryBuilder
with a few methods like for example addMySpecialMagic(): self
.
When the phpstan/phpstan-doctrine
extension is not installed, it dumps the type I would expect it to:
------ ------------------------------------------------
Line Repository\TestEntityRepository.php
------ ------------------------------------------------
26 Dumped type: App\QueryBuilder\TestQueryBuilder
------ ------------------------------------------------
But when it is installed, we get this instead:
------ ----------------------------------------
Line Repository\TestEntityRepository.php
------ ----------------------------------------
33 Dumped type: Doctrine\ORM\QueryBuilder
------ ----------------------------------------
Interesting enough, it does not complain about addMySpecialMagic
being an undefined method, which is what triggered the whole investation for us.
These are the versions used for this test:
- phpstan/phpstan 1.9.12
- phpstan/phpstan-doctrine 1.3.32 ( we also observed the same behaviour with v1.3.27)
- doctrine/orm 2.14.1
Test project is based on symfony/[email protected]
, not sure if that should make a difference.
I can share the sample project I created to reproduce this if you want me. I also tried to recreate it as a testcase in the tests for this project, but failed. Could be related to the repository being ServiceEntityRepository
instead of a classic EntityRepository
, or maybe some other thing related to installed libraries, or maybe I am just not smart enough.
As a work around we add a /** @var TestQueryBuilder $qb */
type annotation to the code. It works, but the IDE complaints that its redudant and I would feel better if we knew why its required.
Oh, I hope this is not a duplicate. I tried to find if it has been already reported, but had no luck.
There's this extension: https://github.com/phpstan/phpstan-doctrine/blob/1.3.x/src/Type/Doctrine/QueryBuilder/EntityRepositoryCreateQueryBuilderDynamicReturnTypeExtension.php
What it does is that it translates any EntityRepository::createQueryBuilder()
method call into:
$entityRepository->getEntityManager()->createQueryBuilder()->select(TestEntity::class)->from()
.
That's why you're seeing what you're seeing.
Here's the PR that did that: https://github.com/phpstan/phpstan-doctrine/pull/140
And here's why: https://github.com/phpstan/phpstan-doctrine/issues/66
Arguably it could be refactored to support your use-case too.
Hey, thanks for the fast response. Since we seem to be the only one having this problem for now I guess its only fair that I'll try to solve it without causing some regresssion.
Thanks for the detailed information, that saves a lot of time!