Passed
Pull Request — master (#201)
by Jean-Christophe
26:40
created

RestBaseController::_update()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2.004

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 11
ccs 9
cts 10
cp 0.9
rs 9.9666
cc 2
nc 1
nop 1
crap 2.004
1
<?php
2
3
namespace Ubiquity\controllers\rest;
4
5
use Ubiquity\cache\CacheManager;
6
use Ubiquity\controllers\Controller;
7
use Ubiquity\controllers\rest\formatters\ResponseFormatter;
8
use Ubiquity\controllers\rest\traits\RestControllerUtilitiesTrait;
9
use Ubiquity\controllers\Startup;
10
use Ubiquity\orm\DAO;
11
use Ubiquity\utils\base\UString;
12
use Ubiquity\controllers\Router;
13
use Ubiquity\orm\OrmUtils;
14
use Ubiquity\events\EventsManager;
15
use Ubiquity\events\RestEvents;
16
17
/**
18
 * Abstract base class for Rest controllers.
19
 * Ubiquity\controllers\rest$RestController
20
 * This class is part of Ubiquity
21
 *
22
 * @author jcheron <[email protected]>
23
 * @version 1.0.8
24
 *
25
 */
26
abstract class RestBaseController extends Controller {
27
	use RestControllerUtilitiesTrait;
28
	protected $config;
29
	protected $model;
30
	protected $contentType;
31
	protected $restCache;
32
	protected $useValidation = true;
33
34
	/**
35
	 *
36
	 * @var formatters\ResponseFormatter
37
	 */
38
	protected $responseFormatter;
39
	/**
40
	 *
41
	 * @var formatters\RequestFormatter
42
	 */
43
	protected $requestFormatter;
44
45
	/**
46
	 *
47
	 * @var RestServer
48
	 */
49
	protected $server;
50
51 13
	public function __construct() {
52 13
		if (! \headers_sent ()) {
53 13
			@\set_exception_handler ( array ($this,'_errorHandler' ) );
54 13
			$this->config = Startup::getConfig ();
55 13
			$this->server = $this->_getRestServer ();
56 13
			$this->server->cors ();
57 13
			$this->responseFormatter = $this->_getResponseFormatter ();
58 13
			$this->requestFormatter = $this->_getRequestFormatter ();
59 13
			$this->server->_setContentType ( $this->contentType );
60 13
			$this->restCache = CacheManager::getRestCacheController ( \get_class ( $this ) );
61
		}
62 13
		if (! $this->isValid ( Startup::getAction () ))
63
			$this->onInvalidControl ();
64
	}
65
66 1
	public function index() {
67 1
		$routesPath = Router::getRoutesPathByController ( get_class ( $this ) );
68 1
		echo $this->_getResponseFormatter ()->format ( [ 'links' => $routesPath ] );
69
	}
70
71 4
	public function isValid($action) {
72 4
		if (isset ( $this->restCache ['authorizations'] )) {
73 1
			if (\array_search ( $action, $this->restCache ['authorizations'] ) !== false) {
74 1
				return $this->server->isValid ( function ($datas = null) use ($action) {
75
					$this->checkPermissions ( $action, $datas );
76 1
				} );
77
			}
78
		}
79 4
		return true;
80
	}
81
82
	/**
83
	 * To override in derived classes.
84
	 * Check if the datas in the authorization token allows access to the action.
85
	 * If the token is expired, this method is not used.
86
	 *
87
	 * @param $action
88
	 * @param mixed $datas datas in authorization token
89
	 * @return bool
90
	 */
91
	protected function checkPermissions($action, $datas = null) {
92
		return true;
93
	}
94
95
	/**
96
	 * Returns true if $action require an authentification with token
97
	 *
98
	 * @param string $action
99
	 * @return boolean
100
	 */
101
	protected function requireAuth($action) {
102
		if (isset ( $this->restCache ["authorizations"] )) {
103
			return \array_search ( $action, $this->restCache ["authorizations"] ) !== false;
104
		}
105
		return false;
106
	}
107
108
	public function onInvalidControl() {
109
		throw new \Exception ( 'HTTP/1.1 401 Unauthorized, you need an access token for this request', 401 );
110
	}
111
112
	/**
113
	 * Realize the connection to the server.
114
	 * To override in derived classes to define your own authentication
115
	 */
116
	public function connect() {
117
		$datas = null;
118
		$resp = $this->server->connect ( $datas );
119
		echo $this->_format ( $resp );
120
	}
121
122
	/**
123
	 * Refresh an active token.
124
	 * @throws \Ubiquity\exceptions\RestException
125
	 */
126
	protected function refreshToken(){
127
		$resp = $this->server->refreshToken();
128
		echo $this->_format($resp);
129
	}
130
131 9
	public function initialize() {
132 9
	}
133
134 13
	public function finalize() {
135 13
		parent::finalize ();
136 13
		$this->server->finalizeTokens ();
137
	}
138
139 1
	public function _errorHandler($e) {
140 1
		$this->_setResponseCode ( Router::getStatusCode() );
141 1
		echo $this->_getResponseFormatter ()->formatException ( $e );
142
	}
143
144 1
	public function _setResponseCode($value) {
145 1
		\http_response_code ( $value );
146
	}
147
148
	/**
149
	 * Returns a list of objects from the server.
150
	 *
151
	 * @param string $condition the sql Where part
152
	 * @param boolean|string $include if true, loads associate members with associations, if string, example : client.*,commands
153
	 * @param boolean $useCache
154
	 */
155
	public function _get($condition = '1=1', $include = false, $useCache = false) {
156
		try {
157
			$condition = $this->getCondition ( $condition );
158
			$include = $this->getInclude ( $include );
159
			$useCache = UString::isBooleanTrue ( $useCache );
160
			$datas = DAO::getAll ( $this->model, $condition, $include, null, $useCache );
161
			echo $this->_getResponseFormatter ()->get ( $datas );
162
		} catch ( \Exception $e ) {
163
			$this->_setResponseCode ( 500 );
164
			echo $this->_getResponseFormatter ()->formatException ( $e );
165
		}
166
	}
167
168
	/**
169
	 * Returns all the instances from the model $resource.
170
	 * Query parameters:
171
	 * - **include**: A string of associated members to load, comma separated (e.g. users,groups,organization...), or a boolean: true for all members, false for none (default: false).
172
	 * - **filter**: The filter to apply to the query (where part of an SQL query) (default: 1=1).
173
	 * - **page[number]**: The page to display (in this case, the page size is set to 1).
174
	 * - **page[size]**: The page size (count of instance per page) (default: 1).
175
	 */
176 5
	public function _getAll() {
177 5
		$filter = $this->getCondition ( $this->getRequestParam ( 'filter', '1=1' ) );
178 5
		$pages = null;
179 5
		if (isset ( $_GET ['page'] )) {
180 2
			$pageNumber = $_GET ['page'] ['number'];
181 2
			$pageSize = $_GET ['page'] ['size'] ?? 1;
182 2
			$pages = $this->generatePagination ( $filter, $pageNumber, $pageSize );
183
		}
184 5
		$datas = DAO::getAll ( $this->model, $filter, $this->getInclude ( $this->getRequestParam ( 'include', false ) ) );
185 5
		echo $this->_getResponseFormatter ()->get ( $datas, $pages );
186
	}
187
188
	/**
189
	 * Get the first object corresponding to the $keyValues.
190
	 *
191
	 * @param string $keyValues primary key(s) value(s) or condition
192
	 * @param boolean|string $include if true, loads associate members with associations, if string, example : client.*,commands
193
	 * @param boolean $useCache if true then response is cached
194
	 */
195 2
	public function _getOne($keyValues, $include = false, $useCache = false) {
196 2
		$keyValues = $this->getCondition ( $keyValues );
197 2
		$include = $this->getInclude ( $include );
198 2
		$useCache = UString::isBooleanTrue ( $useCache );
199 2
		$data = DAO::getById ( $this->model, $keyValues, $include, $useCache );
200 2
		if (isset ( $data )) {
201 2
			$_SESSION ["_restInstance"] = $data;
202 2
			echo $this->_getResponseFormatter ()->getOne ( $data );
203
		} else {
204
			$this->_setResponseCode ( 404 );
205
			echo $this->_getResponseFormatter ()->format ( RestError::notFound ( $keyValues, "RestController/getOne" )->asArray () );
206
		}
207
	}
208
209
	public function _format($arrayMessage) {
210
		return $this->_getResponseFormatter ()->format ( $arrayMessage );
211
	}
212
213
	/**
214
	 *
215
	 * @param string $ids
216
	 * @param string $member
217
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
218
	 * @param boolean $useCache
219
	 */
220
	public function _getManyToOne($ids, $member, $include = false, $useCache = false) {
221
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
222
			return DAO::getManyToOne ( $instance, $member, $include, $useCache );
223
		}, $member, $include, $useCache, false );
224
	}
225
226
	/**
227
	 *
228
	 * @param string $ids
229
	 * @param string $member
230
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
231
	 * @param boolean $useCache
232
	 * @throws \Exception
233
	 */
234 1
	public function _getOneToMany($ids, $member, $include = false, $useCache = false) {
235 1
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
236 1
			return DAO::getOneToMany ( $instance, $member, $include, $useCache );
237 1
		}, $member, $include, $useCache, true );
238
	}
239
240
	/**
241
	 *
242
	 * @param string $ids
243
	 * @param string $member
244
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
245
	 * @param boolean $useCache
246
	 * @throws \Exception
247
	 */
248
	public function _getManyToMany($ids, $member, $include = false, $useCache = false) {
249
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
250
			return DAO::getManyToMany ( $instance, $member, $include, null, $useCache );
251
		}, $member, $include, $useCache, true );
252
	}
253
254
	/**
255
	 * Update an instance of $model selected by the primary key $keyValues
256
	 * Require members values in $_POST array
257
	 *
258
	 * @param array $keyValues
259
	 */
260 1
	public function _update(...$keyValues) {
261 1
		$instance = DAO::getById ( $this->model, $keyValues, false );
262 1
		$this->operate_ ( $instance, function ($instance) {
263 1
			$datas = $this->getDatas ();
264 1
			EventsManager::trigger ( RestEvents::BEFORE_UPDATE, $instance, $datas, $this );
265 1
			$this->_setValuesToObject ( $instance, $datas );
266 1
			if ($this->_validateInstance ( $instance, \array_keys ( $datas ) )) {
267 1
				return $this->updateOperation ( $instance, $datas, true );
268
			}
269
			return null;
270 1
		}, 'updated', 'Unable to update the instance', $keyValues );
271
	}
272
273
	/**
274
	 * Insert a new instance of $model
275
	 * Require members values in $_POST array
276
	 */
277 1
	public function _add() {
278 1
		$model = $this->model;
279 1
		$instance = new $model ();
280 1
		$this->operate_ ( $instance, function ($instance) use ($model) {
281 1
			$datas = $this->getDatas ();
282 1
			EventsManager::trigger ( RestEvents::BEFORE_INSERT, $instance, $datas, $this );
283 1
			$this->_setValuesToObject ( $instance, $datas );
284 1
			$fields = \array_keys ( OrmUtils::getSerializableFields ( $model ) );
285 1
			if ($this->_validateInstance ( $instance, $fields, [ 'id' => false ] )) {
286 1
				return $this->addOperation ( $instance, $datas, true );
287
			}
288
			return null;
289 1
		}, 'inserted', 'Unable to insert the instance', [ ] );
290
	}
291
292
	/**
293
	 * Returns an associated member value(s).
294
	 * Query parameters:
295
	 * - **include**: A string of associated members to load, comma separated (e.g. users,groups,organization...), or a boolean: true for all members, false for none (default: true).
296
	 *
297
	 * @param string $id The primary key value(s), if the primary key is composite, use a comma to separate the values (e.g. 1,115,AB)
298
	 * @param string $member The member to load
299
	 *
300
	 */
301 1
	public function _getRelationShip($id, $member) {
302 1
		$relations = OrmUtils::getAnnotFieldsInRelations ( $this->model );
303 1
		if (isset ( $relations [$member] )) {
304 1
			$include = $this->getRequestParam ( 'include', true );
305 1
			switch ($relations [$member] ['type']) {
306 1
				case 'manyToOne' :
307
					$this->_getManyToOne ( $id, $member, $include );
308
					break;
309 1
				case 'oneToMany' :
310 1
					$this->_getOneToMany ( $id, $member, $include );
311 1
					break;
312
				case 'manyToMany' :
313
					$this->_getManyToMany ( $id, $member, $include );
314
					break;
315
			}
316
		}
317
	}
318
319
	/**
320
	 * Delete the instance of $model selected by the primary key $keyValues
321
	 *
322
	 * @param array $keyValues
323
	 */
324 1
	public function _delete(...$keyValues) {
325 1
		$instance = DAO::getById ( $this->model, $keyValues, false );
326 1
		$this->operate_ ( $instance, function ($instance) {
327 1
			return DAO::remove ( $instance );
328 1
		}, 'deleted', 'Unable to delete the instance', $keyValues );
329
	}
330
331 1
	public static function _getApiVersion() {
332 1
		return '?';
333
	}
334
335
	/**
336
	 * Returns the template for creating this type of controller
337
	 *
338
	 * @return string
339
	 */
340
	public static function _getTemplateFile() {
341
		return 'restController.tpl';
342
	}
343
}
344