JoinGenerator::generateJoinDependencyTree()   B
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
c 0
b 0
f 0
rs 8.8571
cc 5
eloc 12
nc 8
nop 1
1
<?php
2
namespace Netdudes\DataSourceryBundle\DataSource\Driver\Doctrine\QueryBuilder;
3
4
use Doctrine\ORM\Query\Expr\Join;
5
use Netdudes\DataSourceryBundle\DataSource\Configuration\Field;
6
use Netdudes\DataSourceryBundle\Query\Query;
7
8
class JoinGenerator
9
{
10
    /**
11
     * @var array
12
     */
13
    private $uniqueJoinNameCache = [];
14
15
    /**
16
     * @var Field[]
17
     */
18
    private $queryBuilderDataSourceFields;
19
20
    /**
21
     * @var Join[]
22
     */
23
    private $joins;
24
25
    /**
26
     * @var string
27
     */
28
    private $fromAlias;
29
30
    /**
31
     * @var Join[][]
32
     */
33
    private $joinCache = [];
34
35
    /**
36
     * @param array                   $queryBuilderDataSourceFields
37
     * @param                         $fromAlias
38
     * @param RequiredFieldsExtractor $requiredFieldsExtractor
39
     */
40
    public function __construct(array $queryBuilderDataSourceFields, $fromAlias, RequiredFieldsExtractor $requiredFieldsExtractor)
41
    {
42
        $this->requiredFieldsExtractor = $requiredFieldsExtractor;
0 ignored issues
show
Bug introduced by
The property requiredFieldsExtractor does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
43
        $this->queryBuilderDataSourceFields = $queryBuilderDataSourceFields;
44
        $this->fromAlias = $fromAlias;
45
    }
46
47
    /**
48
     * Generate the needed joins given a $query. This function will build a dependency tree and
49
     * walk it recursively to generate an ordered list of Join statements.
50
     *
51
     * @param Query $query
52
     *
53
     * @return Join[]
54
     */
55 View Code Duplication
    public function generate(Query $query)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
56
    {
57
        $uniqueId = spl_object_hash($query);
58
        if (!isset($this->joinCache[$uniqueId])) {
59
            $this->joinCache[$uniqueId] = $this->build($query);
60
        }
61
62
        return $this->joinCache[$uniqueId];
63
    }
64
65
    /**
66
     * @param Query $query
67
     *
68
     * @return Join[]
69
     */
70
    protected function build(Query $query)
71
    {
72
        $elements = [];
73
        $requiredFields = $this->requiredFieldsExtractor->extractRequiredFields($query);
74
        foreach ($this->queryBuilderDataSourceFields as $field) {
75
            if (!is_null($query) && !in_array($field->getUniqueName(), $requiredFields, true)) {
76
                continue;
77
            }
78
            if (!is_null($field->getDatabaseFilterQueryField())) {
79
                $elements[] = $field->getDatabaseFilterQueryField();
80
            }
81
        }
82
        $tree = $this->generateJoinDependencyTree($elements);
83
        $this->joins = [];
84
        foreach ($tree as $child => $grandChildren) {
85
            $this->walkDependencyTreeNode($this->fromAlias, $child, $grandChildren);
86
        }
87
88
        return $this->joins;
89
    }
90
91
    /**
92
     * Builds a tree of dependencies between entity fields, relating what joins
93
     * are needed for each selected field in the query.
94
     *
95
     * This action is performed recursively.
96
     *
97
     * @param array $elements
98
     *
99
     * @return array
100
     */
101
    protected function generateJoinDependencyTree(array $elements)
102
    {
103
        $subtree = [];
104
        foreach ($elements as $element) {
105
            $parts = explode('.', $element, 2);
106
            if (count($parts) == 1) {
107
                continue;
108
            }
109
            if (!isset($subtree[$parts[0]])) {
110
                $subtree[$parts[0]] = [];
111
            }
112
            $subtree[$parts[0]][] = $parts[1];
113
        }
114
        foreach ($subtree as $key => $elements) {
115
            $subtree[$key] = $this->generateJoinDependencyTree($elements);
116
        }
117
118
        return $subtree;
119
    }
120
121
    /**
122
     * Walks a node of the dependency tree, recursively generating an ordered list on Joins
123
     * that is stored in the $this->joins cache.
124
     *
125
     * @param       $parentUniqueIdentifier
126
     * @param       $node
127
     * @param       $descendants
128
     * @param array $completePath
129
     */
130
    protected function walkDependencyTreeNode($parentUniqueIdentifier, $node, $descendants, $completePath = [])
131
    {
132
        $completePath[] = $node;
133
        $joinedCompletePath = implode('.', $completePath);
134
        $joinUniqueIdentifier =
135
            array_key_exists($joinedCompletePath, $this->uniqueJoinNameCache) ?
136
                $this->uniqueJoinNameCache[$joinedCompletePath] :
137
                ($this->uniqueJoinNameCache[$joinedCompletePath] = uniqid("JOIN_${node}_"));
138
        $this->joins[$joinedCompletePath] = new Join(Join::LEFT_JOIN, $parentUniqueIdentifier . '.' . $node, $joinUniqueIdentifier);
139
        foreach ($descendants as $child => $grandChildren) {
140
            $this->walkDependencyTreeNode($joinUniqueIdentifier, $child, $grandChildren, $completePath);
141
        }
142
    }
143
}
144