Completed
Push — master ( 2d3c47...157dd8 )
by Chris
03:55
created

InMemory::execute()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 38
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 30
nc 5
nop 1
dl 0
loc 38
rs 8.439
c 0
b 0
f 0
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
	{
51
		$this->data = $data;
52
		$this->filterer = new Filterer;
53
		$this->sorter = new Sorter;
54
	}
55
	
56
	/**
57
	 * Limit the given data to the given length and offset.
58
	 * 
59
	 * @param array $data
60
	 * @param int   $limit  [optional]
61
	 * @param int   $offset [optional]
62
	 * @return array
63
	 */
64
	protected static function limit(array $data, $limit = 0, $offset = 0)
65
	{
66
		return array_slice($data, $offset, $limit ?: null);
67
	}
68
	
69
	/**
70
	 * Retrieve resource data using the given criteria.
71
	 * 
72
	 * Returns an array of associative arrays.
73
	 * 
74
	 * @param string       $resource
75
	 * @param array        $filter   [optional]
76
	 * @param array|string $order    [optional]
77
	 * @param int          $limit    [optional]
78
	 * @param int          $offset   [optional]
79
	 * @return array
80
	 */
81
	public function read($resource, array $filter = array(), $order = array(), $limit = 0, $offset = 0)
82
	{
83
		if (empty($this->data[$resource])) {
84
			return array();
85
		}
86
		
87
		$data = $this->filterer->filter($this->data[$resource], $filter);
88
		
89
		$data = $this->sorter->sort($data, $order);
90
		
91
		$data = static::limit($data, $limit, $offset);
92
		
93
		return $data;
94
	}
95
	
96
	/**
97
	 * Retrieve specific fields of a resource.
98
	 * 
99
	 * Returns an array of associative arrays.
100
	 * 
101
	 * @param string       $resource
102
	 * @param array|string $fields
103
	 * @param array        $filter   [optional]
104
	 * @param array|string $order    [optional]
105
	 * @param int          $limit    [optional]
106
	 * @param int          $offset   [optional]
107
	 * @return array
108
	 */
109
	public function listing($resource, $fields, array $filter = array(), $order = array(), $limit = 0, $offset = 0)
110
	{
111
		$data = $this->read($resource, $filter, $order, $limit, $offset);
112
		
113
		if (empty($fields) || $fields === '*') {
114
			return $data;
115
		}
116
		
117
		$fields = (array) $fields;
118
		
119
		$result = array();
120
		
121
		foreach ($data as $row) {
122
			$new = array();
123
			
124
			foreach ($row as $field => $value) {
125
				if (in_array($field, $fields)) {
126
					$new[$field] = $value;
127
				}
128
			}
129
			
130
			if (!empty($new)) {
131
				$result[] = $new;
132
			}
133
		}
134
		
135
		return $result;
136
	}
137
	
138
	/**
139
	 * Count the given resource with an optional filter.
140
	 * 
141
	 * @param string $resource
142
	 * @param array  $filter   [optional]
143
	 * @return int
144
	 */
145
	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...
146
	{
147
		if (empty($this->data[$resource])) {
148
			return 0;
149
		}
150
		
151
		return count($this->filterer->filter($this->data[$resource], $filter));
152
	}
153
	
154
	/**
155
	 * Create resource instances in the data store.
156
	 * 
157
	 * @param string $resource
158
	 * @param array  $data
159
	 * @return bool
160
	 */
161
	public function create($resource, $data)
162
	{
163
		if (!isset($this->data[$resource])) {
164
			$this->data[$resource] = array();
165
		}
166
		
167
		$this->data[$resource][] = $data;
168
		
169
		return true;
170
	}
171
	
172
	/**
173
	 * Update resource instances in the data store.
174
	 * 
175
	 * @param string $resource
176
	 * @param array  $data
177
	 * @param array  $filter   [optional]
178
	 * @param int    $limit    [optional]
179
	 * @return int|bool
180
	 */
181
	public function update($resource, $data, array $filter = array(), $limit = 0)
182
	{
183
		if (empty($this->data[$resource])) {
184
			return;
185
		}
186
		
187
		$affected = 0;
188
		
189
		$this->data[$resource] = $this->filterer->map(
190
			$this->data[$resource],
191
			$filter,
192
			function ($row) use ($data, &$affected) {
193
				foreach ($data as $key => $value) {
194
					$row[$key] = $value;
195
				}
196
				
197
				$affected++;
198
				
199
				return $row;
200
			},
201
			$limit
202
		);
203
		
204
		return $affected;
205
	}
206
	
207
	/**
208
	 * Delete resource instances from the data store.
209
	 * 
210
	 * @param string $resource
211
	 * @param array  $filter   [optional]
212
	 * @param int    $limit    [optional]
213
	 * @return int|bool
214
	 */
215
	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...
216
	{
217
		if (empty($this->data[$resource])) {
218
			return;
219
		}
220
		
221
		$this->data[$resource] = $this->filterer->reject($this->data[$resource], $filter);
222
	}
223
	
224
	/**
225
	 * Search for resource data with fields that match the given query and
226
	 * criteria.
227
	 * 
228
	 * @param string       $resource
229
	 * @param string       $query
230
	 * @param array|string $fields
231
	 * @param array        $filter   [optional]
232
	 * @param array|string $order    [optional]
233
	 * @param int          $limit    [optional]
234
	 * @param int          $offset   [optional]
235
	 * @return array
236
	 */
237
	public function search($resource, $query, $fields, array $filter = array(), $order = array(), $limit = null, $offset = 0)
238
	{
239
		if (empty($query) || empty($resource)) {
240
			return $this->read($resource, $filter, $order, $limit, $offset);
241
		}
242
		
243
		$fields = (array) $fields;
244
		$search = array('or' => array());
245
		
246
		foreach ($fields as $field) {
247
			$search['or']["$field like"] = "%$query%";
248
		}
249
		
250
		$filter = array_merge($filter, $search);
251
		
252
		return $this->read($resource, $filter, $order, $limit, $offset);
253
	}
254
	
255
	/**
256
	 * Retrieve the distinct values of the given resource's field.
257
	 * 
258
	 * Returns a flat array of values.
259
	 * 
260
	 * @param string $resource
261
	 * @param string $field
262
	 * @param array  $filter   [optional]
263
	 * @param array  $order    [optional]
264
	 * @param int    $limit    [optional]
265
	 * @param int    $offset   [optional]
266
	 * @return array
267
	 */
268
	public function distinct($resource, $field, array $filter = array(), $order = array(), $limit = 0, $offset = 0)
269
	{
270
		$list = array();
271
		
272
		$listing = $this->listing($resource, $field, $filter, $order, $limit, $offset);
273
		
274
		foreach ($listing as $item) {
275
			$list[] = $item[$field];
276
		}
277
		
278
		return array_unique($list);
279
	}
280
	
281
	/**
282
	 * Execute the given query.
283
	 * 
284
	 * @param Query $query
285
	 * @return Result
286
	 */
287
	public function run(Query $query)
288
	{
289
		$data = array();
290
		$info = array();
291
		
292
		switch ($query->type) {
293
			case Query::CREATE:
294
				$this->create($query->resource, $query->data);
295
				break;
296
			case Query::READ:
297
				$data = $this->listing(
298
					$query->resource,
299
					$query->fields,
300
					$query->filter,
301
					$query->order,
302
					$query->limit,
303
					$query->offset
304
				);
305
				break;
306
			case Query::UPDATE:
307
				$info['affected'] = $this->update(
308
					$query->resource,
309
					$query->data,
310
					$query->filter,
311
					$query->limit
312
				);
313
				break;
314
			case Query::DELETE:
315
				$this->delete(
316
					$query->resource,
317
					$query->filter,
318
					$query->limit
319
				);
320
				break;
321
		}
322
		
323
		return new Result($query, $data, $info);
324
	}
325
	
326
	/**
327
	 * Open a query on the given resource.
328
	 * 
329
	 * @param string       $resource
330
	 * @param array|string $fields   [optional]
331
	 * @return Query\Builder
332
	 */
333
	public function query($resource, $fields = array())
334
	{
335
		return new Query\Builder(new Query($resource, (array) $fields), $this);
336
	}
337
	
338
	/**
339
	 * Retrieve the error that occured with the last operation.
340
	 * 
341
	 * Returns false if there was no error.
342
	 * 
343
	 * @return string|bool
344
	 */
345
	public function error()
346
	{
347
		return false;
348
	}
349
}
350