Completed
Push — master ( 1a52fa...33fb86 )
by Peter
16:06
created

DataProvider::__construct()   D

Complexity

Conditions 13
Paths 289

Size

Total Lines 66
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 24.9745

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 66
ccs 17
cts 29
cp 0.5862
rs 4.3696
cc 13
eloc 30
nc 289
nop 2
crap 24.9745

How to fix   Long Method    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
/**
4
 * This software package is licensed under AGPL or Commercial license.
5
 *
6
 * @package maslosoft/mangan
7
 * @licence AGPL or Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]>
9
 * @copyright Copyright (c) Maslosoft
10
 * @copyright Copyright (c) Others as mentioned in code
11
 * @link http://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan;
15
16
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
17
use Maslosoft\EmbeDi\EmbeDi;
18
use Maslosoft\Mangan\Exceptions\ManganException;
19
use Maslosoft\Mangan\Interfaces\Criteria\DecoratableInterface;
20
use Maslosoft\Mangan\Interfaces\Criteria\LimitableInterface;
21
use Maslosoft\Mangan\Interfaces\CriteriaAwareInterface;
22
use Maslosoft\Mangan\Interfaces\CriteriaInterface;
23
use Maslosoft\Mangan\Interfaces\DataProviderInterface;
24
use Maslosoft\Mangan\Interfaces\FinderInterface;
25
use Maslosoft\Mangan\Interfaces\PaginationInterface;
26
use Maslosoft\Mangan\Interfaces\SortInterface;
27
use Maslosoft\Mangan\Interfaces\WithCriteriaInterface;
28
use Maslosoft\Mangan\Meta\ManganMeta;
29
use UnexpectedValueException;
30
31
/**
32
 * Mongo document data provider
33
 *
34
 * Implements a data provider based on Document.
35
 *
36
 * DataProvider provides data in terms of Document objects which are
37
 * of class {@link modelClass}. It uses the AR {@link EntityManager::findAll} method
38
 * to retrieve the data from database. The {@link query} property can be used to
39
 * specify various query options, such as conditions, sorting, pagination, etc.
40
 *
41
 * @author Ianaré Sévi
42
 * @author Dariusz Górecki <[email protected]>
43
 * @author Invenzzia Group, open-source division of CleverIT company http://www.invenzzia.org
44
 * @copyright 2011 CleverIT http://www.cleverit.com.pl
45
 * @since v1.0
46
 */
47
class DataProvider implements DataProviderInterface
48
{
49
50
	/**
51
	 * Instance of model
52
	 * @var Document
53
	 * @since v1.0
54
	 */
55
	public $model;
56
57
	/**
58
	 * Finder instance
59
	 * @var FinderInterface
60
	 */
61
	private $finder = null;
62
63
	/**
64
	 * @var CriteriaInterface
65
	 */
66
	private $criteria;
67
68
	/**
69
	 * @var SortInterface
70
	 */
71
	private $sort;
72
	private $data = null;
73
	private $totalItemCount = null;
74
75
	/**
76
	 * Pagination instance
77
	 * @var PaginationInterface
78
	 */
79
	private $pagination = null;
80
81
	/**
82
	 * Constructor.
83
	 * @param mixed $modelClass the model class (e.g. 'Post') or the model finder instance
84
	 * (e.g. <code>Post::model()</code>, <code>Post::model()->published()</code>).
85
	 * @param array $config configuration (name=>value) to be applied as the initial property values of this class.
86
	 * @since v1.0
87
	 */
88 1
	public function __construct($modelClass, $config = [])
89
	{
90 1
		if (is_string($modelClass))
91
		{
92
			$this->model = new $modelClass;
93
		}
94 1
		elseif (is_object($modelClass))
95
		{
96 1
			$this->model = $modelClass;
97
		}
98
		else
99
		{
100
			throw new ManganException('Invalid model type for ' . static::class);
101
		}
102
103 1
		$this->finder = Finder::create($this->model);
104 1
		if ($this->model instanceof WithCriteriaInterface)
105
		{
106
			$this->criteria = $this->model->getDbCriteria();
107
		}
108 1
		elseif ($this->model instanceof CriteriaAwareInterface)
109
		{
110
			$this->criteria = $this->model->getCriteria();
111
		}
112
		else
113
		{
114 1
			$this->criteria = new Criteria();
115
		}
116
117
		// Merge criteria from configuration
118 1
		if (isset($config['criteria']))
119
		{
120
			$this->criteria->mergeWith($config['criteria']);
121
			unset($config['criteria']);
122
		}
123
124
		// Merge limit from configuration
125 1
		if (isset($config['limit']) && $config['limit'] > 0)
126
		{
127
			$this->criteria->setLimit($config['limit']);
128
			unset($config['limit']);
129
		}
130
131
		// Merge sorting from configuration
132 1
		if (isset($config['sort']))
133
		{
134
			// Apply default sorting if criteria does not have sort configured
135
			if (isset($config['sort']['defaultOrder']) && empty($this->criteria->getSort()))
136
			{
137
				$this->criteria->setSort($config['sort']['defaultOrder']);
138
			}
139
			unset($config['sort']);
140
		}
141
142 1
		if (!$this->criteria->getSelect())
143
		{
144 1
			$fields = array_keys(ManganMeta::create($this->model)->fields());
145 1
			$fields = array_fill_keys($fields, true);
146 1
			$this->criteria->setSelect($fields);
147
		}
148
149 1
		foreach ($config as $key => $value)
150
		{
151
			$this->$key = $value;
152
		}
153 1
	}
154
155
	/**
156
	 * Get model used by this data provider
157
	 * @return AnnotatedInterface
158
	 */
159
	public function getModel()
160
	{
161
		return $this->model;
162
	}
163
164
	/**
165
	 * Set model
166
	 * @param AnnotatedInterface $model
167
	 */
168
	public function setModel(AnnotatedInterface $model)
169
	{
170
		$this->model = $model;
1 ignored issue
show
Documentation Bug introduced by
$model is of type object<Maslosoft\Addendu...ces\AnnotatedInterface>, but the property $model was declared to be of type object<Maslosoft\Mangan\Document>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
171
	}
172
173
	/**
174
	 * Returns the criteria.
175
	 * @return Criteria the query criteria
176
	 * @since v1.0
177
	 */
178
	public function getCriteria()
179
	{
180
		// Initialise empty criteria, so it's always available via this method call.
181
		if (empty($this->criteria))
182
		{
183
			$this->criteria = new Criteria;
184
		}
185
		return $this->criteria;
186
	}
187
188
	/**
189
	 * Sets the query criteria.
190
	 * @param CriteriaInterface|array $criteria the query criteria. Array representing the MongoDB query criteria.
191
	 * @since v1.0
192
	 */
193
	public function setCriteria($criteria)
194
	{
195
		if (is_array($criteria))
196
		{
197
			$this->criteria = new Criteria($criteria);
198
		}
199
		elseif ($criteria instanceof CriteriaInterface)
200
		{
201
			$this->criteria = $criteria;
202
		}
203
		if ($this->criteria instanceof DecoratableInterface)
204
		{
205
			$this->criteria->decorateWith($this->getModel());
206
		}
207
	}
208
209
	/**
210
	 * Returns the sort object.
211
	 * @return Sort the sorting object. If this is false, it means the sorting is disabled.
212
	 */
213 1
	public function getSort()
214
	{
215 1
		if ($this->sort === null)
216
		{
217 1
			$this->sort = new Sort;
218 1
			$this->sort->setModel($this->model);
219
		}
220 1
		return $this->sort;
221
	}
222
223
	/**
224
	 * Set sort
225
	 * @param SortInterface $sort
226
	 * @return DataProvider
227
	 */
228
	public function setSort(SortInterface $sort)
229
	{
230
		$this->sort = $sort;
231
		$this->sort->setModel($this->model);
232
		return $this;
233
	}
234
235
	/**
236
	 * Returns the pagination object.
237
	 * @param string $className the pagination object class name, use this param to override default pagination class.
238
	 * @return PaginationInterface|Pagination|false the pagination object. If this is false, it means the pagination is disabled.
239
	 */
240 1
	public function getPagination($className = Pagination::class)
241
	{
242 1
		if ($this->pagination === false)
243
		{
244
			return false;
245
		}
246 1
		if ($this->pagination === null)
247
		{
248 1
			$this->pagination = new $className;
249
		}
250
251
		// FIXME: Attach pagination options if it's array.
252
		// It might be array, when configured via constructor
253 1
		if (is_array($this->pagination))
254
		{
255
			if (empty($this->pagination['class']))
256
			{
257
				$this->pagination['class'] = $className;
258
			}
259
			$this->pagination = EmbeDi::fly()->apply($this->pagination);
260
		}
261 1
		return $this->pagination;
262
	}
263
264
	/**
265
	 * Set pagination
266
	 * @param type $pagination
267
	 */
268
	public function setPagination($pagination)
269
	{
270
		// Disable pagination completely
271
		if (false === $pagination)
272
		{
273
			$this->pagination = false;
1 ignored issue
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object<Maslosoft\Mangan\...es\PaginationInterface> of property $pagination.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
274
			return $this;
275
		}
276
277
		// Configure from array
278
		if (is_array($pagination))
279
		{
280
			if (empty($pagination['class']))
281
			{
282
				$pagination['class'] = Pagination::class;
283
			}
284
			$this->pagination = EmbeDi::fly()->apply($pagination);
285
			return $this;
286
		}
287
288
		// Set object instance
289
		if ($pagination instanceof PaginationInterface)
290
		{
291
			$this->pagination = $pagination;
292
			return $this;
293
		}
294
295
		throw new UnexpectedValueException(sprintf('Expected `false` or `array` or `%s`, got %s', PaginationInterface::class, is_object($pagination) ? get_class($pagination) : gettype($pagination)));
296
	}
297
298
	/**
299
	 * Returns the number of data items in the current page.
300
	 * This is equivalent to <code>count($provider->getData())</code>.
301
	 * When {@link pagination} is set false, this returns the same value as {@link totalItemCount}.
302
	 * @param boolean $refresh whether the number of data items should be re-calculated.
303
	 * @return integer the number of data items in the current page.
304
	 */
305
	public function getItemCount($refresh = false)
306
	{
307
		return count($this->getData($refresh));
308
	}
309
310
	/**
311
	 * Returns the total number of data items.
312
	 * When {@link pagination} is set false, this returns the same value as {@link itemCount}.
313
	 * @return integer total number of possible data items.
314
	 */
315 1
	public function getTotalItemCount()
316
	{
317 1
		if ($this->totalItemCount === null)
318
		{
319 1
			$this->totalItemCount = $this->finder->count($this->criteria);
320
		}
321 1
		return $this->totalItemCount;
322
	}
323
324
	/**
325
	 * Fetches the data from the persistent data storage.
326
	 * @return Document[]|Cursor list of data items
327
	 * @since v1.0
328
	 */
329 1
	protected function fetchData()
330
	{
331 1
		$pagination = $this->getPagination();
332 1
		if ($pagination !== false && $this->criteria instanceof LimitableInterface)
333
		{
334 1
			$pagination->setCount($this->getTotalItemCount());
335 1
			$pagination->apply($this->criteria);
336
		}
337
338 1
		$sort = $this->getSort();
339 1
		if ($sort->isSorted())
340
		{
341
			$this->criteria->setSort($sort);
342
		}
343
344 1
		return $this->finder->findAll($this->criteria);
345
	}
346
347
	/**
348
	 * Returns the data items currently available, ensures that result is at leas empty array
349
	 * @param boolean $refresh whether the data should be re-fetched from persistent storage.
350
	 * @return array the list of data items currently available in this data provider.
351
	 */
352 1
	public function getData($refresh = false)
353
	{
354 1
		if ($this->data === null || $refresh)
355
		{
356 1
			$this->data = $this->fetchData();
357
		}
358 1
		if ($this->data === null)
359
		{
360
			return [];
361
		}
362 1
		return $this->data;
363
	}
364
365
}
366