Passed
Push — master ( c4282d...8835f1 )
by Jean-Christophe
10:25
created

JsonApiRestController   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Test Coverage

Coverage 44.9%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 30
eloc 71
c 1
b 0
f 0
dl 0
loc 226
ccs 44
cts 98
cp 0.449
rs 10

18 Methods

Rating   Name   Duplication   Size   Complexity  
A add_() 0 3 1
A options() 0 1 1
A _checkResource() 0 8 2
A getRestServer() 0 2 1
A index() 0 2 1
A _setResource() 0 3 1
A connect() 0 2 1
A getOne_() 0 3 1
A _getTemplateFile() 0 2 1
A getAll_() 0 11 2
A _getApiVersion() 0 2 1
A getRelationShip_() 0 15 5
A updateOperation() 0 3 1
A getDatas() 0 14 3
A AddOperation() 0 3 1
A delete_() 0 3 1
A update_() 0 6 2
A loadRelationshipsDatas() 0 7 4
1
<?php
2
3
/**
4
 * JsonApi implementation
5
 */
6
namespace Ubiquity\controllers\rest\api\jsonapi;
7
8
use Ubiquity\orm\DAO;
9
use Ubiquity\controllers\rest\RestError;
10
use Ubiquity\controllers\Startup;
11
use Ubiquity\controllers\rest\RestBaseController;
12
use Ubiquity\utils\http\URequest;
13
use Ubiquity\orm\OrmUtils;
14
use Ubiquity\controllers\rest\RestServer;
15
use Ubiquity\controllers\crud\CRUDHelper;
16
17
/**
18
 * Rest JsonAPI implementation.
19
 * Ubiquity\controllers\rest\api\jsonapi$JsonApiRestController
20
 * This class is part of Ubiquity
21
 *
22
 * @author jcheron <[email protected]>
23
 * @version 1.1.3
24
 * @since Ubiquity 2.0.11
25
 *
26
 */
27
abstract class JsonApiRestController extends RestBaseController {
28
	const API_VERSION = 'JsonAPI 1.0';
29
30 5
	protected function _setResource($resource) {
31 5
		$modelsNS = $this->config ["mvcNS"] ["models"];
32 5
		$this->model = $modelsNS . "\\" . $this->_getResponseFormatter ()->getModel ( $resource );
33 5
	}
34
35 5
	protected function _checkResource($resource, $callback) {
36 5
		$this->_setResource ( $resource );
37 5
		if (class_exists ( $this->model )) {
38 5
			$callback ();
39
		} else {
40
			$this->_setResponseCode ( 404 );
41
			$error = new RestError ( 404, "Not existing class", $this->model . " class does not exists!", Startup::getController () . "/" . Startup::getAction () );
42
			echo $this->_format ( $error->asArray () );
43
		}
44 5
	}
45
46
	protected function getDatas() {
47
		$datas = URequest::getRealInput ();
48
		if (sizeof ( $datas ) > 0) {
49
			$datas = current ( array_keys ( $datas ) );
50
			$datas = json_decode ( $datas, true );
51
			$attributes = $datas ["data"] ["attributes"] ?? [ ];
52
			if (isset ( $datas ["data"] ["id"] )) {
53
				$key = OrmUtils::getFirstKey ( $this->model );
54
				$attributes [$key] = $datas ["data"] ["id"];
55
			}
56
			$this->loadRelationshipsDatas ( $datas, $attributes );
57
			return $attributes;
58
		}
59
		$this->addError ( 204, 'No content', 'The POST request has no content!' );
60
	}
61
62
	protected function loadRelationshipsDatas($datas, &$attributes) {
63
		if (isset ( $datas ['data'] ['relationships'] )) {
64
			$relationShips = $datas ['data'] ['relationships'];
65
			foreach ( $relationShips as $member => $data ) {
66
				if (isset ( $data ['data'] ['id'] )) {
67
					$m = OrmUtils::getJoinColumnName ( $this->model, $member );
68
					$attributes [$m] = $data ['data'] ['id'];
69
				}
70
			}
71
		}
72
	}
73
74
	/**
75
	 *
76
	 * @return RestServer
77
	 */
78 6
	protected function getRestServer(): RestServer {
79 6
		return new JsonApiRestServer ( $this->config );
80
	}
81
82
	protected function updateOperation($instance, $datas, $updateMany = false) {
83
		$instance->_new = false;
84
		return CRUDHelper::update ( $instance, $datas, false, $updateMany );
85
	}
86
87
	protected function AddOperation($instance, $datas, $insertMany = false) {
88
		$instance->_new = true;
89
		return CRUDHelper::update ( $instance, $datas, false, $insertMany );
90
	}
91
92
	/**
93
	 * Route for CORS
94
	 *
95
	 * @route("{resource}","methods"=>["options"],"priority"=>3000)
96
	 */
97
	public function options(...$resource) {
98
	}
99
100
	/**
101
	 *
102
	 * {@inheritdoc}
103
	 * @see \Ubiquity\controllers\rest\RestBaseController::index()
104
	 * @route("links/","methods"=>["get"],"priority"=>3000)
105
	 */
106 1
	public function index() {
107 1
		parent::index ();
108 1
	}
109
110
	/**
111
	 *
112
	 * {@inheritdoc}
113
	 * @see \Ubiquity\controllers\rest\RestBaseController::connect()
114
	 * @route("connect/","priority"=>2500)
115
	 */
116
	public function connect() {
117
		parent::connect ();
118
	}
119
120
	/**
121
	 * Returns all the instances from the model $resource.
122
	 * Query parameters:
123
	 * - **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).
124
	 * - **filter**: The filter to apply to the query (where part of an SQL query) (default: 1=1).
125
	 * - **page[number]**: The page to display (in this case, the page size is set to 1).
126
	 * - **page[size]**: The page size (count of instance per page) (default: 1).
127
	 *
128
	 * @route("{resource}/","methods"=>["get"],"priority"=>0)
129
	 */
130 1
	public function getAll_($resource) {
131
		$this->_checkResource ( $resource, function () {
132 1
			$filter = $this->getCondition ( $this->getRequestParam ( 'filter', '1=1' ) );
133 1
			$pages = null;
134 1
			if (isset ( $_GET ['page'] )) {
135
				$pageNumber = $_GET ['page'] ['number'];
136
				$pageSize = $_GET ['page'] ['size'] ?? 1;
137
				$pages = $this->generatePagination ( $filter, $pageNumber, $pageSize );
138
			}
139 1
			$datas = DAO::getAll ( $this->model, $filter, $this->getInclude ( $this->getRequestParam ( 'include', true ) ) );
140 1
			echo $this->_getResponseFormatter ()->get ( $datas, $pages );
141 1
		} );
142 1
	}
143
144
	/**
145
	 * Returns an instance of $resource, by primary key $id.
146
	 *
147
	 * @param string $resource The resource (model) to use
148
	 * @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)
149
	 *
150
	 * @route("{resource}/{id}/","methods"=>["get"],"priority"=>1000)
151
	 */
152 2
	public function getOne_($resource, $id) {
153
		$this->_checkResource ( $resource, function () use ($id) {
154 2
			$this->_getOne ( $id, true, false );
155 2
		} );
156 2
	}
157
158
	/**
159
	 * Returns an associated member value(s).
160
	 * Query parameters:
161
	 * - **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).
162
	 *
163
	 * @param string $resource The resource (model) to use
164
	 * @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)
165
	 * @param string $member The member to load
166
	 *
167
	 * @route("{resource}/{id}/relationships/{member}/","methods"=>["get"],"priority"=>2000)
168
	 */
169 3
	public function getRelationShip_($resource, $id, $member) {
170
		$this->_checkResource ( $resource, function () use ($id, $member) {
171 3
			$relations = OrmUtils::getAnnotFieldsInRelations ( $this->model );
172 3
			if (isset ( $relations [$member] )) {
173 3
				$include = $this->getRequestParam ( 'include', true );
174 3
				switch ($relations [$member] ['type']) {
175 3
					case 'manyToOne' :
176 1
						$this->_getManyToOne ( $id, $member, $include );
177 1
						break;
178 2
					case 'oneToMany' :
179 1
						$this->_getOneToMany ( $id, $member, $include );
180 1
						break;
181 1
					case 'manyToMany' :
182 1
						$this->_getManyToMany ( $id, $member, $include );
183 1
						break;
184
				}
185
			}
186 3
		} );
187 3
	}
188
189
	/**
190
	 * Inserts a new instance of $resource.
191
	 * Data attributes are send in data[attributes] request body (in JSON format)
192
	 *
193
	 * @param string $resource The resource (model) to use
194
	 * @route("{resource}/","methods"=>["post"],"priority"=>0)
195
	 * @authorization
196
	 */
197
	public function add_($resource) {
198
		$this->_checkResource ( $resource, function () {
199
			parent::_add ();
200
		} );
201
	}
202
203
	/**
204
	 * Updates an existing instance of $resource.
205
	 * Data attributes are send in data[attributes] request body (in JSON format)
206
	 *
207
	 * @param string $resource The resource (model) to use
208
	 *
209
	 * @route("{resource}/{id}","methods"=>["patch"],"priority"=>0)
210
	 * @authorization
211
	 */
212
	public function update_($resource, ...$id) {
213
		$this->_checkResource ( $resource, function () use ($id) {
214
			if (! $this->hasErrors ()) {
215
				parent::_update ( ...$id );
216
			} else {
217
				echo $this->displayErrors ();
218
			}
219
		} );
220
	}
221
222
	/**
223
	 * Deletes an existing instance of $resource.
224
	 *
225
	 * @param string $resource The resource (model) to use
226
	 * @param string $ids The primary key value(s), if the primary key is composite, use a comma to separate the values (e.g. 1,115,AB)
227
	 *
228
	 * @route("{resource}/{id}/","methods"=>["delete"],"priority"=>0)
229
	 * @authorization
230
	 */
231
	public function delete_($resource, ...$id) {
232
		$this->_checkResource ( $resource, function () use ($id) {
233
			$this->_delete ( ...$id );
234
		} );
235
	}
236
237
	/**
238
	 * Returns the api version.
239
	 *
240
	 * @return string
241
	 */
242 1
	public static function _getApiVersion() {
243 1
		return self::API_VERSION;
244
	}
245
246
	/**
247
	 * Returns the template for creating this type of controller
248
	 *
249
	 * @return string
250
	 */
251
	public static function _getTemplateFile() {
252
		return 'restApiController.tpl';
253
	}
254
}
255
256