api-platform / core

The server component of API Platform: hypermedia and GraphQL APIs in minutes
https://api-platform.com
MIT License
2.4k stars 856 forks source link

Subresource have missing fields / Doctrine ORM SQL #4367

Closed Richard87 closed 3 years ago

Richard87 commented 3 years ago

API Platform version(s) affected: 2.6.5

Description
A request to GET a resource via subresource is missing a lot of information. Retrieving the same resource by its direct IRI works as expected.

In other words, the generated SQL is only including a few of the required columns, not all columns. The context groups are correct and works as expected if the getters return dummy data...

This works perfectly:

GET http://localhost:8000/api/nefles/5

{
  "@context": "\/api\/contexts\/Nefle",
  "@id": "\/api\/nefles\/5",
  "@type": "Nefle",
  "id": 5,
  "siteConfig": {
    "domain": "",
    "siteTags": {
      "phone": "12345678",
      "title": "Test",
      "warning": "Velkommen til vøre nye nettsider",
      "location": {
        "place": "",
        "address": "Example 1",
        "postnumber": ""
      },
      "template": "light",
  }
}

This fails:

GET http://localhost:8000/api/offices/79/nefle

{
  "@context": "\/api\/contexts\/Nefle",
  "@id": "\/api\/nefles\/5",
  "@type": "Nefle",
  "id": 5,
  "siteConfig": []
}

(extraDinaubs abd siteConfig returns [] when its null)

The generated SQL explains why the data is missing:

SELECT
  o0_.id AS id_0,
  o0_.created_at AS created_at_1,
  o0_.contract_id AS contract_id_2,
  o0_.ordered_by_id AS ordered_by_id_3,
  o0_.partnership_id AS partnership_id_4
FROM
  office_nefle o0_
  INNER JOIN office o1_ ON o0_.id = o1_.nefle_id
WHERE
  o1_.id = ?

And maybe the stack trace will help you more than it helped me...

1 | Doctrine\DBAL\Logging\LoggerChain->startQuery 
(line 1282)
-- | --
2 | Doctrine\DBAL\Connection->executeQuery                                                                                                                                                                                                                                                                        (line 44)
3 | Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute                                                                                                                                                                                                                                                                        (line 325)
4 | Doctrine\ORM\Query->_doExecute                                                                                                                                                                                                                                                                        (line 1073)
5 | Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache                                                                                                                                                                                                                                                                        (line 1027)
6 | Doctrine\ORM\AbstractQuery->execute                                                                                                                                                                                                                                                                        (line 830)
7 | Doctrine\ORM\AbstractQuery->getOneOrNullResult                                                                                                                                                                                                                                                                        (line 119)
8 | ApiPlatform\Core\Bridge\Doctrine\Orm\SubresourceDataProvider->getSubresource                                                                                                                                                                                                                                                                        (line 63)
9 | ApiPlatform\Core\Bridge\Symfony\Bundle\DataProvider\TraceableChainSubresourceDataProvider->getSubresource                                                                                                                                                                                                                                                                        (line 88)
10 | ApiPlatform\Core\EventListener\ReadListener->getSubresourceData                                                                                                                                                                                                                                                                        (line 109)
11 | ApiPlatform\Core\EventListener\ReadListener->onKernelRequest                                                                                                                                                                                                                                                                        (line 117)
12 | Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke                                                                                                                                                                                                                                                                        (line 230)
13 | Symfony\Component\EventDispatcher\EventDispatcher->callListeners                                                                                                                                                                                                                                                                        (line 59)
14 | Symfony\Component\EventDispatcher\EventDispatcher->dispatch                                                                                                                                                                                                                                                                        (line 151)
15 | Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch                                                                                                                                                                                                                                                                        (line 133)
16 | Symfony\Component\HttpKernel\HttpKernel->handleRaw                                                                                                                                                                                                                                                                        (line 79)
17 | Symfony\Component\HttpKernel\HttpKernel->handle                                                                                                                                                                                                                                                                        (line 199)
18 | Symfony\Component\HttpKernel\Kernel->handle                                                                                                                                                                                                                                                                        (line 24)

Else I'm using these related versions:


doctrine/annotations                      1.12.1
doctrine/cache                            1.11.0
doctrine/dbal                             2.13.1
doctrine/doctrine-bundle                  2.3.1
doctrine/orm                              2.8.4

Symfony 5.3.4
Richard87 commented 3 years ago

Very strange, I hacked around the problem above by fetching the id from my office, and then requesting the object directly.

But, just know the bug resurfaced anyway:

GET http://localhost:8000/api/office_nefles/5

@context | "/api/contexts/OfficeNefle"
@id | "/api/office_nefles/5"
@type | "OfficeNefle"
id | 5
domain | "test.example.com"
extraDomains | []
siteConfig | {…}

Skjermbilde 2021-08-06 kl  16 25 17 Skjermbilde 2021-08-06 kl  16 27 42

And as you can see in the screenshot, the field generatedAt is null, probably because the SQL query ommits those columns.

GET http://localhost:8000/api/office_nefles/5 I have also tripple checked the Groups and that the getters are public (setters are private) Skjermbilde 2021-08-06 kl  16 28 47

I'm completley stumped, would love some ideas on how to troubleshoot!

Richard87 commented 3 years ago

Database view (for complete-ness!) Skjermbilde 2021-08-06 kl  16 33 20

Richard87 commented 3 years ago

Hmm, it seems the DQL generated by ItemDataProvider SELECT o FROM App\Entity\Office\Nefle o WHERE o.id = :id_id is absolutley correct....

Richard87 commented 3 years ago

So strange, running the query in a command, everything works as expected:

class TestCommand extends Command
{
    public function __construct(
        public EntityManagerInterface $em,
        public SerializerInterface $serializer,
    )
    {
        parent::__construct('app:test:test');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $query = $this->em->createQuery("SELECT o FROM App\Entity\Office\Nefle o WHERE o.id = :id_id");
        $query->setParameter(":id_id", 5);

        $results = $query->getOneOrNullResult();
        $json = $this->serializer->serialize($results,"jsonld", ["groups"=> ["OfficeNefle:view"]]);

        dump($json);

        return Command::SUCCESS;
    }
}

Skjermbilde 2021-08-09 kl  11 15 22

Richard87 commented 3 years ago

The problem all along was outdated cache, specifically a missconfiguration of doctrine.query_cache_driver that had stored an old version of the DQL => SQL conversion