Completed
Push — master ( fe6ccb...798675 )
by Chris
02:47
created

InMemory::distinct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
nop 6
1
<?php
2
namespace Darya\Storage;
3
4
use Darya\Storage\Aggregational;
5
use Darya\Storage\Filterer;
6
use Darya\Storage\Readable;
7
use Darya\Storage\Modifiable;
8
use Darya\Storage\Query;
9
use Darya\Storage\Queryable;
10
use Darya\Storage\Result;
11
use Darya\Storage\Searchable;
12
use Darya\Storage\Sorter;
13
14
/**
15
 * Darya's in-memory storage interface.
16
 * 
17
 * Useful for unit testing!
18
 * 
19
 * @author Chris Andrew <[email protected]>
20
 */
21
class InMemory implements Readable, Modifiable, Searchable, Aggregational, Queryable {
22
	
23
	/**
24
	 * The in-memory data.
25
	 * 
26
	 * @var array
27
	 */
28
	protected $data;
29
	
30
	/**
31
	 * Filters results in-memory.
32
	 * 
33
	 * @var Filterer
34
	 */
35
	protected $filterer;
36
	
37
	/**
38
	 * Sorts results in-memory.
39
	 * 
40
	 * @var Sorter
41
	 */
42
	protected $sorter;
43
	
44
	/**
45
	 * Create a new in-memory storage interface with the given data.
46
	 * 
47
	 * @param array $data [optional]
48
	 */
49
	public function __construct(array $data = array()) {
50
		$this->data = $data;
51
		$this->filterer = new Filterer;
52
		$this->sorter = new Sorter;
53
	}
54
	
55
	/**
56
	 * Limit the given data to the given length and offset.
57
	 * 
58
	 * @param array $data
59
	 * @param int   $limit  [optional]
60
	 * @param int   $offset [optional]
61
	 * @return array
62
	 */
63
	protected static function limit(array $data, $limit = 0, $offset = 0) {
64
		return array_slice($data, $offset, $limit ?: null);
65
	}
66
	
67
	/**
68
	 * Retrieve resource data using the given criteria.
69
	 * 
70
	 * Returns an array of associative arrays.
71
	 * 
72
	 * @param string       $resource
73
	 * @param array        $filter   [optional]
74
	 * @param array|string $order    [optional]
75
	 * @param int          $limit    [optional]
76
	 * @param int          $offset   [optional]
77
	 * @return array
78
	 */
79
	public function read($resource, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
80
		if (empty($this->data[$resource])) {
81
			return array();
82
		}
83
		
84
		$data = $this->filterer->filter($this->data[$resource], $filter);
85
		
86
		$data = $this->sorter->sort($data, $order);
87
		
88
		$data = static::limit($data, $limit, $offset);
89
		
90
		return $data;
91
	}
92
	
93
	/**
94
	 * Retrieve specific fields of a resource.
95
	 * 
96
	 * Returns an array of associative arrays.
97
	 * 
98
	 * @param string       $resource
99
	 * @param array|string $fields
100
	 * @param array        $filter   [optional]
101
	 * @param array|string $order    [optional]
102
	 * @param int          $limit    [optional]
103
	 * @param int          $offset   [optional]
104
	 * @return array
105
	 */
106
	public function listing($resource, $fields, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
107
		$data = $this->read($resource, $filter, $order, $limit, $offset);
108
		
109
		if (empty($fields) || $fields === '*') {
110
			return $data;
111
		}
112
		
113
		$fields = (array) $fields;
114
		
115
		$result = array();
116
		
117
		foreach ($data as $row) {
118
			$new = array();
119
			
120
			foreach ($row as $field => $value) {
121
				if (in_array($field, $fields)) {
122
					$new[$field] = $value;
123
				}
124
			}
125
			
126
			if (!empty($new)) {
127
				$result[] = $new;
128
			}
129
		}
130
		
131
		return $result;
132
	}
133
	
134
	/**
135
	 * Count the given resource with an optional filter.
136
	 * 
137
	 * @param string $resource
138
	 * @param array  $filter   [optional]
139
	 * @return int
140
	 */
141 View Code Duplication
	public function count($resource, array $filter = array()) {
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...
142
		if (empty($this->data[$resource])) {
143
			return 0;
144
		}
145
		
146
		return count($this->filterer->filter($this->data[$resource], $filter));
147
	}
148
	
149
	/**
150
	 * Create resource instances in the data store.
151
	 * 
152
	 * @param string $resource
153
	 * @param array  $data
154
	 * @return bool
155
	 */
156
	public function create($resource, $data) {
157
		if (!isset($this->data[$resource])) {
158
			$this->data[$resource] = array();
159
		}
160
		
161
		$this->data[$resource][] = $data;
162
		
163
		return true;
164
	}
165
	
166
	/**
167
	 * Update resource instances in the data store.
168
	 * 
169
	 * @param string $resource
170
	 * @param array  $data
171
	 * @param array  $filter   [optional]
172
	 * @param int    $limit    [optional]
173
	 * @return int|bool
174
	 */
175
	public function update($resource, $data, array $filter = array(), $limit = 0) {
176
		if (empty($this->data[$resource])) {
177
			return;
178
		}
179
		
180
		$affected = 0;
181
		
182
		$this->data[$resource] = $this->filterer->map(
183
			$this->data[$resource],
184
			$filter,
185
			function ($row) use ($data, &$affected) {
186
				foreach ($data as $key => $value) {
187
					$row[$key] = $value;
188
				}
189
				
190
				$affected++;
191
				
192
				return $row;
193
			},
194
			$limit
195
		);
196
		
197
		return $affected;
198
	}
199
	
200
	/**
201
	 * Delete resource instances from the data store.
202
	 * 
203
	 * @param string $resource
204
	 * @param array  $filter   [optional]
205
	 * @param int    $limit    [optional]
206
	 * @return int|bool
207
	 */
208 View Code Duplication
	public function delete($resource, array $filter = array(), $limit = null) {
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...
209
		if (empty($this->data[$resource])) {
210
			return;
211
		}
212
		
213
		$this->data[$resource] = $this->filterer->reject($this->data[$resource], $filter);
214
	}
215
	
216
	/**
217
	 * Search for resource data with fields that match the given query and
218
	 * criteria.
219
	 * 
220
	 * @param string       $resource
221
	 * @param string       $query
222
	 * @param array|string $fields
223
	 * @param array        $filter   [optional]
224
	 * @param array|string $order    [optional]
225
	 * @param int          $limit    [optional]
226
	 * @param int          $offset   [optional]
227
	 * @return array
228
	 */
229
	public function search($resource, $query, $fields, array $filter = array(), $order = array(), $limit = null, $offset = 0) {
230
		if (empty($query) || empty($resource)) {
231
			return $this->read($resource, $filter, $order, $limit, $offset);
232
		}
233
		
234
		$fields = (array) $fields;
235
		$search = array('or' => array());
236
		
237
		foreach ($fields as $field) {
238
			$search['or']["$field like"] = "%$query%";
239
		}
240
		
241
		$filter = array_merge($filter, $search);
242
		
243
		return $this->read($resource, $filter, $order, $limit, $offset);
244
	}
245
	
246
	/**
247
	 * Retrieve the distinct values of the given resource's field.
248
	 * 
249
	 * Returns a flat array of values.
250
	 * 
251
	 * @param string $resource
252
	 * @param string $field
253
	 * @param array  $filter   [optional]
254
	 * @param array  $order    [optional]
255
	 * @param int    $limit    [optional]
256
	 * @param int    $offset   [optional]
257
	 * @return array
258
	 */
259
	public function distinct($resource, $field, array $filter = array(), $order = array(), $limit = 0, $offset = 0) {
260
		$list = array();
261
		
262
		$listing = $this->listing($resource, $field, $filter, $order, $limit, $offset);
263
		
264
		foreach ($listing as $item) {
265
			$list[] = $item[$field];
266
		}
267
		
268
		return array_unique($list);
269
	}
270
	
271
	/**
272
	 * Execute the given query.
273
	 * 
274
	 * @param Query $query
275
	 * @return Result
276
	 */
277
	public function execute(Query $query)
278
	{
279
		$data = array();
280
		$info = array();
281
		
282
		switch ($query->type) {
283
			case Query::CREATE:
284
				$this->create($query->resource, $query->data);
285
				break;
286
			case Query::READ:
287
				$data = $this->listing(
288
					$query->resource,
289
					$query->fields,
290
					$query->filter,
291
					$query->order,
292
					$query->limit,
293
					$query->offset
294
				);
295
				break;
296
			case Query:UPDATE:
1 ignored issue
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
297
				$info['affected'] = $this->update(
298
					$query->resource,
299
					$query->data,
300
					$query->filter,
301
					$query->limit
302
				);
303
				break;
304
			case Query::DELETE:
305
				$this->delete(
306
					$query->resource,
307
					$query->filter,
308
					$query->limit
309
				);
310
				break;
311
		}
312
		
313
		return new Result($query, $data, $info);
314
	}
315
	
316
	/**
317
	 * Open a query on the given resource.
318
	 * 
319
	 * @param string       $resource
320
	 * @param array|string $fields   [optional]
321
	 * @return Query\Builder
322
	 */
323
	public function query($resource, $fields = array())
324
	{
325
		return new Query\Builder(new Query($resource, (array) $fields), $this);
326
	}
327
	
328
	/**
329
	 * Retrieve the error that occured with the last operation.
330
	 * 
331
	 * Returns false if there was no error.
332
	 * 
333
	 * @return string|bool
334
	 */
335
	public function error() {
336
		return false;
337
	}
338
	
339
}
340