Completed
Push — master ( 43c72a...f32624 )
by Peter
05:47
created

PkManager::getPkKeys()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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