Completed
Push — master ( aada1c...2d6259 )
by Kévin
06:53 queued 03:14
created

EagerLoadingExtension::joinRelations()   C

Complexity

Conditions 11
Paths 18

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 49
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 29
nc 18
nop 5

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Extension;
13
14
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
15
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
16
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
17
use Doctrine\ORM\Mapping\ClassMetadataInfo;
18
use Doctrine\ORM\QueryBuilder;
19
20
/**
21
 * Eager loads relations.
22
 *
23
 * @author Charles Sarrazin <[email protected]>
24
 * @author Kévin Dunglas <[email protected]>
25
 */
26
final class EagerLoadingExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
27
{
28
    private $propertyNameCollectionFactory;
29
    private $propertyMetadataFactory;
30
31
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory)
32
    {
33
        $this->propertyMetadataFactory = $propertyMetadataFactory;
34
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
35
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
41
    {
42
        $this->joinRelations($queryBuilder, $resourceClass);
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null)
49
    {
50
        $this->joinRelations($queryBuilder, $resourceClass);
51
    }
52
53
    public function getMetadataProperties(string $resourceClass) : array
54
    {
55
        $properties = [];
56
57
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
58
            $properties[$property] = $this->propertyMetadataFactory->create($resourceClass, $property);
59
        }
60
61
        return $properties;
62
    }
63
64
    /**
65
     * Left joins relations to eager load.
66
     *
67
     * @param QueryBuilder $queryBuilder
68
     * @param string       $resourceClass
69
     * @param string       $originAlias   the current entity alias (first o, then a1, a2 etc.)
70
     * @param string       $relationAlias the previous relation alias to keep it unique
71
     * @param bool         $wasLeftJoin   if the relation containing the new one had a left join, we have to force the new one to left join too
72
     */
73
    private function joinRelations(QueryBuilder $queryBuilder, string $resourceClass, string $originAlias = 'o', string &$relationAlias = 'a', bool $wasLeftJoin = false)
74
    {
75
        $classMetadata = $queryBuilder->getEntityManager()->getClassMetadata($resourceClass);
76
        $j = 0;
77
78
        foreach ($classMetadata->getAssociationNames() as $i => $association) {
79
            $mapping = $classMetadata->associationMappings[$association];
80
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $association);
81
82
            if (ClassMetadataInfo::FETCH_EAGER !== $mapping['fetch'] || false === $propertyMetadata->isReadableLink()) {
83
                continue;
84
            }
85
86
            if (false === $wasLeftJoin) {
87
                $joinColumns = $mapping['joinColumns'] ?? $mapping['joinTable']['joinColumns'] ?? null;
88
89
                if (null === $joinColumns) {
90
                    $method = 'leftJoin';
91
                } else {
92
                    $method = false === $joinColumns[0]['nullable'] ? 'innerJoin' : 'leftJoin';
93
                }
94
            } else {
95
                $method = 'leftJoin';
96
            }
97
98
            $associationAlias = $relationAlias.$i;
99
            $queryBuilder->{$method}($originAlias.'.'.$association, $associationAlias);
100
            $select = [];
101
            $targetClassMetadata = $queryBuilder->getEntityManager()->getClassMetadata($mapping['targetEntity']);
102
103
            foreach ($this->getMetadataProperties($mapping['targetEntity']) as $property => $propertyMetadata) {
104
                if (true === $propertyMetadata->isIdentifier()) {
105
                    $select[] = $property;
106
                    continue;
107
                }
108
109
                //the field test allows to add methods to a Resource which do not reflect real database fields
110
                if (true === $targetClassMetadata->hasField($property) && true === $propertyMetadata->isReadable()) {
111
                    $select[] = $property;
112
                }
113
            }
114
115
            $queryBuilder->addSelect(sprintf('partial %s.{%s}', $associationAlias, implode(',', $select)));
116
117
            $relationAlias = $relationAlias.++$j;
118
119
            $this->joinRelations($queryBuilder, $mapping['targetEntity'], $associationAlias, $relationAlias, $method === 'leftJoin');
120
        }
121
    }
122
}
123