DdsDataDefinitionManager   F
last analyzed

Complexity

Total Complexity 108

Size/Duplication

Total Lines 620
Duplicated Lines 0 %

Test Coverage

Coverage 89.52%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 279
c 1
b 1
f 0
dl 0
loc 620
ccs 282
cts 315
cp 0.8952
rs 2
wmc 108

33 Methods

Rating   Name   Duplication   Size   Complexity  
A isClassTypeUnique() 0 5 1
A isMemberRefUnique() 0 7 2
A addDataType() 0 21 3
A getMappableClasses() 0 18 2
A editDataType() 0 15 5
A destroyMember() 0 10 2
A deleteMember() 0 9 3
A undeleteDataType() 0 8 3
A deleteDataType() 0 8 3
A undeleteClass() 0 9 3
A isDataTypeUnique() 0 5 1
A getDataType() 0 3 1
A getClassTypeMigrationSql() 0 6 2
A listMembers() 0 3 1
A getMemberUpdateDifferences() 0 21 6
A countClasses() 0 4 1
A memberAllowedLinks() 0 9 1
C addMember() 0 40 12
A getMember() 0 13 2
A listDataTypes() 0 10 2
A deleteClass() 0 9 3
A getClass() 0 9 3
A listClasses() 0 13 3
A setMemberAsMapField() 0 14 5
B destroyClass() 0 25 7
A editClass() 0 16 5
A undeleteMember() 0 10 3
A memberAllowedChoices() 0 5 1
A _getClassesQuery() 0 13 3
B editMember() 0 30 11
A addClass() 0 15 4
A getClassMapField() 0 4 2
A destroyDataType() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like DdsDataDefinitionManager 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 DdsDataDefinitionManager, 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\IDdsDataTypeManagement;
11
use neon\daedalus\interfaces\IDdsClassManagement;
12
use neon\daedalus\services\ddsManager\DdsCore;
13
use neon\daedalus\services\ddsManager\models\DdsClass;
14
use neon\daedalus\services\ddsManager\models\DdsDataType;
15
use neon\daedalus\services\ddsManager\models\DdsMember;
16
17
class DdsDataDefinitionManager extends DdsCore
18
implements IDdsDataTypeManagement, IDdsClassManagement
19
{
20
21
	/** --------------------------------------------------- **/
22
	/** ---------- interface IDdsClassManagement ---------- **/
23
	/** --------------------------------------------------- **/
24
25
	/** ---------- Utility Methods ---------- **/
26
27
	/**
28
	 * @inheritdoc
29
	 */
30 6
	public function isClassTypeUnique($candidate)
31
	{
32 6
		$ct = $this->canonicaliseRef($candidate);
33 6
		$class = DdsClass::find()->where(['class_type'=>$ct])->one();
34 6
		return ($class == null);
35
	}
36
37
	/**
38
	 * @inheritdoc
39
	 */
40 4
	public function isMemberRefUnique($classType, $candidate)
41
	{
42 4
		if ($this->isClassTypeUnique($classType))
43 2
			throw new \InvalidArgumentException("Class type ".$this->canonicaliseRef($classType)." unknown");
44 2
		$mr = $this->canonicaliseRef($candidate);
45 2
		$member = DdsMember::find()->where(['class_type'=>$classType, 'member_ref'=>$mr])->one();
46 2
		return ($member == null);
47
	}
48
49
	/** ---------- Class Manipulations ---------- **/
50
51
	/**
52
	 * @inheritdoc
53
	 */
54
	public function countClasses($module=null, $includeDeleted=false)
55
	{
56
		$query = $this->_getClassesQuery($module, $includeDeleted);
57
		return $query->count();
58
	}
59
60
	/**
61
	 * @inheritdoc
62
	 */
63 6
	public function listClasses($module, &$total, $includeDeleted=false, $start=0, $length=100, $orderBy='label')
64
	{
65 6
		$query = $this->_getClassesQuery($module, $includeDeleted);
66 6
		if ($start == 0)
67 6
			$total = (int) $query->count();
68 6
		$results = $query->asArray()
69 6
			->offset($start)
70 6
			->limit($length)
71 6
			->orderBy($orderBy)
72 6
			->all();
73 6
		foreach ($results as &$r)
74 4
			$r['count_current'] = $r['count_total'] - $r['count_deleted'];
75 6
		return $results;
76
	}
77
78
	/**
79
	 * @inheritdoc
80
	 */
81 2
	public function getMappableClasses()
82
	{
83 2
		$ddsMemberTbl = DdsMember::tableName();
84 2
		$ddsClassTbl = DdsClass::tableName();
85 2
		$mappable = DdsClass::find()
86 2
			->innerJoin($ddsMemberTbl, "$ddsClassTbl.class_type = $ddsMemberTbl.class_type")
87 2
			->select("$ddsClassTbl.class_type, $ddsClassTbl.label")
88 2
			->where([
89 2
				"$ddsMemberTbl.map_field" => 1,
90 2
				"$ddsClassTbl.deleted" => 0,
91 2
				"$ddsMemberTbl.deleted" => 0
92
			])
93 2
			->asArray()
94 2
			->all();
95 2
		$map = [];
96 2
		foreach ($mappable as $m)
97 2
			$map[$m['class_type']] = $m['label'];
98 2
		return $map;
99
	}
100
101
	/**
102
	 * Get the map field for a class
103
	 * @param string $class
104
	 */
105 2
	public function getClassMapField($class)
106
	{
107 2
		$member = $this->getMapMemberForClass($class);
108 2
		return $member ? $member['member_ref'] : null;
109
	}
110
111
	/**
112
	 * @inheritdoc
113
	 */
114 88
	public function addClass(&$classType, $module)
115
	{
116 88
		$classType = $this->canonicaliseRef($classType);
117 88
		if (DdsClass::findOne(['class_type'=>$classType]) !== null)
118
			throw new \InvalidArgumentException("The class $classType already exists");
119 88
		$c = new DdsClass();
120 88
		$c->class_type = $classType;
121 88
		$c->module = (empty($module) ? null : $module);
122 88
		if (!$c->save())
123
			throw new \RuntimeException("Couldn't save the class: ".print_r($c->errors,true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($c->errors, 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

123
			throw new \RuntimeException("Couldn't save the class: "./** @scrutinizer ignore-type */ print_r($c->errors,true));
Loading history...
124 88
		$upSql = $this->getClassTypeMigrationSql($classType);
125 88
		$downSql = "DELETE FROM `dds_class` WHERE `class_type`='$classType';";
126 88
		$this->storeMigration($upSql, $downSql);
127 88
		$this->createClassTable($classType);
0 ignored issues
show
Bug introduced by
$classType of type string is incompatible with the type neon\daedalus\services\ddsManager\type expected by parameter $classType of neon\daedalus\services\d...ore::createClassTable(). ( Ignorable by Annotation )

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

127
		$this->createClassTable(/** @scrutinizer ignore-type */ $classType);
Loading history...
128 88
		return $c->class_type;
129
	}
130
131
	/**
132
	 * @inheritdoc
133
	 */
134 12
	public function getClass($classType, $includeMembers=false)
135
	{
136 12
		if ($this->findClass($classType, $class)) {
137 12
			$data = $class->toArray();
138 12
			if ($includeMembers)
139
				$data['members'] = $this->listMembers($class['class_type']);
140 12
			return $data;
141
		}
142
		return null;
143
	}
144
145
	/**
146
	 * @inheritdoc
147
	 */
148 30
	public function editClass($classType, $changes)
149
	{
150 30
		$allowed = array_intersect_key($changes, array_flip(['label', 'description', 'module', 'change_log']));
151 30
		if (count($allowed)==0)
152
			return;
153 30
		$this->clearClassCache($classType);
154 30
		$downSql = $this->getClassTypeMigrationSql($classType);
155 30
		if ($this->findClass($classType, $class)) {
156
			// find the minimum set of actual changes and only update if there are any
157 30
			$updates = array_diff_assoc($allowed, $class->attributes);
158 30
			if (count($updates)) {
159 30
				$class->attributes = $updates;
160 30
				if (!$class->save())
161
					throw new \RuntimeException("Dds: Couldn't update the class '$classType': ".print_r($class->errors));
0 ignored issues
show
Bug introduced by
Are you sure print_r($class->errors) 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

161
					throw new \RuntimeException("Dds: Couldn't update the class '$classType': "./** @scrutinizer ignore-type */ print_r($class->errors));
Loading history...
162 30
				$upSql = $this->getClassTypeMigrationSql($classType);
163 30
				$this->storeMigration($upSql, $downSql);
164
			}
165
		}
166 30
	}
167
168
	/**
169
	 * @inheritdoc
170
	 */
171 24
	public function deleteClass($classType)
172
	{
173
		// delete a class if it hasn't already been deleted
174 24
		if ($this->findClass($classType, $class) && $class->deleted == 0) {
175 24
			$upSql = "UPDATE `dds_class` SET `deleted`=1 WHERE `class_type`='{$class->class_type}';";
176 24
			neon()->db->createCommand($upSql)->execute();
177 24
			$downSql = "UPDATE `dds_class` SET `deleted`=0 WHERE `class_type`='{$class->class_type}';";
178 24
			$this->storeMigration($upSql, $downSql);
179 24
			$this->clearClassCache($classType);
180
		}
181 24
	}
182
183
	/**
184
	 * @inheritdoc
185
	 */
186 4
	public function undeleteClass($classType)
187
	{
188
		// undelete a class if it is already deleted
189 4
		if ($this->findClass($classType, $class) && $class->deleted == 1) {
190 4
			$upSql = "UPDATE `dds_class` SET `deleted`=0 WHERE `class_type`='{$class->class_type}';";
191 4
			neon()->db->createCommand($upSql)->execute();
192 4
			$downSql = "UPDATE `dds_class` SET `deleted`=1 WHERE `class_type`='{$class->class_type}';";
193 4
			$this->storeMigration($upSql, $downSql);
194 4
			$this->clearClassCache($classType);
195
		}
196 4
	}
197
198
	/**
199
	 * @inheritdoc
200
	 */
201 28
	public function destroyClass($classType)
202
	{
203 28
		if ($this->findClass($classType, $class)) {
204 28
			if ($this->hasObjects($classType))
205
				throw new \RunTimeException("You need to delete all objects before you can destroy a class");
206
207
			// now delete the rows
208 28
			$members = DdsMember::find()->where(['class_type'=>$class->class_type])->asArray()->all();
209 28
			$errors = [];
210 28
			foreach ($members as $member) {
211
				try {
212 22
					$this->destroyMember($member['class_type'], $member['member_ref']);
213
				} catch (\Exception $e) {
214
					$errors[] = $e->getMessage();
215
				}
216
			}
217 28
			if (count($errors))
218
				throw new \RuntimeException("Couldn't destroy all of the members: ".print_r($errors, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($errors, 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

218
				throw new \RuntimeException("Couldn't destroy all of the members: "./** @scrutinizer ignore-type */ print_r($errors, true));
Loading history...
219
			// now delete the class
220
			// can also return 0 - which is valid - false means there has been an error
221 28
			if ($class->delete() === false)
222
				throw new \RuntimeException("Couldn't destroy the class: ".print_r($class->errors, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($class->errors, 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

222
				throw new \RuntimeException("Couldn't destroy the class: "./** @scrutinizer ignore-type */ print_r($class->errors, true));
Loading history...
223
			// and clear away the table
224 28
			$this->dropClassTable($class->class_type);
225 28
			$this->clearClassCache($classType);
226
		}
227 28
	}
228
229
230
	/** ---------- Member Manipulations(!!) ---------- **/
231
232
	/**
233
	 * @inheritdoc
234
	 */
235 8
	public function listMembers($classType, $includeDeleted=false, $keyBy='member_ref')
236
	{
237 8
		return $this->listMembersForClass($classType, $includeDeleted, $keyBy);
238
	}
239
240
	/**
241
	 * @inheritdoc
242
	 */
243 86
	public function addMember($classType, &$memberRef, $dataTypeRef, $label, $description, $additional=null)
244
	{
245 86
		$classType = $this->canonicaliseRef($classType);
246 86
		$memberRef = $this->canonicaliseRef($memberRef);
247 86
		$class = null;
248 86
		if ($this->findClass($classType, $class)) {
249
			// extract out any additional information
250 86
			$choices = ((!empty($additional['choices']) && is_array($additional['choices'])) ? $additional['choices'] : []);
251 86
			$linkClass = (!empty($additional['link_class']) ? $additional['link_class'] : null);
252 86
			if (DdsMember::findOne(['class_type' => $classType, 'member_ref' => $memberRef])!==null)
253
				throw new \InvalidArgumentException("The member $memberRef already exists");
254 86
			if (in_array($dataTypeRef, ['link_uni', 'link_multi']) && !$linkClass) {
255
				throw new \InvalidArgumentException("Link_uni, link_multi data types require a link_class to be passed for member '$classType::$memberRef'. You passed in ".print_r($additional, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($additional, 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

255
				throw new \InvalidArgumentException("Link_uni, link_multi data types require a link_class to be passed for member '$classType::$memberRef'. You passed in "./** @scrutinizer ignore-type */ print_r($additional, true));
Loading history...
256
			}
257 86
			$member = new DdsMember();
258 86
			$member->class_type = $classType;
259 86
			$member->member_ref = $memberRef;
260 86
			$member->data_type_ref = $dataTypeRef;
261 86
			$member->label = $label;
262 86
			$member->description = $description;
263 86
			$member->choices = ($this->memberAllowedChoices($dataTypeRef) ? $choices : null);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->memberAllowedChoi...eRef) ? $choices : null can also be of type array. However, the property $choices is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
264 86
			$member->link_class = ($this->memberAllowedLinks($dataTypeRef) ? $linkClass : null);
265 86
			$member->created = $this->now();
0 ignored issues
show
Bug Best Practice introduced by
The property created does not exist on neon\daedalus\services\ddsManager\models\DdsMember. Since you implemented __set, consider adding a @property annotation.
Loading history...
266 86
			if (!$member->save())
267
				throw new \RuntimeException("Couldn't save the member: ".print_r($member->errors, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($member->errors, 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

267
				throw new \RuntimeException("Couldn't save the member: "./** @scrutinizer ignore-type */ print_r($member->errors, true));
Loading history...
268
269
			// create the migrations
270 86
			$savedMember = $this->getMember($classType, $memberRef);
271 86
			$upMember = $this->getTableRowReplaceSql('dds_member', $savedMember);
272 86
			$downMember = "DELETE FROM `dds_member` WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
273 86
			$mId = $this->storeMigration($upMember, $downMember);
274
275
			// and create the member column
276 86
			if (($error=$this->addClassMemberColumn($classType, $member->member_ref)) !== true) {
277
				$member->delete();
278
				$this->removeMigration($mId);
279
				throw new \RuntimeException("Couldn't create the member column. Error=".$error);
280
			}
281 86
			$this->clearClassMemberCache($classType);
282 86
			return $member->member_ref;
283
		}
284
	}
285
286
	/**
287
	 * @inheritdoc
288
	 */
289 86
	public function getMember($classType, $memberRef, $fields=[])
290
	{
291
		// !! keep member as an object so it runs all conversion code
292 86
		$query = DdsMember::find();
293 86
		if (count($fields)) {
294 2
			$selection = array_intersect($fields, [
295 2
				'class_type', 'member_ref', 'data_type_ref', 'label', 'description', 'definition', 'choices', 'link_class', 'deleted'
296
			]);
297
			// ensure only allowed fields are passed through
298 2
			$query = $query->select($selection);
299
		}
300 86
		$member = $query->where(['member_ref' => $memberRef, 'class_type' => $classType])->one();
301 86
		return $member->toArray();
302
	}
303
304
	/**
305
	 * @inheritdoc
306
	 */
307 6
	public function editMember($classType, $memberRef, $changes)
308
	{
309
		// check that only allowed updates are made
310 6
		$allowed = array_intersect_key($changes, array_flip(['label', 'description', 'definition', 'choices', 'link_class']));
311 6
		if (count($allowed)==0)
312
			return;
313
314
		// find the member and remove any illegitamate updates
315 6
		if (!$this->findMember($classType, $memberRef, $member))
316
			throw new \InvalidArgumentException("No member found");
317 6
		if (isset($allowed['choices'])) {
318 2
			$allowed['choices'] = empty($allowed['choices']) ? [] : $allowed['choices'];
319 2
			if (!(is_array($allowed['choices']) && $this->memberAllowedChoices($member->data_type_ref))) {
320
				unset($allowed['choices']);
321
			}
322
		}
323
324 6
		if (isset($allowed['link_class']) && !$this->memberAllowedLinks($member->data_type_ref))
325
			unset($allowed['link_class']);
326
327
		// finally check to see if there are any actual updates and if so save the provided updates
328 6
		$updates = $this->getMemberUpdateDifferences($allowed, $member->attributes);
329 6
		if (count($updates)) {
330 6
			$downSql = $this->getTableRowReplaceSql('dds_member', $member);
331 6
			$member->attributes = $updates;
332 6
			if (!$member->save())
333
				throw new \RuntimeException("Couldn't update the member: ".print_r ($member->errors, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($member->errors, 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

333
				throw new \RuntimeException("Couldn't update the member: "./** @scrutinizer ignore-type */ print_r ($member->errors, true));
Loading history...
334 6
			$upSql = $this->getTableRowReplaceSql('dds_member', $member);
335 6
			$this->storeMigration($upSql, $downSql);
336 6
			$this->clearClassMemberCache($classType);
337
		}
338 6
	}
339
340
	/**
341
	 * @inheritdoc
342
	 */
343 26
	public function setMemberAsMapField($classType, $memberRef)
344
	{
345 26
		if (!$this->findMember($classType, $memberRef, $member))
346
			throw new \InvalidArgumentException("No member found");
347
348
		// find out which member had the map field set for migrations
349 26
		$oldMember = DdsMember::findOne(['class_type'=>$classType, 'map_field'=>1]);
350 26
		if ($oldMember && $oldMember->member_ref == $memberRef)
351
			return;
352 26
		$upSql = "UPDATE `dds_member` SET `map_field`=(IF (`member_ref`='$memberRef', 1, 0)) WHERE `class_type`='$classType';";
353 26
		$downSql = ($oldMember ? "UPDATE `dds_member` SET `map_field`=(IF (`member_ref`='{$oldMember->member_ref}', 1, 0)) WHERE `class_type`='$classType';" : null);
0 ignored issues
show
introduced by
$oldMember is of type yii\db\ActiveRecord, thus it always evaluated to true.
Loading history...
354 26
		neon()->db->createCommand($upSql)->execute();
355 26
		$this->storeMigration($upSql, $downSql);
356 26
		$this->clearClassMemberCache($classType);
357 26
	}
358
359
360
	/**
361
	 * @inheritdoc
362
	 */
363 2
	public function deleteMember($classType, $memberRef)
364
	{
365
		// delete a member if it isn't already deleted
366 2
		if ($this->findMember($classType, $memberRef, $member) && $member->deleted==0) {
367 2
			$upSql = "UPDATE `dds_member` SET `deleted`=1 WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
368 2
			$downSql = "UPDATE `dds_member` SET `deleted`=0 WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
369 2
			neon()->db->createCommand($upSql)->execute();
370 2
			$this->storeMigration($upSql, $downSql);
371 2
			$this->clearClassMemberCache($classType);
372
		}
373 2
	}
374
375
	/**
376
	 * @inheritdoc
377
	 */
378 2
	public function undeleteMember($classType, $memberRef)
379
	{
380
		// undelete a member if it is already deleted
381 2
		if ($this->findMember($classType, $memberRef, $member) && $member->deleted==1) {
382
			// create migrations
383 2
			$upSql = "UPDATE `dds_member` SET `deleted`=0 WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
384 2
			$downSql = "UPDATE `dds_member` SET `deleted`=1 WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
385 2
			neon()->db->createCommand($upSql)->execute();
386 2
			$this->storeMigration($upSql, $downSql);
387 2
			$this->clearClassMemberCache($classType);
388
		}
389 2
	}
390
391
	/**
392
	 * @inheritdoc
393
	 */
394 26
	public function destroyMember($classType, $memberRef)
395
	{
396 26
		if ($this->findMember($classType, $memberRef, $member)) {
397
			// try and drop the column first and then the member definition
398 26
			$this->dropClassMemberColumn($classType, $memberRef);
399 26
			$upSql = "DELETE FROM `dds_member` WHERE `class_type`='$classType' AND `member_ref`='$memberRef';";
400 26
			$downSql = $this->getTableRowReplaceSql('dds_member', $member);
401 26
			neon()->db->createCommand($upSql)->execute();
402 26
			$this->storeMigration($upSql, $downSql);
403 26
			$this->clearClassMemberCache($classType);
404
		}
405 26
	}
406
407
	/** ------------------------------------------------------ **/
408
	/** ---------- interface IDdsDataTypeManagement ---------- **/
409
	/** ------------------------------------------------------ **/
410
411
	/** ---------- Utility Methods ---------- **/
412
413
	/**
414
	 * @inheritdoc
415
	 */
416 2
	public function isDataTypeUnique($candidate)
417
	{
418 2
		$dt = $this->canonicaliseRef($candidate);
419 2
		$dataType = DdsDataType::find()->where(['data_type_ref'=>$dt])->one();
420 2
		return ($dataType == null);
421
	}
422
423
	/** ---------- Data Type Methods ---------- **/
424
425
	/**
426
	 * @inheritdoc
427
	 */
428 2
	public function listDataTypes($includeDeleted=false)
429
	{
430 2
		$query = DdsDataType::find();
431 2
		$select = ['data_type_ref', 'label', 'description', 'definition', 'storage_ref'];
432 2
		if ($includeDeleted) {
433 2
			$select[] = 'deleted';
434
		} else {
435 2
			$query->where(['deleted'=>0]);
436
		}
437 2
		return $query->select($select)->asArray()->all();
438
	}
439
440
	/**
441
	 * @inheritdoc
442
	 */
443 88
	public function addDataType(&$dataTypeRef, $label, $description, $definition, $storageRef)
444
	{
445 88
		$dataTypeRef = $this->canonicaliseRef($dataTypeRef);
446 88
		if ($this->findDataType($dataTypeRef))
447
			throw new \InvalidArgumentException("The dataType $dataTypeRef already exists");
448 88
		$dt = new DdsDataType();
449 88
		$dt->data_type_ref = $dataTypeRef;
450 88
		$dt->label = $label;
451 88
		$dt->description = $description;
452 88
		$dt->definition = $definition;
453 88
		$dt->storage_ref = $storageRef;
454 88
		if (!$dt->save())
455
			throw new \RuntimeException("Couldn't create the datatype $dataTypeRef: ".print_r($dt->errors, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($dt->errors, 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

455
			throw new \RuntimeException("Couldn't create the datatype $dataTypeRef: "./** @scrutinizer ignore-type */ print_r($dt->errors, true));
Loading history...
456
457
		// store the migration
458 88
		$this->findDataType($dataTypeRef, $dt);
459 88
		$upSql = $this->getTableRowReplaceSql('dds_data_type', $dt);
460 88
		$downSql = "DELETE FROM `dds_data_type` WHERE `data_type_ref`='$dataTypeRef';";
461 88
		$this->storeMigration($upSql, $downSql);
462
463 88
		return $dt->data_type_ref;
464
	}
465
466
	/**
467
	 * @inheritdoc
468
	 */
469 6
	public function getDataType($dataTypeRef)
470
	{
471 6
		return DdsDataType::find()->where(['data_type_ref'=>$dataTypeRef])->asArray()->one();
472
	}
473
474
	/**
475
	 * @inheritdoc
476
	 */
477 2
	public function editDataType($dataTypeRef, $changes)
478
	{
479 2
		$allowed = array_intersect_key($changes, array_flip(['label', 'description', 'definition']));
480 2
		if (count($allowed) == 0)
481
			return;
482 2
		if ($this->findDataType($dataTypeRef, $dataType)) {
483
			// make the updates if there is anything to change
484 2
			$updates = array_diff($allowed, $dataType->attributes);
485 2
			if (count($updates)) {
486 2
				$downSql = $this->getTableRowReplaceSql('dds_data_type', $dataType);
487 2
				$dataType->attributes = $updates;
488 2
				if (!$dataType->save())
489
					throw new \RuntimeException("Couldn't save the datatype: ".print_r($dataType->errors(),true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($dataType->errors(), 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

489
					throw new \RuntimeException("Couldn't save the datatype: "./** @scrutinizer ignore-type */ print_r($dataType->errors(),true));
Loading history...
490 2
				$upSql = $this->getTableRowReplaceSql('dds_data_type', $dataType);
491 2
				$this->storeMigration($upSql, $downSql);
492
			}
493
		}
494 2
	}
495
496
	/**
497
	 * @inheritdoc
498
	 */
499 2
	public function deleteDataType($dataTypeRef)
500
	{
501
		// delete the datatype if not already deleted
502 2
		if ($this->findDataType($dataTypeRef, $dataType) && $dataType->deleted == 0) {
503 2
			$upSql = "UPDATE `dds_data_type` SET `deleted`=1 WHERE `data_type_ref`='$dataTypeRef';";
504 2
			$downSql = "UPDATE `dds_data_type` SET `deleted`=0 WHERE `data_type_ref`='$dataTypeRef';";
505 2
			neon()->db->createCommand($upSql)->execute();
506 2
			$this->storeMigration($upSql, $downSql);
507
		}
508 2
	}
509
510
	/**
511
	 * @inheritdoc
512
	 */
513 2
	public function undeleteDataType($dataTypeRef)
514
	{
515
		// undelete the datatype if not already undeleleted
516 2
		if ($this->findDataType($dataTypeRef, $dataType) && $dataType->deleted = 1) {
517 2
			$upSql = "UPDATE `dds_data_type` SET `deleted`=0 WHERE `data_type_ref`='$dataTypeRef';";
518 2
			$downSql = "UPDATE `dds_data_type` SET `deleted`=1 WHERE `data_type_ref`='$dataTypeRef';";
519 2
			neon()->db->createCommand($upSql)->execute();
520 2
			$this->storeMigration($upSql, $downSql);
521
		}
522 2
	}
523
524
	/**
525
	 * @inheritdoc
526
	 */
527 2
	public function destroyDataType($dataTypeRef)
528
	{
529
		// destroy the datatype if it exists
530 2
		if ($this->findDataType($dataTypeRef, $dataType)) {
531 2
			$upSql = "DELETE FROM `dds_data_type` WHERE `data_type_ref`='$dataTypeRef';";
532 2
			$downSql = $this->getTableRowReplaceSql('dds_data_type', $dataType);
533 2
			neon()->db->createCommand($upSql)->execute();
534 2
			$this->storeMigration($upSql, $downSql);
535
		}
536 2
	}
537
538
539
540
	/** ------------------------------------ **/
541
	/** ---------- Private Methods --------- **/
542
	/** ------------------------------------ **/
543
544
545
	/**
546
	 * Convert a dds_class entry into an insert statement for migrations
547
	 * @param string $classType
548
	 * @return null|string
549
	 */
550 88
	private function getClassTypeMigrationSql($classType)
551
	{
552 88
		$ddsClass = DdsClass::findOne(['class_type'=>$classType]);
553 88
		if ($ddsClass == null)
554
			return null;
555 88
		return $this->getTableRowReplaceSql('dds_class', $ddsClass);
556
	}
557
558
	/**
559
	 * Check if a particular member type is allowed to store choices.
560
	 *
561
	 * @param string $dataTypeRef
562
	 * @return bool
563
	 */
564 86
	private function memberAllowedChoices($dataTypeRef)
565
	{
566
		// if additional datatypes are allowed then update appropriately
567 86
		$allowedDataTypes = ['choice', 'choice_multiple'];
568 86
		return in_array($dataTypeRef, $allowedDataTypes);
569
	}
570
571
	/**
572
	 * Check if a particular member type is allowed to store links in the links table
573
	 *
574
	 * @param string $dataTypeRef
575
	 * @return bool
576
	 */
577 86
	private function memberAllowedLinks($dataTypeRef)
578
	{
579
		// if additional datatypes can be links then update appropriately
580
		$allowedDataTypes = [
581 86
			'link_multi',
582
			'link_uni',
583
			'file_ref_multi'
584
		];
585 86
		return in_array($dataTypeRef, $allowedDataTypes);
586
	}
587
588
	/**
589
	 * Get the difference between a set of changes and the member fields
590
	 *
591
	 * @param array $changes  set of requested changes
592
	 * @param array $member  current member values
593
	 */
594 6
	protected function getMemberUpdateDifferences($changes, $member)
595
	{
596 6
		$a = $changes;
597 6
		$b = $member;
598 6
		$diffs = [];
599 6
		foreach ($a as $k=>$v) {
600 6
			if (array_key_exists($k, $b)) {
601 6
				if (is_array($v)) {
602 2
					$diff = $this->getMemberUpdateDifferences($v, $b[$k]);
603
					// we want all of the array if there were any differences
604 2
					if (count($diff))
605 2
						$diffs[$k] = $v;
606
				} else {
607 6
					if ((string)$v !== (string)$b[$k])
608 6
						$diffs[$k] = $v;
609
				}
610
			} else {
611 2
				$diffs[$k] = $v;
612
			}
613
		}
614 6
		return $diffs;
615
	}
616
617
618
	/**
619
	 * static cache array of class data to reduce database calls
620
	 * @var array
621
	 */
622
	private static $_classCache = [];
0 ignored issues
show
introduced by
The private property $_classCache is not used, and could be removed.
Loading history...
623
624 6
	private function _getClassesQuery($module=null, $includeDeleted=false)
625
	{
626 6
		$query = DdsClass::find();
627 6
		$select = ['class_type', 'label', 'description', 'module', 'count_total', 'count_deleted', 'change_log'];
628 6
		if ($includeDeleted) {
629 4
			$select[] = 'deleted';
630
		} else {
631 4
			$query->andWhere(['deleted' => 0]);
632
		}
633 6
		$query->select($select);
634 6
		if ($module != null)
635 6
			$query->andWhere(['or', ['module' => $module], ['module' => NULL]]);
636 6
		return $query;
637
	}
638
639
}
640