Passed
Push — develop ( 187c27...c66d22 )
by nguereza
04:00
created

CrudAction   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 458
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 186
c 1
b 0
f 0
dl 0
loc 458
rs 10
wmc 28

7 Methods

Rating   Name   Duplication   Size   Complexity  
B create() 0 68 6
A index() 0 40 4
A detail() 0 23 2
A __construct() 0 14 1
A delete() 0 31 3
B update() 0 80 9
A getEntityFields() 0 14 3
1
<?php
2
3
/**
4
 * Platine PHP
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine PHP
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file CrudAction.php
34
 *
35
 *  The Base CRUD action class
36
 *
37
 *  @package    Platine\Framework\Http\Action
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\Http\Action;
49
50
use Exception;
51
use Platine\Framework\Form\Param\BaseParam;
52
use Platine\Framework\Helper\Flash;
53
use Platine\Framework\Http\RequestData;
54
use Platine\Framework\Http\Response\RedirectResponse;
55
use Platine\Framework\Http\Response\TemplateResponse;
56
use Platine\Framework\Http\RouteHelper;
57
use Platine\Http\ResponseInterface;
58
use Platine\Http\ServerRequestInterface;
59
use Platine\Lang\Lang;
60
use Platine\Logger\LoggerInterface;
61
use Platine\Orm\Entity;
62
use Platine\Orm\Repository;
63
use Platine\Pagination\Pagination;
64
use Platine\Stdlib\Helper\Str;
65
use Platine\Template\Template;
66
67
/**
68
* @class CrudAction
69
* @package Platine\Framework\Http\Action
70
* @template TEntity as Entity
71
*/
72
class CrudAction
73
{
74
    /**
75
    * The Lang instance
76
    * @var Lang
77
    */
78
    protected Lang $lang;
79
80
    /**
81
    * The Pagination instance
82
    * @var Pagination
83
    */
84
    protected Pagination $pagination;
85
86
    /**
87
    * The Template instance
88
    * @var Template
89
    */
90
    protected Template $template;
91
92
    /**
93
    * The Flash instance
94
    * @var Flash
95
    */
96
    protected Flash $flash;
97
98
    /**
99
    * The RouteHelper instance
100
    * @var RouteHelper
101
    */
102
    protected RouteHelper $routeHelper;
103
104
    /**
105
    * The LoggerInterface instance
106
    * @var LoggerInterface
107
    */
108
    protected LoggerInterface $logger;
109
110
    /**
111
    * The Repository instance
112
    * @var Repository<TEntity>
113
    */
114
    protected Repository $repository;
115
116
    /**
117
     * The entity fields
118
     * @var array<int|string, string>
119
     */
120
    protected array $fields = [];
121
122
    /**
123
     * The order fields
124
     * @var array<int|string, string>
125
     */
126
    protected array $orderFields = [];
127
128
    /**
129
     * The unique fields
130
     * @var array<int|string, string>
131
     */
132
    protected array $uniqueFields = [];
133
134
    /**
135
     * The template prefix
136
     * @var string
137
     */
138
    protected string $templatePrefix = '';
139
140
    /**
141
     * The route name prefix
142
     * @var string
143
     */
144
    protected string $routePrefix = '';
145
146
    /**
147
     * The entity context name
148
     * @var string
149
     */
150
    protected string $entityContextName = 'entity';
151
152
    /**
153
     * The entity record not found error message
154
     * @var string
155
     */
156
    protected string $messageNotFound = 'This record doesn\'t exist';
157
158
    /**
159
     * The entity duplicate record error message
160
     * @var string
161
     */
162
    protected string $messageDuplicate = 'This record already exist';
163
164
    /**
165
     * The entity record process error message
166
     * @var string
167
     */
168
    protected string $messageProcessError = 'Data processing error';
169
170
    /**
171
     * The entity record create message
172
     * @var string
173
     */
174
    protected string $messageCreate = 'Data successfully created';
175
176
    /**
177
     * The entity record update message
178
     * @var string
179
     */
180
    protected string $messageUpdate = 'Data successfully updated';
181
182
    /**
183
     * The entity record delete message
184
     * @var string
185
     */
186
    protected string $messageDelete = 'Data successfully deleted';
187
188
    /**
189
     * The form parameter class
190
     * @var class-string<\Platine\Framework\Form\Param\BaseParam<TEntity>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<\Platine\Fr...ram\BaseParam<TEntity>> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<\Platine\Framework\Form\Param\BaseParam<TEntity>>.
Loading history...
191
     */
192
    protected string $paramClass;
193
194
    /**
195
     * The form validator class
196
     * @var class-string<\Platine\Framework\Form\Validator\AbstractValidator>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<\Platine\Fr...ator\AbstractValidator> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<\Platine\Framework\Form\Validator\AbstractValidator>.
Loading history...
197
     */
198
    protected string $validatorClass;
199
200
    /**
201
    * Create new instance
202
    * @param Lang $lang
203
    * @param Pagination $pagination
204
    * @param Template $template
205
    * @param Flash $flash
206
    * @param RouteHelper $routeHelper
207
    * @param LoggerInterface $logger
208
    */
209
    public function __construct(
210
        Lang $lang,
211
        Pagination $pagination,
212
        Template $template,
213
        Flash $flash,
214
        RouteHelper $routeHelper,
215
        LoggerInterface $logger
216
    ) {
217
        $this->lang = $lang;
218
        $this->pagination = $pagination;
219
        $this->template = $template;
220
        $this->flash = $flash;
221
        $this->routeHelper = $routeHelper;
222
        $this->logger = $logger;
223
    }
224
225
    /**
226
    * List all entities
227
    * @param ServerRequestInterface $request
228
    * @return ResponseInterface
229
    */
230
    public function index(ServerRequestInterface $request): ResponseInterface
231
    {
232
        $context = [];
233
        $templateName = sprintf('%s/list', $this->templatePrefix);
234
        $param = new RequestData($request);
235
        $totalItems = $this->repository->query()
236
                                        ->count('id');
237
238
        $currentPage = (int) $param->get('page', 1);
239
240
        $this->pagination->setTotalItems($totalItems)
241
                         ->setCurrentPage($currentPage);
242
243
        $limit = $this->pagination->getItemsPerPage();
244
        $offset = $this->pagination->getOffset();
245
246
        $query = $this->repository->query();
247
        $query->offset($offset)
248
               ->limit($limit);
249
250
        if (count($this->orderFields) > 0) {
251
            foreach ($this->orderFields as $field => $dir) {
252
                if (is_int($field)) {
253
                    $query->orderBy($dir);
254
                } else {
255
                    $query->orderBy($field, $dir);
256
                }
257
            }
258
        }
259
260
        $results = $query->all();
261
262
        $context['list'] = $results;
263
        $context['pagination'] = $this->pagination->render();
264
265
266
        return new TemplateResponse(
267
            $this->template,
268
            $templateName,
269
            $context
270
        );
271
    }
272
273
    /**
274
    * List entity detail
275
    * @param ServerRequestInterface $request
276
    * @return ResponseInterface
277
    */
278
    public function detail(ServerRequestInterface $request): ResponseInterface
279
    {
280
        $routeListName = sprintf('%s_list', $this->routePrefix);
281
        $templateName = sprintf('%s/detail', $this->templatePrefix);
282
        $context = [];
283
        $id = (int) $request->getAttribute('id');
284
285
        /** @var TEntity|null $entity */
286
        $entity = $this->repository->find($id);
287
288
        if ($entity === null) {
289
            $this->flash->setError($this->lang->tr($this->messageNotFound));
290
291
            return new RedirectResponse(
292
                $this->routeHelper->generateUrl($routeListName)
293
            );
294
        }
295
        $context[$this->entityContextName] = $entity;
296
297
        return new TemplateResponse(
298
            $this->template,
299
            $templateName,
300
            $context
301
        );
302
    }
303
304
    /**
305
    * Create new entity
306
    * @param ServerRequestInterface $request
307
    * @return ResponseInterface
308
    */
309
    public function create(ServerRequestInterface $request): ResponseInterface
310
    {
311
        $routeListName = sprintf('%s_list', $this->routePrefix);
312
        $templateName = sprintf('%s/create', $this->templatePrefix);
313
        $context = [];
314
        $param = new RequestData($request);
315
316
        $formParam = new $this->paramClass($param->posts());
317
        $context['param'] = $formParam;
318
319
        if ($request->getMethod() === 'GET') {
320
            return new TemplateResponse(
321
                $this->template,
322
                $templateName,
323
                $context
324
            );
325
        }
326
327
        $validator = new $this->validatorClass($formParam, $this->lang);
328
        if ($validator->validate() === false) {
329
            $context['errors'] = $validator->getErrors();
330
331
            return new TemplateResponse(
332
                $this->template,
333
                $templateName,
334
                $context
335
            );
336
        }
337
338
        if (count($this->uniqueFields) > 0) {
339
            $entityExist = $this->repository->findBy($this->getEntityFields(
340
                $this->uniqueFields,
341
                $formParam
342
            ));
343
344
            if ($entityExist !== null) {
345
                $this->flash->setError($this->lang->tr($this->messageDuplicate));
346
347
                return new TemplateResponse(
348
                    $this->template,
349
                    $templateName,
350
                    $context
351
                );
352
            }
353
        }
354
355
        /** @var TEntity $entity */
356
        $entity = $this->repository->create($this->getEntityFields(
357
            $this->fields,
358
            $formParam
359
        ));
360
        try {
361
            $this->repository->save($entity);
362
363
            $this->flash->setSuccess($this->lang->tr($this->messageCreate));
364
365
            return new RedirectResponse(
366
                $this->routeHelper->generateUrl($routeListName)
367
            );
368
        } catch (Exception $ex) {
369
            $this->logger->error('Error when saved the data {error}', ['error' => $ex->getMessage()]);
370
371
            $this->flash->setError($this->lang->tr($this->messageProcessError));
372
373
            return new TemplateResponse(
374
                $this->template,
375
                $templateName,
376
                $context
377
            );
378
        }
379
    }
380
381
    /**
382
    * Update existing entity
383
    * @param ServerRequestInterface $request
384
    * @return ResponseInterface
385
    */
386
    public function update(ServerRequestInterface $request): ResponseInterface
387
    {
388
        $routeListName = sprintf('%s_list', $this->routePrefix);
389
        $templateName = sprintf('%s/update', $this->templatePrefix);
390
        $context = [];
391
        $param = new RequestData($request);
392
393
        $id = (int) $request->getAttribute('id');
394
395
        /** @var TEntity|null $entity */
396
        $entity = $this->repository->find($id);
397
398
        if ($entity === null) {
399
            $this->flash->setError($this->lang->tr($this->messageNotFound));
400
401
            return new RedirectResponse(
402
                $this->routeHelper->generateUrl($routeListName)
403
            );
404
        }
405
        $context[$this->entityContextName] = $entity;
406
        $context['param'] = (new $this->paramClass())->fromEntity($entity);
407
        if ($request->getMethod() === 'GET') {
408
            return new TemplateResponse(
409
                $this->template,
410
                $templateName,
411
                $context
412
            );
413
        }
414
        $formParam = new $this->paramClass($param->posts());
415
        $context['param'] = $formParam;
416
417
        $validator = new $this->validatorClass($formParam, $this->lang);
418
        if ($validator->validate() === false) {
419
            $context['errors'] = $validator->getErrors();
420
421
            return new TemplateResponse(
422
                $this->template,
423
                $templateName,
424
                $context
425
            );
426
        }
427
428
        if (count($this->uniqueFields) > 0) {
429
            $entityExist = $this->repository->findBy($this->getEntityFields(
430
                $this->uniqueFields,
431
                $formParam
432
            ));
433
434
            if ($entityExist !== null && $entityExist->id !== $id) {
435
                $this->flash->setError($this->lang->tr($this->messageDuplicate));
436
437
                return new TemplateResponse(
438
                    $this->template,
439
                    $templateName,
440
                    $context
441
                );
442
            }
443
        }
444
445
        $fields = $this->getEntityFields($this->fields, $formParam);
446
        foreach ($fields as $field => $value) {
447
            $entity->{$field} = $value;
448
        }
449
        try {
450
            $this->repository->save($entity);
451
452
            $this->flash->setSuccess($this->lang->tr($this->messageUpdate));
453
454
            return new RedirectResponse(
455
                $this->routeHelper->generateUrl($routeListName)
456
            );
457
        } catch (Exception $ex) {
458
            $this->logger->error('Error when saved the data {error}', ['error' => $ex->getMessage()]);
459
460
            $this->flash->setError($this->lang->tr($this->messageProcessError));
461
462
            return new TemplateResponse(
463
                $this->template,
464
                $templateName,
465
                $context
466
            );
467
        }
468
    }
469
470
    /**
471
    * Delete the entity
472
    * @param ServerRequestInterface $request
473
    * @return ResponseInterface
474
    */
475
    public function delete(ServerRequestInterface $request): ResponseInterface
476
    {
477
        $routeListName = sprintf('%s_list', $this->routePrefix);
478
        $id = (int) $request->getAttribute('id');
479
480
        /** @var TEntity|null $entity */
481
        $entity = $this->repository->find($id);
482
483
        if ($entity === null) {
484
            $this->flash->setError($this->lang->tr($this->messageNotFound));
485
486
            return new RedirectResponse(
487
                $this->routeHelper->generateUrl($routeListName)
488
            );
489
        }
490
491
        try {
492
            $this->repository->delete($entity);
493
494
            $this->flash->setSuccess($this->lang->tr($this->messageDelete));
495
496
            return new RedirectResponse(
497
                $this->routeHelper->generateUrl($routeListName)
498
            );
499
        } catch (Exception $ex) {
500
            $this->logger->error('Error when delete the data {error}', ['error' => $ex->getMessage()]);
501
502
            $this->flash->setError($this->lang->tr($this->messageProcessError));
503
504
            return new RedirectResponse(
505
                $this->routeHelper->generateUrl($routeListName)
506
            );
507
        }
508
    }
509
510
    /**
511
     * Return the entity fields
512
     * @param array<int|string, string> $fields
513
     * @param BaseParam<TEntity> $param
514
     * @return array<string, mixed>
515
     */
516
    protected function getEntityFields(array $fields, BaseParam $param): array
517
    {
518
        $results = [];
519
520
        foreach ($fields as $field => $paramName) {
521
            if (is_int($field)) {
522
                $field = $paramName;
523
            }
524
525
            $paramMethodName = sprintf('get%s', Str::camel($paramName, false));
526
            $results[$field] = $param->{$paramMethodName}();
527
        }
528
529
        return $results;
530
    }
531
}
532