Completed
Push — master ( 507c4a...51e00b )
by Peter
07:40
created

PkManager::compare()   C

Complexity

Conditions 11
Paths 15

Size

Total Lines 52
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 13.2061

Importance

Changes 0
Metric Value
dl 0
loc 52
ccs 14
cts 19
cp 0.7368
rs 5.9999
c 0
b 0
f 0
cc 11
eloc 19
nc 15
nop 2
crap 13.2061

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 https://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan\Helpers;
15
16
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
17
use Maslosoft\Mangan\Criteria;
18
use Maslosoft\Mangan\Exceptions\CriteriaException;
19
use Maslosoft\Mangan\Helpers\PkManager;
20
use Maslosoft\Mangan\Helpers\Sanitizer\Sanitizer;
21
use Maslosoft\Mangan\Interfaces\CriteriaInterface;
22
use Maslosoft\Mangan\Meta\ManganMeta;
23
use MongoId;
24
use UnexpectedValueException;
25
26
/**
27
 * Primary key manager
28
 *
29
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
30
 */
31
class PkManager
32
{
33
34
	/**
35
	 * Prepare multi pk criteria
36
	 * @param AnnotatedInterface $model
37
	 * @param mixed[] $pkValues
38
	 * @param CriteriaInterface|null $criteria
39
	 */
40 9
	public static function prepareAll($model, $pkValues, CriteriaInterface $criteria = null)
41
	{
42 9
		if (null === $criteria)
43
		{
44
			$criteria = new Criteria();
45
		}
46 9
		assert($criteria instanceof Criteria, new UnexpectedValueException(sprintf("Unsupported criteria class, currently only `%s` is supported, `%s` given", Criteria::class, get_class($criteria))));
47 9
		$conditions = [];
48 9
		foreach ($pkValues as $pkValue)
49
		{
50 9
			$c = PkManager::prepare($model, $pkValue);
51 9
			foreach ($c->getConditions() as $field => $value)
52
			{
53 9
				$conditions[$field][] = $value;
54
			}
55
		}
56 9
		foreach ($conditions as $field => $value)
57
		{
58 9
			$criteria->addCond($field, 'in', $value);
59
		}
60 9
		return $criteria;
61
	}
62
63
	/**
64
	 * Prepare pk criteria from user provided data
65
	 * @param AnnotatedInterface $model
66
	 * @param mixed|mixed[] $pkValue
67
	 * @return Criteria
68
	 * @throws CriteriaException
69
	 */
70 93
	public static function prepare(AnnotatedInterface $model, $pkValue)
71
	{
72 93
		$pkField = self::getPkKeys($model);
73 93
		$criteria = new Criteria();
74
75 93
		if (is_array($pkField))
76
		{
77 5
			foreach ($pkField as $name)
78
			{
79 5
				if (!array_key_exists($name, $pkValue))
80
				{
81
					throw new CriteriaException(sprintf('Composite primary key field `%s` not specied for model `%s`, required fields: `%s`', $name, get_class($model), implode('`, `', $pkField)));
82
				}
83 5
				self::_prepareField($model, $name, $pkValue[$name], $criteria);
84
			}
85
		}
86
		else
87
		{
88 88
			self::_prepareField($model, $pkField, $pkValue, $criteria);
89
		}
90 93
		return $criteria;
91
	}
92
93
	/**
94
	 * Create pk criteria from model data
95
	 * @param AnnotatedInterface $model
96
	 * @return Criteria
97
	 */
98 81
	public static function prepareFromModel(AnnotatedInterface $model)
99
	{
100 81
		return self::prepare($model, self::getFromModel($model));
101
	}
102
103
	/**
104
	 * Get primary key from model
105
	 * @param AnnotatedInterface $model
106
	 * @return MongoId|mixed|mixed[]
107
	 */
108 82
	public static function getFromModel(AnnotatedInterface $model)
109
	{
110 82
		$pkField = self::getPkKeys($model);
111 82
		$pkValue = [];
112 82
		$sanitizer = new Sanitizer($model);
113 82
		if (is_array($pkField))
114
		{
115 2
			foreach ($pkField as $name)
116
			{
117 2
				$pkValue[$name] = $sanitizer->write($name, $model->$name);
118
			}
119
		}
120
		else
121
		{
122 80
			$pkValue = $sanitizer->write($pkField, $model->$pkField);
123
		}
124 82
		return $pkValue;
125
	}
126
127
	/**
128
	 * Get primary key(s).
129
	 *
130
	 * Might return single string value for one primary key, or array
131
	 * for composite keys.
132
	 *
133
	 *
134
	 * @param AnnotatedInterface $model
135
	 * @return string|string[]
136
	 */
137 130
	public static function getPkKeys(AnnotatedInterface $model)
138
	{
139 130
		return ManganMeta::create($model)->type()->primaryKey ?: '_id';
140
	}
141
142
	/**
143
	 * Get pk criteria from raw array
144
	 * @param mixed[] $data
145
	 * @param AnnotatedInterface $model
146
	 * @return mixed[]
147
	 */
148 5
	public static function getFromArray($data, AnnotatedInterface $model)
149
	{
150 5
		$pkField = ManganMeta::create($model)->type()->primaryKey ?: '_id';
151 5
		$pkValue = [];
152 5
		$sanitizer = new Sanitizer($model);
153 5
		if (is_array($pkField))
154
		{
155
			foreach ($pkField as $name)
156
			{
157
				$pkValue[$name] = $sanitizer->write($name, $data[$name]);
158
			}
159
		}
160
		else
161
		{
162 5
			$pkValue = $sanitizer->write($pkField, $data[$pkField]);
163
		}
164 5
		return $pkValue;
165
	}
166
167
	/**
168
	 * Apply pk value to model
169
	 * @param AnnotatedInterface $model
170
	 * @param MongoId|mixed|mixed[] $pkValue
171
	 * @return mixed Primary key value
172
	 * @throws CriteriaException
173
	 */
174 12
	public static function applyToModel(AnnotatedInterface $model, $pkValue)
175
	{
176 12
		$pkField = self::getPkKeys($model);
177
178 12
		$sanitizer = new Sanitizer($model);
179 12
		if (is_array($pkField))
180
		{
181
			foreach ($pkField as $name)
182
			{
183
				if (!array_key_exists($name, $pkValue))
184
				{
185
					throw new CriteriaException(sprintf('Composite primary key field `%s` not specied for model `%s`, required fields: `%s`', $name, get_class($model), implode('`, `', $pkField)));
186
				}
187
				$model->$name = $sanitizer->read($name, $pkValue[$name]);
188
			}
189
		}
190
		else
191
		{
192 12
			$model->$pkField = $sanitizer->read($pkField, $pkValue);
193
		}
194 12
		return $pkValue;
195
	}
196
197
	/**
198
	 * Compare primary keys. For both params primary keys values or models can be used.
199
	 * Example use:
200
	 * <pre>
201
	 * <code>
202
	 * $model = new Model();
203
	 * $pk = ['_id' => new MongoId()];
204
	 * PkManager::compare($model, $pk);
205
	 *
206
	 * $pk1 = ['keyOne' => 1, 'keyTwo' => 2];
207
	 * $pk2 = ['keyOne' => 1, 'keyTwo' => 2];;
208
	 * PkManager::compare($pk1, $pk2);
209
	 *
210
	 * $model1 = new Model();
211
	 * $model2 = new Model();
212
	 * PkManager::compare($model1, $model2);
213
	 *
214
	 * </code>
215
	 * </pre>
216
	 * @param AnnotatedInterface|mixed[] $source
217
	 * @param AnnotatedInterface|mixed[] $target
218
	 * @return boolean true if pk's points to same document
219
	 */
220 8
	public static function compare($source, $target)
221
	{
222
		// Check if both params are models
223 8
		if ($source instanceof AnnotatedInterface && $target instanceof AnnotatedInterface)
224
		{
225
			// If different types return false
226 3
			if (!$source instanceof $target)
227
			{
228
				return false;
229
			}
230
		}
231
232 8
		$src = self::_compareNormalize($source);
233 8
		$trg = self::_compareNormalize($target);
234
235
		// Different pk's
236 8
		if (count($src) !== count($trg))
237
		{
238
			return false;
239
		}
240
241
		// Different pk keys
242 8
		if (array_keys($src) !== array_keys($trg))
243
		{
244
			return false;
245
		}
246
247
		// Compare values
248 8
		foreach ($src as $name => $srcVal)
249
		{
250
			// This is safe as keys are checked previously
251 8
			$trgVal = $trg[$name];
252
253
			// Special case for mongo id
254 8
			if ($srcVal instanceof \MongoId || $trgVal instanceof \MongoId)
255
			{
256 8
				if ((string) $srcVal !== (string) $trgVal)
257
				{
258 8
					return false;
259
				}
260 7
				continue;
261
			}
262
263
			// Finally compare values
264
			if ($srcVal !== $trgVal)
265
			{
266
				return false;
267
			}
268
		}
269
270 7
		return true;
271
	}
272
273 8
	private static function _compareNormalize($value)
274
	{
275 8
		if ($value instanceof AnnotatedInterface)
276
		{
277 3
			$value = self::getFromModel($value);
278
		}
279
280
		// Simple pk
281 8
		if (!is_array($value))
282
		{
283 8
			return [$value];
284
		}
285
286
		// Composite pk
287
		return $value;
288
	}
289
290
	/**
291
	 * Create pk criteria for single field
292
	 * @param AnnotatedInterface $model Model instance
293
	 * @param string $name
294
	 * @param mixed $value
295
	 * @param Criteria $criteria
296
	 */
297 93
	private static function _prepareField(AnnotatedInterface $model, $name, $value, Criteria &$criteria)
298
	{
299 93
		$sanitizer = new Sanitizer($model);
300 93
		$criteria->addCond($name, '==', $sanitizer->write($name, $value));
301 93
	}
302
303
}
304