Passed
Push — master ( 579940...10397f )
by Povilas
03:12
created

JoinMapper::addJoin()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 19
c 1
b 0
f 0
dl 0
loc 34
ccs 16
cts 16
cp 1
rs 8.8333
cc 7
nc 12
nop 3
crap 7
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->getByPath($path, $options[JoinField::OPTION_LAZY] ?? false)) {
166
            if ($alias) {
167
                $joinField->setAlias($alias);
168 4
            }
169
170
            if (isset($options[JoinField::OPTION_JOIN_TYPE])) {
171 9
                $joinField->setOption(JoinField::OPTION_JOIN_TYPE, $options[JoinField::OPTION_JOIN_TYPE]);
172 9
            }
173 9
174
            return $joinField;
175 9
        }
176 3
177
        $pathElements = explode('.', $path);
178 9
        $pathCount = count($pathElements);
179
        $prop = array_pop($pathElements);
180
181 9
        if ($pathCount > 1) {
182
            $parent = $this->addJoin(implode('.', $pathElements), $options, null);
183 9
        } else {
184 3
            $parent = null;
185
        }
186
187 9
        $path = $parent ? sprintf('%s.%s', $parent->getPath(), $prop) : $prop;
188 9
189
        if (!$alias) {
190 9
            $alias = sprintf('%s_a', str_replace('.', '_', $path));
191
        }
192
193
        $joinField = new JoinField($path, $prop, $alias, $options, $parent);
194
        $this->addField($joinField);
195
196
        return $joinField;
197
    }
198
199
    /**
200
     * Removes last element (attribute) from path string
201 2
     * For example order.user.name => order.user
202
     *
203 2
     * @param string $fullPath
204
     *
205 2
     * @return string|null
206 2
     */
207
    private function getPath(string $fullPath): ?string
208
    {
209 2
        $pathElements = explode('.', $fullPath);
210
211 2
        if (count($pathElements) === 1) {
212
            return null;
213
        }
214
215
        array_pop($pathElements);
216
217
        return implode('.', $pathElements);
218
    }
219
}
220