Completed
Push — master ( 1a1676...f7e1a1 )
by Thomas
04:58
created

ApiDomainTrait::create()   B

Complexity

Conditions 3
Paths 2

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
c 7
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 3
eloc 14
nc 2
nop 1
1
<?php
2
namespace keeko\core\domain\base;
3
4
use keeko\core\event\ApiEvent;
5
use keeko\core\model\ApiQuery;
6
use keeko\core\model\Api;
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 ApiDomainTrait {
24
25
	/**
26
	 */
27
	protected $pool;
28
29
	/**
30
	 * Creates a new Api with the provided data
31
	 * 
32
	 * @param mixed $data
33
	 * @return PayloadInterface
34
	 */
35
	public function create($data) {
36
		// hydrate
37
		$serializer = Api::getSerializer();
38
		$model = $serializer->hydrate(new Api(), $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(ApiEvent::PRE_CREATE, $model, $data);
43
		$this->dispatch(ApiEvent::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(ApiEvent::POST_CREATE, $model, $data);
56
		$this->dispatch(ApiEvent::POST_SAVE, $model, $data);
57
58
		return new Created(['model' => $model]);
59
	}
60
61
	/**
62
	 * Deletes a Api 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' => 'Api not found.']);
73
		}
74
75
		// delete
76
		$this->dispatch(ApiEvent::PRE_DELETE, $model);
77
		$model->delete();
78
79
		if ($model->isDeleted()) {
80
			$this->dispatch(ApiEvent::POST_DELETE, $model);
81
			return new Deleted(['model' => $model]);
82
		}
83
84
		return new NotDeleted(['message' => 'Could not delete Api']);
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 = ApiQuery::create();
100
101
		// sorting
102
		$sort = $params->getSort(Api::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 Api 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' => 'Api not found.']);
134
		}
135
136
		return new Found(['model' => $model]);
137
	}
138
139
	/**
140
	 * Sets the Action id
141
	 * 
142
	 * @param mixed $id
143
	 * @param mixed $relatedId
144
	 * @return PayloadInterface
145
	 */
146
	public function setActionId($id, $relatedId) {
147
		// find
148
		$model = $this->get($id);
149
150
		if ($model === null) {
151
			return new NotFound(['message' => 'Api not found.']);
152
		}
153
154
		// update
155
		if ($this->doSetActionId($model, $relatedId)) {
156
			$this->dispatch(ApiEvent::PRE_ACTION_UPDATE, $model);
157
			$this->dispatch(ApiEvent::PRE_SAVE, $model);
158
			$model->save();
159
			$this->dispatch(ApiEvent::POST_ACTION_UPDATE, $model);
160
			$this->dispatch(ApiEvent::POST_SAVE, $model);
161
162
			return Updated(['model' => $model]);
163
		}
164
165
		return NotUpdated(['model' => $model]);
166
	}
167
168
	/**
169
	 * Updates a Api with the given idand the provided data
170
	 * 
171
	 * @param mixed $id
172
	 * @param mixed $data
173
	 * @return PayloadInterface
174
	 */
175
	public function update($id, $data) {
176
		// find
177
		$model = $this->get($id);
178
179
		if ($model === null) {
180
			return new NotFound(['message' => 'Api not found.']);
181
		}
182
183
		// hydrate
184
		$serializer = Api::getSerializer();
185
		$model = $serializer->hydrate($model, $data);
186
		$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...
187
188
		// dispatch pre save hooks
189
		$this->dispatch(ApiEvent::PRE_UPDATE, $model, $data);
190
		$this->dispatch(ApiEvent::PRE_SAVE, $model, $data);
191
192
		// validate
193
		$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...
194
		if ($validator !== null && !$validator->validate($model)) {
195
			return new NotValid([
196
				'errors' => $validator->getValidationFailures()
197
			]);
198
		}
199
200
		// save and dispath post save hooks
201
		$rows = $model->save();
202
		$this->dispatch(ApiEvent::POST_UPDATE, $model, $data);
203
		$this->dispatch(ApiEvent::POST_SAVE, $model, $data);
204
205
		$payload = ['model' => $model];
206
207
		if ($rows === 0) {
208
			return new NotUpdated($payload);
209
		}
210
211
		return new Updated($payload);
212
	}
213
214
	/**
215
	 * @param mixed $query
216
	 * @param mixed $filter
217
	 * @return void
218
	 */
219
	protected function applyFilter($query, $filter) {
220
		foreach ($filter as $column => $value) {
221
			$pos = strpos($column, '.');
222
			if ($pos !== false) {
223
				$rel = NameUtils::toStudlyCase(substr($column, 0, $pos));
224
				$col = substr($column, $pos + 1);
225
				$method = 'use' . $rel . 'Query';
226
				if (method_exists($query, $method)) {
227
					$sub = $query->$method();
228
					$this->applyFilter($sub, [$col => $value]);
229
					$sub->endUse();
230
				}
231
			} else {
232
				$method = 'filterBy' . NameUtils::toStudlyCase($column);
233
				if (method_exists($query, $method)) {
234
					$query->$method($value);
235
				}
236
			}
237
		}
238
	}
239
240
	/**
241
	 * @param string $type
242
	 * @param Api $model
243
	 * @param array $data
244
	 */
245
	protected function dispatch($type, Api $model, array $data = []) {
246
		$methods = [
247
			ApiEvent::PRE_CREATE => 'preCreate',
248
			ApiEvent::POST_CREATE => 'postCreate',
249
			ApiEvent::PRE_UPDATE => 'preUpdate',
250
			ApiEvent::POST_UPDATE => 'postUpdate',
251
			ApiEvent::PRE_DELETE => 'preDelete',
252
			ApiEvent::POST_DELETE => 'postDelete',
253
			ApiEvent::PRE_SAVE => 'preSave',
254
			ApiEvent::POST_SAVE => 'postSave'
255
		];
256
257
		if (isset($methods[$type])) {
258
			$method = $methods[$type];
259
			if (method_exists($this, $method)) {
260
				$this->$method($model, $data);
261
			}
262
		}
263
264
		$dispatcher = $this->getServiceContainer()->getDispatcher();
265
		$dispatcher->dispatch($type, new ApiEvent($model));
266
	}
267
268
	/**
269
	 * Internal mechanism to set the Action id
270
	 * 
271
	 * @param Api $model
272
	 * @param mixed $relatedId
273
	 */
274
	protected function doSetActionId(Api $model, $relatedId) {
275
		if ($model->getActionId() !== $relatedId) {
276
			$model->setActionId($relatedId);
277
278
			return true;
279
		}
280
281
		return false;
282
	}
283
284
	/**
285
	 * Returns one Api with the given id from cache
286
	 * 
287
	 * @param mixed $id
288
	 * @return Api|null
289
	 */
290
	protected function get($id) {
291
		if ($this->pool === null) {
292
			$this->pool = new Map();
293
		} else if ($this->pool->has($id)) {
294
			return $this->pool->get($id);
295
		}
296
297
		$model = ApiQuery::create()->findOneById($id);
298
		$this->pool->set($id, $model);
299
300
		return $model;
301
	}
302
303
	/**
304
	 * Returns the service container
305
	 * 
306
	 * @return ServiceContainer
307
	 */
308
	abstract protected function getServiceContainer();
309
}
310