Completed
Push — master ( 4dddb7...e1d674 )
by Peter
08:56
created

DataProvider::getPagination()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 6.6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 23
ccs 6
cts 10
cp 0.6
rs 8.5906
cc 5
eloc 10
nc 7
nop 1
crap 6.6
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\CriteriaInterface;
22
use Maslosoft\Mangan\Interfaces\DataProviderInterface;
23
use Maslosoft\Mangan\Interfaces\FinderInterface;
24
use Maslosoft\Mangan\Interfaces\PaginationInterface;
25
use Maslosoft\Mangan\Interfaces\SortInterface;
26
use Maslosoft\Mangan\Interfaces\WithCriteriaInterface;
27
use Maslosoft\Mangan\Meta\ManganMeta;
28
29
/**
30
 * Mongo document data provider
31
 *
32
 * Implements a data provider based on Document.
33
 *
34
 * DataProvider provides data in terms of Document objects which are
35
 * of class {@link modelClass}. It uses the AR {@link EntityManager::findAll} method
36
 * to retrieve the data from database. The {@link query} property can be used to
37
 * specify various query options, such as conditions, sorting, pagination, etc.
38
 *
39
 * @author Ianaré Sévi
40
 * @author Dariusz Górecki <[email protected]>
41
 * @author Invenzzia Group, open-source division of CleverIT company http://www.invenzzia.org
42
 * @copyright 2011 CleverIT http://www.cleverit.com.pl
43
 * @since v1.0
44
 */
45
class DataProvider implements DataProviderInterface
46
{
47
48
	/**
49
	 * Instance of model
50
	 * @var Document
51
	 * @since v1.0
52
	 */
53
	public $model;
54
55
	/**
56
	 * Finder instance
57
	 * @var FinderInterface
58
	 */
59
	private $finder = null;
60
61
	/**
62
	 * @var CriteriaInterface
63
	 */
64
	private $criteria;
65
66
	/**
67
	 * @var SortInterface
68
	 */
69
	private $sort;
70
	private $data = null;
71
	private $totalItemCount = null;
72
73
	/**
74
	 * Pagination instance
75
	 * @var PaginationInterface
76
	 */
77
	private $pagination = null;
78
79
	/**
80
	 * Constructor.
81
	 * @param mixed $modelClass the model class (e.g. 'Post') or the model finder instance
82
	 * (e.g. <code>Post::model()</code>, <code>Post::model()->published()</code>).
83
	 * @param array $config configuration (name=>value) to be applied as the initial property values of this class.
84
	 * @since v1.0
85
	 */
86 1
	public function __construct($modelClass, $config = [])
87
	{
88 1
		if (is_string($modelClass))
89
		{
90
			$this->model = new $modelClass;
91
		}
92 1
		elseif (is_object($modelClass))
93
		{
94 1
			$this->model = $modelClass;
95
		}
96
		else
97
		{
98
			throw new ManganException('Invalid model type for ' . __CLASS__);
99
		}
100
101 1
		$this->finder = Finder::create($this->model);
102 1
		if ($this->model instanceof WithCriteriaInterface)
103
		{
104
			$this->criteria = $this->model->getDbCriteria();
105
		}
106
		else
107
		{
108 1
			$this->criteria = new Criteria();
109
		}
110
111
		// Merge criteria from configuration
112 1
		if (isset($config['criteria']))
113
		{
114
			$this->criteria->mergeWith($config['criteria']);
115
			unset($config['criteria']);
116
		}
117
118
		// Merge limit from configuration
119 1
		if (isset($config['limit']) && $config['limit'] > 0)
120
		{
121
			$this->criteria->setLimit($config['limit']);
122
			unset($config['limit']);
123
		}
124
125
		// Merge sorting from configuration
126 1
		if (isset($config['sort']))
127
		{
128
			// Apply default sorting if criteria does not have sort configured
129
			if (isset($config['sort']['defaultOrder']) && empty($this->criteria->getSort()))
130
			{
131
				$this->criteria->setSort($config['sort']['defaultOrder']);
132
			}
133
			unset($config['sort']);
134
		}
135
136 1
		if (!$this->criteria->getSelect())
137
		{
138 1
			$fields = array_keys(ManganMeta::create($this->model)->fields());
139 1
			$fields = array_fill_keys($fields, true);
140 1
			$this->criteria->setSelect($fields);
141
		}
142
143 1
		foreach ($config as $key => $value)
144
		{
145
			$this->$key = $value;
146
		}
147 1
	}
148
149
	/**
150
	 * Get model used by this dataprovider
151
	 * @return AnnotatedInterface
152
	 */
153
	public function getModel()
154
	{
155
		return $this->model;
156
	}
157
158
	/**
159
	 * Returns the criteria.
160
	 * @return Criteria the query criteria
161
	 * @since v1.0
162
	 */
163
	public function getCriteria()
164
	{
165
		// Initialise empty criteria, so it's always available via this method call.
166
		if (empty($this->criteria))
167
		{
168
			$this->criteria = new Criteria;
169
		}
170
		return $this->criteria;
171
	}
172
173
	/**
174
	 * Sets the query criteria.
175
	 * @param CriteriaInterface|array $criteria the query criteria. Array representing the MongoDB query criteria.
176
	 * @since v1.0
177
	 */
178
	public function setCriteria($criteria)
179
	{
180
		if (is_array($criteria))
181
		{
182
			$this->criteria = new Criteria($criteria);
183
		}
184
		elseif ($criteria instanceof CriteriaInterface)
185
		{
186
			$this->criteria = $criteria;
187
		}
188
		if ($this->criteria instanceof DecoratableInterface)
189
		{
190
			$this->criteria->decorateWith($this->getModel());
191
		}
192
	}
193
194
	/**
195
	 * Returns the sort object.
196
	 * @return Sort the sorting object. If this is false, it means the sorting is disabled.
197
	 */
198 1
	public function getSort()
199
	{
200 1
		if ($this->sort === null)
201
		{
202 1
			$this->sort = new Sort;
203 1
			$this->sort->setModel($this->model);
204
		}
205 1
		return $this->sort;
206
	}
207
208
	/**
209
	 * Set sort
210
	 * @param SortInterface $sort
211
	 * @return DataProvider
212
	 */
213
	public function setSort(SortInterface $sort)
214
	{
215
		$this->sort = $sort;
216
		$this->sort->setModel($this->model);
217
		return $this;
218
	}
219
220
	/**
221
	 * Returns the pagination object.
222
	 * @param string $className the pagination object class name, use this param to override default pagination class.
223
	 * @return Pagination|false the pagination object. If this is false, it means the pagination is disabled.
224
	 */
225 1
	public function getPagination($className = Pagination::class)
226
	{
227 1
		if ($this->pagination === false)
228
		{
229
			return false;
230
		}
231 1
		if ($this->pagination === null)
232
		{
233 1
			$this->pagination = new $className;
234
		}
235
236
		// FIXME: Attach pagination options if it's array.
237
		// It might be array, when configured via constructor
238 1
		if (is_array($this->pagination))
239
		{
240
			if (empty($this->pagination['class']))
241
			{
242
				$this->pagination['class'] = $className;
243
			}
244
			$this->pagination = EmbeDi::fly()->apply($this->pagination);
245
		}
246 1
		return $this->pagination;
247
	}
248
249
	/**
250
	 * Returns the number of data items in the current page.
251
	 * This is equivalent to <code>count($provider->getData())</code>.
252
	 * When {@link pagination} is set false, this returns the same value as {@link totalItemCount}.
253
	 * @param boolean $refresh whether the number of data items should be re-calculated.
254
	 * @return integer the number of data items in the current page.
255
	 */
256
	public function getItemCount($refresh = false)
257
	{
258
		return count($this->getData($refresh));
259
	}
260
261
	/**
262
	 * Returns the total number of data items.
263
	 * When {@link pagination} is set false, this returns the same value as {@link itemCount}.
264
	 * @return integer total number of possible data items.
265
	 */
266 1
	public function getTotalItemCount()
267
	{
268 1
		if ($this->totalItemCount === null)
269
		{
270 1
			$this->totalItemCount = $this->finder->count($this->criteria);
271
		}
272 1
		return $this->totalItemCount;
273
	}
274
275
	/**
276
	 * Fetches the data from the persistent data storage.
277
	 * @return Document[]|Cursor list of data items
278
	 * @since v1.0
279
	 */
280 1
	protected function fetchData()
281
	{
282 1
		$pagination = $this->getPagination();
283 1
		if ($pagination !== false && $this->criteria instanceof LimitableInterface)
284
		{
285 1
			$pagination->setCount($this->getTotalItemCount());
286 1
			$pagination->apply($this->criteria);
287
		}
288
289 1
		$sort = $this->getSort();
290 1
		if ($sort->isSorted())
291
		{
292
			$this->criteria->setSort($sort);
293
		}
294
295 1
		return $this->finder->findAll($this->criteria);
296
	}
297
298
	/**
299
	 * Returns the data items currently available, ensures that result is at leas empty array
300
	 * @param boolean $refresh whether the data should be re-fetched from persistent storage.
301
	 * @return array the list of data items currently available in this data provider.
302
	 */
303 1
	public function getData($refresh = false)
304
	{
305 1
		if ($this->data === null || $refresh)
306
		{
307 1
			$this->data = $this->fetchData();
308
		}
309 1
		if ($this->data === null)
310
		{
311
			return [];
312
		}
313 1
		return $this->data;
314
	}
315
316
}
317