CrudAction   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 420
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 175
c 3
b 0
f 0
dl 0
loc 420
rs 10
wmc 29

8 Methods

Rating   Name   Duplication   Size   Complexity  
B create() 0 67 6
A index() 0 39 4
A detail() 0 23 2
A getTemplateData() 0 3 1
A __construct() 0 8 1
A delete() 0 30 3
B update() 0 79 9
A getEntityFields() 0 13 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\Repository;
62
use Platine\Pagination\Pagination;
63
use Platine\Stdlib\Helper\Str;
64
use Platine\Template\Template;
65
66
/**
67
* @class CrudAction
68
* @package Platine\Framework\Http\Action
69
* @template TEntity as \Platine\Orm\Entity
70
*/
71
class CrudAction
72
{
73
    /**
74
    * The Repository instance
75
    * @var Repository<TEntity>
76
    */
77
    protected Repository $repository;
78
79
    /**
80
     * The entity fields
81
     * @var array<int|string, string>
82
     */
83
    protected array $fields = [];
84
85
    /**
86
     * The order fields
87
     * @var array<int|string, string>
88
     */
89
    protected array $orderFields = [];
90
91
    /**
92
     * The unique fields
93
     * @var array<int|string, string>
94
     */
95
    protected array $uniqueFields = [];
96
97
    /**
98
     * The template prefix
99
     * @var string
100
     */
101
    protected string $templatePrefix = '';
102
103
    /**
104
     * The route name prefix
105
     * @var string
106
     */
107
    protected string $routePrefix = '';
108
109
    /**
110
     * The entity context name
111
     * @var string
112
     */
113
    protected string $entityContextName = 'entity';
114
115
    /**
116
     * The entity record not found error message
117
     * @var string
118
     */
119
    protected string $messageNotFound = 'This record doesn\'t exist';
120
121
    /**
122
     * The entity duplicate record error message
123
     * @var string
124
     */
125
    protected string $messageDuplicate = 'This record already exist';
126
127
    /**
128
     * The entity record process error message
129
     * @var string
130
     */
131
    protected string $messageProcessError = 'Data processing error';
132
133
    /**
134
     * The entity record create message
135
     * @var string
136
     */
137
    protected string $messageCreate = 'Data successfully created';
138
139
    /**
140
     * The entity record update message
141
     * @var string
142
     */
143
    protected string $messageUpdate = 'Data successfully updated';
144
145
    /**
146
     * The entity record delete message
147
     * @var string
148
     */
149
    protected string $messageDelete = 'Data successfully deleted';
150
151
    /**
152
     * The form parameter class
153
     * @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...
154
     */
155
    protected string $paramClass;
156
157
    /**
158
     * The form validator class
159
     * @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...
160
     */
161
    protected string $validatorClass;
162
163
    /**
164
    * Create new instance
165
    * @param Lang $lang
166
    * @param Pagination $pagination
167
    * @param Template $template
168
    * @param Flash $flash
169
    * @param RouteHelper $routeHelper
170
    * @param LoggerInterface $logger
171
    */
172
    public function __construct(
173
        protected Lang $lang,
174
        protected Pagination $pagination,
175
        protected Template $template,
176
        protected Flash $flash,
177
        protected RouteHelper $routeHelper,
178
        protected LoggerInterface $logger
179
    ) {
180
    }
181
182
    /**
183
    * List all entities
184
    * @param ServerRequestInterface $request
185
    * @return ResponseInterface
186
    */
187
    public function index(ServerRequestInterface $request): ResponseInterface
188
    {
189
        $context = $this->getTemplateData();
190
        $templateName = sprintf('%s/list', $this->templatePrefix);
191
        $param = new RequestData($request);
192
        $totalItems = $this->repository->query()
193
                                        ->count('id');
194
195
        $currentPage = (int) $param->get('page', 1);
196
197
        $this->pagination->setTotalItems($totalItems)
198
                         ->setCurrentPage($currentPage);
199
200
        $limit = $this->pagination->getItemsPerPage();
201
        $offset = $this->pagination->getOffset();
202
203
        $query = $this->repository->query();
204
        $query->offset($offset)
205
               ->limit($limit);
206
207
        if (count($this->orderFields) > 0) {
208
            foreach ($this->orderFields as $field => $dir) {
209
                if (is_int($field)) {
210
                    $query->orderBy($dir);
211
                } else {
212
                    $query->orderBy($field, $dir);
213
                }
214
            }
215
        }
216
217
        $results = $query->all();
218
219
        $context['list'] = $results;
220
        $context['pagination'] = $this->pagination->render();
221
222
        return new TemplateResponse(
223
            $this->template,
224
            $templateName,
225
            $context
226
        );
227
    }
228
229
    /**
230
    * List entity detail
231
    * @param ServerRequestInterface $request
232
    * @return ResponseInterface
233
    */
234
    public function detail(ServerRequestInterface $request): ResponseInterface
235
    {
236
        $routeListName = sprintf('%s_list', $this->routePrefix);
237
        $templateName = sprintf('%s/detail', $this->templatePrefix);
238
        $context = $this->getTemplateData();
239
        $id = (int) $request->getAttribute('id');
240
241
        /** @var TEntity|null $entity */
242
        $entity = $this->repository->find($id);
243
244
        if ($entity === null) {
245
            $this->flash->setError($this->lang->tr($this->messageNotFound));
246
247
            return new RedirectResponse(
248
                $this->routeHelper->generateUrl($routeListName)
249
            );
250
        }
251
        $context[$this->entityContextName] = $entity;
252
253
        return new TemplateResponse(
254
            $this->template,
255
            $templateName,
256
            $context
257
        );
258
    }
259
260
    /**
261
    * Create new entity
262
    * @param ServerRequestInterface $request
263
    * @return ResponseInterface
264
    */
265
    public function create(ServerRequestInterface $request): ResponseInterface
266
    {
267
        $routeListName = sprintf('%s_list', $this->routePrefix);
268
        $templateName = sprintf('%s/create', $this->templatePrefix);
269
        $context = $this->getTemplateData();
270
        $param = new RequestData($request);
271
272
        $formParam = new $this->paramClass($param->posts());
273
        $context['param'] = $formParam;
274
275
        if ($request->getMethod() === 'GET') {
276
            return new TemplateResponse(
277
                $this->template,
278
                $templateName,
279
                $context
280
            );
281
        }
282
283
        $validator = new $this->validatorClass($formParam, $this->lang);
284
        if ($validator->validate() === false) {
285
            $context['errors'] = $validator->getErrors();
286
287
            return new TemplateResponse(
288
                $this->template,
289
                $templateName,
290
                $context
291
            );
292
        }
293
294
        if (count($this->uniqueFields) > 0) {
295
            $entityExist = $this->repository->findBy($this->getEntityFields(
296
                $this->uniqueFields,
297
                $formParam
298
            ));
299
300
            if ($entityExist !== null) {
301
                $this->flash->setError($this->lang->tr($this->messageDuplicate));
302
303
                return new TemplateResponse(
304
                    $this->template,
305
                    $templateName,
306
                    $context
307
                );
308
            }
309
        }
310
311
        /** @var TEntity $entity */
312
        $entity = $this->repository->create($this->getEntityFields(
313
            $this->fields,
314
            $formParam
315
        ));
316
        try {
317
            $this->repository->save($entity);
318
319
            $this->flash->setSuccess($this->lang->tr($this->messageCreate));
320
321
            return new RedirectResponse(
322
                $this->routeHelper->generateUrl($routeListName)
323
            );
324
        } catch (Exception $ex) {
325
            $this->logger->error('Error when saved the data {error}', ['error' => $ex->getMessage()]);
326
            $this->flash->setError($this->lang->tr($this->messageProcessError));
327
328
            return new TemplateResponse(
329
                $this->template,
330
                $templateName,
331
                $context
332
            );
333
        }
334
    }
335
336
    /**
337
    * Update existing entity
338
    * @param ServerRequestInterface $request
339
    * @return ResponseInterface
340
    */
341
    public function update(ServerRequestInterface $request): ResponseInterface
342
    {
343
        $routeListName = sprintf('%s_list', $this->routePrefix);
344
        $templateName = sprintf('%s/update', $this->templatePrefix);
345
        $context = $this->getTemplateData();
346
        $param = new RequestData($request);
347
348
        $id = (int) $request->getAttribute('id');
349
350
        /** @var TEntity|null $entity */
351
        $entity = $this->repository->find($id);
352
353
        if ($entity === null) {
354
            $this->flash->setError($this->lang->tr($this->messageNotFound));
355
356
            return new RedirectResponse(
357
                $this->routeHelper->generateUrl($routeListName)
358
            );
359
        }
360
        $context[$this->entityContextName] = $entity;
361
        $context['param'] = (new $this->paramClass())->fromEntity($entity);
362
        if ($request->getMethod() === 'GET') {
363
            return new TemplateResponse(
364
                $this->template,
365
                $templateName,
366
                $context
367
            );
368
        }
369
        $formParam = new $this->paramClass($param->posts());
370
        $context['param'] = $formParam;
371
372
        $validator = new $this->validatorClass($formParam, $this->lang);
373
        if ($validator->validate() === false) {
374
            $context['errors'] = $validator->getErrors();
375
376
            return new TemplateResponse(
377
                $this->template,
378
                $templateName,
379
                $context
380
            );
381
        }
382
383
        if (count($this->uniqueFields) > 0) {
384
            $entityExist = $this->repository->findBy($this->getEntityFields(
385
                $this->uniqueFields,
386
                $formParam
387
            ));
388
389
            if ($entityExist !== null && $entityExist->id !== $id) {
390
                $this->flash->setError($this->lang->tr($this->messageDuplicate));
391
392
                return new TemplateResponse(
393
                    $this->template,
394
                    $templateName,
395
                    $context
396
                );
397
            }
398
        }
399
400
        $fields = $this->getEntityFields($this->fields, $formParam);
401
        foreach ($fields as $field => $value) {
402
            $entity->{$field} = $value;
403
        }
404
        try {
405
            $this->repository->save($entity);
406
407
            $this->flash->setSuccess($this->lang->tr($this->messageUpdate));
408
409
            return new RedirectResponse(
410
                $this->routeHelper->generateUrl($routeListName)
411
            );
412
        } catch (Exception $ex) {
413
            $this->logger->error('Error when saved the data {error}', ['error' => $ex->getMessage()]);
414
            $this->flash->setError($this->lang->tr($this->messageProcessError));
415
416
            return new TemplateResponse(
417
                $this->template,
418
                $templateName,
419
                $context
420
            );
421
        }
422
    }
423
424
    /**
425
    * Delete the entity
426
    * @param ServerRequestInterface $request
427
    * @return ResponseInterface
428
    */
429
    public function delete(ServerRequestInterface $request): ResponseInterface
430
    {
431
        $routeListName = sprintf('%s_list', $this->routePrefix);
432
        $id = (int) $request->getAttribute('id');
433
434
        /** @var TEntity|null $entity */
435
        $entity = $this->repository->find($id);
436
437
        if ($entity === null) {
438
            $this->flash->setError($this->lang->tr($this->messageNotFound));
439
440
            return new RedirectResponse(
441
                $this->routeHelper->generateUrl($routeListName)
442
            );
443
        }
444
445
        try {
446
            $this->repository->delete($entity);
447
448
            $this->flash->setSuccess($this->lang->tr($this->messageDelete));
449
450
            return new RedirectResponse(
451
                $this->routeHelper->generateUrl($routeListName)
452
            );
453
        } catch (Exception $ex) {
454
            $this->logger->error('Error when delete the data {error}', ['error' => $ex->getMessage()]);
455
            $this->flash->setError($this->lang->tr($this->messageProcessError));
456
457
            return new RedirectResponse(
458
                $this->routeHelper->generateUrl($routeListName)
459
            );
460
        }
461
    }
462
463
    /**
464
     * Return the entity fields
465
     * @param array<int|string, string> $fields
466
     * @param BaseParam<TEntity> $param
467
     * @return array<string, mixed>
468
     */
469
    protected function getEntityFields(array $fields, BaseParam $param): array
470
    {
471
        $results = [];
472
        foreach ($fields as $field => $paramName) {
473
            if (is_int($field)) {
474
                $field = $paramName;
475
            }
476
477
            $paramMethodName = sprintf('get%s', Str::camel($paramName, false));
478
            $results[$field] = $param->{$paramMethodName}();
479
        }
480
481
        return $results;
482
    }
483
484
    /**
485
     * Return the additional template data
486
     * @return array<string, mixed>
487
     */
488
    protected function getTemplateData(): array
489
    {
490
        return [];
491
    }
492
}
493