Completed
Branch feature/pre-split (a42d1e)
by Anton
03:22
created

AbstractArray::has()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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