Doctrine many-to-one in enkele query voor meerdere objecten

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • gnoe93
  • Registratie: September 2016
  • Laatst online: 08-04 13:00
Ik heb een lijst met entities die ik op een pagina wil weergeven in een tabel. De entities hebben elk een aantal one-to-many relatie die ik ook als kolom wil weergeven in de tabel. Dit is een probleem omdat de tabel mogelijks honderden rijen bevat, en door lazy loading wordt er telkens per rij een extra query uitgevoerd om de relaties te fetchen.

Een oplossing om het aantal queries terug te brengen zou het toevoegen van fetch joins kunnen zijn, maar dit wil ik vermijden omdat dit op zich ook performance problemen geeft bij het hydraten naar objecten.

Ik zat dus te denken aan een custom repository methode waaraan ik voor de verkregen lijst met entities meegeef, die in één enkele query de many-to-one relatie ophaalt (de entities voor de tabel zijn "cards" en die uit de relatie "variantTypes"):

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public function getAndAssignVariantTypes(array $cards) : void
{
    $dql = '
        SELECT
            card.id AS cardId,
            cardVariantType.id as cardVariantTypeId
        FROM
            AppBundle:Card card
            INNER JOIN card.variantTypes cardVariantType
        WHERE
            card.id IN (:cardIds)
        ORDER BY
            card.id ASC,
            cardVariantType.id ASC
    ';

    $cardsIndexedPerId = array();
    $cardIds = array();

    foreach ($cards as $card)
    {
        $cardId = $card->getId();
        $cardsIndexedPerId[$cardId] = $card;
        $cardIds[] = $cardId;
    }

    $query = $this->getEntityManager()->createQuery($dql);
    $query->setParameter('cardIds', $cardIds);

    $cardVariantTypes = $this->getEntityManager()->getRepository(\AppBundle\Entity\CardVariantType::class)->findAllIndexedPerId();
    $cardVariantTypesPerCardId = array();
    
    foreach ($query->getResult() as $queryResultRow)
    {
        $cardId = $queryResultRow['cardId'];
        if (!isset($cardVariantTypesPerCardId[$cardId]))
        {
            $cardVariantTypesPerCardId[$cardId] = array();
        }
        $cardVariantTypesPerCardId[$cardId][] = $cardVariantTypes[$queryResultRow['cardVariantTypeId']];
    }

    foreach ($cardsIndexedPerId as $cardId => $card)
    {
        $card->setVariantTypes($cardVariantTypesPerCardId[$cardId]);
    }
}


Ik maak me hierbij de bedenking dat dit omslachtig en niet echt elegant is (zeker als ik dit eventueel voor meerdere relaties moet doen). Kan doctrine dit niet op een andere manier doen waar ik (blijkbaar) niet van weet? Ook vraag ik me af of al deze iteraties wel een betere performance geven dan het probleem dat ik probeer op te lossen (moet dit nog testen).

Alle reacties


  • gnoe93
  • Registratie: September 2016
  • Laatst online: 08-04 13:00
Na even zoeken heb ik een oplossing gevonden door partial objects te gebruiken:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Eagerly fetch the given fields.
if (count($cards) !== 0 && is_array($eagerlyFetchedFields))
{
    $cardsIds = $cardSearchResult->getCardIds();

    foreach ($eagerlyFetchedFields as $eagerlyFetchedField)
    {
        $fieldsQueryBuilder = $entityManager->createQueryBuilder();

        $fieldsQueryBuilder
            ->addSelect($eagerlyFetchedField)
            ->addSelect('PARTIAL card.{id}')
            ->from(Card::class, 'card')
            ->leftJoin('card.' . $eagerlyFetchedField, $eagerlyFetchedField)
            ->where($fieldsQueryBuilder->expr()->in('card.id', ':cardIds'));

        $fieldsQueryBuilder->setParameter('cardIds', $cardsIds);
        $fieldsQueryBuilder->getQuery()->getResult();
    }
}


I.p.v. fetch joins waarbij de performance exponentieel degradeert (voor elke fetch join alias), wordt de hydration nu in meerdere stappen gedaan: https://ocramius.github.i...m-optimization-hydration/

[ Voor 3% gewijzigd door gnoe93 op 13-09-2017 05:59 ]