Completed
Branch feature/pre-split (6ce9be)
by Anton
03:09
created

AbstractArray::buildAtomics()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 20
Code Lines 9

Duplication

Lines 4
Ratio 20 %

Importance

Changes 0
Metric Value
dl 4
loc 20
c 0
b 0
f 0
cc 5
eloc 9
nc 4
nop 1
rs 8.8571
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM\Accessors;
8
9
use Spiral\Models\Traits\SolidableTrait;
10
use Spiral\ODM\CompositableInterface;
11
12
/**
13
 * Provides ability to perform scalar operations on arrays.
14
 *
15
 * Attention, array will be saved as one big $set operation in case when multiple atomic
16
 * operations applied to it (not supported by Mongo).
17
 */
18
abstract class AbstractArray implements CompositableInterface, \Countable, \IteratorAggregate
19
{
20
    use SolidableTrait;
21
22
    /**
23
     * @var array
24
     */
25
    protected $values = [];
26
27
    /**
28
     * Low level atomic operations.
29
     *
30
     * @var array
31
     */
32
    protected $atomics = [];
33
34
    /**
35
     * @param mixed $values
36
     */
37
    public function __construct($values)
38
    {
39
        if (!is_array($values)) {
40
            //Since we have to overwrite non array field
41
            $this->solidState = true;
42
        }
43
44
        $this->addValues($values);
45
    }
46
47
    /**
48
     * Check if value presented in array.
49
     *
50
     * @param mixed $needle
51
     * @param bool  $strict
52
     *
53
     * @return bool
54
     */
55
    public function has($needle, bool $strict = true): bool
56
    {
57
        foreach ($this->values as $value) {
58
            if ($strict && $value === $needle) {
59
                return true;
60
            }
61
62
            if ($strict && $value == $needle) {
63
                return true;
64
            }
65
        }
66
67
        return false;
68
    }
69
70
    /**
71
     * Alias for atomic operation $push. Only values passed type filter will be added.
72
     *
73
     * @param mixed $value
74
     *
75
     * @return self|$this
76
     */
77
    public function push($value): AbstractArray
78
    {
79
        $value = $this->filterValue($value);
80
        if (is_null($value)) {
81
            return $this;
82
        }
83
84
        array_push($this->values, $value);
85
        $this->atomics['$push']['$each'][] = $value;
86
87
        return $this;
88
    }
89
90
    /**
91
     * Alias for atomic operation $addToSet. Only values passed type filter will be added.
92
     *
93
     * @param mixed $value
94
     *
95
     * @return self|$this
96
     */
97 View Code Duplication
    public function add($value): AbstractArray
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...
98
    {
99
        $value = $this->filterValue($value);
100
        if (is_null($value)) {
101
            return $this;
102
        }
103
104
        if (!in_array($value, $this->values)) {
105
            array_push($this->values, $value);
106
        }
107
108
        $this->atomics['$addToSet']['$each'][] = $value;
109
110
        return $this;
111
    }
112
113
    /**
114
     * Alias for atomic operation $pull. Only values passed type filter will be added.
115
     *
116
     * @param mixed $value
117
     *
118
     * @return self|$this
119
     */
120 View Code Duplication
    public function pull($value): AbstractArray
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...
121
    {
122
        $value = $this->filterValue($value);
123
        if (is_null($value)) {
124
            return $this;
125
        }
126
127
        //Removing values from array (non strict)
128
        $this->values = array_filter($this->values, function ($item) use ($value) {
129
            return $item != $value;
130
        });
131
132
        $this->atomics['$pull']['$in'][] = $value;
133
134
        return $this;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function stateValue($data)
141
    {
142
        //Manually altered arrays must always end in solid state
143
        $this->solidState = true;
144
145
        //Flushing existed values
146
        $this->values = [];
147
148
        //Pushing filtered values in array
149
        $this->addValues($data);
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function hasUpdates(): bool
156
    {
157
        return !empty($this->atomics);
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163
    public function flushUpdates()
164
    {
165
        $this->atomics = [];
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171
    public function buildAtomics(string $container = ''): array
172
    {
173
        if (!$this->hasUpdates()) {
174
            return [];
175
        }
176
177
        //Mongo does not support multiple operations for one field, switching to $set (make sure it's
178
        //reasonable)
179 View Code Duplication
        if ($this->solidState || count($this->atomics) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
180
            //We don't care about atomics in solid state
181
            return ['$set' => [$container => $this->packValue()]];
182
        }
183
184
        $atomics = [];
185
        foreach ($this->atomics as $operation => $values) {
186
            $atomics[$operation] = [$container => $values];
187
        }
188
189
        return $atomics;
190
    }
191
192
    /**
193
     * @return array
194
     */
195
    public function packValue(): array
196
    {
197
        return $this->values;
198
    }
199
200
    /**
201
     * @return int
202
     */
203
    public function count(): int
204
    {
205
        return count($this->values);
206
    }
207
208
    /**
209
     * @return \ArrayIterator
210
     */
211
    public function getIterator()
212
    {
213
        return new \ArrayIterator($this->values);
214
    }
215
216
    /**
217
     * Clone accessor and ensure that it state is updated.
218
     */
219
    public function __clone()
220
    {
221
        $this->solidState = true;
222
        $this->atomics = [];
223
    }
224
225
    /**
226
     * @return array
227
     */
228
    public function __debugInfo()
229
    {
230
        return [
231
            'values'  => $this->packValue(),
232
            'atomics' => $this->buildAtomics('@scalarArray'),
233
        ];
234
    }
235
236
    /**
237
     * @return array
238
     */
239
    public function jsonSerialize()
240
    {
241
        return $this->packValue();
242
    }
243
244
    /**
245
     * Add values matched with filter.
246
     *
247
     * @param mixed $values
248
     */
249
    protected function addValues($values)
250
    {
251
        if (!is_array($values) && !$values instanceof \Traversable) {
252
            //Unable to process values
253
            return;
254
        }
255
256
        foreach ($values as $value) {
257
            //Passing every value thought the filter
258
            $value = $this->filterValue($value);
259
            if (!is_null($value)) {
260
                $this->values[] = $value;
261
            }
262
        }
263
    }
264
265
    /**
266
     * Filter value, MUST return null if value is invalid.
267
     *
268
     * @param mixed $value
269
     *
270
     * @return mixed|null
271
     */
272
    abstract protected function filterValue($value);
273
}