Completed
Push — master ( 98c885...360704 )
by Aimeos
03:00
created

Base::saveRelationships()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
rs 8.439
cc 6
eloc 15
nc 7
nop 3
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015
6
 * @package Admin
7
 * @subpackage JsonAdm
8
 */
9
10
11
namespace Aimeos\Admin\JsonAdm;
12
13
14
/**
15
 * JSON API common client
16
 *
17
 * @package Admin
18
 * @subpackage JsonAdm
19
 */
20
class Base
21
{
22
	private $view;
23
	private $context;
24
	private $templatePaths;
25
	private $path;
26
27
28
	/**
29
	 * Initializes the client
30
	 *
31
	 * @param \Aimeos\MShop\Context\Item\Iface $context MShop context object
32
	 * @param \Aimeos\MW\View\Iface $view View object
33
	 * @param array $templatePaths List of file system paths where the templates are stored
34
	 * @param string $path Name of the client separated by slashes, e.g "product/stock"
35
	 */
36
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, \Aimeos\MW\View\Iface $view, array $templatePaths, $path )
37
	{
38
		$this->view = $view;
39
		$this->context = $context;
40
		$this->templatePaths = $templatePaths;
41
		$this->path = $path;
42
	}
43
44
45
	/**
46
	 * Deletes the resource or the resource list
47
	 *
48
	 * @param string $body Request body
49
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
50
	 * @param integer &$status Variable which contains the HTTP status afterwards
51
	 * @return string Content for response body
52
	 */
53
	public function delete( $body, array &$header, &$status )
54
	{
55
		$header = array( 'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"' );
56
		$context = $this->getContext();
57
		$view = $this->getView();
58
59
		try
60
		{
61
			$view = $this->deleteItems( $view, $body );
62
			$status = 200;
63
		}
64
		catch( \Aimeos\Admin\JsonAdm\Exception $e )
65
		{
66
			$status = $e->getCode();
67
			$view->errors = array( array(
68
				'title' => $context->getI18n()->dt( 'admin/jsonadm', $e->getMessage() ),
69
				'detail' => $e->getTraceAsString(),
70
			) );
71
		}
72
		catch( \Aimeos\MAdmin\Exception $e )
73
		{
74
			$status = 404;
75
			$view->errors = array( array(
76
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
77
				'detail' => $e->getTraceAsString(),
78
			) );
79
		}
80
		catch( \Aimeos\MShop\Exception $e )
81
		{
82
			$status = 404;
83
			$view->errors = array( array(
84
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
85
				'detail' => $e->getTraceAsString(),
86
			) );
87
		}
88
		catch( \Exception $e )
89
		{
90
			$status = 500;
91
			$view->errors = array( array(
92
				'title' => $e->getMessage(),
93
				'detail' => $e->getTraceAsString(),
94
			) );
95
		}
96
97
		/** admin/jsonadm/standard/template-delete
98
		 * Relative path to the JSON API template for DELETE requests
99
		 *
100
		 * The template file contains the code and processing instructions
101
		 * to generate the result shown in the JSON API body. The
102
		 * configuration string is the path to the template file relative
103
		 * to the templates directory (usually in admin/jsonadm/templates).
104
		 *
105
		 * You can overwrite the template file configuration in extensions and
106
		 * provide alternative templates. These alternative templates should be
107
		 * named like the default one but with the string "standard" replaced by
108
		 * an unique name. You may use the name of your project for this. If
109
		 * you've implemented an alternative client class as well, "standard"
110
		 * should be replaced by the name of the new class.
111
		 *
112
		 * @param string Relative path to the template creating the body for the DELETE method of the JSON API
113
		 * @since 2015.12
114
		 * @category Developer
115
		 * @see admin/jsonadm/standard/template-get
116
		 * @see admin/jsonadm/standard/template-patch
117
		 * @see admin/jsonadm/standard/template-post
118
		 * @see admin/jsonadm/standard/template-put
119
		 * @see admin/jsonadm/standard/template-options
120
		 */
121
		$tplconf = 'admin/jsonadm/standard/template-delete';
122
		$default = 'delete-default.php';
123
124
		return $view->render( $view->config( $tplconf, $default ) );
125
	}
126
127
128
	/**
129
	 * Returns the requested resource or the resource list
130
	 *
131
	 * @param string $body Request body
132
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
133
	 * @param integer &$status Variable which contains the HTTP status afterwards
134
	 * @return string Content for response body
135
	 */
136
	public function get( $body, array &$header, &$status )
137
	{
138
		$header = array( 'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"' );
139
		$view = $this->getView();
140
141
		try
142
		{
143
			$view = $this->getItem( $view );
144
			$status = 200;
145
		}
146
		catch( \Aimeos\MAdmin\Exception $e )
147
		{
148
			$status = 404;
149
			$view->errors = array( array(
150
				'title' => $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ),
151
				'detail' => $e->getTraceAsString(),
152
			) );
153
		}
154
		catch( \Aimeos\MShop\Exception $e )
155
		{
156
			$status = 404;
157
			$view->errors = array( array(
158
				'title' => $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ),
159
				'detail' => $e->getTraceAsString(),
160
			) );
161
		}
162
		catch( \Exception $e )
163
		{
164
			$status = 500;
165
			$view->errors = array( array(
166
				'title' => $e->getMessage(),
167
				'detail' => $e->getTraceAsString(),
168
			) );
169
		}
170
171
		/** admin/jsonadm/standard/template-get
172
		 * Relative path to the JSON API template for GET requests
173
		 *
174
		 * The template file contains the code and processing instructions
175
		 * to generate the result shown in the JSON API body. The
176
		 * configuration string is the path to the template file relative
177
		 * to the templates directory (usually in admin/jsonadm/templates).
178
		 *
179
		 * You can overwrite the template file configuration in extensions and
180
		 * provide alternative templates. These alternative templates should be
181
		 * named like the default one but with the string "standard" replaced by
182
		 * an unique name. You may use the name of your project for this. If
183
		 * you've implemented an alternative client class as well, "standard"
184
		 * should be replaced by the name of the new class.
185
		 *
186
		 * @param string Relative path to the template creating the body for the GET method of the JSON API
187
		 * @since 2015.12
188
		 * @category Developer
189
		 * @see admin/jsonadm/standard/template-delete
190
		 * @see admin/jsonadm/standard/template-patch
191
		 * @see admin/jsonadm/standard/template-post
192
		 * @see admin/jsonadm/standard/template-put
193
		 * @see admin/jsonadm/standard/template-options
194
		 */
195
		$tplconf = 'admin/jsonadm/standard/template-get';
196
		$default = 'get-default.php';
197
198
		return $view->render( $view->config( $tplconf, $default ) );
199
	}
200
201
202
	/**
203
	 * Updates the resource or the resource list partitially
204
	 *
205
	 * @param string $body Request body
206
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
207
	 * @param integer &$status Variable which contains the HTTP status afterwards
208
	 * @return string Content for response body
209
	 */
210
	public function patch( $body, array &$header, &$status )
211
	{
212
		$header = array( 'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"' );
213
		$context = $this->getContext();
214
		$view = $this->getView();
215
216
		try
217
		{
218
			$view = $this->patchItems( $view, $body, $header );
219
			$status = 200;
220
		}
221
		catch( \Aimeos\Admin\JsonAdm\Exception $e )
222
		{
223
			$status = $e->getCode();
224
			$view->errors = array( array(
225
				'title' => $context->getI18n()->dt( 'admin/jsonadm', $e->getMessage() ),
226
				'detail' => $e->getTraceAsString(),
227
			) );
228
		}
229
		catch( \Aimeos\MAdmin\Exception $e )
230
		{
231
			$status = 404;
232
			$view->errors = array( array(
233
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
234
				'detail' => $e->getTraceAsString(),
235
			) );
236
		}
237
		catch( \Aimeos\MShop\Exception $e )
238
		{
239
			$status = 404;
240
			$view->errors = array( array(
241
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
242
				'detail' => $e->getTraceAsString(),
243
			) );
244
		}
245
		catch( \Exception $e )
246
		{
247
			$status = 500;
248
			$view->errors = array( array(
249
				'title' => $e->getMessage(),
250
				'detail' => $e->getTraceAsString(),
251
			) );
252
		}
253
254
		/** admin/jsonadm/standard/template-patch
255
		 * Relative path to the JSON API template for PATCH requests
256
		 *
257
		 * The template file contains the code and processing instructions
258
		 * to generate the result shown in the JSON API body. The
259
		 * configuration string is the path to the template file relative
260
		 * to the templates directory (usually in admin/jsonadm/templates).
261
		 *
262
		 * You can overwrite the template file configuration in extensions and
263
		 * provide alternative templates. These alternative templates should be
264
		 * named like the default one but with the string "standard" replaced by
265
		 * an unique name. You may use the name of your project for this. If
266
		 * you've implemented an alternative client class as well, "standard"
267
		 * should be replaced by the name of the new class.
268
		 *
269
		 * @param string Relative path to the template creating the body for the PATCH method of the JSON API
270
		 * @since 2015.12
271
		 * @category Developer
272
		 * @see admin/jsonadm/standard/template-get
273
		 * @see admin/jsonadm/standard/template-post
274
		 * @see admin/jsonadm/standard/template-delete
275
		 * @see admin/jsonadm/standard/template-put
276
		 * @see admin/jsonadm/standard/template-options
277
		 */
278
		$tplconf = 'admin/jsonadm/standard/template-patch';
279
		$default = 'patch-default.php';
280
281
		return $view->render( $view->config( $tplconf, $default ) );
282
	}
283
284
285
	/**
286
	 * Creates or updates the resource or the resource list
287
	 *
288
	 * @param string $body Request body
289
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
290
	 * @param integer &$status Variable which contains the HTTP status afterwards
291
	 * @return string Content for response body
292
	 */
293
	public function post( $body, array &$header, &$status )
294
	{
295
		$header = array( 'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"' );
296
		$context = $this->getContext();
297
		$view = $this->getView();
298
299
		try
300
		{
301
			$view = $this->postItems( $view, $body, $header );
302
			$status = 201;
303
		}
304
		catch( \Aimeos\Admin\JsonAdm\Exception $e )
305
		{
306
			$status = $e->getCode();
307
			$view->errors = array( array(
308
				'title' => $context->getI18n()->dt( 'admin/jsonadm', $e->getMessage() ),
309
				'detail' => $e->getTraceAsString(),
310
			) );
311
		}
312
		catch( \Aimeos\MAdmin\Exception $e )
313
		{
314
			$status = 404;
315
			$view->errors = array( array(
316
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
317
				'detail' => $e->getTraceAsString(),
318
			) );
319
		}
320
		catch( \Aimeos\MShop\Exception $e )
321
		{
322
			$status = 404;
323
			$view->errors = array( array(
324
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
325
				'detail' => $e->getTraceAsString(),
326
			) );
327
		}
328
		catch( \Exception $e )
329
		{
330
			$status = 500;
331
			$view->errors = array( array(
332
				'title' => $e->getMessage(),
333
				'detail' => $e->getTraceAsString(),
334
			) );
335
		}
336
337
		/** admin/jsonadm/standard/template-post
338
		 * Relative path to the JSON API template for POST requests
339
		 *
340
		 * The template file contains the code and processing instructions
341
		 * to generate the result shown in the JSON API body. The
342
		 * configuration string is the path to the template file relative
343
		 * to the templates directory (usually in admin/jsonadm/templates).
344
		 *
345
		 * You can overwrite the template file configuration in extensions and
346
		 * provide alternative templates. These alternative templates should be
347
		 * named like the default one but with the string "standard" replaced by
348
		 * an unique name. You may use the name of your project for this. If
349
		 * you've implemented an alternative client class as well, "standard"
350
		 * should be replaced by the name of the new class.
351
		 *
352
		 * @param string Relative path to the template creating the body for the POST method of the JSON API
353
		 * @since 2015.12
354
		 * @category Developer
355
		 * @see admin/jsonadm/standard/template-get
356
		 * @see admin/jsonadm/standard/template-patch
357
		 * @see admin/jsonadm/standard/template-delete
358
		 * @see admin/jsonadm/standard/template-put
359
		 * @see admin/jsonadm/standard/template-options
360
		 */
361
		$tplconf = 'admin/jsonadm/standard/template-post';
362
		$default = 'post-default.php';
363
364
		return $view->render( $view->config( $tplconf, $default ) );
365
	}
366
367
368
	/**
369
	 * Creates or updates the resource or the resource list
370
	 *
371
	 * @param string $body Request body
372
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
373
	 * @param integer &$status Variable which contains the HTTP status afterwards
374
	 * @return string Content for response body
375
	 */
376
	public function put( $body, array &$header, &$status )
377
	{
378
		$header = array( 'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"' );
379
		$status = 501;
380
381
		$context = $this->getContext();
382
		$view = $this->getView();
383
384
		$view->errors = array( array(
385
			'title' => $context->getI18n()->dt( 'admin/jsonadm', 'Not implemented, use PATCH instead' ),
386
		) );
387
388
		/** admin/jsonadm/standard/template-put
389
		 * Relative path to the JSON API template for PUT requests
390
		 *
391
		 * The template file contains the code and processing instructions
392
		 * to generate the result shown in the JSON API body. The
393
		 * configuration string is the path to the template file relative
394
		 * to the templates directory (usually in admin/jsonadm/templates).
395
		 *
396
		 * You can overwrite the template file configuration in extensions and
397
		 * provide alternative templates. These alternative templates should be
398
		 * named like the default one but with the string "standard" replaced by
399
		 * an unique name. You may use the name of your project for this. If
400
		 * you've implemented an alternative client class as well, "standard"
401
		 * should be replaced by the name of the new class.
402
		 *
403
		 * @param string Relative path to the template creating the body for the PUT method of the JSON API
404
		 * @since 2015.12
405
		 * @category Developer
406
		 * @see admin/jsonadm/standard/template-delete
407
		 * @see admin/jsonadm/standard/template-patch
408
		 * @see admin/jsonadm/standard/template-post
409
		 * @see admin/jsonadm/standard/template-get
410
		 * @see admin/jsonadm/standard/template-options
411
		 */
412
		$tplconf = 'admin/jsonadm/standard/template-put';
413
		$default = 'put-default.php';
414
415
		return $view->render( $view->config( $tplconf, $default ) );
416
	}
417
418
419
	/**
420
	 * Returns the available REST verbs and the available resources
421
	 *
422
	 * @param string $body Request body
423
	 * @param array &$header Variable which contains the HTTP headers and the new ones afterwards
424
	 * @param integer &$status Variable which contains the HTTP status afterwards
425
	 * @return string Content for response body
426
	 */
427
	public function options( $body, array &$header, &$status )
428
	{
429
		$context = $this->getContext();
430
		$view = $this->getView();
431
432
		try
433
		{
434
			$resources = $attributes = array();
435
436
			foreach( $this->getDomains( $view ) as $domain )
437
			{
438
				$manager = \Aimeos\MShop\Factory::createManager( $context, $domain );
439
				$resources = array_merge( $resources, $manager->getResourceType( true ) );
440
				$attributes = array_merge( $attributes, $manager->getSearchAttributes( true ) );
441
			}
442
443
			$view->resources = $resources;
444
			$view->attributes = $attributes;
445
446
			$header = array(
447
				'Content-Type' => 'application/vnd.api+json; supported-ext="bulk"',
448
				'Allow' => 'DELETE,GET,POST,OPTIONS'
449
			);
450
			$status = 200;
451
		}
452
		catch( \Aimeos\MAdmin\Exception $e )
453
		{
454
			$status = 404;
455
			$view->errors = array( array(
456
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
457
				'detail' => $e->getTraceAsString(),
458
			) );
459
		}
460
		catch( \Aimeos\MShop\Exception $e )
461
		{
462
			$status = 404;
463
			$view->errors = array( array(
464
				'title' => $context->getI18n()->dt( 'mshop', $e->getMessage() ),
465
				'detail' => $e->getTraceAsString(),
466
			) );
467
		}
468
		catch( \Exception $e )
469
		{
470
			$status = 500;
471
			$view->errors = array( array(
472
				'title' => $e->getMessage(),
473
				'detail' => $e->getTraceAsString(),
474
			) );
475
		}
476
477
		/** admin/jsonadm/standard/template-options
478
		 * Relative path to the JSON API template for OPTIONS requests
479
		 *
480
		 * The template file contains the code and processing instructions
481
		 * to generate the result shown in the JSON API body. The
482
		 * configuration string is the path to the template file relative
483
		 * to the templates directory (usually in admin/jsonadm/templates).
484
		 *
485
		 * You can overwrite the template file configuration in extensions and
486
		 * provide alternative templates. These alternative templates should be
487
		 * named like the default one but with the string "standard" replaced by
488
		 * an unique name. You may use the name of your project for this. If
489
		 * you've implemented an alternative client class as well, "standard"
490
		 * should be replaced by the name of the new class.
491
		 *
492
		 * @param string Relative path to the template creating the body for the OPTIONS method of the JSON API
493
		 * @since 2015.12
494
		 * @category Developer
495
		 * @see admin/jsonadm/standard/template-delete
496
		 * @see admin/jsonadm/standard/template-patch
497
		 * @see admin/jsonadm/standard/template-post
498
		 * @see admin/jsonadm/standard/template-get
499
		 * @see admin/jsonadm/standard/template-put
500
		 */
501
		$tplconf = 'admin/jsonadm/standard/template-options';
502
		$default = 'options-default.php';
503
504
		return $view->render( $view->config( $tplconf, $default ) );
505
	}
506
507
508
	/**
509
	 * Deletes one or more items
510
	 *
511
	 * @param \Aimeos\MW\View\Iface $view View instance with "param" view helper
512
	 * @param string $body Request body
513
	 * @return \Aimeos\MW\View\Iface $view View object that will contain the "total" property afterwards
514
	 * @throws \Aimeos\Admin\JsonAdm\Exception If the request body is invalid
515
	 */
516
	protected function deleteItems( \Aimeos\MW\View\Iface $view, $body )
517
	{
518
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $this->getPath() );
519
520
		if( ( $id = $view->param( 'id' ) ) == null )
521
		{
522
			if( ( $request = json_decode( $body ) ) === null || !isset( $request->data ) || !is_array( $request->data ) ) {
523
				throw new \Aimeos\Admin\JsonAdm\Exception( sprintf( 'Invalid JSON in body' ), 400 );
524
			}
525
526
			$ids = $this->getIds( $request );
527
			$manager->deleteItems( $ids );
528
			$view->total = count( $ids );
529
		}
530
		else
531
		{
532
			$manager->deleteItem( $id );
533
			$view->total = 1;
534
		}
535
536
		return $view;
537
	}
538
539
540
	/**
541
	 * Retrieves the item or items and adds the data to the view
542
	 *
543
	 * @param \Aimeos\MW\View\Iface $view View instance
544
	 * @return \Aimeos\MW\View\Iface View instance with additional data assigned
545
	 */
546
	protected function getItem( \Aimeos\MW\View\Iface $view )
547
	{
548
		$total = 1;
549
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $this->getPath() );
550
		$include = ( ( $include = $view->param( 'include' ) ) !== null ? explode( ',', $include ) : array() );
551
552
		if( ( $id = $view->param( 'id' ) ) == null )
553
		{
554
			$search = $this->initCriteria( $manager->createSearch(), $view->param() );
555
			$view->data = $manager->searchItems( $search, array(), $total );
556
			$view->childItems = $this->getChildItems( $view->data, $include );
557
			$view->listItems = $this->getListItems( $view->data, $include );
558
		}
559
		else
560
		{
561
			$view->data = $manager->getItem( $id, array() );
562
			$view->childItems = $this->getChildItems( array( $id => $view->data ), $include );
563
			$view->listItems = $this->getListItems( array( $id => $view->data ), $include );
564
		}
565
566
		$view->refItems = $this->getRefItems( $view->listItems );
567
568
		$view->total = $total;
569
570
		return $view;
571
	}
572
573
	/**
574
	 * Returns the view object
575
	 *
576
	 * @return \Aimeos\MW\View\Iface View object
577
	 */
578
	protected function getView()
579
	{
580
		return $this->view;
581
	}
582
583
584
	/**
585
	 * Initializes the criteria object based on the given parameter
586
	 *
587
	 * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object
588
	 * @param array $params List of criteria data with condition, sorting and paging
589
	 * @return \Aimeos\MW\Criteria\Iface Initialized criteria object
590
	 */
591
	protected function initCriteria( \Aimeos\MW\Criteria\Iface $criteria, array $params )
592
	{
593
		$this->initCriteriaConditions( $criteria, $params );
594
		$this->initCriteriaSortations( $criteria, $params );
595
		$this->initCriteriaSlice( $criteria, $params );
596
597
		return $criteria;
598
	}
599
600
601
	/**
602
	 * Initializes the criteria object with conditions based on the given parameter
603
	 *
604
	 * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object
605
	 * @param array $params List of criteria data with condition, sorting and paging
606
	 */
607
	private function initCriteriaConditions( \Aimeos\MW\Criteria\Iface $criteria, array $params )
608
	{
609
		if( isset( $params['filter'] ) && is_array( $params['filter'] ) )
610
		{
611
			$existing = $criteria->getConditions();
612
			$criteria->setConditions( $criteria->toConditions( (array) $params['filter'] ) );
613
614
			$expr = array( $criteria->getConditions(), $existing );
615
			$criteria->setConditions( $criteria->combine( '&&', $expr ) );
616
		}
617
	}
618
619
620
	/**
621
	 * Initializes the criteria object with the slice based on the given parameter.
622
	 *
623
	 * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object
624
	 * @param array $params List of criteria data with condition, sorting and paging
625
	 */
626
	private function initCriteriaSlice( \Aimeos\MW\Criteria\Iface $criteria, array $params )
627
	{
628
		$start = ( isset( $params['page']['offset'] ) ? $params['page']['offset'] : 0 );
629
		$size = ( isset( $params['page']['limit'] ) ? $params['page']['limit'] : 25 );
630
631
		$criteria->setSlice( $start, $size );
632
	}
633
634
635
	/**
636
	 * Initializes the criteria object with sortations based on the given parameter
637
	 *
638
	 * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object
639
	 * @param array $params List of criteria data with condition, sorting and paging
640
	 */
641
	private function initCriteriaSortations( \Aimeos\MW\Criteria\Iface $criteria, array $params )
642
	{
643
		if( !isset( $params['sort'] ) ) {
644
			return;
645
		}
646
647
		$sortation = array();
648
649
		foreach( explode( ',', $params['sort'] ) as $sort )
650
		{
651
			if( $sort[0] === '-' ) {
652
				$sortation[] = $criteria->sort( '-', substr( $sort, 1 ) );
653
			} else {
654
				$sortation[] = $criteria->sort( '+', $sort ); break;
655
			}
656
		}
657
658
		$criteria->setSortations( $sortation );
659
	}
660
661
662
	/**
663
	 * Returns the list of domains that are available as resources
664
	 *
665
	 * @param \Aimeos\MW\View\Iface $view View object with "resource" parameter
666
	 * @return array List of domain names
667
	 */
668
	protected function getDomains( \Aimeos\MW\View\Iface $view )
669
	{
670
		if( ( $domains = $view->param( 'resource' ) ) == '' )
671
		{
672
			/** admin/jsonadm/domains
673
			 * A list of domain names whose clients are available for the JSON API
674
			 *
675
			 * The HTTP OPTIONS method returns a list of resources known by the
676
			 * JSON API including their URLs. The list of available resources
677
			 * can be exteded dynamically be implementing a new Jsonadm client
678
			 * class handling request for this new domain.
679
			 *
680
			 * To add the new domain client to the list of resources returned
681
			 * by the HTTP OPTIONS method, you have to add its name in lower case
682
			 * to the existing configuration.
683
			 *
684
			 * @param array List of domain names
685
			 * @since 2016.01
686
			 * @category Developer
687
			 */
688
			$default = array(
689
				'attribute', 'catalog', 'coupon', 'customer', 'locale', 'media',
690
				'order', 'plugin', 'price', 'product', 'service', 'supplier', 'tag', 'text'
691
			);
692
			$domains = $this->getContext()->getConfig()->get( 'admin/jsonadm/domains', $default );
693
		}
694
695
		return (array) $domains;
696
	}
697
698
699
	/**
700
	 * Returns the items with parent/child relationships
701
	 *
702
	 * @param array $items List of items implementing \Aimeos\MShop\Common\Item\Iface
703
	 * @param array $include List of resource types that should be fetched
704
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Iface
705
	 */
706
	protected function getChildItems( array $items, array $include )
707
	{
708
		return array();
709
	}
710
711
712
	/**
713
	 * Returns the list items for association relationships
714
	 *
715
	 * @param array $items List of items implementing \Aimeos\MShop\Common\Item\Iface
716
	 * @param array $include List of resource types that should be fetched
717
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Lists\Iface
718
	 */
719
	protected function getListItems( array $items, array $include )
720
	{
721
		return array();
722
	}
723
724
725
	/**
726
	 * Returns the items associated via a lists table
727
	 *
728
	 * @param array $listItems List of items implementing \Aimeos\MShop\Common\Item\Lists\Iface
729
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Iface
730
	 */
731
	protected function getRefItems( array $listItems )
732
	{
733
		$list = $map = array();
734
735
		foreach( $listItems as $listItem ) {
736
			$map[$listItem->getDomain()][] = $listItem->getRefId();
737
		}
738
739
		foreach( $map as $domain => $ids )
740
		{
741
			$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $domain );
742
743
			$search = $manager->createSearch();
744
			$search->setConditions( $search->compare( '==', $domain . '.id', $ids ) );
745
746
			$list = array_merge( $list, $manager->searchItems( $search ) );
747
		}
748
749
		return $list;
750
	}
751
752
753
	/**
754
	 * Returns the IDs sent in the request body
755
	 *
756
	 * @param \stdClass $request Decoded request body
757
	 * @return array List of item IDs
758
	 */
759
	protected function getIds( $request )
760
	{
761
		$ids = array();
762
763
		if( isset( $request->data ) )
764
		{
765
			foreach( $request->data as $entry )
766
			{
767
				if( isset( $entry->id ) ) {
768
					$ids[] = $entry->id;
769
				}
770
			}
771
		}
772
773
		return $ids;
774
	}
775
776
777
	/**
778
	 * Returns the context item object
779
	 *
780
	 * @return \Aimeos\MShop\Context\Item\Iface Context object
781
	 */
782
	protected function getContext()
783
	{
784
		return $this->context;
785
	}
786
787
788
	/**
789
	 * Returns the paths to the template files
790
	 *
791
	 * @return array List of file system paths
792
	 */
793
	protected function getTemplatePaths()
794
	{
795
		return $this->templatePaths;
796
	}
797
798
799
	/**
800
	 * Returns the path to the client
801
	 *
802
	 * @return string Client path, e.g. "product/stock"
803
	 */
804
	protected function getPath()
805
	{
806
		return $this->path;
807
	}
808
809
810
	/**
811
	 * Saves new attributes for one or more items
812
	 *
813
	 * @param \Aimeos\MW\View\Iface $view View that will contain the "data" and "total" properties afterwards
814
	 * @param string $body Request body
815
	 * @param array &$header Associative list of HTTP headers as value/result parameter
816
	 * @throws \Aimeos\Admin\JsonAdm\Exception If "id" parameter isn't available or the body is invalid
817
	 * @return \Aimeos\MW\View\Iface Updated view instance
818
	 */
819
	protected function patchItems( \Aimeos\MW\View\Iface $view, $body, array &$header )
820
	{
821
		if( ( $request = json_decode( $body ) ) === null || !isset( $request->data ) ) {
822
			throw new \Aimeos\Admin\JsonAdm\Exception( sprintf( 'Invalid JSON in body' ), 400 );
823
		}
824
825
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $this->getPath() );
826
827
		if( is_array( $request->data ) )
828
		{
829
			$data = $this->saveData( $manager, $request );
830
831
			$view->data = $data;
832
			$view->total = count( $data );
833
			$header['Content-Type'] = 'application/vnd.api+json; ext="bulk"; supported-ext="bulk"';
834
		}
835
		else
836
		{
837
			if( ( $id = $view->param( 'id' ) ) == null ) {
838
				throw new \Aimeos\Admin\JsonAdm\Exception( sprintf( 'No ID given' ), 400 );
839
			}
840
841
			$request->data->id = $id;
842
			$data = $this->saveEntry( $manager, $request->data );
843
844
			$view->data = $data;
845
			$view->total = 1;
846
		}
847
848
		return $view;
849
	}
850
851
852
	/**
853
	 * Creates one or more new items
854
	 *
855
	 * @param \Aimeos\MW\View\Iface $view View that will contain the "data" and "total" properties afterwards
856
	 * @param string $body Request body
857
	 * @param array &$header Associative list of HTTP headers as value/result parameter
858
	 * @return \Aimeos\MW\View\Iface Updated view instance
859
	 */
860
	protected function postItems( \Aimeos\MW\View\Iface $view, $body, array &$header )
861
	{
862
		if( ( $request = json_decode( $body ) ) === null || !isset( $request->data ) ) {
863
			throw new \Aimeos\Admin\JsonAdm\Exception( sprintf( 'Invalid JSON in body' ), 400 );
864
		}
865
866
		if( isset( $request->data->id ) || $view->param( 'id' ) != null ) {
867
			throw new \Aimeos\Admin\JsonAdm\Exception( sprintf( 'Client generated IDs are not supported' ), 403 );
868
		}
869
870
871
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $this->getPath() );
872
873
		if( is_array( $request->data ) )
874
		{
875
			$data = $this->saveData( $manager, $request );
876
877
			$view->data = $data;
878
			$view->total = count( $data );
879
			$header['Content-Type'] = 'application/vnd.api+json; ext="bulk"; supported-ext="bulk"';
880
		}
881
		else
882
		{
883
			$request->data->id = null;
884
			$data = $this->saveEntry( $manager, $request->data );
885
886
			$view->data = $data;
887
			$view->total = 1;
888
		}
889
890
		return $view;
891
	}
892
893
894
	/**
895
	 * Creates of updates several items at once
896
	 *
897
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Manager responsible for the items
898
	 * @param \stdClass $request Object with request body data
899
	 * @return array List of items
900
	 */
901
	protected function saveData( \Aimeos\MShop\Common\Manager\Iface $manager, \stdClass $request )
902
	{
903
		$data = array();
904
905
		if( isset( $request->data ) )
906
		{
907
			foreach( (array) $request->data as $entry ) {
908
				$data[] = $this->saveEntry( $manager, $entry );
909
			}
910
		}
911
912
		return $data;
913
	}
914
915
916
	/**
917
	 * Saves and returns the new or updated item
918
	 *
919
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Manager responsible for the items
920
	 * @param \stdClass $entry Object including "id" and "attributes" elements
921
	 * @return \Aimeos\MShop\Common\Item\Iface New or updated item
922
	 */
923
	protected function saveEntry( \Aimeos\MShop\Common\Manager\Iface $manager, \stdClass $entry )
924
	{
925
		$attr = ( isset( $entry->attributes ) ? (array) $entry->attributes : array() );
926
927
		if( isset( $entry->id ) && $entry->id !== null ) {
928
			$item = $manager->getItem( $entry->id );
929
		} else {
930
			$item = $manager->createItem();
931
		}
932
933
		$item->fromArray( $attr );
934
		$manager->saveItem( $item );
935
936
		if( isset( $entry->relationships ) ) {
937
			$this->saveRelationships( $manager, $item, $entry->relationships );
938
		}
939
940
		return $manager->getItem( $item->getId() );
941
	}
942
943
944
	/**
945
	 * Saves the item references associated via the list
946
	 *
947
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Manager responsible for the items
948
	 * @param \Aimeos\MShop\Common\Item\Iface $item Domain item with an unique ID set
949
	 * @param \stdClass $relationships Object including the <domain>/data/attributes structure
950
	 */
951
	protected function saveRelationships( \Aimeos\MShop\Common\Manager\Iface $manager,
952
		\Aimeos\MShop\Common\Item\Iface $item, \stdClass $relationships )
953
	{
954
		$id = $item->getId();
955
		$listManager = $manager->getSubManager( 'lists' );
956
957
		foreach( (array) $relationships as $domain => $list )
958
		{
959
			if( isset( $list->data ) )
960
			{
961
				foreach( (array) $list->data as $data )
962
				{
963
					$listItem = $listManager->createItem();
964
965
					if( isset( $data->attributes ) ) {
966
						$listItem->fromArray( (array) $data->attributes );
967
					}
968
969
					if( isset( $data->id ) ) {
970
						$listItem->setRefId( $data->id );
971
					}
972
973
					$listItem->setParentId( $id );
974
					$listItem->setDomain( $domain );
975
976
					$listManager->saveItem( $listItem, false );
977
				}
978
			}
979
		}
980
	}
981
}
982