ArrayQuery::addCondition()   D
last analyzed

Complexity

Conditions 16
Paths 89

Size

Total Lines 48
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 48
rs 4.9765
c 0
b 0
f 0
cc 16
eloc 39
nc 89
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2013-2017 2amigos! Consulting Group LLC
4
 * @link http://2amigos.us
5
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
6
 */
7
namespace dosamigos\arrayquery;
8
9
use dosamigos\arrayquery\conditions\Equal;
10
use dosamigos\arrayquery\conditions\GreaterThan;
11
use dosamigos\arrayquery\conditions\GreaterThanOrEqual;
12
use dosamigos\arrayquery\conditions\LessThan;
13
use dosamigos\arrayquery\conditions\LessThanOrEqual;
14
use dosamigos\arrayquery\conditions\Like;
15
use dosamigos\arrayquery\conditions\NotLike;
16
17
/**
18
 * ArrayQuery allows to filter an array by apply conditions.
19
 *
20
 * @author Antonio Ramirez <[email protected]>
21
 * @link http://www.ramirezcobos.com/
22
 * @link http://www.2amigos.us/
23
 * @package dosamigos\arrayquery
24
 */
25
class ArrayQuery
26
{
27
    /**
28
     * @var array the data to search, filter.
29
     */
30
    private $data;
31
    /**
32
     * @var array the array tokenized so user can search multidimensional array by key paths -ie `parentkey.child`
33
     */
34
    private $tokens;
35
    /**
36
     * @var array the conditions to apply
37
     */
38
    private $conditions = [];
39
40
41
    /**
42
     * @param array $array
43
     */
44
    public function __construct(array $array)
45
    {
46
        $this->data = $array;
47
        foreach ($array as $k => $item) {
48
            $this->tokens[$k] = $this->tokenize($item, '', false);
49
        }
50
    }
51
52
    /**
53
     * Adds a condition to apply the array
54
     *
55
     * @param string $key the key to search in the array
56
     * @param mixed $value the value to search. It supports SQL like operator plus some custom ones:
57
     * - `~` or `like` : like `%value%` in SQL
58
     * - `n~` or `nlike` : like `NOT LIKE` in SQL
59
     * @param string $operator the operator. It can be `and` or `or`. If any of `or` matches it will be added to the
60
     * successful results.
61
     *
62
     * @return static
63
     */
64
    public function addCondition($key, $value, $operator = 'and')
65
    {
66
        if ($value != null) { // not accepting null values
67
            $operation = null;
68
            $operator = strcasecmp($operator, 'or') !== 0 ? 'and' : 'or';
69
70
            if (preg_match('/^(?:\s*(<>|<=|>=|<|>|=|~|n~|like|nlike))?(.*)$/i', $value, $matches)) {
71
                $operation = $matches[1];
72
                $value = trim($matches[2]);
73
            }
74
75
            if (empty($operation) || strlen($operation) > 5) {
76
                $operation = '=';
77
            }
78
79
            switch ($operation) {
80
                case '<':
81
                    $condition = new LessThan($value);
82
                    break;
83
                case '>':
84
                    $condition = new GreaterThan($value);
85
                    break;
86
                case '<>':
87
                    $condition = new Equal($value);
88
                    $condition->reverse();
89
                    break;
90
                case '<=':
91
                    $condition = new LessThanOrEqual($value);
92
                    break;
93
                case '>=':
94
                    $condition = new GreaterThanOrEqual($value);
95
                    break;
96
                case '~':
97
                case 'like':
98
                    $condition = new Like($value);
99
                    break;
100
                case 'n~':
101
                case 'nlike':
102
                    $condition = new NotLike($value);
103
                    break;
104
                case '=':
105
                default:
106
                    $condition = new Equal($value);
107
            }
108
            $this->conditions[$operator][] = ['condition' => $condition, 'key' => $key];
109
        }
110
        return $this;
111
    }
112
113
    /**
114
     * Returns the first matched result.
115
     * @return array the first matched result, empty array if none found.
116
     */
117
    public function one()
118
    {
119
        foreach ($this->tokens as $key => $token) {
120
            if (!$this->matches($token)) {
121
                continue;
122
            }
123
            return $this->data[$key];
124
        }
125
        return [];
126
    }
127
128
    /**
129
     * Returns array of matched results.
130
     * @return array the matched results.
131
     */
132
    public function find()
133
    {
134
        if (empty($this->conditions)) {
135
            return $this->data;
136
        }
137
        $results = [];
138
        foreach ($this->tokens as $key => $token) {
139
            if (!$this->matches($token)) {
140
                continue;
141
            }
142
            $results[$key] = $this->data[$key];
143
        }
144
        return $results;
145
    }
146
147
    /**
148
     * Tokenizes the array to ease the search in multidimensional arrays.
149
     *
150
     * @param array $array the array to tokenize
151
     * @param string $prefix the key prefix
152
     * @param bool $addParent whether to add parent value anyway. False
153
     *
154
     * @return array
155
     */
156
    public function tokenize($array, $prefix = '', $addParent = true)
157
    {
158
        $paths = [];
159
        $px = empty($prefix) ? null : $prefix . ".";
160
        foreach ($array as $key => $items) {
161
            if (is_array($items)) {
162
                $addParent && $paths[$px . $key] = $items;
163
                foreach ($this->tokenize($items, $px . $key) as $k => $path) {
164
                    $paths[$k] = $path;
165
                }
166
            } elseif (is_object($items)) {
167
                $addParent && $paths[$px . $key] = $items;
168
                foreach ($this->tokenize(get_object_vars($items), $px . $key) as $k => $path) {
169
                    $paths[$k] = $path;
170
                }
171
            } else {
172
                $paths[$px . $key] = $items;
173
            }
174
        }
175
        return $paths;
176
    }
177
178
    /**
179
     * Checks data against conditions
180
     *
181
     * @param mixed $data the data to match against.
182
     *
183
     * @return bool true if matches condition
184
     */
185
    private function matches($data)
186
    {
187
        $matches = true;
188
        $conditions = isset($this->conditions['and']) ? $this->conditions['and'] : [];
189
        foreach ($conditions as $condition) {
190
            $key = $condition['key'];
191
            $condition = $condition['condition'];
192
            if (!array_key_exists($key, $data) || !$condition->matches($data[$key])) {
193
                $matches = false;
194
                break;
195
            }
196
        }
197
        $conditions = isset($this->conditions['or']) ? $this->conditions['or'] : [];
198
        foreach ($conditions as $condition) {
199
            $key = $condition['key'];
200
            $condition = $condition['condition'];
201
            if (array_key_exists($key, $data) && $condition->matches($data[$key])) {
202
                $matches = true;
203
                break;
204
            }
205
        }
206
        return $matches;
207
    }
208
}
209