ActivityDomainTrait   C
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 377
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 12
Bugs 0 Features 0
Metric Value
wmc 42
c 12
b 0
f 0
lcom 1
cbo 18
dl 0
loc 377
rs 6.1097

15 Methods

Rating   Name   Duplication   Size   Complexity  
B paginate() 0 27 3
A read() 0 11 2
B create() 0 25 3
A delete() 0 19 3
A setActorId() 0 21 3
A setObjectId() 0 21 3
A setTargetId() 0 21 3
B update() 0 38 5
B applyFilter() 0 20 5
A dispatch() 0 22 3
A doSetActorId() 0 9 2
A doSetObjectId() 0 9 2
A doSetTargetId() 0 9 2
A get() 0 12 3
getServiceContainer() 0 1 ?

How to fix   Complexity   

Complex Class

Complex classes like ActivityDomainTrait 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 ActivityDomainTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace keeko\core\domain\base;
3
4
use keeko\core\event\ActivityEvent;
5
use keeko\core\model\ActivityQuery;
6
use keeko\core\model\Activity;
7
use keeko\framework\domain\payload\Created;
8
use keeko\framework\domain\payload\Deleted;
9
use keeko\framework\domain\payload\Found;
10
use keeko\framework\domain\payload\NotDeleted;
11
use keeko\framework\domain\payload\NotFound;
12
use keeko\framework\domain\payload\NotUpdated;
13
use keeko\framework\domain\payload\NotValid;
14
use keeko\framework\domain\payload\PayloadInterface;
15
use keeko\framework\domain\payload\Updated;
16
use keeko\framework\service\ServiceContainer;
17
use keeko\framework\utils\NameUtils;
18
use keeko\framework\utils\Parameters;
19
use phootwork\collection\Map;
20
21
/**
22
 */
23
trait ActivityDomainTrait {
24
25
	/**
26
	 */
27
	protected $pool;
28
29
	/**
30
	 * Creates a new Activity with the provided data
31
	 * 
32
	 * @param mixed $data
33
	 * @return PayloadInterface
34
	 */
35
	public function create($data) {
36
		// hydrate
37
		$serializer = Activity::getSerializer();
38
		$model = $serializer->hydrate(new Activity(), $data);
39
		$this->hydrateRelationships($model, $data);
0 ignored issues
show
Bug introduced by
It seems like hydrateRelationships() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
40
41
		// dispatch pre save hooks
42
		$this->dispatch(ActivityEvent::PRE_CREATE, $model, $data);
43
		$this->dispatch(ActivityEvent::PRE_SAVE, $model, $data);
44
45
		// validate
46
		$validator = $this->getValidator();
0 ignored issues
show
Bug introduced by
It seems like getValidator() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
47
		if ($validator !== null && !$validator->validate($model)) {
48
			return new NotValid([
49
				'errors' => $validator->getValidationFailures()
50
			]);
51
		}
52
53
		// save and dispatch post save hooks
54
		$model->save();
55
		$this->dispatch(ActivityEvent::POST_CREATE, $model, $data);
56
		$this->dispatch(ActivityEvent::POST_SAVE, $model, $data);
57
58
		return new Created(['model' => $model]);
59
	}
60
61
	/**
62
	 * Deletes a Activity with the given id
63
	 * 
64
	 * @param mixed $id
65
	 * @return PayloadInterface
66
	 */
67
	public function delete($id) {
68
		// find
69
		$model = $this->get($id);
70
71
		if ($model === null) {
72
			return new NotFound(['message' => 'Activity not found.']);
73
		}
74
75
		// delete
76
		$this->dispatch(ActivityEvent::PRE_DELETE, $model);
77
		$model->delete();
78
79
		if ($model->isDeleted()) {
80
			$this->dispatch(ActivityEvent::POST_DELETE, $model);
81
			return new Deleted(['model' => $model]);
82
		}
83
84
		return new NotDeleted(['message' => 'Could not delete Activity']);
85
	}
86
87
	/**
88
	 * Returns a paginated result
89
	 * 
90
	 * @param Parameters $params
91
	 * @return PayloadInterface
92
	 */
93
	public function paginate(Parameters $params) {
94
		$sysPrefs = $this->getServiceContainer()->getPreferenceLoader()->getSystemPreferences();
95
		$defaultSize = $sysPrefs->getPaginationSize();
96
		$page = $params->getPage('number');
97
		$size = $params->getPage('size', $defaultSize);
98
99
		$query = ActivityQuery::create();
100
101
		// sorting
102
		$sort = $params->getSort(Activity::getSerializer()->getSortFields());
103
		foreach ($sort as $field => $order) {
104
			$method = 'orderBy' . NameUtils::toStudlyCase($field);
105
			$query->$method($order);
106
		}
107
108
		// filtering
109
		$filter = $params->getFilter();
110
		if (!empty($filter)) {
111
			$this->applyFilter($query, $filter);
112
		}
113
114
		// paginate
115
		$model = $query->paginate($page, $size);
116
117
		// run response
118
		return new Found(['model' => $model]);
119
	}
120
121
	/**
122
	 * Returns one Activity with the given id
123
	 * 
124
	 * @param mixed $id
125
	 * @return PayloadInterface
126
	 */
127
	public function read($id) {
128
		// read
129
		$model = $this->get($id);
130
131
		// check existence
132
		if ($model === null) {
133
			return new NotFound(['message' => 'Activity not found.']);
134
		}
135
136
		return new Found(['model' => $model]);
137
	}
138
139
	/**
140
	 * Sets the Actor id
141
	 * 
142
	 * @param mixed $id
143
	 * @param mixed $relatedId
144
	 * @return PayloadInterface
145
	 */
146
	public function setActorId($id, $relatedId) {
147
		// find
148
		$model = $this->get($id);
149
150
		if ($model === null) {
151
			return new NotFound(['message' => 'Activity not found.']);
152
		}
153
154
		// update
155
		if ($this->doSetActorId($model, $relatedId)) {
156
			$this->dispatch(ActivityEvent::PRE_ACTOR_UPDATE, $model);
157
			$this->dispatch(ActivityEvent::PRE_SAVE, $model);
158
			$model->save();
159
			$this->dispatch(ActivityEvent::POST_ACTOR_UPDATE, $model);
160
			$this->dispatch(ActivityEvent::POST_SAVE, $model);
161
162
			return Updated(['model' => $model]);
163
		}
164
165
		return NotUpdated(['model' => $model]);
166
	}
167
168
	/**
169
	 * Sets the Object id
170
	 * 
171
	 * @param mixed $id
172
	 * @param mixed $relatedId
173
	 * @return PayloadInterface
174
	 */
175
	public function setObjectId($id, $relatedId) {
176
		// find
177
		$model = $this->get($id);
178
179
		if ($model === null) {
180
			return new NotFound(['message' => 'Activity not found.']);
181
		}
182
183
		// update
184
		if ($this->doSetObjectId($model, $relatedId)) {
185
			$this->dispatch(ActivityEvent::PRE_OBJECT_UPDATE, $model);
186
			$this->dispatch(ActivityEvent::PRE_SAVE, $model);
187
			$model->save();
188
			$this->dispatch(ActivityEvent::POST_OBJECT_UPDATE, $model);
189
			$this->dispatch(ActivityEvent::POST_SAVE, $model);
190
191
			return Updated(['model' => $model]);
192
		}
193
194
		return NotUpdated(['model' => $model]);
195
	}
196
197
	/**
198
	 * Sets the Target id
199
	 * 
200
	 * @param mixed $id
201
	 * @param mixed $relatedId
202
	 * @return PayloadInterface
203
	 */
204
	public function setTargetId($id, $relatedId) {
205
		// find
206
		$model = $this->get($id);
207
208
		if ($model === null) {
209
			return new NotFound(['message' => 'Activity not found.']);
210
		}
211
212
		// update
213
		if ($this->doSetTargetId($model, $relatedId)) {
214
			$this->dispatch(ActivityEvent::PRE_TARGET_UPDATE, $model);
215
			$this->dispatch(ActivityEvent::PRE_SAVE, $model);
216
			$model->save();
217
			$this->dispatch(ActivityEvent::POST_TARGET_UPDATE, $model);
218
			$this->dispatch(ActivityEvent::POST_SAVE, $model);
219
220
			return Updated(['model' => $model]);
221
		}
222
223
		return NotUpdated(['model' => $model]);
224
	}
225
226
	/**
227
	 * Updates a Activity with the given idand the provided data
228
	 * 
229
	 * @param mixed $id
230
	 * @param mixed $data
231
	 * @return PayloadInterface
232
	 */
233
	public function update($id, $data) {
234
		// find
235
		$model = $this->get($id);
236
237
		if ($model === null) {
238
			return new NotFound(['message' => 'Activity not found.']);
239
		}
240
241
		// hydrate
242
		$serializer = Activity::getSerializer();
243
		$model = $serializer->hydrate($model, $data);
244
		$this->hydrateRelationships($model, $data);
0 ignored issues
show
Bug introduced by
It seems like hydrateRelationships() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
245
246
		// dispatch pre save hooks
247
		$this->dispatch(ActivityEvent::PRE_UPDATE, $model, $data);
248
		$this->dispatch(ActivityEvent::PRE_SAVE, $model, $data);
249
250
		// validate
251
		$validator = $this->getValidator();
0 ignored issues
show
Bug introduced by
It seems like getValidator() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
252
		if ($validator !== null && !$validator->validate($model)) {
253
			return new NotValid([
254
				'errors' => $validator->getValidationFailures()
255
			]);
256
		}
257
258
		// save and dispath post save hooks
259
		$rows = $model->save();
260
		$this->dispatch(ActivityEvent::POST_UPDATE, $model, $data);
261
		$this->dispatch(ActivityEvent::POST_SAVE, $model, $data);
262
263
		$payload = ['model' => $model];
264
265
		if ($rows === 0) {
266
			return new NotUpdated($payload);
267
		}
268
269
		return new Updated($payload);
270
	}
271
272
	/**
273
	 * @param mixed $query
274
	 * @param mixed $filter
275
	 * @return void
276
	 */
277
	protected function applyFilter($query, $filter) {
278
		foreach ($filter as $column => $value) {
279
			$pos = strpos($column, '.');
280
			if ($pos !== false) {
281
				$rel = NameUtils::toStudlyCase(substr($column, 0, $pos));
282
				$col = substr($column, $pos + 1);
283
				$method = 'use' . $rel . 'Query';
284
				if (method_exists($query, $method)) {
285
					$sub = $query->$method();
286
					$this->applyFilter($sub, [$col => $value]);
287
					$sub->endUse();
288
				}
289
			} else {
290
				$method = 'filterBy' . NameUtils::toStudlyCase($column);
291
				if (method_exists($query, $method)) {
292
					$query->$method($value);
293
				}
294
			}
295
		}
296
	}
297
298
	/**
299
	 * @param string $type
300
	 * @param Activity $model
301
	 * @param array $data
302
	 */
303
	protected function dispatch($type, Activity $model, array $data = []) {
304
		$methods = [
305
			ActivityEvent::PRE_CREATE => 'preCreate',
306
			ActivityEvent::POST_CREATE => 'postCreate',
307
			ActivityEvent::PRE_UPDATE => 'preUpdate',
308
			ActivityEvent::POST_UPDATE => 'postUpdate',
309
			ActivityEvent::PRE_DELETE => 'preDelete',
310
			ActivityEvent::POST_DELETE => 'postDelete',
311
			ActivityEvent::PRE_SAVE => 'preSave',
312
			ActivityEvent::POST_SAVE => 'postSave'
313
		];
314
315
		if (isset($methods[$type])) {
316
			$method = $methods[$type];
317
			if (method_exists($this, $method)) {
318
				$this->$method($model, $data);
319
			}
320
		}
321
322
		$dispatcher = $this->getServiceContainer()->getDispatcher();
323
		$dispatcher->dispatch($type, new ActivityEvent($model));
324
	}
325
326
	/**
327
	 * Internal mechanism to set the Actor id
328
	 * 
329
	 * @param Activity $model
330
	 * @param mixed $relatedId
331
	 */
332
	protected function doSetActorId(Activity $model, $relatedId) {
333
		if ($model->getActorId() !== $relatedId) {
334
			$model->setActorId($relatedId);
335
336
			return true;
337
		}
338
339
		return false;
340
	}
341
342
	/**
343
	 * Internal mechanism to set the Object id
344
	 * 
345
	 * @param Activity $model
346
	 * @param mixed $relatedId
347
	 */
348
	protected function doSetObjectId(Activity $model, $relatedId) {
349
		if ($model->getObjectId() !== $relatedId) {
350
			$model->setObjectId($relatedId);
351
352
			return true;
353
		}
354
355
		return false;
356
	}
357
358
	/**
359
	 * Internal mechanism to set the Target id
360
	 * 
361
	 * @param Activity $model
362
	 * @param mixed $relatedId
363
	 */
364
	protected function doSetTargetId(Activity $model, $relatedId) {
365
		if ($model->getTargetId() !== $relatedId) {
366
			$model->setTargetId($relatedId);
367
368
			return true;
369
		}
370
371
		return false;
372
	}
373
374
	/**
375
	 * Returns one Activity with the given id from cache
376
	 * 
377
	 * @param mixed $id
378
	 * @return Activity|null
379
	 */
380
	protected function get($id) {
381
		if ($this->pool === null) {
382
			$this->pool = new Map();
383
		} else if ($this->pool->has($id)) {
384
			return $this->pool->get($id);
385
		}
386
387
		$model = ActivityQuery::create()->findOneById($id);
388
		$this->pool->set($id, $model);
389
390
		return $model;
391
	}
392
393
	/**
394
	 * Returns the service container
395
	 * 
396
	 * @return ServiceContainer
397
	 */
398
	abstract protected function getServiceContainer();
399
}
400