Passed
Branch master (86f473)
by Povilas
02:34
created

JoinMapper   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 4
dl 0
loc 202
ccs 74
cts 74
cp 1
rs 9.92
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A add() 0 6 1
A getByPath() 0 9 4
A getFields() 0 10 2
A build() 0 7 1
A buildListJoins() 0 20 5
A buildFilterJoins() 0 14 6
A buildJoins() 0 15 3
B addJoin() 0 31 6
A getPath() 0 12 2
1
<?php
2
namespace Povs\ListerBundle\Mapper;
3
4
use Doctrine\Common\Collections\ArrayCollection;
5
6
/**
7
 * @author Povilas Margaiatis <[email protected]>
8
 *
9
 * @property JoinField[]|ArrayCollection $fields
10
 * @method JoinField get(string $id)
11
 */
12
class JoinMapper extends AbstractMapper
13
{
14
    /**
15
     * @var ListMapper
16
     */
17
    private $listMapper;
18
19
    /**
20
     * @var FilterMapper
21
     */
22
    private $filterMapper;
23
24
    /**
25
     * JoinMapper constructor.
26
     *
27
     * @param ListMapper   $listMapper   fully built list mapper
28
     * @param FilterMapper $filterMapper fully build filter mapper
29
     */
30 9
    public function __construct(
31
        ListMapper $listMapper,
32
        FilterMapper $filterMapper
33
    ) {
34 9
        parent::__construct();
35 9
        $this->listMapper = $listMapper;
36 9
        $this->filterMapper = $filterMapper;
37 9
    }
38
39
    /**
40
     * @param string $path   ORM path to join
41
     * @param string $alias  join as alias
42
     * @param array $options
43
     *
44
     * @return JoinMapper
45
     */
46 8
    public function add(string $path, string $alias, array $options = []): JoinMapper
47
    {
48 8
        $this->addJoin($path, $options, $alias);
49
50 8
        return $this;
51
    }
52
53
    /**
54
     * @param string $path
55
     * @param bool   $lazy
56
     *
57
     * @return JoinField|null
58
     */
59 11
    public function getByPath(string $path, bool $lazy = false): ?JoinField
60
    {
61
        $field = $this->fields->filter(static function(JoinField $field) use ($path, $lazy) {
62 7
            return ($field->getAlias() === $path || $field->getPath() === $path) &&
63 7
                $field->getOption(JoinField::OPTION_LAZY) === $lazy;
64 11
        })->first();
65
66 11
        return $field ?: null;
67
    }
68
69
    /**
70
     * @param bool|null $lazy
71
     *
72
     * @return ArrayCollection
73
     */
74 6
    public function getFields(?bool $lazy = null): ArrayCollection
75
    {
76 6
        if (null === $lazy) {
77 6
            return $this->fields;
78
        }
79
80
        return $this->fields->filter(static function (JoinField $joinField) use ($lazy) {
81 2
            return $joinField->getOption(JoinField::OPTION_LAZY) === $lazy;
82 2
        });
83
    }
84
85
    /**
86
     * @return JoinMapper
87
     */
88 2
    public function build(): self
89
    {
90 2
        $this->buildListJoins();
91 2
        $this->buildFilterJoins();
92
93 2
        return $this;
94
    }
95
96 2
    private function buildListJoins(): void
97
    {
98 2
        foreach ($this->listMapper->getFields() as $field) {
99 1
            $paths = $field->getPaths();
100 1
            $joinType = $field->getOption(ListField::OPTION_JOIN_TYPE);
101 1
            $lazy = $field->getOption(ListField::OPTION_LAZY);
102
103 1
            $this->buildJoins($paths, $joinType, $lazy);
104
105 1
            if ($field->getOption(ListField::OPTION_SORTABLE) &&
106 1
                $field->getOption(ListField::OPTION_SORT_VALUE)
107
            ) {
108 1
                if ($sortPath = $field->getOption(ListField::OPTION_SORT_PATH)) {
109 1
                    $paths = (array) $sortPath;
110
                }
111
112 1
                $this->buildJoins($paths, $joinType, false);
113
            }
114
        }
115 2
    }
116
117 2
    private function buildFilterJoins(): void
118
    {
119 2
        foreach ($this->filterMapper->getFields() as $field) {
120 1
            if ($field->hasValue()) {
121 1
                $paths = $field->getPaths();
122 1
                $joinType = $field->getOption(FilterField::OPTION_JOIN_TYPE);
123 1
                $mapped = $field->getOption(FilterField::OPTION_MAPPED);
124
125 1
                if ($paths && $joinType && $mapped) {
126 1
                    $this->buildJoins($paths, $joinType, false);
127
                }
128
            }
129
        }
130 2
    }
131
132
    /**
133
     * @param array  $paths
134
     * @param string $joinType
135
     * @param bool   $lazy
136
     */
137 2
    private function buildJoins(array $paths, string $joinType, bool $lazy): void
138
    {
139 2
        foreach ($paths as $path) {
140 2
            if (!$path = $this->getPath($path)) {
141 2
                continue;
142
            }
143
144
            $options = [
145 2
                JoinField::OPTION_JOIN_TYPE => $joinType,
146 2
                JoinField::OPTION_LAZY => $lazy
147
            ];
148
149 2
            $this->addJoin($path, $options, null);
150
        }
151 2
    }
152
153
    /**
154
     * @param string      $path         join path
155
     * @param array       $options      JoinField options
156
     * @param string|null $alias        join alias. If null - alias will be auto generated by replacing "." with "_"
157
     *                                  For example path = entity.parentEntity => alias = entity_parentEntity
158
     *
159
     * @return JoinField|null if nothing was joined
160
     */
161 10
    private function addJoin(string $path, array $options, ?string $alias = null): ?JoinField
162
    {
163 10
        if ($joinField = $this->getByPath($path, $options[JoinField::OPTION_LAZY] ?? false)) {
164 4
            if ($alias) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alias of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
165 1
                $joinField->setAlias($alias);
166
            }
167
168 4
            return $joinField;
169
        }
170
171 9
        $pathElements = explode('.', $path);
172 9
        $pathCount = count($pathElements);
173 9
        $prop = array_pop($pathElements);
174
175 9
        if ($pathCount > 1) {
176 3
            $parent = $this->addJoin(implode('.', $pathElements), $options, null);
177
        } else {
178 9
            $parent = null;
179
        }
180
181 9
        $path = $parent ? sprintf('%s.%s', $parent->getPath(), $prop) : $prop;
182
183 9
        if (!$alias)  {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alias of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
184 3
            $alias = str_replace('.', '_', $path);
185
        }
186
187 9
        $joinField = new JoinField($path, $prop, $alias, $options, $parent);
188 9
        $this->addField($joinField);
189
190 9
        return $joinField;
191
    }
192
193
    /**
194
     * Removes last element (attribute) from path string
195
     * For example order.user.name => order.user
196
     *
197
     * @param string $fullPath
198
     *
199
     * @return string|null
200
     */
201 2
    private function getPath(string $fullPath): ?string
202
    {
203 2
        $pathElements = explode('.', $fullPath);
204
205 2
        if (count($pathElements) === 1) {
206 2
            return null;
207
        }
208
209 2
        array_pop($pathElements);
210
211 2
        return implode('.', $pathElements);
212
    }
213
}