DdsObjectMapManager   F
last analyzed

Complexity

Total Complexity 99

Size/Duplication

Total Lines 532
Duplicated Lines 0 %

Test Coverage

Coverage 89.01%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 99
eloc 287
dl 0
loc 532
rs 2
c 2
b 0
f 0
ccs 251
cts 282
cp 0.8901

11 Methods

Rating   Name   Duplication   Size   Complexity  
A makeMapLookupRequest() 0 24 5
D getMapLookupByLevel() 0 95 18
A resetMapRequests() 0 4 1
F getObjectMap() 0 125 29
B convertMapRequestResults() 0 45 11
A getMapChainLookupResults() 0 14 3
B getMapLookupResults() 0 24 8
D addRequest() 0 53 14
A makeMapChainLookupRequest() 0 23 4
A clearMapRequests() 0 4 1
A commitMapRequests() 0 17 5

How to fix   Complexity   

Complex Class

Complex classes like DdsObjectMapManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DdsObjectMapManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.newicon.net/neon
4
 * @copyright Copyright (c) 2016 Newicon Ltd
5
 * @license http://www.newicon.net/neon/license/
6
 */
7
8
namespace neon\daedalus\services\ddsManager;
9
10
use neon\daedalus\interfaces\IDdsObjectMapManagement;
11
12
use neon\daedalus\services\ddsManager\DdsCore;
13
use neon\daedalus\services\ddsManager\models\DdsMember;
14
use neon\daedalus\services\ddsManager\models\DdsObject;
15
use neon\daedalus\services\ddsManager\models\DdsLink;
16
use neon\core\helpers\Arr;
17
18
class DdsObjectMapManager extends DdsCore implements IDdsObjectMapManagement
19
{
20
	/**
21
	 * Caches for results from queries so can protect against over-zealous
22
	 * requests to the database within one call
23
	 * @var array
24
	 */
25
	private static $_mapResultsCache=[];
26
	/**
27
	 * The set of map requests
28
	 * An array with the requests and the maximum number in the chain
29
	 * @var array
30
	 */
31
	private static $_mapRequests = ['keys'=>[], 'chains'=>[], 'depth'=>0];
32
	/**
33
	 * The set of map results
34
	 * @var array of ['key'=>[chain]
35
	 */
36
	private static $_mapResults = [];
37
	/**
38
	 * The set of multiMapRequests which store individual map requests
39
	 * @var array of ['requestKey' => ['subRequestKey']]
40
	 */
41
	private static $_multiMapRequests = [];
42
43
	private static $_defaultMapChain = '__DEFAULT_MAP_CHAIN__';
44
45
	/**
46
	 * @inheritdoc
47
	 */
48 4
	public function getObjectMap($classType, $filters=[], $fields=[], $start=0, $length=1000, $includeDeleted=false)
49
	{
50 4
		if (!$classType)
51
			throw new \InvalidArgumentException("Invalid classType '$classType' in getObjectMap. Correct calling code");
52
53 4
		$cacheKey = md5(serialize(func_get_args()));
54 4
		if (empty(self::$_mapResultsCache[$cacheKey])) {
55
			try {
56 4
				$limit = ['start' => max(0, (integer)$start), 'length' => min(1000, (integer)$length)];
57 4
				$classType = $this->canonicaliseRef($classType);
58 4
				$fieldsClause = '';
59 4
				$linkJoins = [];
60 4
				if ($fields) {
61 4
					$getFields = [];
62 4
					$field2link = [];
63 4
					$linkedMemberRows = DdsMember::find()
64 4
						->select(['member_ref', 'link_class', 'data_type_ref'])
65 4
						->where(['class_type'=>$classType, 'data_type_ref' => ['link_uni', 'link_multi'], 'member_ref'=>$fields, 'deleted' => 0])
66 4
						->asArray()->all();
67 4
					$linkedMembers = Arr::index($linkedMemberRows, 'member_ref');
68 4
					if ($linkedMemberRows) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $linkedMemberRows of type yii\db\ActiveRecordInterface[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
69 2
						$linkedMemberClasses = DdsMember::find()
70 2
							->select(['class_type', 'member_ref'])
71 2
							->where(['class_type'=>Arr::pluck($linkedMemberRows, 'link_class'), 'map_field'=>1, 'deleted' => 0])
72 2
							->asArray()->all();
73 2
						$class2Member = Arr::index($linkedMemberClasses, 'class_type');
74 2
						foreach ($linkedMembers as $lm) {
75
							// only try to link through to tables that are within daedalus
76 2
							if (isset($class2Member[$lm['link_class']]))
77 2
								$field2link[$lm['member_ref']] = $class2Member[$lm['link_class']];
78
						}
79
					}
80 4
					foreach ($fields as $field) {
81 4
						if (array_key_exists($field, $field2link)) {
82 2
							$fl = $field2link[$field];
83 2
							$table = $this->getTableFromClassType($fl['class_type']);
84 2
							$getFields[] = "`$table`.`$fl[member_ref]`";
85 2
							if ($linkedMembers[$field]['data_type_ref'] == 'link_uni')
86 2
								$linkJoins[$table] = "LEFT JOIN `$table` ON `t`.`$field`=`$table`.`_uuid`";
87
							else {
88
								$linkName = 'link'.$field;
89
								$linkJoins[$linkName] = "LEFT JOIN `dds_link` `$linkName` ON (`o`.`_uuid`=`$linkName`.`from_id` AND `$linkName`.`from_member`='$field')";
90 2
								$linkJoins[$table] = "LEFT JOIN `$table` ON `$linkName`.`to_id` =`$table`.`_uuid`";
91
							}
92
						} else {
93 4
							$getFields[] = "`t`.`$field`";
94
						}
95
					}
96 4
					$fieldsClause = implode(', ', $getFields);
97
				} else {
98 2
					$member = $this->getMapMemberForClass($classType);
99 2
					if (!$member)
100
						throw new \RuntimeException("There is no mappable member field set on class $classType");
101
					// set this as an array for later
102 2
					$fields = [$member['member_ref']];
103 2
					$fieldsClause = "`t`.`$member[member_ref]`";
104
				}
105 4
				$map = [];
106 4
				$notDeleted = $includeDeleted ? '' : 'AND `o`.`_deleted`=0';
107 4
				$table = $this->getTableFromClassType($classType);
108
				// set up any additional filtering
109 4
				$filterClause = '';
110 4
				$filterValues = [];
111 4
				if ($filters) {
112 2
					$ddsObjectFields = ['_uuid','_created','_deleted','_updated'];
113
					// whitelist filter keys to prevent sql injection
114 2
					$members = $this->getClassMembers($classType);
115 2
					$filterSubClause = [];
116 2
					foreach ($filters as $field=>$value) {
117 2
						if ($field == '_map_field_') {
118 2
							$mapMember = $this->getMapMemberForClass($classType);
119 2
							if ($mapMember && $value)
120 2
								$filterSubClause[] = "AND `t`.`$mapMember[member_ref]` LIKE '%$value%'";
121 2
							continue;
122
						}
123 2
						if (!(isset($members[$field]) || in_array($field, $ddsObjectFields)))
124
							continue;
125 2
						$keyClause = (in_array($field, $ddsObjectFields)) ? "`o`.`$field`" : "`t`.`$field`";
126 2
						$value = is_array($value) ? $value : [$value];
127 2
						$vcount = 1;
128 2
						$inClause = [];
129 2
						foreach ($value as $v) {
130 2
							$inClause[] = ":$field$vcount";
131 2
							$filterValues[":$field$vcount"] = $v;
132 2
							$vcount++;
133
						}
134 2
						$inClause = implode(',',$inClause);
135 2
						if (!empty($members[$field]) && $members[$field]['data_type_ref'] == 'link_multi') {
136
							$linkName = "link$field";
137
							if (!isset($linkJoins[$linkName]))
138
								$linkJoins[$linkName] = "LEFT JOIN `dds_link` `$linkName` ON (`o`.`_uuid`=`$linkName`.`from_id` AND `$linkName`.`from_member`='$field')";
139
							$filterSubClause[] = "AND `$linkName`.`to_id` IN ($inClause)";
140
						} else {
141 2
							$filterSubClause[] = "AND $keyClause IN ($inClause)";
142
						}
143
					}
144 2
					$filterClause = implode (' ', $filterSubClause);
145
				}
146 4
				$linkJoinsClause = implode(' ', $linkJoins);
147
148 4
				$query = "SELECT DISTINCT `o`.`_uuid`, $fieldsClause FROM `dds_object` `o` LEFT JOIN  `$table` `t` ON `o`.`_uuid`=`t`.`_uuid` " .
149 4
					"$linkJoinsClause WHERE `o`.`_class_type` = '$classType' $filterClause $notDeleted ORDER BY $fieldsClause " .
150 4
					"LIMIT $limit[start], $limit[length]";
151 4
				$command = neon()->db->createCommand($query);
152 4
				if ($filters)
153 2
					$command->bindValues($filterValues);
154 4
				$rows = $command->queryAll();
155 4
				foreach ($rows as $r) {
156
					// extract out the uuid result
157 4
					$uuid = $r['_uuid'];
158 4
					unset($r['_uuid']);
159
160
					// set the map
161 4
					$map[$uuid] = (count($fields)==1) ? current($r) : $r;
162
				}
163 4
				self::$_mapResultsCache[$cacheKey] = $map;
164
			} catch (\Exception $e) {
165
				$errorMessage = "Exception during getting a map. This may be caused by empty filters - you cannot filter on null. ".
166
					"Your filters were: ".print_r($filters,true).". The error message is ".$e->getMessage();
0 ignored issues
show
Bug introduced by
Are you sure print_r($filters, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
					"Your filters were: "./** @scrutinizer ignore-type */ print_r($filters,true).". The error message is ".$e->getMessage();
Loading history...
167
				\Neon::error($errorMessage, 'DDS');
168
				debug_message($errorMessage);
169
				return [];
170
			}
171
		}
172 4
		return self::$_mapResultsCache[$cacheKey];
173
	}
174
175
	/**
176
	 * @inheritdoc
177
	 */
178 24
	public function makeMapLookupRequest($objectUuids, $chainMapFields=[], $classType=null)
179
	{
180 24
		if (empty($objectUuids))
181 2
			return null;
182
		/*  See if there are any linked fields that we need to get
183
			The implementation here is somewhat crude. Generally speaking we are probably looking
184
			at needing to implement graphql or something similar to easily manage all the possible
185
			ways in which we might want to handle linked data through a request. For now if we need to
186
			link from one table to another, the simplest is to do it as another map chain request
187
			and merge them all together at the end when getting results.
188
		*/
189 22
		$linkedMembers = [];
190 22
		if ($classType && $chainMapFields) {
191
			$linkMemberKeys = array_keys($this->getClassMembers($classType, ['link_uni']));
192
			$linkedMembers = array_values(array_intersect($chainMapFields, $linkMemberKeys));
193
		}
194 22
		$requestKeys = [];
195
		// make the initial request key
196 22
		$requestKeys[] = $this->makeMapChainLookupRequest($objectUuids, null, [$chainMapFields]);
197
		// now make one for each linked member
198 22
		foreach ($linkedMembers as $lm) {
199
			$requestKeys[] = $this->makeMapChainLookupRequest($objectUuids, [$lm]);
200
		}
201 22
		return implode('|',$requestKeys);
202
	}
203
204
	/**
205
	 * @inheritdoc
206
	 */
207 24
	public function getMapLookupResults($requestKey)
208
	{
209 24
		$requestKeys = explode('|', $requestKey);
210 24
		$partials = [];
211 24
		foreach ($requestKeys as $rk) {
212 24
			$partials[] = $this->getMapChainLookupResults($rk);
213
		}
214 24
		if (count($requestKeys) > 1) {
215
			// skip over the first partial as this is our map
216
			// and look through others to fill back in
217
			foreach ($partials[0] as $u=>$p) {
218
				foreach ($p as $i=>$v) {
219
					if ($this->isUuid($v)) {
220
						for ($i=1; $i<count($requestKeys); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
221
							if (array_key_exists($v, $partials[$i][1])) {
222
								$partials[0][$u][$i] = $partials[$i][1][$v];
223
								continue;
224
							}
225
						}
226
					}
227
				}
228
			}
229
		}
230 24
		return $partials[0];
231
	}
232
233
	/**
234
	 * @inheritdoc
235
	 */
236 22
	public function makeMapChainLookupRequest($objectUuids, $chainMembers=[], $chainMapFields=[], $inReverse=false)
237
	{
238 22
		if (empty($objectUuids))
239
			return null;
240
241
		// create a request key based on the input parameters
242 22
		$requestKey = md5(serialize(func_get_args()));
243
244
		// handle this request if we haven't already done so
245
		if (
246 22
			!isset(static::$_mapRequests[$requestKey])
0 ignored issues
show
Bug introduced by
Since $_mapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapRequests to at least protected.
Loading history...
247 22
			&& !isset(static::$_mapResults[$requestKey])
0 ignored issues
show
Bug introduced by
Since $_mapResults is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapResults to at least protected.
Loading history...
248
		) {
249 22
			$this->addRequest(
250 22
				$requestKey,
251
				$objectUuids,
252
				$chainMembers,
253
				$chainMapFields,
254
				$inReverse
255
			);
256
		}
257
258 22
		return $requestKey;
259
	}
260
261
	/**
262
	 * @inheritdoc
263
	 */
264 24
	public function commitMapRequests()
265
	{
266 24
		$requests = &static::$_mapRequests;
0 ignored issues
show
Bug introduced by
Since $_mapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapRequests to at least protected.
Loading history...
267
		// check we have any requests to make
268 24
		if (count($requests['chains']) === 0)
269 22
			return;
270
		// run through the chain and get the reverse maps for each level of requests
271 22
		foreach ($requests['chains'] as $i=>&$chain) {
272
			// the requests will be filled in during this call
273 22
			$this->getMapLookupByLevel($chain);
274 22
			foreach ($chain as $k=>$r) {
275 22
				if ($r['next_uuid'])
276 8
					$requests['chains'][($i+1)][$k]['uuid'] = $r['next_uuid'];
277
			}
278
		}
279 22
		$this->convertMapRequestResults();
280 22
		$this->resetMapRequests();
281 22
	}
282
283
	/**
284
	 * @inheritdoc
285
	 */
286 24
	public function getMapChainLookupResults($requestKey)
287
	{
288
		// first commit any pending requests and then check results
289 24
		$this->commitMapRequests();
290
291
		// temporary while we are dealing with multiple uuid requests as
292
		// a set of single map requests
293 24
		if (isset(static::$_multiMapRequests[$requestKey])) {
0 ignored issues
show
Bug introduced by
Since $_multiMapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_multiMapRequests to at least protected.
Loading history...
294
			return $this->getMultiMapResults($requestKey);
0 ignored issues
show
Bug introduced by
The method getMultiMapResults() does not exist on neon\daedalus\services\d...ger\DdsObjectMapManager. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

294
			return $this->/** @scrutinizer ignore-call */ getMultiMapResults($requestKey);
Loading history...
295
		}
296
297 24
		return (!empty(static::$_mapResults[$requestKey])
0 ignored issues
show
Bug introduced by
Since $_mapResults is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapResults to at least protected.
Loading history...
298 22
			? static::$_mapResults[$requestKey]
299 24
			: []);
300
	}
301
302
	/**
303
	 * @inheritdoc
304
	 */
305 22
	public function clearMapRequests()
306
	{
307 22
		$this->resetMapRequests();
308 22
		static::$_mapResults = [];
0 ignored issues
show
Bug introduced by
Since $_mapResults is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapResults to at least protected.
Loading history...
309 22
	}
310
311
312
	// ----------------------
313
	// Implementation Methods
314
315
316
	/**
317
	 * This makes a query for all of the maps at a particular request level and
318
	 * pushes the replies back into the requests themselves ready for extraction
319
	 * later into the results
320
	 *
321
	 * @param array &$requests
322
	 */
323 22
	private function getMapLookupByLevel(&$requests)
324
	{
325 22
		if (empty($requests))
326
			return;
327
328 22
		$endpoints = [];
329 22
		$requestsByUuid = [];
330 22
		foreach ($requests as $r) {
331 22
			$endpoints[] = $r['uuid'];
332 22
			$requestsByUuid[$r['uuid']][] = $r;
333
		}
334 22
		$objectRows = DdsObject::find()
335 22
			->select(['_uuid','_class_type'])
336 22
			->where(['_uuid'=>$endpoints])
337 22
			->andWhere(['_deleted'=>0])
338 22
			->asArray()
339 22
			->all();
340 22
		$maps = [];
341 22
		$hackySeparator = '§|§@§|§'; // use a horrible unlikely separator to manage multiple map field requests
342 22
		if (!empty($objectRows)) {
343 20
			$classTypes = []; // get a list of the required class types
344 20
			$objectsByClassMapsAndChain = []; // get all ids for the same class combo
345 20
			foreach ($objectRows as $o) {
346
				// get the chain field on the object
347 20
				foreach ($requestsByUuid[$o['_uuid']] as $r) {
348 20
					$objectsByClassMapsAndChain[$o['_class_type']][$r['chain']][$r['mapFields']][$o['_uuid']] = $o['_uuid'];
349
				}
350 20
				$classTypes[$o['_class_type']] = $o['_class_type'];
351
			}
352
353
			// now find all the maps members for the required classes
354 20
			$mapFields = DdsMember::find()
355 20
				->select(['class_type','member_ref'])
356 20
				->where(['class_type'=>$classTypes, 'deleted'=>0, 'map_field'=>1])
357 20
				->asArray()
358 20
				->all();
359 20
			$mapsByClass = [];
360 20
			foreach ($mapFields as $m) {
361 20
				$mapsByClass[$m['class_type']] = $m['member_ref'];
362
			}
363
364
			// now find all of the objects for the level up in the chain
365 20
			$subQuery = [];
366 20
			$chainValues = [];
367 20
			$count = 0;
368 20
			foreach ($objectsByClassMapsAndChain as $class => $chainMapObjects) {
369 20
				foreach ($chainMapObjects as $chain => $mapObjects) {
370 20
					foreach ($mapObjects as $mapField => $objectUuids) {
371 20
						$objs = "'".implode("','",$objectUuids)."'";
372
						// make sure a map field can be created
373 20
						$mapFieldsClause = '';
374 20
						if ($mapField === self::$_defaultMapChain) {
375 20
							if (!isset($mapsByClass[$class]))
376
								continue;
377 20
							$mapFieldsClause = "CAST(`$mapsByClass[$class]` AS CHAR)";
378
						} else {
379 10
							$mapFieldsClause = "CONCAT_WS('$hackySeparator'";
380 10
							foreach (explode(',',$mapField) as $m) {
381 10
								$mapFieldsClause .= ",CAST(`$m` AS CHAR)";
382
							}
383 10
							$mapFieldsClause .= ')';
384
						}
385 20
						$nextUuidClause = ($chain == '__FINAL__' ? "null" : "`$chain`").' AS `next_uuid`';
386 20
						$subQuery[] = <<<EOQ
387 20
	SELECT `_uuid` as `uuid`, '$mapField' AS `mapFields`, $mapFieldsClause AS `map`, '{$chain}' AS `chain`, $nextUuidClause FROM `ddt_$class` WHERE `_uuid` IN ($objs)
388
EOQ;
389 20
						$chainValues[":CHAIN$count"] = $chain;
390 20
						$count++;
391
					}
392
				}
393
			}
394 20
			$query = implode(' UNION ', $subQuery);
395 20
			$command = neon()->db->createCommand($query);
396
397
			// get the maps
398 20
			$maps = $command->queryAll();
399
		}
400
401
		// sort the maps by chain and uuid
402 22
		$mapsByChainAndUuid = [];
403 22
		foreach ($maps as $m) {
404 20
			if (strpos($m['map'], $hackySeparator) !== false) {
405 10
				$m['map'] = explode($hackySeparator,$m['map']);
406
			}
407 20
			$mapsByChainAndUuid[$m['chain']][$m['mapFields']][$m['uuid']] = $m;
408
		}
409
410
		// now backfill the results
411 22
		foreach ($requests as &$r) {
412 22
			if (isset($mapsByChainAndUuid[$r['chain']][$r['mapFields']][$r['uuid']])) {
413 20
				$map = $mapsByChainAndUuid[$r['chain']][$r['mapFields']][$r['uuid']];
414 20
				$r['map'] = $map['map'];
415 20
				$r['next_uuid'] = $map['next_uuid'];
416
			} else {
417 2
				$r['next_uuid'] = null;
418
			}
419
		}
420 22
	}
421
422
	/**
423
	 * Add a request to the set of map requests
424
	 *
425
	 * Requests are split into a series of requests at each chain level so
426
	 * that we can make all the requests for one level at the same time. This
427
	 * way the total number of requests is limited to the maximum number of
428
	 * levels in the chain rather than a sum of (request[i]*length[i]) across
429
	 * each request
430
	 *
431
	 * @param string $requestKey
432
	 * @param string|array $objectUuids  string for single object and array for multiple
433
	 * @param null|array $chainMembers  null to mean not a chain
434
	 * @param array $chainMapFields
435
	 * @param boolean $inReverse
436
	 */
437 22
	private function addRequest($requestKey, $objectUuid, $chainMembers, $chainMapFields, $inReverse)
438
	{
439 22
		if (empty($objectUuid))
440
			return;
441
442
		// Treat a request for objectUuids as multiple single object map requests
443
		// TODO NJ 20190415 - make this work properly with multiple uuids without needing multiple requests
444 22
		if (is_array($objectUuid)) {
445 20
			foreach ($objectUuid as $uuid) {
446 20
				$subRequestKey = $requestKey.$uuid;
447 20
				$this->addRequest(
448 20
					$subRequestKey,
449 20
					$uuid,
450 20
					$chainMembers,
451 20
					$chainMapFields,
452 20
					$inReverse
453
				);
454 20
				static::$_multiMapRequests[$requestKey][] = $subRequestKey;
0 ignored issues
show
Bug introduced by
Since $_multiMapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_multiMapRequests to at least protected.
Loading history...
455
			}
456 20
			return;
457
		}
458
459
		// see if this is a chain or not
460 22
		$chainable = true;
461 22
		if ($chainMembers === null) {
462 22
			$chainMembers = [];
463 22
			$chainable = false;
464
		}
465
466 22
		$requests = &static::$_mapRequests; // to make the code slightly shorter
0 ignored issues
show
Bug introduced by
Since $_mapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapRequests to at least protected.
Loading history...
467
468
		// check to see if the request has already added
469 22
		if (isset($requests['keys'][$requestKey]))
470
			return;
471
472
		// calculate how many chains we are going down
473 22
		$chainCount = count($chainMembers);
474
		// split the chain up into requests per chain level if there are any
475 22
		foreach ($chainMembers as $i=>$mc) {
476 8
			$mapFieldKey = ($i == 0 ? 0 : $chainMembers[$i-1]);
477 8
			$mapFields = !empty($chainMapFields[$mapFieldKey]) ? implode(',',$chainMapFields[$mapFieldKey]) : self::$_defaultMapChain;
478 8
			$requests['chains'][$i][$requestKey] = ['uuid'=>($i===0?$objectUuid:null), 'map'=>null, 'mapFields'=>$mapFields, 'chain'=>$mc, 'key'=>$requestKey];
479
		}
480
		// add a final chain for the last item
481 22
		$mapFieldKey = ($chainCount == 0 ? 0 : $chainMembers[$chainCount-1]);
482 22
		$mapFields = !empty($chainMapFields[$mapFieldKey]) ? implode(',',$chainMapFields[$mapFieldKey]) : self::$_defaultMapChain;
483 22
		$requests['chains'][$chainCount][$requestKey] = ['uuid'=>($chainCount===0?$objectUuid:null), 'map'=>null, 'mapFields'=>$mapFields, 'chain'=>'__FINAL__', 'key'=>$requestKey];
484
485
		// and add the requestKey so we don't calculate this again
486 22
		$requests['keys'][$requestKey] = [
487 22
			'objectUuid' => $objectUuid,
488 22
			'inChains'=>$chainable,
489 22
			'inReverse'=>($chainable && $inReverse),
490
		];
491 22
	}
492
493
	/**
494
	 * Reset the map requests so new requests can be added
495
	 */
496 22
	private function resetMapRequests()
497
	{
498 22
		static::$_mapRequests = ['keys'=>[], 'chains'=>[], 'depth'=>0];
0 ignored issues
show
Bug introduced by
Since $_mapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapRequests to at least protected.
Loading history...
499 22
		static::$_multiMapRequests = [];
0 ignored issues
show
Bug introduced by
Since $_multiMapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_multiMapRequests to at least protected.
Loading history...
500 22
	}
501
502
	/**
503
	 * Convert requests results into results expected for mappings
504
	 */
505 22
	private function convertMapRequestResults()
506
	{
507 22
		$keys = &static::$_mapRequests['keys'];
0 ignored issues
show
Bug introduced by
Since $_mapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapRequests to at least protected.
Loading history...
508 22
		$chains = &static::$_mapRequests['chains'];
509 22
		$results = &static::$_mapResults;
0 ignored issues
show
Bug introduced by
Since $_mapResults is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_mapResults to at least protected.
Loading history...
510
511 22
		$tempResults = [];
512 22
		if (count($chains)===0)
513
			return;
514 22
		foreach ($chains as $level => $maps) {
515 22
			foreach ($maps as $key=>$map) {
516 22
				$tempResults[$key][$level][$map['uuid']] = $map['map'];
517
			}
518
		}
519
520
		// make any conversions to the results according to the request settings
521 22
		foreach ($keys as $k=>$settings) {
522 22
			if ($settings['inChains'] == true) {
523 10
				$results[$k] = ($settings['inReverse']) ? array_reverse($tempResults[$k]) : $tempResults[$k];
524
			} else {
525
				// in this case we just need the lowest level map within the chain
526 22
				$results[$k] = $tempResults[$k][0];
527
			}
528
		}
529
530
		// sort out any multimaps that need to be converted
531
		// TODO NJ 20190415 Remove the need for this once all converted into
532
		// initial query
533 22
		foreach (static::$_multiMapRequests as $requestKey=>$subRequestKeys) {
0 ignored issues
show
Bug introduced by
Since $_multiMapRequests is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $_multiMapRequests to at least protected.
Loading history...
534 20
			$multiMapResult = [];
535 20
			foreach ($subRequestKeys as $subRequestKey) {
536 20
				if (isset($results[$subRequestKey])) {
537 20
					$r = $results[$subRequestKey];
538 20
					$settings = $keys[$subRequestKey];
539 20
					if ($settings['inChains']) {
540
						// set results as [['uuid'] => chain,...]
541 10
						$multiMapResult[$settings['objectUuid']] = $r;
542
					} else {
543
						// set results as [uuid=>map,...]
544 20
						$multiMapResult[key($r)] = current($r);
545
					}
546
				}
547
			}
548
			// and store these results onto the total results
549 20
			$results[$requestKey] = $multiMapResult;
550
		}
551 22
	}
552
}
553