JoinGenerator   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 136
Duplicated Lines 6.62 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 2
dl 9
loc 136
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A generate() 9 9 2
B build() 0 20 6
B generateJoinDependencyTree() 0 19 5
A walkDependencyTreeNode() 0 13 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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