Passed
Push — master ( e8f56b...3a2a94 )
by Jean-Christophe
23:12
created

RestBaseController::_errorHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 3
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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
				} );
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
	public function initialize() {
123
	}
124
125 13
	public function finalize() {
126 13
		parent::finalize ();
127 13
		$this->server->finalizeTokens ();
128
	}
129
130 1
	public function _errorHandler($e) {
131 1
		$this->_setResponseCode ( Router::getStatusCode() );
132 1
		echo $this->_getResponseFormatter ()->formatException ( $e );
133
	}
134
135 1
	public function _setResponseCode($value) {
136 1
		\http_response_code ( $value );
137
	}
138
139
	/**
140
	 * Returns a list of objects from the server.
141
	 *
142
	 * @param string $condition the sql Where part
143
	 * @param boolean|string $include if true, loads associate members with associations, if string, example : client.*,commands
144
	 * @param boolean $useCache
145
	 */
146
	public function _get($condition = '1=1', $include = false, $useCache = false) {
147
		try {
148
			$condition = $this->getCondition ( $condition );
149
			$include = $this->getInclude ( $include );
150
			$useCache = UString::isBooleanTrue ( $useCache );
151
			$datas = DAO::getAll ( $this->model, $condition, $include, null, $useCache );
152
			echo $this->_getResponseFormatter ()->get ( $datas );
153
		} catch ( \Exception $e ) {
154
			$this->_setResponseCode ( 500 );
155
			echo $this->_getResponseFormatter ()->formatException ( $e );
156
		}
157
	}
158
159
	/**
160
	 * Returns all the instances from the model $resource.
161
	 * Query parameters:
162
	 * - **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).
163
	 * - **filter**: The filter to apply to the query (where part of an SQL query) (default: 1=1).
164
	 * - **page[number]**: The page to display (in this case, the page size is set to 1).
165
	 * - **page[size]**: The page size (count of instance per page) (default: 1).
166
	 */
167 5
	public function _getAll() {
168 5
		$filter = $this->getCondition ( $this->getRequestParam ( 'filter', '1=1' ) );
169 5
		$pages = null;
170 5
		if (isset ( $_GET ['page'] )) {
171 2
			$pageNumber = $_GET ['page'] ['number'];
172 2
			$pageSize = $_GET ['page'] ['size'] ?? 1;
173 2
			$pages = $this->generatePagination ( $filter, $pageNumber, $pageSize );
174
		}
175 5
		$datas = DAO::getAll ( $this->model, $filter, $this->getInclude ( $this->getRequestParam ( 'include', false ) ) );
176 5
		echo $this->_getResponseFormatter ()->get ( $datas, $pages );
177
	}
178
179
	/**
180
	 * Get the first object corresponding to the $keyValues.
181
	 *
182
	 * @param string $keyValues primary key(s) value(s) or condition
183
	 * @param boolean|string $include if true, loads associate members with associations, if string, example : client.*,commands
184
	 * @param boolean $useCache if true then response is cached
185
	 */
186 2
	public function _getOne($keyValues, $include = false, $useCache = false) {
187 2
		$keyValues = $this->getCondition ( $keyValues );
188 2
		$include = $this->getInclude ( $include );
189 2
		$useCache = UString::isBooleanTrue ( $useCache );
190 2
		$data = DAO::getById ( $this->model, $keyValues, $include, $useCache );
191 2
		if (isset ( $data )) {
192 2
			$_SESSION ["_restInstance"] = $data;
193 2
			echo $this->_getResponseFormatter ()->getOne ( $data );
194
		} else {
195
			$this->_setResponseCode ( 404 );
196
			echo $this->_getResponseFormatter ()->format ( RestError::notFound ( $keyValues, "RestController/getOne" )->asArray () );
197
		}
198
	}
199
200
	public function _format($arrayMessage) {
201
		return $this->_getResponseFormatter ()->format ( $arrayMessage );
202
	}
203
204
	/**
205
	 *
206
	 * @param string $ids
207
	 * @param string $member
208
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
209
	 * @param boolean $useCache
210
	 */
211
	public function _getManyToOne($ids, $member, $include = false, $useCache = false) {
212
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
213
			return DAO::getManyToOne ( $instance, $member, $include, $useCache );
214
		}, $member, $include, $useCache, false );
215
	}
216
217
	/**
218
	 *
219
	 * @param string $ids
220
	 * @param string $member
221
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
222
	 * @param boolean $useCache
223
	 * @throws \Exception
224
	 */
225 1
	public function _getOneToMany($ids, $member, $include = false, $useCache = false) {
226 1
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
227 1
			return DAO::getOneToMany ( $instance, $member, $include, $useCache );
228
		}, $member, $include, $useCache, true );
229
	}
230
231
	/**
232
	 *
233
	 * @param string $ids
234
	 * @param string $member
235
	 * @param string|boolean $include if true, loads associate members with associations, if string, example : client.*,commands
236
	 * @param boolean $useCache
237
	 * @throws \Exception
238
	 */
239
	public function _getManyToMany($ids, $member, $include = false, $useCache = false) {
240
		$this->getAssociatedMemberValues_ ( $ids, function ($instance, $member, $include, $useCache) {
241
			return DAO::getManyToMany ( $instance, $member, $include, null, $useCache );
242
		}, $member, $include, $useCache, true );
243
	}
244
245
	/**
246
	 * Update an instance of $model selected by the primary key $keyValues
247
	 * Require members values in $_POST array
248
	 *
249
	 * @param array $keyValues
250
	 */
251 1
	public function _update(...$keyValues) {
252 1
		$instance = DAO::getById ( $this->model, $keyValues, false );
253 1
		$this->operate_ ( $instance, function ($instance) {
254 1
			$datas = $this->getDatas ();
255 1
			EventsManager::trigger ( RestEvents::BEFORE_UPDATE, $instance, $datas, $this );
256 1
			$this->_setValuesToObject ( $instance, $datas );
257 1
			if ($this->_validateInstance ( $instance, \array_keys ( $datas ) )) {
258 1
				return $this->updateOperation ( $instance, $datas, true );
259
			}
260
			return null;
261
		}, 'updated', 'Unable to update the instance', $keyValues );
262
	}
263
264
	/**
265
	 * Insert a new instance of $model
266
	 * Require members values in $_POST array
267
	 */
268 1
	public function _add() {
269 1
		$model = $this->model;
270 1
		$instance = new $model ();
271 1
		$this->operate_ ( $instance, function ($instance) use ($model) {
272 1
			$datas = $this->getDatas ();
273 1
			EventsManager::trigger ( RestEvents::BEFORE_INSERT, $instance, $datas, $this );
274 1
			$this->_setValuesToObject ( $instance, $datas );
275 1
			$fields = \array_keys ( OrmUtils::getSerializableFields ( $model ) );
276 1
			if ($this->_validateInstance ( $instance, $fields, [ 'id' => false ] )) {
277 1
				return $this->addOperation ( $instance, $datas, true );
278
			}
279
			return null;
280 1
		}, 'inserted', 'Unable to insert the instance', [ ] );
281
	}
282
283
	/**
284
	 * Returns an associated member value(s).
285
	 * Query parameters:
286
	 * - **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).
287
	 *
288
	 * @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)
289
	 * @param string $member The member to load
290
	 *
291
	 */
292 1
	public function _getRelationShip($id, $member) {
293 1
		$relations = OrmUtils::getAnnotFieldsInRelations ( $this->model );
294 1
		if (isset ( $relations [$member] )) {
295 1
			$include = $this->getRequestParam ( 'include', true );
296 1
			switch ($relations [$member] ['type']) {
297 1
				case 'manyToOne' :
298
					$this->_getManyToOne ( $id, $member, $include );
299
					break;
300 1
				case 'oneToMany' :
301 1
					$this->_getOneToMany ( $id, $member, $include );
302 1
					break;
303
				case 'manyToMany' :
304
					$this->_getManyToMany ( $id, $member, $include );
305
					break;
306
			}
307
		}
308
	}
309
310
	/**
311
	 * Delete the instance of $model selected by the primary key $keyValues
312
	 *
313
	 * @param array $keyValues
314
	 */
315 1
	public function _delete(...$keyValues) {
316 1
		$instance = DAO::getById ( $this->model, $keyValues, false );
317 1
		$this->operate_ ( $instance, function ($instance) {
318 1
			return DAO::remove ( $instance );
319
		}, 'deleted', 'Unable to delete the instance', $keyValues );
320
	}
321
322 1
	public static function _getApiVersion() {
323 1
		return '?';
324
	}
325
326
	/**
327
	 * Returns the template for creating this type of controller
328
	 *
329
	 * @return string
330
	 */
331
	public static function _getTemplateFile() {
332
		return 'restController.tpl';
333
	}
334
}
335