Completed
Push — master ( e8b3d2...889b57 )
by Thomas
14:29
created

ApiDomainTrait::read()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 5
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
		// validate
42
		$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...
43
		if ($validator !== null && !$validator->validate($model)) {
44
			return new NotValid([
45
				'errors' => $validator->getValidationFailures()
46
			]);
47
		}
48
49
		// dispatch
50
		$event = new ApiEvent($model);
51
		$this->dispatch(ApiEvent::PRE_CREATE, $event);
52
		$this->dispatch(ApiEvent::PRE_SAVE, $event);
53
		$model->save();
54
		$this->dispatch(ApiEvent::POST_CREATE, $event);
55
		$this->dispatch(ApiEvent::POST_SAVE, $event);
56
		return new Created(['model' => $model]);
57
	}
58
59
	/**
60
	 * Deletes a Api with the given id
61
	 * 
62
	 * @param mixed $id
63
	 * @return PayloadInterface
64
	 */
65
	public function delete($id) {
66
		// find
67
		$model = $this->get($id);
68
69
		if ($model === null) {
70
			return new NotFound(['message' => 'Api not found.']);
71
		}
72
73
		// delete
74
		$event = new ApiEvent($model);
75
		$this->dispatch(ApiEvent::PRE_DELETE, $event);
76
		$model->delete();
77
78
		if ($model->isDeleted()) {
79
			$this->dispatch(ApiEvent::POST_DELETE, $event);
80
			return new Deleted(['model' => $model]);
81
		}
82
83
		return new NotDeleted(['message' => 'Could not delete Api']);
84
	}
85
86
	/**
87
	 * Returns a paginated result
88
	 * 
89
	 * @param Parameters $params
90
	 * @return PayloadInterface
91
	 */
92
	public function paginate(Parameters $params) {
93
		$sysPrefs = $this->getServiceContainer()->getPreferenceLoader()->getSystemPreferences();
94
		$defaultSize = $sysPrefs->getPaginationSize();
95
		$page = $params->getPage('number');
96
		$size = $params->getPage('size', $defaultSize);
97
98
		$query = ApiQuery::create();
99
100
		// sorting
101
		$sort = $params->getSort(Api::getSerializer()->getSortFields());
102
		foreach ($sort as $field => $order) {
103
			$method = 'orderBy' . NameUtils::toStudlyCase($field);
104
			$query->$method($order);
105
		}
106
107
		// filtering
108
		$filter = $params->getFilter();
109
		if (!empty($filter)) {
110
			$this->applyFilter($query, $filter);
111
		}
112
113
		// paginate
114
		$model = $query->paginate($page, $size);
115
116
		// run response
117
		return new Found(['model' => $model]);
118
	}
119
120
	/**
121
	 * Returns one Api with the given id
122
	 * 
123
	 * @param mixed $id
124
	 * @return PayloadInterface
125
	 */
126
	public function read($id) {
127
		// read
128
		$model = $this->get($id);
129
130
		// check existence
131
		if ($model === null) {
132
			return new NotFound(['message' => 'Api not found.']);
133
		}
134
135
		return new Found(['model' => $model]);
136
	}
137
138
	/**
139
	 * Sets the Action id
140
	 * 
141
	 * @param mixed $id
142
	 * @param mixed $relatedId
143
	 * @return PayloadInterface
144
	 */
145
	public function setActionId($id, $relatedId) {
146
		// find
147
		$model = $this->get($id);
148
149
		if ($model === null) {
150
			return new NotFound(['message' => 'Api not found.']);
151
		}
152
153
		// update
154
		if ($this->doSetActionId($model, $relatedId)) {
155
			$event = new ApiEvent($model);
156
			$this->dispatch(ApiEvent::PRE_ACTION_UPDATE, $event);
157
			$this->dispatch(ApiEvent::PRE_SAVE, $event);
158
			$model->save();
159
			$this->dispatch(ApiEvent::POST_ACTION_UPDATE, $event);
160
			$this->dispatch(ApiEvent::POST_SAVE, $event);
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
		// validate
189
		$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...
190
		if ($validator !== null && !$validator->validate($model)) {
191
			return new NotValid([
192
				'errors' => $validator->getValidationFailures()
193
			]);
194
		}
195
196
		// dispatch
197
		$event = new ApiEvent($model);
198
		$this->dispatch(ApiEvent::PRE_UPDATE, $event);
199
		$this->dispatch(ApiEvent::PRE_SAVE, $event);
200
		$rows = $model->save();
201
		$this->dispatch(ApiEvent::POST_UPDATE, $event);
202
		$this->dispatch(ApiEvent::POST_SAVE, $event);
203
204
		$payload = ['model' => $model];
205
206
		if ($rows === 0) {
207
			return new NotUpdated($payload);
208
		}
209
210
		return new Updated($payload);
211
	}
212
213
	/**
214
	 * @param mixed $query
215
	 * @param mixed $filter
216
	 * @return void
217
	 */
218
	protected function applyFilter($query, $filter) {
219
		foreach ($filter as $column => $value) {
220
			$pos = strpos($column, '.');
221
			if ($pos !== false) {
222
				$rel = NameUtils::toStudlyCase(substr($column, 0, $pos));
223
				$col = substr($column, $pos + 1);
224
				$method = 'use' . $rel . 'Query';
225
				if (method_exists($query, $method)) {
226
					$sub = $query->$method();
227
					$this->applyFilter($sub, [$col => $value]);
228
					$sub->endUse();
229
				}
230
			} else {
231
				$method = 'filterBy' . NameUtils::toStudlyCase($column);
232
				if (method_exists($query, $method)) {
233
					$query->$method($value);
234
				}
235
			}
236
		}
237
	}
238
239
	/**
240
	 * @param string $type
241
	 * @param ApiEvent $event
242
	 */
243
	protected function dispatch($type, ApiEvent $event) {
244
		$model = $event->getApi();
245
		$methods = [
246
			ApiEvent::PRE_CREATE => 'preCreate',
247
			ApiEvent::POST_CREATE => 'postCreate',
248
			ApiEvent::PRE_UPDATE => 'preUpdate',
249
			ApiEvent::POST_UPDATE => 'postUpdate',
250
			ApiEvent::PRE_DELETE => 'preDelete',
251
			ApiEvent::POST_DELETE => 'postDelete',
252
			ApiEvent::PRE_SAVE => 'preSave',
253
			ApiEvent::POST_SAVE => 'postSave'
254
		];
255
256
		if (isset($methods[$type])) {
257
			$method = $methods[$type];
258
			if (method_exists($this, $method)) {
259
				$this->$method($model);
260
			}
261
		}
262
263
		$dispatcher = $this->getServiceContainer()->getDispatcher();
264
		$dispatcher->dispatch($type, $event);
265
	}
266
267
	/**
268
	 * Internal mechanism to set the Action id
269
	 * 
270
	 * @param Api $model
271
	 * @param mixed $relatedId
272
	 */
273
	protected function doSetActionId(Api $model, $relatedId) {
274
		if ($model->getActionId() !== $relatedId) {
275
			$model->setActionId($relatedId);
276
277
			return true;
278
		}
279
280
		return false;
281
	}
282
283
	/**
284
	 * Returns one Api with the given id from cache
285
	 * 
286
	 * @param mixed $id
287
	 * @return Api|null
288
	 */
289
	protected function get($id) {
290
		if ($this->pool === null) {
291
			$this->pool = new Map();
292
		} else if ($this->pool->has($id)) {
293
			return $this->pool->get($id);
294
		}
295
296
		$model = ApiQuery::create()->findOneById($id);
297
		$this->pool->set($id, $model);
298
299
		return $model;
300
	}
301
302
	/**
303
	 * Returns the service container
304
	 * 
305
	 * @return ServiceContainer
306
	 */
307
	abstract protected function getServiceContainer();
308
}
309