Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query indexBy with OneToOne relation result in nullable TKey #422

Open
noemi-salaun opened this issue Feb 1, 2023 · 3 comments
Open

Query indexBy with OneToOne relation result in nullable TKey #422

noemi-salaun opened this issue Feb 1, 2023 · 3 comments

Comments

@noemi-salaun
Copy link

It works fine with version 1.3.28 but since version 1.3.29 with this commit 4490e56 , when using a query builder with indexBy refering to a OneToOne relation, I get the error

Method SomethingRepository::getAllIndexedByUsers() should return array<int, Something> but returns array<int|null, Something>.

The userId column in my table something is not nullable, it's literally the primary key

My entities are defined as follow

#[ORM\Entity]
class Something
{
    #[ORM\OneToOne, ORM\Id]
    private readonly User $user;

    #[Column]
    private int $value = 1;

    public function __construct(User $user) {
        $this->user = $user;
    }
}
#[ORM\Entity]
class User
{
    #[ORM\Column, ORM\Id, ORM\GeneratedValue]
    private int $id;

    #[Column]
    private string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }
}

and the query

    /**
     * @param list<User> $users
     *
     * @return array<int, Something>
     */
    public function getAllIndexedByUsers(array $users): array
    {
        return $this->entityManager->getRepository(Something::class)
            ->createQueryBuilder('something', 'something.user')
            ->where('something.user IN (:users)')->setParameter('users', $users)
            ->getQuery()
            ->getResult();
    }

I see in the unit test added in the commit that it's not tested with relationship, only with plain string or int column

@noemi-salaun
Copy link
Author

Explicitly specifying #[ORM\JoinColumn(nullable: false)] seems to fix the issue. But the column is already implicitly not nullable with #[ORM\Id] and PHPStan knows it. If I remove the #[ORM\Id] attribute, then PHPStan complains that

Property type mapping mismatch: database can contain User|null but property expects User.

@mvhirsch
Copy link
Contributor

mvhirsch commented Feb 3, 2023

I'm running into a similar issue - same conclusion. It's broken since v1.3.29 and my wild guess would be the exact same commit as mentioned by @noemi-salaun .

Here's my stack trace:

 Internal error: Internal error: Call to a member function dispatch() on null in file /path/to/project/src/VideoBundle/Command/FixSegmentsCompleteCommand.php                                                           
                                                                                                                                                                                                                                     
 Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md:                                                                                                                             
 #0 /path/to/project/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php(51): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker->walkIndexBy()                                                                   
 #1 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(316): Doctrine\ORM\Query\AST\IndexBy->dispatch()                                                                 
 #2 /path/to/project/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php(65): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker->walkIdentificationVariableDeclaration()               
 #3 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(304): Doctrine\ORM\Query\AST\IdentificationVariableDeclaration->dispatch()                                       
 #4 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(180): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker->walkFromClause()                                        
 #5 /path/to/project/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php(42): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker->walkSelectStatement()                                             
 #6 /path/to/project/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php(288): Doctrine\ORM\Query\Exec\SingleSelectExecutor->__construct()                                                                         
 #7 /path/to/project/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(411): Doctrine\ORM\Query\SqlWalker->getExecutor()                                                                                            
 #8 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/Query/QueryResultTypeWalker.php(119): Doctrine\ORM\Query\Parser->parse()                                                                         
 #9 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(163): PHPStan\Type\Doctrine\Query\QueryResultTypeWalker::walk()                  
 #10 /path/to/project/vendor/phpstan/phpstan-doctrine/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php(146):                                                                           
 PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getQueryType()                                                                                                                                   
 #11 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(3353): PHPStan\Type\Doctrine\QueryBuilder\QueryBuilderGetQueryDynamicReturnTypeExtension->getTypeFromMethodCall()       
 #12 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1365): PHPStan\Analyser\MutatingScope->methodCallReturnType()                                                           
 #13 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1371): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()                                                     
 #14 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(557): PHPStan\Analyser\MutatingScope->resolveType()                                                                     
 #15 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1365): PHPStan\Analyser\MutatingScope->getType()                                                                        
 #16 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(1371): PHPStan\Analyser\MutatingScope->PHPStan\Analyser\{closure}()                                                     
 #17 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(557): PHPStan\Analyser\MutatingScope->resolveType()                                                                     
 #18 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(777): PHPStan\Analyser\MutatingScope->getType()                                                                         
 #19 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/MutatingScope.php(557): PHPStan\Analyser\MutatingScope->resolveType()                                                                     
 #20 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1446): PHPStan\Analyser\MutatingScope->getType()                                                                    
 #21 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(557): PHPStan\Analyser\NodeScopeResolver->findEarlyTerminatingExpr()                                                
 #22 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(360): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                         
 #23 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(521): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                        
 #24 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(360): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                         
 #25 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(599): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                        
 #26 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(360): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                         
 #27 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(571): PHPStan\Analyser\NodeScopeResolver->processStmtNodes()                                                        
 #28 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(327): PHPStan\Analyser\NodeScopeResolver->processStmtNode()                                                         
 #29 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(175): PHPStan\Analyser\NodeScopeResolver->processNodes()                                                                 
 #30 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(148): PHPStan\Analyser\FileAnalyser->analyseFile()                                                                       
 #31 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): PHPStan\Command\WorkerCommand->PHPStan\Command\{closure}()                         
 #32 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/clue/ndjson-react/src/Decoder.php(110): _PHPStan_4dd92cd93\Evenement\EventEmitter->emit()                                                       
 #33 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_4dd92cd93\Clue\React\NDJson\Decoder->handleData()                         
 #34 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/Util.php(62): _PHPStan_4dd92cd93\Evenement\EventEmitter->emit()                                                                
 #35 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(97): _PHPStan_4dd92cd93\React\Stream\Util::_PHPStan_4dd92cd93\React\Stream\{closure}()  
 #36 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/react/stream/src/DuplexResourceStream.php(154): _PHPStan_4dd92cd93\Evenement\EventEmitter->emit()                                               
 #37 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(201): _PHPStan_4dd92cd93\React\Stream\DuplexResourceStream->handleData()                              
 #38 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/react/event-loop/src/StreamSelectLoop.php(173): _PHPStan_4dd92cd93\React\EventLoop\StreamSelectLoop->waitForStreamActivity()                    
 #39 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/src/Command/WorkerCommand.php(108): _PHPStan_4dd92cd93\React\EventLoop\StreamSelectLoop->run()                                                         
 #40 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\WorkerCommand->execute()                                                              
 #41 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_4dd92cd93\Symfony\Component\Console\Command\Command->run()                                       
 #42 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_4dd92cd93\Symfony\Component\Console\Application->doRunCommand()                                  
 #43 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_4dd92cd93\Symfony\Component\Console\Application->doRun()                                         
 #44 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_4dd92cd93\Symfony\Component\Console\Application->run()                                                                      
 #45 phar:///path/to/project/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_4dd92cd93\{closure}()                                                                                                       
 #46 /path/to/project/vendor/phpstan/phpstan/phpstan(7): require('phar:///home/me...')                                                                                                                                  
 #47 /path/to/project/vendor/bin/phpstan(115): include('/home/meh/proje...')                                                                                                                                            
 #48 {main}                                                                                                                                                                                                                          
 Child process error (exit code 1):

my dynamic query:

$qb = $this->em->createQueryBuilder()
    ->select('episode.id')
    ->from('VideoBundle:Episode', 'episode', 'episode.id')
    ->where('episode.segmentsComplete = false')
    ->andWhere('0 != SIZE(episode.segments)')
    ->andWhere(
        '0 < (SELECT COUNT(last_segment.id) FROM VideoBundle:Segment as last_segment
              WHERE last_segment.episode = episode.id AND last_segment.isLastSegment = true)'
    )
    ->andWhere(
        "0 = (SELECT COUNT(segment.id) FROM VideoBundle:Segment as segment
              WHERE segment.episode = episode.id AND segment.state != 'distributed')"
    )
;
class Episode
{
    /**
     * @var Collection<Segment>
     * @ORM\OneToMany(
     *      targetEntity="App\VideoBundle\Entity\Segment",
     *      mappedBy="episode",
     *      cascade={"persist", "remove"},
     *      orphanRemoval=true
     * )
     * @ORM\OrderBy({"position" = "ASC"})
     */
    private $segments;
}
class Segment
{
/**
     * @ORM\ManyToOne(
     *     targetEntity="App\VideoBundle\Entity\Episode",
     *     inversedBy="segments",
     *     cascade={"persist"}
     * )
     * @ORM\JoinColumn(name="episode_id", referencedColumnName="id", nullable=false)
     * @var Episode
     */
    private $episode;
}

@ondrejmirtes
Copy link
Member

@mvhirsch Wrong type inference, and an internal error crash, are not similar issues. Please track yours in a separate issue. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants