1 <?php
2
3 /* --------------------------------------------------------------
4 CategoriesApiV2Controller.inc.php 2016-03-07
5 Gambio GmbH
6 http://www.gambio.de
7 Copyright (c) 2016 Gambio GmbH
8 Released under the GNU General Public License (Version 2)
9 [http://www.gnu.org/licenses/gpl-2.0.html]
10 --------------------------------------------------------------
11 */
12
13 MainFactory::load_class('HttpApiV2Controller');
14
15 /**
16 * Class CategoriesApiV2Controller
17 *
18 * Provides a gateway to the CategoryWriteService and CategoryReadService classes, which handle the shop category
19 * resources.
20 *
21 * @category System
22 * @package ApiV2Controllers
23 */
24 class CategoriesApiV2Controller extends HttpApiV2Controller
25 {
26 /**
27 * Category write service.
28 *
29 * @var CategoryWriteService
30 */
31 protected $categoryWriteService;
32
33 /**
34 * Category read service.
35 *
36 * @var CategoryReadService
37 */
38 protected $categoryReadService;
39
40 /**
41 * Category JSON serializer.
42 *
43 * @var CategoryJsonSerializer
44 */
45 protected $categoryJsonSerializer;
46
47 /**
48 * Category list item JSON serializer.
49 *
50 * @var CategoryListItemJsonSerializer
51 */
52 protected $categoryListItemJsonSerializer;
53
54
55 /**
56 * Initialize API Controller
57 */
58 protected function __initialize()
59 {
60 $this->categoryWriteService = StaticGXCoreLoader::getService('CategoryWrite');
61 $this->categoryReadService = StaticGXCoreLoader::getService('CategoryRead');
62 $this->categoryJsonSerializer = MainFactory::create('CategoryJsonSerializer');
63 $this->categoryListItemJsonSerializer = MainFactory::create('CategoryListItemJsonSerializer');
64 }
65
66
67 /**
68 * @api {post} /categories Create Category
69 * @apiVersion 2.1.0
70 * @apiName CreateCategory
71 * @apiGroup Categories
72 *
73 * @apiDescription
74 * Creates new category in the system. To see an example usage take a look at
75 * `docs/REST/samples/category-service/create_category.php`
76 *
77 * @apiParamExample {json} Request-Body
78 * {
79 * "parentId": 0,
80 * "isActive": true,
81 * "sortOrder": 0,
82 * "name": {
83 * "en": "test category",
84 * "de": "Testkategorie"
85 * },
86 * "headingTitle": {
87 * "en": "test category",
88 * "de": "Testkategorie"
89 * },
90 * "description": {
91 * "en": "<p>test category description</p>",
92 * "de": "<p>Testkategorie Beschreibung</p>"
93 * },
94 * "metaTitle": {
95 * "en": "",
96 * "de": ""
97 * },
98 * "metaDescription": {
99 * "en": "",
100 * "de": ""
101 * },
102 * "metaKeywords": {
103 * "en": "",
104 * "de": ""
105 * },
106 * "urlKeywords": {
107 * "en": "test-category",
108 * "de": "Testkategorie"
109 * },
110 * "icon": "item_ltr.gif",
111 * "image": "",
112 * "imageAltText": {
113 * "en": "",
114 * "de": ""
115 * },
116 * "settings": {
117 * "categoryListingTemplate": "categorie_listing.html",
118 * "productListingTemplate": "product_listing_v1.html",
119 * "sortColumn": "p.products_price",
120 * "sortDirection": "ASC",
121 * "onSitemap": true,
122 * "sitemapPriority": "0.5",
123 * "sitemapChangeFrequency": "daily",
124 * "showAttributes": false,
125 * "showGraduatedPrice": false,
126 * "showQuantity": true,
127 * "showQuantityInfo": false,
128 * "showSubCategories": true,
129 * "showSubCategoryImages": true,
130 * "showSubCategoryNames": true,
131 * "showSubCategoryProducts": false,
132 * "isViewModeTiled": false,
133 * "groupPermissions": [
134 * {
135 * "id": "0",
136 * "isPermitted": false
137 * },
138 * {
139 * "id": "1",
140 * "isPermitted": false
141 * },
142 * {
143 * "id": "2",
144 * "isPermitted": false
145 * },
146 * {
147 * "id": "3",
148 * "isPermitted": false
149 * }
150 * ]
151 * }
152 * }
153 *
154 * @apiParam {Number} parentId The ID of the parent category (use 0 if there is no parent category).
155 * @apiParam {Boolean} isActive Whether the category is active.
156 * @apiParam {Number} sortOrder Category's sort order starts from 0.
157 * @apiParam {Object} name Multi-language object with the category's name.
158 * @apiParam {Object} headingTitle Multi-language object with the category's title.
159 * @apiParam {Object} description Multi-language object with the category's description.
160 * @apiParam {Object} metaTitle Multi-language object with the category's meta title.
161 * @apiParam {Object} metaDescription Multi-language object with the category's meta description.
162 * @apiParam {Object} metaKeywords Multi-language object with the category's meta keywords.
163 * @apiParam {Object} urlKeywords Multi-language object with the category's meta URL keywords.
164 * @apiParam {String} icon The category icon filename.
165 * @apiParam {String} image The category image filename.
166 * @apiParam {Object} imageAltText Multi-language object with image alt text.
167 * @apiParam {Object} settings Contains the category settings.
168 * @apiParam {String} settings.categoryListingTemplate Provide a category listing template
169 * (`categorie_listing.html`).
170 * @apiParam {String} settings.productListingTemplate Provide a product listing template
171 * (`product_listing_v1.html`).
172 * @apiParam {String} settings.sortColumn The name of the products column that will be used to sort the products.
173 * @apiParam {String} settings.sortDirection Provide `ASC` or `DESC`
174 * @apiParam {Boolean} settings.onSitemap Whether the category appears on sitemap.
175 * @apiParam {String} settings.sitemapPriority A numerical string value that defines the priority.
176 * @apiParam {String} settings.sitemapChangeFrequency Possible values can contain the `always`, `hourly`, `daily`,
177 * `weekly`, `monthly`, `yearly`, `never`.
178 * @apiParam {Boolean} settings.showAttributes Show attributes flag.
179 * @apiParam {Boolean} settings.showGraduatedPrice Show graduated price flag.
180 * @apiParam {Boolean} settings.showQuantity Show quantity flag.
181 * @apiParam {Boolean} settings.showQuantityInfo Show quantity information flag.
182 * @apiParam {Boolean} settings.showSubCategories Show sub categories flag.
183 * @apiParam {Boolean} settings.showSubCategoryImages Show sub category images flag.
184 * @apiParam {Boolean} settings.showSubCategoryNames Show sub category names flag.
185 * @apiParam {Boolean} settings.showSubCategoryProducts Show sub category products flag.
186 * @apiParam {Boolean} settings.isViewModeTiled Whether the category view mode is tiled.
187 * @apiParam {Array} settings.groupPermissions Contains objects that have info about the customer group
188 * permissions.
189 * @apiParam {Number} settings.groupPermissions.id The customer group permissions.
190 * @apiParam {Boolean} settings.groupPermissions.isPermitted Whether the current group is permitted to view the
191 * category.
192 *
193 * @apiSuccess (Success 201) Response-Body If successful, this method returns a complete Category resource in the
194 * response body.
195 *
196 * @apiError 400-BadRequest Category data were not provided.
197 *
198 * @apiErrorExample Error-Response
199 * HTTP/1.1 400 Bad Request
200 * {
201 * "code": 400,
202 * "status": "error",
203 * "message": "Category data were not provided."
204 * }
205 */
206 public function post()
207 {
208 $categoryJsonString = $this->api->request->getBody();
209
210 if(empty($categoryJsonString))
211 {
212 throw new HttpApiV2Exception('Category data were not provided.', 400);
213 }
214
215 if(isset($this->uri[1]) && is_numeric($this->uri[1])) // Duplicate Category
216 {
217 $categoryJsonObject = json_decode($categoryJsonString);
218
219 if($categoryJsonObject->parentId === null || !is_numeric($categoryJsonObject->parentId))
220 {
221 $categoryJsonObject = new stdClass;
222 $categoryJsonObject->parentId = 0; // Default category value.
223 }
224
225 $categoryId = $this->categoryWriteService->duplicateCategory(new IdType($this->uri[1]),
226 new IdType($categoryJsonObject->parentId));
227 }
228 else // Create New Category
229 {
230 $category = $this->categoryJsonSerializer->deserialize($categoryJsonString);
231 $categoryId = $this->categoryWriteService->createCategory($category);
232 }
233
234 $storedCategory = $this->categoryReadService->getCategoryById(new IdType($categoryId));
235 $response = $this->categoryJsonSerializer->serialize($storedCategory, false);
236 $this->_linkResponse($response);
237 $this->_locateResource('categories', $categoryId);
238 $this->_writeResponse($response, 201);
239 }
240
241
242 /**
243 * @api {put} /categories/:id Update Category
244 * @apiVersion 2.1.0
245 * @apiName UpdateCategory
246 * @apiGroup Categories
247 *
248 * @apiDescription
249 * Use this method to update an existing category record. Take a look in the POST method for more detailed
250 * explanation on every resource property. To see an example usage take a look at
251 * `docs/REST/samples/category-service/update_category.php`
252 *
253 * @apiSuccess Response-Body If successful, this method returns the updated Category resource in the response body.
254 *
255 * @apiError 400-BadRequest Category record ID was not provided or is invalid.
256 * @apiError 400-BadRequest Category data were not provided.
257 *
258 * @apiErrorExample Error-Response (Missing or invalid ID)
259 * HTTP/1.1 400 Bad Request
260 * {
261 * "code": 400,
262 * "status": "error",
263 * "message": "Category record ID was not provided or is invalid."
264 * }
265 *
266 * @apiErrorExample Error-Response (Empty request body)
267 * HTTP/1.1 400 Bad Request
268 * {
269 * "code": 400,
270 * "status": "error",
271 * "message": "Category data were not provided."
272 * }
273 */
274 public function put()
275 {
276 if(!isset($this->uri[1]) || !is_numeric($this->uri[1]))
277 {
278 throw new HttpApiV2Exception('Category record ID was not provided or is invalid: ' . gettype($this->uri[1]),
279 400);
280 }
281
282 $categoryJsonString = $this->api->request->getBody();
283
284 if(empty($categoryJsonString))
285 {
286 throw new HttpApiV2Exception('Category data were not provided.', 400);
287 }
288
289 $categoryId = new IdType($this->uri[1]);
290
291 // Ensure that the category has the correct category id of the request url
292 $categoryJsonString = $this->_setJsonValue($categoryJsonString, 'id', $categoryId->asInt());
293
294 $category = $this->categoryJsonSerializer->deserialize($categoryJsonString,
295 $this->categoryReadService->getCategoryById($categoryId));
296
297 $this->categoryWriteService->updateCategory($category);
298
299 $response = $this->categoryJsonSerializer->serialize($category, false);
300 $this->_linkResponse($response);
301 $this->_writeResponse($response, 200);
302 }
303
304
305 /**
306 * @api {delete} /categories/:id Delete Category
307 * @apiVersion 2.1.0
308 * @apiName DeleteCategory
309 * @apiGroup Categories
310 *
311 * @apiDescription
312 * Removes a category record from the database. The products that are assigned to this category will not
313 * be removed. To see an example usage take a look at
314 * `docs/REST/samples/category-service/remove_category.php`
315 *
316 * @apiExample {curl} Delete Category With ID = 57
317 * curl -X DELETE --user admin@shop.de:12345 http://shop.de/api.php/v2/categories/57
318 *
319 * @apiSuccessExample {json} Success-Response
320 * {
321 * "code": 200,
322 * "status": "success",
323 * "action": "delete",
324 * "resource": "Category",
325 * "categoryId": 57
326 * }
327 *
328 * @apiError 400-BadRequest Category record ID was not provided in the resource URL.
329 *
330 * @apiErrorExample Error-Response
331 * HTTP/1.1 400 Bad Request
332 * {
333 * "code": 400,
334 * "status": "error",
335 * "message": "Category record ID was not provided in the resource URL."
336 * }
337 */
338 public function delete()
339 {
340 // Check if record ID was provided.
341 if(!isset($this->uri[1]) || !is_numeric($this->uri[1]))
342 {
343 throw new HttpApiV2Exception('Category record ID was not provided in the resource URL.', 400);
344 }
345
346 // Remove category record from database.
347 $this->categoryWriteService->deleteCategoryById(new IdType($this->uri[1]));
348
349 // Return response JSON.
350 $response = array(
351 'code' => 200,
352 'status' => 'success',
353 'action' => 'delete',
354 'resource' => 'Category',
355 'categoryId' => (int)$this->uri[1]
356 );
357
358 $this->_writeResponse($response);
359 }
360
361
362 /**
363 * @api {get} /categories/:id Get Categories
364 * @apiVersion 2.1.0
365 * @apiName GetCategory
366 * @apiGroup Categories
367 *
368 * @apiDescription
369 * Get multiple or a single category records through a GET request. This method supports all the GET parameters
370 * that are mentioned in the "Introduction" section of this documentation. To see an example usage take a look at
371 * `docs/REST/samples/category-service/fetch_category.php`
372 *
373 * @apiExample {curl} Get All Categories
374 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/categories
375 *
376 * @apiExample {curl} Get Category With ID = 57
377 * curl -i --user admin@shop.de:12345 http://shop.de/api.php/v2/categories/57
378 *
379 * @apiError 404-NotFound Category does not exist.
380 *
381 * @apiErrorExample Error-Response
382 * HTTP/1.1 404 Not Found
383 * {
384 * "code": 404,
385 * "status": "error",
386 * "message": "Category does not exist."
387 * }
388 */
389 public function get()
390 {
391 // Parse customer status limit GET parameter.
392 $customerStatusLimit = null;
393 if($this->api->request->get('customer_status_limit') !== null)
394 {
395 $customerStatusLimit = new IdType($this->api->request->get('customer_status_limit'));
396 }
397
398 // Parse language code GET parameter.
399 $languageParameter = ($this->api->request->get('lang') !== null) ? $this->api->request->get('lang') : 'de';
400 $languageCode = new LanguageCode(new NonEmptyStringType($languageParameter));
401
402 // Fetch the response data through the CategoryReadService.
403 if(isset($this->uri[1]) && is_numeric($this->uri[1]))
404 {
405 if(isset($this->uri[2]) && $this->uri[2] === 'children') // Get Category Children
406 {
407 $categories = $this->categoryReadService->getCategoryList($languageCode, new IdType($this->uri[1]),
408 $customerStatusLimit)->getArray();
409 }
410 else // Get Single Record
411 {
412 try
413 {
414 $categories = array($this->categoryReadService->getCategoryById(new IdType($this->uri[1])));
415 }
416 catch(UnexpectedValueException $e)
417 {
418 throw new HttpApiV2Exception('Category does not exist.', 404);
419 }
420 }
421 }
422 else // Get All Categories
423 {
424 $categories = $this->categoryReadService->getCategoryList($languageCode, null, $customerStatusLimit)
425 ->getArray();
426 }
427
428 // Prepare the response array.
429 $response = array();
430
431 foreach($categories as $category)
432 {
433 if($category instanceof CategoryInterface)
434 {
435 $serialized = $this->categoryJsonSerializer->serialize($category, false);
436 }
437 else
438 {
439 $serialized = $this->categoryListItemJsonSerializer->serialize($category, false);
440 }
441
442 $response[] = $serialized;
443 }
444
445 if($this->api->request->get('q') !== null)
446 {
447 $this->_searchResponse($response, $this->api->request->get('q'));
448 }
449
450 $this->_paginateResponse($response);
451 $this->_sortResponse($response);
452 $this->_minimizeResponse($response);
453 $this->_linkResponse($response);
454
455 // Return single resource to client and not array (if needed).
456 if(isset($this->uri[1]) && is_numeric($this->uri[1]) && !isset($this->uri[2]) && count($response) > 0)
457 {
458 $response = $response[0];
459 }
460
461 $this->_writeResponse($response);
462 }
463 }
464