ModuleHierarchy   F
last analyzed

Complexity

Total Complexity 91

Size/Duplication

Total Lines 406
Duplicated Lines 0 %

Test Coverage

Coverage 13.27%

Importance

Changes 0
Metric Value
wmc 91
eloc 205
dl 0
loc 406
ccs 26
cts 196
cp 0.1327
rs 2
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
C getRelatedRecords() 0 43 14
A getHierarchyByRelation() 0 22 4
A getModulesMap1M() 0 6 2
A getRelatedRecordsByField() 0 7 1
A getRelationFieldByHierarchy() 0 9 4
A getUitypeByModule() 0 22 6
A getModulesByLevel() 0 9 3
A getFieldsForListFilter() 0 12 5
A accessModulesByLevel() 0 11 4
C getChildModules() 0 33 12
A getModulesMapMMCustom() 0 6 2
A getQueriesForRelatedRecords() 0 22 5
A getModulesMapMMBase() 0 6 2
A getQueryRelatedRecords() 0 13 3
A getModuleLevel() 0 3 2
A accessModulesByParent() 0 9 4
A getModulesByUitype() 0 22 6
B getMappingRelatedField() 0 23 6
A getModulesHierarchy() 0 3 1
A init() 0 9 5

How to fix   Complexity   

Complex Class

Complex classes like ModuleHierarchy 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 ModuleHierarchy, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App;
4
5
/**
6
 * Modules hierarchy basic class.
7
 *
8
 * @package App
9
 *
10
 * @copyright YetiForce S.A.
11
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
12
 * @author    Mariusz Krzaczkowski <[email protected]>
13
 * @author    Radosław Skrzypczak <[email protected]>
14
 */
15
class ModuleHierarchy
16
{
17
	protected static $hierarchy;
18
	protected static $modulesByLevels = [];
19
20
	public static function init()
21
	{
22
		if (isset(static::$hierarchy)) {
23
			return true;
24
		}
25
		static::$hierarchy = require ROOT_DIRECTORY . '/app_data/moduleHierarchy.php';
26
		foreach (static::$hierarchy['modulesHierarchy'] as $module => $details) {
27
			if (Module::isModuleActive($module) && Privilege::isPermitted($module)) {
28
				static::$modulesByLevels[$details['level']][$module] = $details;
29
			}
30 1
		}
31
	}
32 1
33
	public static function getModulesHierarchy()
34
	{
35
		return static::$hierarchy['modulesHierarchy'];
36
	}
37
38
	public static function getModuleLevel($moduleName)
39
	{
40 1
		return isset(static::$hierarchy['modulesHierarchy'][$moduleName]) ? static::$hierarchy['modulesHierarchy'][$moduleName]['level'] : false;
41
	}
42 1
43 1
	public static function getModulesMap1M($moduleName)
44
	{
45
		if (isset(static::$hierarchy['modulesMap1M'][$moduleName])) {
46
			return static::$hierarchy['modulesMap1M'][$moduleName];
47
		}
48
		return [];
49
	}
50
51
	public static function getModulesMapMMBase()
52
	{
53
		if (isset(static::$hierarchy['modulesMapMMBase'])) {
54
			return static::$hierarchy['modulesMapMMBase'];
55
		}
56
		return false;
57
	}
58
59
	public static function getModulesMapMMCustom($moduleName)
60
	{
61
		if (isset(static::$hierarchy['modulesMapMMCustom'][$moduleName])) {
62
			return static::$hierarchy['modulesMapMMCustom'][$moduleName];
63
		}
64
		return false;
65
	}
66
67
	public static function getModulesByLevel($level = null)
68
	{
69
		if (null === $level) {
70
			return static::$modulesByLevels;
71
		}
72
		if (isset(static::$modulesByLevels[$level])) {
73
			return static::$modulesByLevels[$level];
74
		}
75
		return [];
76
	}
77
78
	/**
79 3
	 * Get modules list by uitype field.
80
	 *
81 3
	 * @param int $uitype
82
	 *
83
	 * @return array
84 3
	 */
85
	public static function getModulesByUitype($uitype)
86
	{
87
		switch ($uitype) {
88
			case 67:
89
				$level = 0;
90
				break;
91
			case 66:
92
				$level = 1;
93
				break;
94 3
			case 68:
95
				$level = 2;
96 3
				break;
97 3
			case 64:
98 3
				$level = 3;
99 3
				break;
100 3
			case 65:
101 3
				$level = 4;
102 3
				break;
103 3
			default:
104 3
				break;
105 3
		}
106 3
		return static::getModulesByLevel($level);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $level does not seem to be defined for all execution paths leading up to this point.
Loading history...
107 3
	}
108 3
109 3
	public static function accessModulesByLevel($level = 0, $actionName = 'EditView')
110 3
	{
111 3
		$modules = [];
112
		if (isset(static::$modulesByLevels[$level])) {
113
			foreach (static::$modulesByLevels[$level] as $module => &$details) {
114
				if (Privilege::isPermitted($module, $actionName)) {
115 3
					$modules[$module] = $details;
116
				}
117
			}
118
		}
119
		return $modules;
120
	}
121
122
	public static function accessModulesByParent($parent, $actionName = 'EditView')
123
	{
124
		$modules = [];
125
		foreach (static::$hierarchy['modulesHierarchy'] as $module => &$details) {
126
			if (Privilege::isPermitted($module, $actionName) && isset($details['parentModule'])) {
127
				$modules[$details['parentModule']][$module] = $details;
128
			}
129
		}
130
		return $modules[$parent];
131
	}
132
133
	public static function getMappingRelatedField($moduleName)
134
	{
135
		$return = false;
136
		switch ((string) static::getModuleLevel($moduleName)) {
137
			case '0':
138
				$return = 'link';
139
				break;
140
			case '1':
141
				$return = 'process';
142
				break;
143
			case '2':
144
				$return = 'subprocess';
145
				break;
146
			case '3':
147
				$return = 'subprocess_sl';
148
				break;
149
			case '4':
150
				$return = 'linkextend';
151
				break;
152
			default:
153
				break;
154
		}
155
		return $return;
156
	}
157
158
	/**
159
	 * The function takes a hierarchy relationship.
160
	 *
161
	 * @param string $moduleName
162
	 * @param bool   $field
163
	 *
164
	 * @return array
165
	 */
166
	public static function getRelationFieldByHierarchy($moduleName, $field = false)
167
	{
168
		if (false !== $field && isset(static::$hierarchy['modulesMapRelatedFields'][$moduleName][$field])) {
169
			return static::$hierarchy['modulesMapRelatedFields'][$moduleName][$field];
170
		}
171
		if (isset(static::$hierarchy['modulesMapRelatedFields'][$moduleName])) {
172
			return static::$hierarchy['modulesMapRelatedFields'][$moduleName];
173
		}
174
		return [];
175
	}
176
177
	public static function getUitypeByModule($moduleName)
178
	{
179
		switch (static::getModuleLevel($moduleName)) {
180
			case 0:
181
				$return = 67;
182
				break;
183
			case 1:
184
				$return = 66;
185
				break;
186
			case 2:
187
				$return = 68;
188
				break;
189
			case 3:
190
				$return = 64;
191
				break;
192
			case 4:
193
				$return = 65;
194
				break;
195
			default:
196
				break;
197
		}
198
		return $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return does not seem to be defined for all execution paths leading up to this point.
Loading history...
199
	}
200
201
	/**
202
	 * Get child modules.
203
	 *
204
	 * @param string $moduleName
205
	 * @param int[]  $hierarchy
206
	 *
207
	 * @return string[]
208
	 */
209
	public static function getChildModules($moduleName, $hierarchy = [1])
210
	{
211
		$modules = [];
212
		switch (static::getModuleLevel($moduleName)) {
213
			case 0:
214
				$is1Level = \in_array(1, $hierarchy);
215
				$isLevel4 = \in_array(4, $hierarchy);
216
				if ($is1Level && $isLevel4) {
217
					$modules = array_keys(array_merge(static::getModulesByLevel(1), static::getModulesByLevel(4)));
218
				} elseif ($is1Level) {
219
					$modules = array_keys(static::getModulesByLevel(1));
220
				} elseif ($isLevel4) {
221
					$modules = array_keys(static::getModulesByLevel(4));
222
				}
223
				break;
224
			case 1:
225
				if ($levelMod = static::getModulesByLevel(2)) {
226
					foreach ($levelMod as $mod => $details) {
227
						if ($moduleName === $details['parentModule']) {
228
							$modules[] = $mod;
229
						}
230
					}
231
				}
232
				break;
233
			case 2:
234
				if (\in_array(3, $hierarchy)) {
235
					$modules = array_keys(static::getModulesByLevel(3));
236
				}
237
				break;
238
			default:
239
				break;
240
		}
241
		return $modules;
242
	}
243
244
	/**
245
	 * Get related records by hierarchy.
246
	 *
247
	 * @param int   $record
248
	 * @param array $hierarchy
249
	 *
250
	 * @return int[]
251
	 */
252
	public static function getRelatedRecords($record, $hierarchy)
253
	{
254
		$moduleName = Record::getType($record);
255
		$records = $recordsLevel1 = $recordsLevel2 = [];
256
		if (\in_array(0, $hierarchy)) {
257
			$records[] = $record;
258
		}
259
		$modules = static::getChildModules($moduleName, $hierarchy);
260
		if ($modules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $modules of type string[] 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...
261
			$fields = Field::getRelatedFieldForModule(false, $moduleName);
262
			foreach ($fields as $field) {
263
				if (\in_array($field['name'], $modules)) {
264
					$recordsByField = static::getRelatedRecordsByField($record, $field);
265
					$recordsLevel1 = array_merge($recordsLevel1, $recordsByField);
266
				}
267
			}
268
		}
269
		$level = static::getModuleLevel($moduleName);
270
		if (!(0 == $level && !\in_array(1, $hierarchy))) {
271
			$records = array_merge($records, $recordsLevel1);
272
		}
273
		if (0 === $level) {
274
			if (\in_array(2, $hierarchy)) {
275
				$modules = static::getChildModules($moduleName, [1]);
276
				if ($modules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $modules of type string[] 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...
277
					$fields = Field::getRelatedFieldForModule(false, $moduleName);
278
					foreach ($fields as $field) {
279
						if (\in_array($field['name'], $modules)) {
280
							$recordsByField = static::getRelatedRecordsByField($record, $field);
281
							$recordsLevel2 = array_merge($recordsLevel2, $recordsByField);
282
						}
283
					}
284
				}
285
				foreach ($recordsLevel2 as $record) {
0 ignored issues
show
introduced by
$record is overwriting one of the parameters of this function.
Loading history...
286
					$recordsByHierarchy = static::getRelatedRecords($record, $hierarchy);
287
					$records = array_merge($records, $recordsByHierarchy);
288
				}
289
			}
290
			if (\in_array(3, $hierarchy)) {
291
				$records = array_merge($records, $recordsLevel1);
292
			}
293
		}
294
		return array_unique($records);
295
	}
296
297
	/**
298
	 * Get related records by field.
299
	 *
300
	 * @param int   $record
301
	 * @param array $field
302
	 *
303
	 * @return int[]
304
	 */
305
	protected static function getRelatedRecordsByField($record, $field)
306
	{
307
		$queryGenerator = new QueryGenerator($field['name']);
308
		$queryGenerator->setFields(['id']);
309
		$queryGenerator->addNativeCondition([$field['tablename'] . '.' . $field['columnname'] => $record]);
310
311
		return $queryGenerator->createQuery()->column();
312
	}
313
314
	/**
315
	 * Function to get array of queries. Quries are used to create union.
316
	 *
317
	 * @param int      $record
318
	 * @param string   $moduleName
319
	 * @param array    $hierarchy
320
	 * @param Db\Query $subQuery
321
	 *
322
	 * @return array
323
	 */
324
	private static function getQueriesForRelatedRecords(int $record, string $moduleName, array $hierarchy, Db\Query $subQuery = null): array
325
	{
326
		$modules = static::getChildModules($moduleName, $hierarchy);
327
		$queries = [];
328
		if ($modules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $modules of type string[] 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...
329
			$fields = Field::getRelatedFieldForModule(false, $moduleName);
330
			foreach ($fields as $field) {
331
				if (\in_array($field['name'], $modules)) {
332
					$queryGenerator = new QueryGenerator($field['name']);
333
					$queryGenerator->setFields(['id']);
334
					if ($subQuery) {
335
						$queryGenerator->addNativeCondition([$field['tablename'] . '.' . $field['columnname'] => $subQuery]);
336
					} else {
337
						$queryGenerator->addNativeCondition([$field['tablename'] . '.' . $field['columnname'] => $record]);
338
					}
339
					$tempQuery = $queryGenerator->createQuery();
340
					$queries[] = $tempQuery;
341
					$queries = array_merge($queries, static::getQueriesForRelatedRecords($record, $field['name'], $hierarchy, clone $tempQuery));
342
				}
343
			}
344
		}
345
		return $queries;
346
	}
347
348
	/**
349
	 * Get related query by hierarchy.
350
	 *
351
	 * @param int   $record
352
	 * @param array $hierarchy
353
	 *
354
	 * @return Db\Query|null
355
	 */
356
	public static function getQueryRelatedRecords(int $record, array $hierarchy): ?Db\Query
357
	{
358
		$moduleName = Record::getType($record);
359
		$queries = static::getQueriesForRelatedRecords($record, $moduleName, $hierarchy);
360
		if (0 === \count($queries)) {
361
			return null;
362
		}
363
		$subQuery = $queries[0];
364
		unset($queries[0]);
365
		foreach ($queries as $query) {
366
			$subQuery->union($query);
367
		}
368
		return $subQuery;
369
	}
370
371
	/**
372
	 * Get fields for list filter.
373
	 *
374
	 * @param string $moduleName
375
	 *
376
	 * @return array
377
	 */
378
	public static function getFieldsForListFilter(string $moduleName): array
379
	{
380
		$fields = [];
381
		$moduleId = \App\Module::getModuleId($moduleName);
382
		foreach (static::getHierarchyByRelation() as $relations) {
383
			foreach ($relations as $relation) {
384
				if ($relation['related_tabid'] === $moduleId && !empty($relation['rel_field_name'])) {
385
					$fields[$relation['field_name']][\App\Module::getModuleName($relation['tabid'])] = [$relation['rel_field_name'] => \App\Module::getModuleName($relation['rel_tabid'])];
386
				}
387
			}
388
		}
389
		return $fields;
390
	}
391
392
	/**
393
	 * Get hierarchy info by relation.
394
	 *
395
	 * @param int|null $relationId
396
	 *
397
	 * @return array
398
	 */
399
	public static function getHierarchyByRelation(int $relationId = null): array
400
	{
401
		if (Cache::has('HierarchyByRelation', '')) {
402
			$data = Cache::get('HierarchyByRelation', '');
403
		} else {
404
			$data = [];
405
			$dataReader = (new \App\Db\Query())
406
				->select(['SR.tabid', 'SR.field_name', 'SR.related_tabid', 'rel_field_name' => 'RR.field_name', 'rel_tabid' => 'RR.tabid', 'a_yf_record_list_filter.*'])
407
				->from('a_yf_record_list_filter')
408
				->leftJoin(['SR' => 'vtiger_relatedlists'], 'SR.relation_id=a_yf_record_list_filter.relationid')
409
				->leftJoin(['RR' => 'vtiger_relatedlists'], 'RR.relation_id=a_yf_record_list_filter.rel_relationid')
410
				->createCommand()->query();
411
			while ($row = $dataReader->read()) {
412
				$data[$row['relationid']][] = $row;
413
			}
414
			Cache::save('HierarchyByRelation', '', $data, Cache::LONG);
415
		}
416
		if (null === $relationId) {
417
			return $data;
418
		}
419
420
		return $data[$relationId] ?? [];
421
	}
422
}
423
424
ModuleHierarchy::init();
425