Passed
Push — develop ( c66d22...ba64a1 )
by nguereza
03:53
created

CrudAction::create()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 68
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 43
c 1
b 0
f 0
nc 11
nop 1
dl 0
loc 68
rs 8.6097

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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