Vtiger_RelationListView_Model   F
last analyzed

Complexity

Total Complexity 125

Size/Duplication

Total Lines 773
Duplicated Lines 0 %

Test Coverage

Coverage 41.38%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 125
eloc 347
dl 0
loc 773
ccs 96
cts 232
cp 0.4138
rs 2
c 1
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A getRelationModel() 0 3 1
B getCreateViewUrl() 0 11 7
B loadCondition() 0 18 7
A getRelatedModuleModel() 0 3 1
B getTreeEntries() 0 37 7
A getTreeHeaders() 0 6 1
A loadOrderBy() 0 10 6
A getParentRecordModel() 0 3 1
A getEntries() 0 17 2
B getRelationQuery() 0 25 7
A loadCustomView() 0 9 3
A getAllEntries() 0 3 1
B getRecordsFromArray() 0 31 7
A isQuickSearchEnabled() 0 3 2
A setParentRecordModel() 0 5 1
A getInstance() 0 23 4
A getRelatedTreeEntriesCount() 0 9 1
A getTreeViewModel() 0 3 1
A getRelatedEntriesCount() 0 3 1
A getQueryGenerator() 0 3 1
A setRelatedModuleModel() 0 5 1
A getEntryExtend() 0 2 1
C getHeaders() 0 45 16
A setRelationModel() 0 4 1
A getWidgetsList() 0 16 3
A getWidgets() 0 17 5
B loadSearchLockedFields() 0 23 10
B getLinks() 0 81 9
A getFavoriteRecords() 0 10 1
A isWidgetsList() 0 4 3
A getAddRelationLinks() 0 33 4
A getSelectRelationLinks() 0 15 4
A setFields() 0 16 5

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
 /* +***********************************************************************************
4
 * The contents of this file are subject to the vtiger CRM Public License Version 1.0
5
 * ("License"); You may not use this file except in compliance with the License
6
 * The Original Code is:  vtiger CRM Open Source
7
 * The Initial Developer of the Original Code is vtiger.
8
 * Portions created by vtiger are Copyright (C) vtiger.
9
 * All Rights Reserved.
10
 * Contributor(s): YetiForce S.A.
11
 * *********************************************************************************** */
12
13
class Vtiger_RelationListView_Model extends \App\Base
14
{
15
	/**
16
	 * Relation model instance.
17
	 *
18
	 * @var Vtiger_Relation_Model
19
	 */
20
	protected $relationModel;
21
22
	/**
23
	 * Parent record model instance.
24
	 *
25
	 * @var Vtiger_Record_Model
26
	 */
27
	protected $parentRecordModel;
28
29
	/**
30
	 * Related module model instance.
31
	 *
32
	 * @var Vtiger_Module_Model
33
	 */
34
	protected $relatedModuleModel;
35
36
	/**
37
	 * Mandatory columns.
38
	 *
39
	 * @var array
40
	 */
41
	protected $mandatoryColumns = [];
42
43
	/**
44
	 * Set relation model instance.
45
	 *
46
	 * @param Vtiger_Relation_Model $relation
47
	 *
48
	 * @return $this
49 1
	 */
50
	public function setRelationModel($relation)
51 1
	{
52
		$this->relationModel = $relation;
53 1
		return $this;
54
	}
55
56
	/**
57
	 * Get relation model instance.
58
	 *
59
	 * @return Vtiger_Relation_Model
60
	 */
61 1
	public function getRelationModel()
62
	{
63 1
		return $this->relationModel;
64
	}
65
66
	/**
67
	 * Set parent record model instance.
68
	 *
69
	 * @param Vtiger_Record_Model $parentRecord
70
	 *
71
	 * @return $this
72
	 */
73 1
	public function setParentRecordModel($parentRecord)
74
	{
75 1
		$this->parentRecordModel = $parentRecord;
76
77 1
		return $this;
78
	}
79
80
	/**
81
	 * Get parent record model instance.
82
	 *
83
	 * @return Vtiger_Record_Model
84
	 */
85 1
	public function getParentRecordModel()
86
	{
87 1
		return $this->parentRecordModel;
88
	}
89
90
	/**
91
	 * Set related module model instance.
92
	 *
93
	 * @param Vtiger_Module_Model $relatedModuleModel
94
	 *
95
	 * @return $this
96
	 */
97 1
	public function setRelatedModuleModel($relatedModuleModel)
98
	{
99 1
		$this->relatedModuleModel = $relatedModuleModel;
100
101 1
		return $this;
102
	}
103
104
	/**
105
	 * Get related module model instance.
106
	 *
107
	 * @return Vtiger_Module_Model
108
	 */
109 1
	public function getRelatedModuleModel()
110
	{
111 1
		return $this->relatedModuleModel;
112
	}
113
114
	/**
115
	 * Get query generator instance.
116
	 *
117
	 * @return \App\QueryGenerator
118
	 */
119 1
	public function getQueryGenerator()
120
	{
121 1
		return $this->getRelationModel()->getQueryGenerator();
122
	}
123
124
	/**
125
	 * Function to identify if the module supports quick search or not.
126
	 */
127
	public function isQuickSearchEnabled(): bool
128
	{
129
		return $this->has('quickSearchEnabled') ? $this->get('quickSearchEnabled') : true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->has('quick...kSearchEnabled') : true could return the type null which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
130
	}
131
132
	/**
133 1
	 * Get relation list view model instance.
134
	 *
135 1
	 * @param Vtiger_Record_Model $parentRecordModel
136 1
	 * @param string              $relationModuleName
137 1
	 * @param bool|int            $relationId
138
	 * @param int|string          $cvId
139 1
	 *
140 1
	 * @return self
141 1
	 */
142
	public static function getInstance(Vtiger_Record_Model $parentRecordModel, string $relationModuleName, $relationId = false, $cvId = 0)
143 1
	{
144 1
		$parentModuleModel = $parentRecordModel->getModule();
145 1
		$className = Vtiger_Loader::getComponentClassName('Model', 'RelationListView', $parentModuleModel->getName());
146 1
		$instance = new $className();
147
		if ($relationId) {
148 1
			$relationModelInstance = Vtiger_Relation_Model::getInstanceById($relationId);
149 1
		} else {
150 1
			$relationModelInstance = Vtiger_Relation_Model::getInstance($parentModuleModel, Vtiger_Module_Model::getInstance($relationModuleName), $relationId);
151 1
		}
152
		if (!$relationModelInstance) {
0 ignored issues
show
introduced by
$relationModelInstance is of type Vtiger_Relation_Model, thus it always evaluated to true.
Loading history...
153 1
			return false;
154
		}
155
		$instance->setParentRecordModel($parentRecordModel);
156
		$instance->setRelatedModuleModel($relationModelInstance->getRelationModuleModel());
157
		$queryGenerator = new \App\QueryGenerator($relationModelInstance->getRelationModuleModel()->getName());
158
		if (is_numeric($cvId)) {
159
			$instance->set('viewId', $cvId);
160
		}
161
		$relationModelInstance->set('query_generator', $queryGenerator);
162
		$relationModelInstance->set('parentRecord', $parentRecordModel);
163 1
		$instance->setRelationModel($relationModelInstance);
164
		return $instance;
165 1
	}
166
167
	/**
168 1
	 * Function to get Relation query.
169 1
	 *
170 1
	 * @param mixed $returnQueryGenerator
171 1
	 *
172 1
	 * @return \App\Db\Query|\App\QueryGenerator
173 1
	 */
174 1
	public function getRelationQuery($returnQueryGenerator = false)
175
	{
176
		if ($this->has('Query')) {
177
			return $this->get('Query');
178
		}
179 1
		$this->loadCustomView();
180
		$this->loadCondition();
181
		$this->loadOrderBy();
182 1
		$relationModelInstance = $this->getRelationModel();
183 1
		if (!empty($relationModelInstance) && $relationModelInstance->get('name')) {
184 1
			$queryGenerator = $relationModelInstance->getQuery();
185
			$relationModuleName = $queryGenerator->getModule();
186
			if (isset($this->mandatoryColumns[$relationModuleName])) {
187
				foreach ($this->mandatoryColumns[$relationModuleName] as &$columnName) {
188
					$queryGenerator->setCustomColumn($columnName);
189
				}
190
			}
191
			if ($returnQueryGenerator) {
192 1
				return $queryGenerator;
193
			}
194 1
			$query = $queryGenerator->createQuery();
195 1
			$this->set('Query', $query);
196 1
			return $query;
197
		}
198
		throw new \App\Exceptions\AppException('>>> No relationModel instance, requires verification 2 <<<');
199 1
	}
200
201
	/**
202 1
	 * Load list view conditions.
203 1
	 */
204
	public function loadCondition()
205 1
	{
206
		$relatedModuleName = $this->getRelatedModuleModel()->getName();
207
		$queryGenerator = $this->getRelationModel()->getQueryGenerator();
208 1
		if ($entityState = $this->get('entityState')) {
209
			$queryGenerator->setStateCondition($entityState);
210
		}
211
		if ($relatedModuleName === $this->get('src_module') && !$this->isEmpty('src_record')) {
212
			$queryGenerator->addCondition('id', $this->get('src_record'), 'n');
213
		}
214
		if ($searchParams = $this->getArray('search_params')) {
215
			$queryGenerator->parseAdvFilter($searchParams);
216
		}
217 1
		if (!$this->isEmpty('search_key')) {
218
			$queryGenerator->addCondition($this->get('search_key'), $this->get('search_value'), $this->get('operator'));
219 1
		}
220 1
		if ($searchParams = $this->getArray('search_rel_params')) {
221 1
			$this->getRelationModel()->setRelationConditions($searchParams);
222 1
		}
223 1
	}
224
225 1
	/**
226 1
	 * Load custom view.
227 1
	 */
228
	public function loadCustomView()
229
	{
230
		if ($this->has('viewId')) {
231 1
			$cvId = $this->get('viewId');
232
		} else {
233 1
			$cvId = array_key_first($this->getRelationModel()->getCustomViewList());
234 1
		}
235 1
		if ('relation' !== $cvId) {
236 1
			$this->getRelationModel()->getQueryGenerator()->initForCustomViewById($cvId);
237 1
		}
238 1
	}
239 1
240
	/**
241
	 * Function to get the related list view entries.
242 1
	 *
243 1
	 * @param Vtiger_Paging_Model $pagingModel
244
	 *
245
	 * @return Vtiger_Record_Model[]
246
	 */
247
	public function getEntries(Vtiger_Paging_Model $pagingModel)
248
	{
249
		$pageLimit = $pagingModel->getPageLimit();
250
		$query = $this->getRelationQuery();
251 1
		$query->limit($pageLimit + 1)->offset($pagingModel->getStartIndex());
0 ignored issues
show
Bug introduced by
The method limit() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

251
		$query->/** @scrutinizer ignore-call */ 
252
          limit($pageLimit + 1)->offset($pagingModel->getStartIndex());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method offset() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

251
		$query->limit($pageLimit + 1)->/** @scrutinizer ignore-call */ offset($pagingModel->getStartIndex());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
252
		$rows = $query->all();
0 ignored issues
show
Bug introduced by
The method all() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

252
		/** @scrutinizer ignore-call */ 
253
  $rows = $query->all();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
253 1
254
		$count = \count($rows);
255
		if ($count > $pageLimit) {
256
			array_pop($rows);
257
			$pagingModel->set('nextPageExists', true);
258 1
		} else {
259
			$pagingModel->set('nextPageExists', false);
260 1
		}
261 1
		$relatedRecordList = $this->getRecordsFromArray($rows);
262
		$pagingModel->calculatePageRange(\count($relatedRecordList));
263
		return $relatedRecordList;
264
	}
265
266
	/**
267
	 * Gets all entries.
268
	 *
269
	 * @return \Vtiger_Record_Model[]
270
	 */
271 1
	public function getAllEntries(): array
272
	{
273
		return $this->getRecordsFromArray($this->getRelationQuery()->all());
274
	}
275
276
	/**
277
	 * Get models of records from array.
278 1
	 *
279
	 * @param array $rows
280 1
	 *
281 1
	 * @return \Vtiger_Record_Model[]
282 1
	 */
283 1
	public function getRecordsFromArray(array $rows)
284
	{
285
		$listViewRecordModels = $relatedFields = [];
286
		$moduleModel = $this->getRelationModel()->getRelationModuleModel();
287 1
		$recordId = $this->getParentRecordModel()->getId();
288
		foreach ($this->getQueryGenerator()->getRelatedFields() as $fieldInfo) {
289
			$relatedFields[$fieldInfo['relatedModule']][$fieldInfo['sourceField']][] = $fieldInfo['relatedField'];
290
		}
291
		foreach ($rows as $row) {
292
			if ($recordId == $row['id']) {
293
				continue;
294
			}
295
			$extRecordModel = [];
296
			foreach ($relatedFields as $relatedModuleName => $fields) {
297
				foreach ($fields as $sourceField => $field) {
298
					$recordData = [
299
						'id' => $row[$sourceField . $relatedModuleName . 'id'] ?? 0,
300
					];
301
					foreach ($field as $relatedFieldName) {
302
						$recordData[$relatedFieldName] = $row[$sourceField . $relatedModuleName . $relatedFieldName];
303
						unset($row[$sourceField . $relatedModuleName . $relatedFieldName]);
304
					}
305
					$extRecordModel[$sourceField][$relatedModuleName] = Vtiger_Module_Model::getInstance($relatedModuleName)->getRecordFromArray($recordData);
306
				}
307
			}
308
			$recordModel = $moduleModel->getRecordFromArray($row);
309
			$recordModel->ext = $extRecordModel;
310
			$this->getEntryExtend($recordModel);
311
			$listViewRecordModels[$row['id']] = $recordModel;
312
		}
313
		return $listViewRecordModels;
314
	}
315
316
	/**
317
	 * Function extending recordModel object with additional information.
318
	 *
319
	 * @param Vtiger_Record_Model $recordModel
320
	 */
321
	public function getEntryExtend(Vtiger_Record_Model $recordModel)
0 ignored issues
show
Unused Code introduced by
The parameter $recordModel is not used and could be removed. ( Ignorable by Annotation )

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

321
	public function getEntryExtend(/** @scrutinizer ignore-unused */ Vtiger_Record_Model $recordModel)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
322
	{
323
	}
324
325
	/**
326
	 * Set list view order by.
327
	 */
328
	public function loadOrderBy()
329
	{
330
		$orderBy = $this->get('orderby');
331
		if (!empty($orderBy) && \is_array($orderBy)) {
332
			foreach ($orderBy as $fieldName => $sortFlag) {
333
				$field = $this->getRelationModel()->getRelationModuleModel()->getFieldByName($fieldName);
334
				if ($field || 'id' === $fieldName) {
335
					$this->getRelationModel()->getQueryGenerator()->setOrder($fieldName, $sortFlag);
336
				} else {
337
					\App\Log::warning("[RelationListView] Incorrect value of sorting: '$fieldName'");
338
				}
339
			}
340
		}
341
	}
342
343
	/**
344
	 * Get header fields.
345
	 *
346
	 * @return Vtiger_Field_Model[]
347
	 */
348
	public function getHeaders()
349
	{
350
		$fields = [];
351
		if ($this->get('viewId')) {
352
			$moduleModel = $this->getRelationModel()->getRelationModuleModel();
353
			$customView = App\CustomView::getInstance($moduleModel->getName());
354
			foreach ($customView->getColumnsListByCvid($this->get('viewId')) as $fieldInfo) {
355
				$fieldName = $fieldInfo['field_name'];
356
				$sourceFieldName = $fieldInfo['source_field_name'] ?? '';
357
				$fieldModel = Vtiger_Field_Model::getInstance($fieldName, Vtiger_Module_Model::getInstance($fieldInfo['module_name']));
358
				if (!$fieldModel) {
359
					\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
360
					continue;
361
				}
362
				if (!$fieldModel->isActiveField() || ($sourceFieldName && !$moduleModel->getFieldByName($sourceFieldName)->isActiveField())) {
363
					continue;
364
				}
365
				if ($sourceFieldName) {
366
					$fieldModel->set('source_field_name', $sourceFieldName);
367
				}
368
				$fields[$fieldModel->getFullName()] = $fieldModel;
369
			}
370
		}
371
		if (empty($fields)) {
372
			$fields = $this->getRelationModel()->getQueryFields();
373
		}
374
		unset($fields['id']);
375
		foreach ($fields as $fieldName => $fieldModel) {
376
			if (!$fieldModel) {
377
				\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
378
				unset($fields[$fieldName]);
379
			} elseif (!$fieldModel->isViewable() && !$fieldModel->get('fromOutsideList')) {
380
				unset($fields[$fieldName]);
381
			}
382
		}
383
		if ($relFields = $this->getRelationModel()->getRelationFields()) {
384
			foreach ($relFields as $fieldName => $fieldModel) {
385
				if (!$fieldModel) {
386
					\App\Log::warning("The field does not exist: '$fieldName' | Module: " . $this->getRelationModel()->getRelationModuleModel()->getName(), __METHOD__);
387
					unset($relFields[$fieldName]);
388
				}
389
			}
390
			$fields = array_merge($fields, $relFields);
391
		}
392
		return $fields;
393
	}
394
395
	/**
396
	 * Function to get Total number of record in this relation.
397
	 *
398
	 * @return int
399
	 */
400
	public function getRelatedEntriesCount()
401
	{
402
		return $this->getRelationQuery()->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getRelationQuery()->count() also could return the type string which is incompatible with the documented return type integer.
Loading history...
Bug introduced by
The method count() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

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

402
		return $this->getRelationQuery()->/** @scrutinizer ignore-call */ count();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
403
	}
404
405
	/**
406
	 * Get tree view model.
407
	 *
408
	 * @return Vtiger_TreeCategoryModal_Model
409
	 */
410
	public function getTreeViewModel()
411
	{
412
		return Vtiger_TreeCategoryModal_Model::getInstance($this->getRelatedModuleModel());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Vtiger_TreeCatego...etRelatedModuleModel()) returns the type Vtiger_TreeView_Model which is incompatible with the documented return type Vtiger_TreeCategoryModal_Model.
Loading history...
413
	}
414
415
	/**
416
	 * Get tree headers.
417
	 *
418
	 * @return string[]
419
	 */
420
	public function getTreeHeaders()
421
	{
422
		$fields = $this->getTreeViewModel()->getTreeField();
423
424
		return [
425
			'name' => $fields['fieldlabel'],
426
		];
427
	}
428
429
	/**
430
	 * Get tree entries.
431
	 *
432
	 * @return array[]
433
	 */
434
	public function getTreeEntries()
435
	{
436
		$relModuleName = $this->getRelatedModuleModel()->getName();
437
		$relationModelInstance = $this->getRelationModel();
438
		$template = $this->getTreeViewModel()->getTemplate();
439
		$showCreatorDetail = $relationModelInstance->get('creator_detail');
440
		$showComment = $relationModelInstance->get('relation_comment');
441
442
		$rows = $relationModelInstance->getRelationTree();
443
		$trees = [];
444
		foreach ($rows as &$row) {
445
			$pieces = explode('::', $row['parentTree']);
446
			end($pieces);
447
			$parent = prev($pieces);
448
			$parentName = '';
449
			if ($row['depth'] > 0) {
450
				$treeDetail = App\Fields\Tree::getValueByTreeId($template, $parent);
0 ignored issues
show
Bug introduced by
$template of type type is incompatible with the type integer expected by parameter $templateId of App\Fields\Tree::getValueByTreeId(). ( Ignorable by Annotation )

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

450
				$treeDetail = App\Fields\Tree::getValueByTreeId(/** @scrutinizer ignore-type */ $template, $parent);
Loading history...
451
				$parentName = '(' . App\Language::translate($treeDetail['name'], $relModuleName) . ') ';
0 ignored issues
show
Bug introduced by
$relModuleName of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

451
				$parentName = '(' . App\Language::translate($treeDetail['name'], /** @scrutinizer ignore-type */ $relModuleName) . ') ';
Loading history...
452
			}
453
			$tree = [
454
				'id' => $row['tree'],
455
				'name' => $parentName . App\Language::translate($row['name'], $relModuleName),
456
				'parent' => 0 == $parent ? '#' : $parent,
457
			];
458
			if ($showCreatorDetail) {
459
				$tree['rel_created_user'] = \App\Fields\Owner::getLabel($row['rel_created_user']);
460
				$tree['rel_created_time'] = App\Fields\DateTime::formatToDisplay($row['rel_created_time']);
461
			}
462
			if ($showComment) {
463
				$tree['rel_comment'] = $row['rel_comment'];
464
			}
465
			if (!empty($row['icon'])) {
466
				$tree['icon'] = $row['icon'];
467
			}
468
			$trees[] = $tree;
469
		}
470
		return $trees;
471
	}
472
473
	/**
474
	 * Function to get Total number of record in this relation.
475
	 *
476
	 * @return int
477
	 */
478
	public function getRelatedTreeEntriesCount()
479
	{
480
		$recordId = $this->getParentRecordModel()->getId();
481
		$relModuleId = $this->getRelatedModuleModel()->getId();
482
		$treeViewModel = $this->getTreeViewModel();
483
		$template = $treeViewModel->getTemplate();
484
485
		return (new \App\Db\Query())->from('vtiger_trees_templates_data ttd')->innerJoin('u_#__crmentity_rel_tree rel', 'rel.tree = ttd.tree')
0 ignored issues
show
Bug Best Practice introduced by
The expression return new App\Db\Query(...$relModuleId))->count() also could return the type string which is incompatible with the documented return type integer.
Loading history...
486
			->where(['ttd.templateid' => $template, 'rel.crmid' => $recordId, 'rel.relmodule' => $relModuleId])->count();
487
	}
488
489
	/**
490
	 * Get create url from parent record.
491
	 *
492
	 * @param bool $fullView
493
	 *
494
	 * @return string
495
	 */
496
	public function getCreateViewUrl(bool $fullView = false)
497
	{
498
		$createViewUrl = $this->getRelationModel()->getCreateViewUrl($fullView);
499
		if (!empty(Config\Relation::$addSearchParamsToCreateView) && ($searchParams = $this->getArray('search_params')) && isset($searchParams['and']) && \is_array($searchParams['and'])) {
0 ignored issues
show
Bug introduced by
The type Config\Relation was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
500
			foreach ($searchParams['and'] as $row) {
501
				if ('e' === $row['comparator']) {
502
					$createViewUrl .= "&{$row['field_name']}={$row['value']}";
503
				}
504
			}
505
		}
506
		return $createViewUrl;
507
	}
508
509
	/**
510
	 * Function to get the links for related list.
511
	 *
512
	 * @return Vtiger_Link_Model[]
513
	 */
514
	public function getLinks(): array
515
	{
516
		$parentRecordModel = $this->getParentRecordModel();
517
		$relationModelInstance = $this->getRelationModel();
518
		$relatedModuleModel = $relationModelInstance->getRelationModuleModel();
519
		$relatedLink = [
520
			'RELATEDLIST_VIEWS' => [
521
				Vtiger_Link_Model::getInstanceFromValues([
522
					'linktype' => 'RELATEDLIST_VIEWS',
523
					'linklabel' => 'LBL_RECORDS_LIST',
524
					'view' => 'List',
525 1
					'linkicon' => 'far fa-list-alt',
526
				]),
527 1
				Vtiger_Link_Model::getInstanceFromValues([
528 1
					'linktype' => 'RELATEDLIST_VIEWS',
529
					'linklabel' => 'LBL_RECORDS_PREVIEW_LIST',
530 1
					'view' => 'ListPreview',
531 1
					'linkicon' => 'fas fa-desktop',
532 1
				]),
533 1
			],
534 1
		];
535
		if (!$parentRecordModel->isReadOnly()) {
536
			$selectLinks = $this->getSelectRelationLinks();
537 1
			foreach ($selectLinks as $selectLinkModel) {
538 1
				$selectLinkModel->set('_selectRelation', true)->set('_module', $relatedModuleModel);
539
			}
540
			$relatedLink['LISTVIEWBASIC'] = $selectLinks;
541
			$relatedLink = array_merge_recursive($relatedLink, $this->getAddRelationLinks());
542
			if ('Documents' === $relatedModuleModel->getName()) {
0 ignored issues
show
introduced by
The condition 'Documents' === $relatedModuleModel->getName() is always false.
Loading history...
543
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
544
					'linktype' => 'RELATEDLIST_MASSACTIONS',
545
					'linklabel' => 'LBL_MASS_DOWNLOAD',
546
					'linkurl' => "javascript:Vtiger_RelatedList_Js.triggerMassDownload('index.php?module={$parentRecordModel->getModuleName()}&action=RelationAjax&mode=massDownload&src_record={$parentRecordModel->getId()}&relatedModule=Documents&mode=multiple','sendByForm')",
547
					'linkclass' => '',
548
					'linkicon' => 'fas fa-download',
549
				]);
550
			}
551
			if ($relatedModuleModel->isPermitted('MassSendSMS') && ($smsNotifierModel = \Vtiger_Module_Model::getInstance('SMSNotifier'))->isSMSActiveForModule($relatedModuleModel->getName())) {
0 ignored issues
show
Bug introduced by
The method isSMSActiveForModule() does not exist on Vtiger_Module_Model. It seems like you code against a sub-type of Vtiger_Module_Model such as SMSNotifier_Module_Model. ( Ignorable by Annotation )

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

551
			if ($relatedModuleModel->isPermitted('MassSendSMS') && ($smsNotifierModel = \Vtiger_Module_Model::getInstance('SMSNotifier'))->/** @scrutinizer ignore-call */ isSMSActiveForModule($relatedModuleModel->getName())) {
Loading history...
552
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
553
					'linktype' => 'RELATEDLIST_MASSACTIONS',
554
					'linklabel' => 'LBL_MASS_SEND_SMS',
555
					'linkdata' => ['url' => $smsNotifierModel->getMassSMSUrlForModule($relatedModuleModel->getName()), 'type' => 'modal'],
0 ignored issues
show
Bug introduced by
The method getMassSMSUrlForModule() does not exist on Vtiger_Module_Model. It seems like you code against a sub-type of Vtiger_Module_Model such as SMSNotifier_Module_Model. ( Ignorable by Annotation )

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

555
					'linkdata' => ['url' => $smsNotifierModel->/** @scrutinizer ignore-call */ getMassSMSUrlForModule($relatedModuleModel->getName()), 'type' => 'modal'],
Loading history...
556
					'linkicon' => 'fas fa-comment-sms',
557
					'linkclass' => 'js-mass-record-event',
558
				]);
559
			}
560
			if ($relatedModuleModel->isPermitted('QuickExportToExcel')) {
561
				$relatedLink['RELATEDLIST_MASSACTIONS'][] = Vtiger_Link_Model::getInstanceFromValues([
562
					'linktype' => 'RELATEDLIST_MASSACTIONS',
563
					'linklabel' => 'LBL_QUICK_EXPORT',
564
					'linkurl' => "javascript:Vtiger_RelatedList_Js.triggerMassAction('index.php?module={$parentRecordModel->getModuleName()}&action=RelationAjax&mode=exportToExcel&src_record={$parentRecordModel->getId()}&relatedModule={$relatedModuleModel->getName()}&relationId={$this->getRelationModel()->getId()}&isSortActive=true','sendByForm')",
565
					'linkclass' => '',
566
					'linkicon' => 'fas fa-file-export',
567
				]);
568
			}
569
			if ($relatedModuleModel->isPermitted('ExportPdf')) {
570
				$handlerClass = Vtiger_Loader::getComponentClassName('Model', 'PDF', $relatedModuleModel->getName());
571
				$pdfModel = new $handlerClass();
572
				if ($pdfModel->getActiveTemplatesForModule($relatedModuleModel->getName(), 'RelatedList')) {
573
					$relatedLink['RELATEDLIST_BASIC'][] = Vtiger_Link_Model::getInstanceFromValues([
574
						'linktype' => 'RELATEDLIST_BASIC',
575
						'linkdata' => [
576
							'type' => 'modal',
577
							'url' => "index.php?module={$parentRecordModel->getModuleName()}&view=PDF&fromview=RelatedList",
578
						],
579
						'linkclass' => 'btn-light js-mass-record-event',
580
						'linkicon' => 'fas fa-file-pdf',
581
						'linkhint' => \App\Language::translate('LBL_EXPORT_PDF'),
582
					]);
583
				}
584
			}
585
		}
586
		$eventHandler = new App\EventHandler();
587
		$eventHandler->setRecordModel($parentRecordModel);
588
		$eventHandler->setModuleName($relatedModuleModel->getName());
589
		$eventHandler->setParams([
590
			'relatedLink' => $relatedLink,
591
			'viewInstance' => $this,
592
		]);
593
		$eventHandler->trigger('RelationListLinks');
594
		return $eventHandler->getParam('relatedLink');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $eventHandler->getParam('relatedLink') could return the type App\EventHandler|null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
595
	}
596
597
	/**
598
	 * Function to get the select links for related list.
599
	 *
600
	 * @return Vtiger_Link_Model[]
601
	 */
602
	public function getSelectRelationLinks(): array
603
	{
604
		if (!$this->getRelationModel()->isSelectActionSupported() || $this->getParentRecordModel()->isReadOnly()) {
605
			return [];
606
		}
607
		$relatedModel = $this->getRelationModel()->getRelationModuleModel();
608
		if (!$relatedModel->isPermitted('DetailView')) {
609
			return [];
610
		}
611
		return [
612
			Vtiger_Link_Model::getInstanceFromValues([
613
				'linktype' => 'LISTVIEWBASIC',
614
				'linklabel' => \App\Language::translate('LBL_SELECT_RELATION', $relatedModel->getName()),
0 ignored issues
show
Bug introduced by
$relatedModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

614
				'linklabel' => \App\Language::translate('LBL_SELECT_RELATION', /** @scrutinizer ignore-type */ $relatedModel->getName()),
Loading history...
615
				'linkurl' => '',
616
				'linkicon' => 'fas fa-level-up-alt',
617
			]),
618
		];
619
	}
620
621
	/**
622
	 * Function to get the add links for related list.
623
	 *
624
	 * @return Vtiger_Link_Model[][]
625
	 */
626
	public function getAddRelationLinks(): array
627
	{
628
		$relationModelInstance = $this->getRelationModel();
629
		if (!$relationModelInstance->isAddActionSupported() || $this->getParentRecordModel()->isReadOnly()) {
630
			return [];
631
		}
632
		$relatedModel = $relationModelInstance->getRelationModuleModel();
633
		$addLinkModel = [
634
			'LISTVIEWBASIC' => [
635
				Vtiger_Link_Model::getInstanceFromValues([
636
					'linktype' => 'LISTVIEWBASIC',
637
					'linklabel' => App\Language::translate('LBL_ADD_RELATION', $relatedModel->getName()),
0 ignored issues
show
Bug introduced by
$relatedModel->getName() of type boolean is incompatible with the type string expected by parameter $moduleName of App\Language::translate(). ( Ignorable by Annotation )

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

637
					'linklabel' => App\Language::translate('LBL_ADD_RELATION', /** @scrutinizer ignore-type */ $relatedModel->getName()),
Loading history...
638
					'linkurl' => $this->getCreateViewUrl(),
639
					'linkqcs' => $relatedModel->isQuickCreateSupported(),
640
					'linkicon' => 'fas fa-plus',
641
				]),
642
			]
643
		];
644
		if ('Documents' === $relatedModel->getName()) {
0 ignored issues
show
introduced by
The condition 'Documents' === $relatedModel->getName() is always false.
Loading history...
645
			$addLinkModel['RELATEDLIST_BASIC'][] = Vtiger_Link_Model::getInstanceFromValues([
646
				'linktype' => 'LISTVIEWBASIC',
647
				'linklabel' => App\Language::translate('LBL_MASS_ADD', 'Documents'),
648
				'linkdata' => [
649
					'url' => 'index.php?module=Documents&view=MassAddDocuments&sourceView=Detail',
650
					'cb' => 'Documents_MassAddDocuments_Js.register',
651
					'view' => 'Detail',
652
				],
653
				'linkclass' => 'btn-light js-show-modal',
654
				'linkicon' => 'yfi-document-templates',
655
				'showLabel' => 1,
656
			]);
657
		}
658
		return $addLinkModel;
659
	}
660
661
	public function getFavoriteRecords()
662
	{
663
		return (new App\Db\Query())->select(['relcrmid'])->from('u_#__favorites')
664
			->where([
665
				'module' => $this->getParentRecordModel()->getModuleName(),
666
				'relmodule' => $this->getRelatedModuleModel()->getName(),
667
				'crmid' => $this->getParentRecordModel()->getId(),
668
				'userid' => App\User::getCurrentUserId(),
669
			])
670
			->column();
671
	}
672
673
	/**
674
	 * Set fileds.
675
	 *
676
	 * @param string|string[] $fields
677
	 */
678
	public function setFields($fields)
679
	{
680
		if (\is_string($fields)) {
681
			$fields = explode(',', $fields);
682
		}
683
		$relatedListFields = [];
684
		$relFields = $this->getRelationModel()->getRelationFields();
685
		foreach ($fields as $fieldName) {
686
			$fieldModel = $this->relatedModuleModel->getFieldByName($fieldName);
687
			if ($fieldModel) {
688
				$relatedListFields[$fieldName] = $fieldModel;
689
			} elseif (isset($relFields[$fieldName])) {
690
				$relatedListFields[$fieldName] = $relFields[$fieldName];
691
			}
692
		}
693
		$this->relationModel->set('QueryFields', $relatedListFields);
694
	}
695
696
	/**
697
	 * Get widgets instances.
698
	 *
699
	 * @param int $recordId
700
	 *
701
	 * @return array
702
	 */
703
	public function getWidgets(int $recordId): array
704
	{
705
		$widgets = [];
706
		$moduleModel = $this->getRelatedModuleModel();
707
		foreach ($this->getWidgetsList() as $widgetCol) {
708
			foreach ($widgetCol as $widget) {
709
				$widgetName = Vtiger_Loader::getComponentClassName('Widget', $widget['type'], $moduleModel->getName());
710
				if (class_exists($widgetName)) {
711
					$widgetInstance = new $widgetName($moduleModel->getName(), $moduleModel, $recordId, $widget);
712
					$widgetObject = $widgetInstance->getWidget();
713
					if (\count($widgetObject) > 0) {
714
						$widgets[$widgetObject['wcol']][] = $widgetObject;
715
					}
716
				}
717
			}
718
		}
719
		return $widgets;
720
	}
721
722
	/**
723
	 * Get widgets list.
724
	 *
725
	 * @return array
726
	 */
727
	public function getWidgetsList(): array
728
	{
729
		$relationId = $this->getRelationModel()->getId();
730
		if (\App\Cache::has('RelatedModuleWidgets', $relationId)) {
731
			return \App\Cache::get('RelatedModuleWidgets', $relationId);
732
		}
733
		$query = (new App\Db\Query())->from('a_#__relatedlists_widgets')->where(['relation_id' => $relationId]);
734
		$dataReader = $query->orderBy(['sequence' => SORT_ASC])->createCommand()->query();
735
		$widgets = [1 => [], 2 => [], 3 => []];
736
		while ($row = $dataReader->read()) {
737
			$row['data'] = \App\Json::decode($row['data']);
738
			$widgets[$row['wcol']][$row['id']] = $row;
739
		}
740
		$dataReader->close();
741
		App\Cache::save('RelatedModuleWidgets', $relationId, $widgets);
742
		return $widgets;
743
	}
744
745
	/**
746
	 * Check if widgets exist.
747
	 *
748
	 * @return bool
749
	 */
750
	public function isWidgetsList(): bool
751
	{
752
		$widgets = $this->getWidgetsList();
753
		return !empty($widgets[1]) || !empty($widgets[2]) || !empty($widgets[3]);
754
	}
755
756
	/**
757
	 * Locked fields according to parameters passed.
758
	 *
759
	 * @param App\Request $request
760
	 *
761
	 * @return void
762
	 */
763
	public function loadSearchLockedFields(App\Request $request): void
764
	{
765
		$moduleModel = $this->getRelationModel()->getRelationModuleModel();
766
		if (!$request->isEmpty('lockedFields')) {
767
			foreach ($request->getArray('lockedFields') as $value) {
768
				$moduleModel->getFieldByName($value)->set('searchLockedFields', true);
769
			}
770
		}
771
		if (!$request->isEmpty('lockedEmptyFields')) {
772
			foreach ($request->getArray('lockedEmptyFields') as $value) {
773
				if (strpos($value, ':')) {
774
					[$fieldName, $moduleName] = explode(':', $value);
775
					$moduleModel = \Vtiger_Module_Model::getInstance($moduleName);
776
					$value = $fieldName;
777
				}
778
				$moduleModel->getFieldByName($value)->set('searchLockedEmptyFields', true);
779
			}
780
		}
781
		if (!$request->isEmpty('search_params')) {
782
			foreach ($request->getArray('search_params') as $values) {
783
				foreach ($values as $value) {
784
					if ('y' === $value[1]) {
785
						$moduleModel->getFieldByName($value[0])->set('searchLockedEmptyFields', true);
786
					}
787
				}
788
			}
789
		}
790
	}
791
}
792