Passed
Push — master ( 10397f...218376 )
by Povilas
02:22
created

JoinMapper::getField()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 17
ccs 0
cts 0
cp 0
rs 10
cc 4
nc 5
nop 3
crap 20
1
<?php
2
3
namespace Povs\ListerBundle\Mapper;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
7
/**
8
 * @author Povilas Margaiatis <[email protected]>
9
 *
10
 * @property JoinField[]|ArrayCollection $fields
11
 * @method JoinField get(string $id)
12
 */
13
class JoinMapper extends AbstractMapper
14
{
15
    /**
16
     * @var ListMapper
17
     */
18
    private $listMapper;
19
20
    /**
21
     * @var FilterMapper
22
     */
23
    private $filterMapper;
24
25
    /**
26
     * JoinMapper constructor.
27
     *
28
     * @param ListMapper   $listMapper   fully built list mapper
29
     * @param FilterMapper $filterMapper fully build filter mapper
30 9
     */
31
    public function __construct(
32
        ListMapper $listMapper,
33
        FilterMapper $filterMapper
34 9
    ) {
35 9
        parent::__construct();
36 9
        $this->listMapper = $listMapper;
37 9
        $this->filterMapper = $filterMapper;
38
    }
39
40
    /**
41
     * @param string $path   ORM path to join
42
     * @param string $alias  join as alias
43
     * @param array $options
44
     *
45
     * @return JoinMapper
46 8
     */
47
    public function add(string $path, string $alias, array $options = []): JoinMapper
48 8
    {
49
        $this->addJoin($path, $options, $alias);
50 8
51
        return $this;
52
    }
53
54
    /**
55
     * @param string $path
56
     * @param bool   $lazy
57
     *
58
     * @return JoinField|null
59 11
     */
60
    public function getByPath(string $path, bool $lazy = false): ?JoinField
61
    {
62 7
        $field = $this->fields->filter(static function (JoinField $field) use ($path, $lazy) {
63 7
            return ($field->getAlias() === $path || $field->getPath() === $path || $field->getJoinPath(null) === $path)
64 11
                && $field->getOption(JoinField::OPTION_LAZY) === $lazy;
65
        })->first();
66 11
67
        return $field ?: null;
68
    }
69
70
    /**
71
     * @param bool|null $lazy
72
     *
73
     * @return ArrayCollection|JoinField[]
74 6
     */
75
    public function getFields(?bool $lazy = null): ArrayCollection
76 6
    {
77 6
        if (null === $lazy) {
78
            return $this->fields;
79
        }
80
81 2
        return $this->fields->filter(static function (JoinField $joinField) use ($lazy) {
82 2
            return $joinField->getOption(JoinField::OPTION_LAZY) === $lazy;
83
        });
84
    }
85
86
    /**
87
     * @return JoinMapper
88 2
     */
89
    public function build(): self
90 2
    {
91 2
        $this->buildListJoins();
92
        $this->buildFilterJoins();
93 2
94
        return $this;
95
    }
96 2
97
    private function buildListJoins(): void
98 2
    {
99 1
        foreach ($this->listMapper->getFields() as $field) {
100 1
            $paths = $field->getPaths();
101 1
            $joinType = $field->getOption(ListField::OPTION_JOIN_TYPE);
102
            $lazy = $field->getOption(ListField::OPTION_LAZY);
103 1
104
            $this->buildJoins($paths, $joinType, $lazy);
105 1
106 1
            if (
107
                $field->getOption(ListField::OPTION_SORTABLE) &&
108 1
                $field->getOption(ListField::OPTION_SORT_VALUE)
109 1
            ) {
110
                if ($sortPath = $field->getOption(ListField::OPTION_SORT_PATH)) {
111
                    $paths = (array) $sortPath;
112 1
                }
113
114
                $this->buildJoins($paths, $joinType, false);
115 2
            }
116
        }
117 2
    }
118
119 2
    private function buildFilterJoins(): void
120 1
    {
121 1
        foreach ($this->filterMapper->getFields() as $field) {
122 1
            if ($field->hasValue()) {
123 1
                $paths = $field->getPaths();
124
                $joinType = $field->getOption(FilterField::OPTION_JOIN_TYPE);
125 1
                $mapped = $field->getOption(FilterField::OPTION_MAPPED);
126 1
127
                if (!empty($paths) && $joinType && $mapped) {
128
                    $this->buildJoins($paths, $joinType, false);
129
                }
130 2
            }
131
        }
132
    }
133
134
    /**
135
     * @param array  $paths
136
     * @param string $joinType
137 2
     * @param bool   $lazy
138
     */
139 2
    private function buildJoins(array $paths, string $joinType, bool $lazy): void
140 2
    {
141 2
        foreach ($paths as $path) {
142
            if (!$path = $this->getPath($path)) {
143
                continue;
144
            }
145 2
146 2
            $options = [
147
                JoinField::OPTION_JOIN_TYPE => $joinType,
148
                JoinField::OPTION_LAZY => $lazy
149 2
            ];
150
151 2
            $this->addJoin($path, $options, null);
152
        }
153
    }
154
155
    /**
156
     * @param string      $path         join path
157
     * @param array       $options      JoinField options
158
     * @param string|null $alias        join alias. If null - alias will be auto generated by replacing "." with "_"
159
     *                                  For example path = entity.parentEntity => alias = entity_parentEntity
160
     *
161 10
     * @return JoinField|null if nothing was joined
162
     */
163 10
    private function addJoin(string $path, array $options, ?string $alias = null): ?JoinField
164 4
    {
165 1
        if ($joinField = $this->getField($path, $options, $alias)) {
166
            return $joinField;
167
        }
168 4
169
        $pathElements = explode('.', $path);
170
        $pathCount = count($pathElements);
171 9
        $prop = array_pop($pathElements);
172 9
173 9
        if ($pathCount > 1) {
174
            $parent = $this->addJoin(implode('.', $pathElements), $options, null);
175 9
        } else {
176 3
            $parent = null;
177
        }
178 9
179
        $path = $parent ? sprintf('%s.%s', $parent->getPath(), $prop) : $prop;
180
181 9
        if (!$alias) {
182
            $alias = sprintf('%s_a', str_replace('.', '_', $path));
183 9
        }
184 3
185
        $joinField = new JoinField($path, $prop, $alias, $options, $parent);
186
        $this->addField($joinField);
187 9
188 9
        return $joinField;
189
    }
190 9
191
    /**
192
     * Removes last element (attribute) from path string
193
     * For example order.user.name => order.user
194
     *
195
     * @param string $fullPath
196
     *
197
     * @return string|null
198
     */
199
    private function getPath(string $fullPath): ?string
200
    {
201 2
        $pathElements = explode('.', $fullPath);
202
203 2
        if (count($pathElements) === 1) {
204
            return null;
205 2
        }
206 2
207
        array_pop($pathElements);
208
209 2
        return implode('.', $pathElements);
210
    }
211 2
212
    /**
213
     * Finds whether field already exists and if so sets it's alias and options
214
     *
215
     * @param string      $path
216
     * @param array       $options
217
     * @param string|null $alias
218
     *
219
     * @return JoinField|null
220
     */
221
    private function getField(string $path, array $options, ?string $alias): ?JoinField
222
    {
223
        $joinField = $this->getByPath($path, $options[JoinField::OPTION_LAZY] ?? false);
224
225
        if (!$joinField) {
226
            return null;
227
        }
228
229
        if ($alias) {
230
            $joinField->setAlias($alias);
231
        }
232
233
        if (isset($options[JoinField::OPTION_JOIN_TYPE])) {
234
            $joinField->setOption(JoinField::OPTION_JOIN_TYPE, $options[JoinField::OPTION_JOIN_TYPE]);
235
        }
236
237
        return $joinField;
238
    }
239
}
240