Passed
Push — develop ( 9a8994...73f816 )
by nguereza
02:55
created

MakeResourceActionCommand::getEntityFieldsTemplate()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 17
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 27
rs 9.0777
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 MakeResourceActionCommand.php
34
 *
35
 *  The Command to generate new resource action class
36
 *
37
 *  @package    Platine\Framework\Console\Command
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\Console\Command;
49
50
use Platine\Console\Input\Reader;
51
use Platine\Console\Output\Writer;
52
use Platine\Filesystem\Filesystem;
53
use Platine\Framework\App\Application;
54
use Platine\Framework\Console\MakeCommand;
55
use Platine\Framework\Helper\Flash;
56
use Platine\Framework\Http\RouteHelper;
57
use Platine\Lang\Lang;
58
use Platine\Logger\LoggerInterface;
59
use Platine\Pagination\Pagination;
60
use Platine\Stdlib\Helper\Json;
61
use Platine\Stdlib\Helper\Str;
62
use Platine\Template\Template;
63
64
/**
65
 * @class MakeResourceActionCommand
66
 * @package Platine\Framework\Console\Command
67
 */
68
class MakeResourceActionCommand extends MakeCommand
69
{
70
    /**
71
     * {@inheritdoc}
72
     */
73
    protected string $type = 'resource';
74
75
    /**
76
     * The form parameter class name
77
     * @var class-string
78
     */
79
    protected string $paramClass;
80
81
    /**
82
     * The form validator class name
83
     * @var class-string
84
     */
85
    protected string $validatorClass;
86
87
    /**
88
     * The entity class name
89
     * @var string
90
     */
91
    protected string $entityClass;
92
93
    /**
94
     * The repository class name
95
     * @var string
96
     */
97
    protected string $repositoryClass;
98
99
    /**
100
     * Create new instance
101
     * @param Application $application
102
     * @param Filesystem $filesystem
103
     */
104
    public function __construct(
105
        Application $application,
106
        Filesystem $filesystem
107
    ) {
108
        parent::__construct($application, $filesystem);
109
        $this->setName('make:resource')
110
              ->setDescription('Command to generate platine resource action');
111
112
        $this->addOption(
113
            '-c|--fields',
114
            'The entity fields. Example field1:param1,field2:param2,field3',
115
            null,
116
            false
117
        );
118
119
        $this->addOption(
120
            '-i|--fields-unique',
121
            'The entity unique fields. Example field1:param1,field2:param2,field3',
122
            null,
123
            false
124
        );
125
126
        $this->addOption(
127
            '-o|--fields-order',
128
            'The entity orders fields. Example field1:ASC,field2:DESC,field3',
129
            null,
130
            false
131
        );
132
133
        $this->addOption(
134
            '-t|--template-prefix',
135
            'The template prefix',
136
            null,
137
            false
138
        );
139
140
        $this->addOption(
141
            '-r|--route-prefix',
142
            'The route name prefix',
143
            null,
144
            false
145
        );
146
147
        $this->addOption(
148
            '-e|--message-not-found',
149
            'The entity not found error message',
150
            'This record doesn\'t exist',
151
            false
152
        );
153
154
        $this->addOption(
155
            '-l|--message-duplicate',
156
            'The entity duplicate error message',
157
            'This record already exist',
158
            false
159
        );
160
161
        $this->addOption(
162
            '-a|--message-create',
163
            'The entity successfully create message',
164
            'Data successfully created',
165
            false
166
        );
167
168
        $this->addOption(
169
            '-u|--message-update',
170
            'The entity successfully update message',
171
            'Data successfully updated',
172
            false
173
        );
174
175
        $this->addOption(
176
            '-d|--message-delete',
177
            'The entity successfully delete message',
178
            'Data successfully deleted',
179
            false
180
        );
181
182
        $this->addOption(
183
            '-p|--message-process-error',
184
            'The entity processing error message',
185
            'Data processing error',
186
            false
187
        );
188
189
        $this->addOption(
190
            '-j|--config',
191
            'Use JSON config file for options',
192
            null,
193
            false
194
        );
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200
    public function interact(Reader $reader, Writer $writer): void
201
    {
202
        parent::interact($reader, $writer);
203
204
        // Load configuration file if exist
205
        $this->loadConfig();
206
207
        $baseClasses = $this->getBaseClasses();
208
209
        foreach ($baseClasses as $value) {
210
            $this->addProperty($value);
211
        }
212
213
        $this->recordResourceClasses();
214
215
        $this->recordProperties();
216
        
217
        $this->addProperty($this->repositoryClass);
218
    }
219
220
     /**
221
     * {@inheritdoc}
222
     */
223
    public function getClassTemplate(): string
224
    {
225
        return <<<EOF
226
        <?php
227
        
228
        declare(strict_types=1);
229
        
230
        namespace %namespace%;
231
        
232
        use Exception;
233
        use Platine\Http\ResponseInterface;
234
        use Platine\Http\ServerRequestInterface;
235
        use Platine\Framework\Http\RequestData;
236
        use Platine\Framework\Http\Response\TemplateResponse;
237
        use Platine\Framework\Http\Response\RedirectResponse;
238
        %uses%
239
240
        /**
241
        * @class %classname%
242
        * @package %namespace%
243
        */
244
        class %classname%
245
        {
246
            
247
            %properties%
248
        
249
            %constructor%
250
        
251
            /**
252
            * List all entities
253
            * @param ServerRequestInterface \$request
254
            * @return ResponseInterface
255
            */
256
            public function index(ServerRequestInterface \$request): ResponseInterface
257
            {
258
                %method_body_index%
259
            }
260
        
261
            /**
262
            * List entity detail
263
            * @param ServerRequestInterface \$request
264
            * @return ResponseInterface
265
            */
266
            public function detail(ServerRequestInterface \$request): ResponseInterface
267
            {
268
                %method_body_detail%
269
            }
270
        
271
            /**
272
            * Create new entity
273
            * @param ServerRequestInterface \$request
274
            * @return ResponseInterface
275
            */
276
            public function create(ServerRequestInterface \$request): ResponseInterface
277
            {
278
                %method_body_create%
279
            }
280
        
281
            /**
282
            * Update existing entity
283
            * @param ServerRequestInterface \$request
284
            * @return ResponseInterface
285
            */
286
            public function update(ServerRequestInterface \$request): ResponseInterface
287
            {
288
                %method_body_update%
289
            }
290
        
291
            /**
292
            * Delete the entity
293
            * @param ServerRequestInterface \$request
294
            * @return ResponseInterface
295
            */
296
            public function delete(ServerRequestInterface \$request): ResponseInterface
297
            {
298
                %method_body_delete%
299
            }
300
        }
301
        
302
        EOF;
303
    }
304
305
    /**
306
     * Record class properties
307
     * @return void
308
     */
309
    protected function recordProperties(): void
310
    {
311
        $io = $this->io();
312
313
        $writer = $io->writer();
314
315
        $writer->boldYellow('Enter the properties list (empty value to finish):', true);
316
        $value = '';
317
        while ($value !== null) {
318
            $value = $io->prompt('Property full class name', null, null, false);
319
320
            if (!empty($value)) {
321
                $value = trim($value);
322
                if (!class_exists($value) && !interface_exists($value)) {
323
                    $writer->boldWhiteBgRed(sprintf('The class [%s] does not exists', $value), true);
324
                } else {
325
                    $shortClass = $this->getClassBaseName($value);
326
                    $name = Str::camel($shortClass, true);
327
                    //replace"interface", "abstract"
328
                    $nameClean = str_ireplace(['interface', 'abstract'], '', $name);
329
330
                    $this->properties[$value] = [
331
                        'name' => $nameClean,
332
                        'short' => $shortClass,
333
                    ];
334
                }
335
            }
336
        }
337
    }
338
339
    /**
340
     * Record the resource classes
341
     * @return void
342
     */
343
    protected function recordResourceClasses(): void
344
    {
345
        $io = $this->io();
346
347
        $paramClass = $io->prompt('Enter the form parameter full class name', null);
348
        while (!class_exists($paramClass)) {
349
            $paramClass = $io->prompt('Class does not exists, please enter the form parameter full class name', null);
350
        }
351
352
        $this->paramClass = $paramClass;
353
354
        $validatorClass = $io->prompt('Enter the form validator full class name', null);
355
        while (!class_exists($validatorClass)) {
356
            $validatorClass = $io->prompt(
357
                'Class does not exists, please enter the form validator full class name',
358
                null
359
            );
360
        }
361
362
        $this->validatorClass = $validatorClass;
363
364
        $entityClass = $io->prompt('Enter the entity full class name', null);
365
        while (!class_exists($entityClass)) {
366
            $entityClass = $io->prompt('Class does not exists, please enter the entity full class name', null);
367
        }
368
369
        $this->entityClass = $entityClass;
370
371
        $repositoryClass = $io->prompt('Enter the repository full class name', null);
372
        while (!class_exists($repositoryClass)) {
373
            $repositoryClass = $io->prompt('Class does not exists, please enter the repository full class name', null);
374
        }
375
376
        $this->repositoryClass = $repositoryClass;
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    protected function createClass(): string
383
    {
384
        $content = parent::createClass();
385
386
387
        $contentIndex = $this->getIndexMethodBody($content);
388
        $contentDetail = $this->getDetailMethodBody($contentIndex);
389
        $contentCreate = $this->getCreateMethodBody($contentDetail);
390
        $contentUpdate = $this->getUpdateMethodBody($contentCreate);
391
        $contentDelete = $this->getDeleteMethodBody($contentUpdate);
392
393
        return $contentDelete;
394
    }
395
396
    /**
397
     * Return the index method body
398
     * @param string $content
399
     * @return string
400
     */
401
    protected function getIndexMethodBody(string $content): string
402
    {
403
        $repositoryName = $this->getPropertyName($this->repositoryClass);
404
        $templatePrefix = $this->getTemplatePrefix();
405
406
        $orderByTemplate = $this->getOrderByTemplate();
407
408
        $result = <<<EOF
409
        \$context = [];
410
                \$param = new RequestData(\$request);
411
                \$totalItems = \$this->{$repositoryName}->query()
412
                                                       ->count('id');
413
414
                \$currentPage = (int) \$param->get('page', 1);
415
416
                \$this->pagination->setTotalItems(\$totalItems)
417
                                ->setCurrentPage(\$currentPage);
418
419
                \$limit = \$this->pagination->getItemsPerPage();
420
                \$offset = \$this->pagination->getOffset();
421
422
                \$results = \$this->{$repositoryName}->query()
423
                                                    ->offset(\$offset)
424
                                                    ->limit(\$limit)
425
                                                    $orderByTemplate
426
                                                    ->all();
427
                
428
                \$context['list'] = \$results;
429
                \$context['pagination'] = \$this->pagination->render();
430
431
432
                return new TemplateResponse(
433
                    \$this->template,
434
                    '$templatePrefix/list',
435
                    \$context
436
                );
437
        EOF;
438
439
        return str_replace('%method_body_index%', $result, $content);
440
    }
441
442
    /**
443
     * Return the detail method body
444
     * @param string $content
445
     * @return string
446
     */
447
    protected function getDetailMethodBody(string $content): string
448
    {
449
        $repositoryName = $this->getPropertyName($this->repositoryClass);
450
        $entityBaseClass = $this->getClassBaseName($this->entityClass);
451
        $templatePrefix = $this->getTemplatePrefix();
452
        $notFoundMessage = $this->getMessage('messageNotFound');
453
454
        $listRoute = $this->getRouteName('list');
455
456
        $result = <<<EOF
457
        \$context = [];
458
                \$id = (int) \$request->getAttribute('id');
459
460
                /** @var $entityBaseClass|null \$entity */
461
                \$entity = \$this->{$repositoryName}->find(\$id);
462
463
                if (\$entity === null) {
464
                    \$this->flash->setError(\$this->lang->tr('$notFoundMessage'));
465
466
                    return new RedirectResponse(
467
                        \$this->routeHelper->generateUrl('$listRoute')
468
                    );
469
                }
470
                \$context['entity'] = \$entity;
471
                        
472
                return new TemplateResponse(
473
                    \$this->template,
474
                    '$templatePrefix/detail',
475
                    \$context
476
                );
477
        EOF;
478
479
480
        return str_replace('%method_body_detail%', $result, $content);
481
    }
482
483
    /**
484
     * Return the create method body
485
     * @param string $content
486
     * @return string
487
     */
488
    protected function getCreateMethodBody(string $content): string
489
    {
490
        $repositoryName = $this->getPropertyName($this->repositoryClass);
491
        $formParamBaseClass = $this->getClassBaseName($this->paramClass);
492
        $validatorBaseClass = $this->getClassBaseName($this->validatorClass);
493
        $entityBaseClass = $this->getClassBaseName($this->entityClass);
494
        $templatePrefix = $this->getTemplatePrefix();
495
        $listRoute = $this->getRouteName('list');
496
        $createMessage = $this->getMessage('messageCreate');
497
        $processErrorMessage = $this->getMessage('messageProcessError');
498
499
        $uniqueCheckStr = $this->getUniqueFieldCheckTemplate(true);
500
        $fieldTemplates = $this->getEntityFieldsTemplate(true);
501
502
        $result = <<<EOF
503
        \$context = [];
504
                \$param = new RequestData(\$request);
505
                
506
                \$formParam = new $formParamBaseClass(\$param->posts());
507
                \$context['param'] = \$formParam;
508
                
509
                if (\$request->getMethod() === 'GET') {
510
                    return new TemplateResponse(
511
                        \$this->template,
512
                        '$templatePrefix/create',
513
                        \$context
514
                    );
515
                }
516
                
517
                \$validator = new $validatorBaseClass(\$formParam, \$this->lang);
518
                if (\$validator->validate() === false) {
519
                    \$context['errors'] = \$validator->getErrors();
520
521
                    return new TemplateResponse(
522
                        \$this->template,
523
                        '$templatePrefix/create',
524
                        \$context
525
                    );
526
                }
527
                
528
                $uniqueCheckStr
529
530
                /** @var $entityBaseClass \$entity */
531
                \$entity = \$this->{$repositoryName}->create([
532
                   $fieldTemplates
533
                ]);
534
                
535
                try {
536
                    \$this->{$repositoryName}->save(\$entity);
537
538
                    \$this->flash->setSuccess(\$this->lang->tr('$createMessage'));
539
540
                    return new RedirectResponse(
541
                        \$this->routeHelper->generateUrl('$listRoute')
542
                    );
543
                } catch (Exception \$ex) {
544
                    \$this->logger->error('Error when saved the data {error}', ['error' => \$ex->getMessage()]);
545
546
                    \$this->flash->setError(\$this->lang->tr('$processErrorMessage'));
547
548
                    return new TemplateResponse(
549
                        \$this->template,
550
                        '$templatePrefix/create',
551
                        \$context
552
                    );
553
                }
554
        EOF;
555
556
557
        return str_replace('%method_body_create%', $result, $content);
558
    }
559
560
    /**
561
     * Return the update method body
562
     * @param string $content
563
     * @return string
564
     */
565
    protected function getUpdateMethodBody(string $content): string
566
    {
567
        $repositoryName = $this->getPropertyName($this->repositoryClass);
568
        $formParamBaseClass = $this->getClassBaseName($this->paramClass);
569
        $validatorBaseClass = $this->getClassBaseName($this->validatorClass);
570
        $entityBaseClass = $this->getClassBaseName($this->entityClass);
571
        $templatePrefix = $this->getTemplatePrefix();
572
        $listRoute = $this->getRouteName('list');
573
        $notFoundMessage = $this->getMessage('messageNotFound');
574
        $updateMessage = $this->getMessage('messageUpdate');
575
        $processErrorMessage = $this->getMessage('messageProcessError');
576
577
        $uniqueCheckStr = $this->getUniqueFieldCheckTemplate(false);
578
        $fieldTemplates = $this->getEntityFieldsTemplate(false);
579
580
        $result = <<<EOF
581
        \$context = [];
582
                \$param = new RequestData(\$request);
583
                
584
                \$id = (int) \$request->getAttribute('id');
585
586
                /** @var $entityBaseClass|null \$entity */
587
                \$entity = \$this->{$repositoryName}->find(\$id);
588
589
                if (\$entity === null) {
590
                    \$this->flash->setError(\$this->lang->tr('$notFoundMessage'));
591
592
                    return new RedirectResponse(
593
                        \$this->routeHelper->generateUrl('$listRoute')
594
                    );
595
                }
596
                \$context['entity'] = \$entity;
597
                \$context['param'] = (new $formParamBaseClass())->fromEntity(\$entity);
598
                if (\$request->getMethod() === 'GET') {
599
                    return new TemplateResponse(
600
                        \$this->template,
601
                        '$templatePrefix/update',
602
                        \$context
603
                    );
604
                }
605
                \$formParam = new $formParamBaseClass(\$param->posts());
606
                \$context['param'] = \$formParam;
607
                
608
                \$validator = new $validatorBaseClass(\$formParam, \$this->lang);
609
                if (\$validator->validate() === false) {
610
                    \$context['errors'] = \$validator->getErrors();
611
612
                    return new TemplateResponse(
613
                        \$this->template,
614
                        '$templatePrefix/update',
615
                        \$context
616
                    );
617
                }
618
                
619
                $uniqueCheckStr
620
621
                $fieldTemplates
622
                
623
                try {
624
                    \$this->{$repositoryName}->save(\$entity);
625
626
                    \$this->flash->setSuccess(\$this->lang->tr('$updateMessage'));
627
628
                    return new RedirectResponse(
629
                        \$this->routeHelper->generateUrl('$listRoute')
630
                    );
631
                } catch (Exception \$ex) {
632
                    \$this->logger->error('Error when saved the data {error}', ['error' => \$ex->getMessage()]);
633
634
                    \$this->flash->setError(\$this->lang->tr('$processErrorMessage'));
635
636
                    return new TemplateResponse(
637
                        \$this->template,
638
                        '$templatePrefix/update',
639
                        \$context
640
                    );
641
                }
642
        EOF;
643
644
645
        return str_replace('%method_body_update%', $result, $content);
646
    }
647
648
    /**
649
     * Return the delete method body
650
     * @param string $content
651
     * @return string
652
     */
653
    protected function getDeleteMethodBody(string $content): string
654
    {
655
        $repositoryName = $this->getPropertyName($this->repositoryClass);
656
        $entityBaseClass = $this->getClassBaseName($this->entityClass);
657
        $notFoundMessage = $this->getMessage('messageNotFound');
658
        $deleteMessage = $this->getMessage('messageDelete');
659
        $processErrorMessage = $this->getMessage('messageProcessError');
660
661
        $listRoute = $this->getRouteName('list');
662
663
        $result = <<<EOF
664
        \$id = (int) \$request->getAttribute('id');
665
666
                /** @var $entityBaseClass|null \$entity */
667
                \$entity = \$this->{$repositoryName}->find(\$id);
668
669
                if (\$entity === null) {
670
                    \$this->flash->setError(\$this->lang->tr('$notFoundMessage'));
671
672
                    return new RedirectResponse(
673
                        \$this->routeHelper->generateUrl('$listRoute')
674
                    );
675
                }
676
677
                try {
678
                    \$this->{$repositoryName}->delete(\$entity);
679
680
                    \$this->flash->setSuccess(\$this->lang->tr('$deleteMessage'));
681
682
                    return new RedirectResponse(
683
                        \$this->routeHelper->generateUrl('$listRoute')
684
                    );
685
                } catch (Exception \$ex) {
686
                    \$this->logger->error('Error when delete the data {error}', ['error' => \$ex->getMessage()]);
687
688
                    \$this->flash->setError(\$this->lang->tr('$processErrorMessage'));
689
690
                    return new RedirectResponse(
691
                        \$this->routeHelper->generateUrl('$listRoute')
692
                    );
693
                }
694
        EOF;
695
696
697
        return str_replace('%method_body_delete%', $result, $content);
698
    }
699
700
    /**
701
     * Return the template for unique field check
702
     * @param bool $create
703
     * @return string
704
     */
705
    protected function getUniqueFieldCheckTemplate(bool $create = true): string
706
    {
707
        $repositoryName = $this->getPropertyName($this->repositoryClass);
708
        $templatePrefix = $this->getTemplatePrefix();
709
        $uniqueFields = $this->getOptionValue('fieldsUnique');
710
        $uniqueCheckStr = '';
711
        if ($uniqueFields !== null) {
712
            $duplicateMessage = $this->getMessage('messageDuplicate');
713
714
            $fields = (array) explode(',', $uniqueFields);
715
            $i = 1;
716
            $result = '';
717
            foreach ($fields as $field) {
718
                $column = $field;
719
                $param = $field;
720
                $uniqueField = (array) explode(':', $field);
721
                if (isset($uniqueField[0])) {
722
                    $column = $uniqueField[0];
723
                }
724
725
                if (isset($uniqueField[1])) {
726
                    $param = $uniqueField[1];
727
                }
728
729
                $result .= ($i > 1 ? "\t\t\t\t\t       " : '') .
730
                        $this->getFormParamEntityFieldTemplate($column, $param, count($fields) > $i, $create);
731
                $i++;
732
            }
733
734
            $updateStr = $create ? '' : ' && $entityExist->id !== $id';
735
            $templateName = $create ? 'create' : 'update';
736
737
            $uniqueCheckStr = <<<EOF
738
            \$entityExist = \$this->{$repositoryName}->findBy([
739
                                                           $result
740
                                                       ]);
741
                    
742
                    if(\$entityExist !== null$updateStr){
743
                        \$this->flash->setError(\$this->lang->tr('$duplicateMessage'));
744
745
                        return new TemplateResponse(
746
                            \$this->template,
747
                            '$templatePrefix/$templateName',
748
                            \$context
749
                        );
750
                    }
751
            EOF;
752
        }
753
754
        return $uniqueCheckStr;
755
    }
756
757
    /**
758
     * Return the template for order by
759
     * @return string
760
     */
761
    protected function getOrderByTemplate(): string
762
    {
763
        $result = '';
764
        $orderFields = $this->getOptionValue('fieldsOrder');
765
766
        if ($orderFields !== null) {
767
            $fields = (array) explode(',', $orderFields);
768
            $i = 1;
769
            foreach ($fields as $field) {
770
                $column = $field;
771
                $dir = 'ASC';
772
                $orderField = (array) explode(':', $field);
773
                if (isset($orderField[0])) {
774
                    $column = $orderField[0];
775
                }
776
777
                if (isset($orderField[1]) && in_array(strtolower($orderField[1]), ['asc', 'desc'])) {
778
                    $dir = $orderField[1];
779
                }
780
781
                $result .= ($i > 1 ? "\t\t\t\t\t    " : '') .
782
                        sprintf('->orderBy(\'%s\', \'%s\')', $column, Str::upper($dir)) .
783
                        (count($fields) > $i ? PHP_EOL : '');
784
                $i++;
785
            }
786
        }
787
788
        return $result;
789
    }
790
791
    /**
792
     * Return the template for entity field for saving
793
     * @param bool $create
794
     * @return string
795
     */
796
    protected function getEntityFieldsTemplate(bool $create = true): string
797
    {
798
        $fields = $this->getOptionValue('fields');
799
        $result = '';
800
        if ($fields !== null) {
801
            $fields = (array) explode(',', $fields);
802
            $i = 1;
803
804
            foreach ($fields as $field) {
805
                $column = $field;
806
                $param = $field;
807
                $entityField = (array) explode(':', $field);
808
                if (isset($entityField[0])) {
809
                    $column = $entityField[0];
810
                }
811
812
                if (isset($entityField[1])) {
813
                    $param = $entityField[1];
814
                }
815
816
                $result .= ($i > 1 ? "\t   " : '') .
817
                        $this->getEntityRecordFieldTemplate($column, $param, count($fields) > $i, $create);
818
                $i++;
819
            }
820
        }
821
822
        return $result;
823
    }
824
825
    /**
826
     * {@inheritdoc}
827
     */
828
    protected function getUsesContent(): string
829
    {
830
        $uses = parent::getUsesContent();
831
832
        $uses .= $this->getUsesTemplate($this->entityClass);
833
        $uses .= $this->getUsesTemplate($this->paramClass);
834
        $uses .= $this->getUsesTemplate($this->validatorClass);
835
836
        return <<<EOF
837
        $uses
838
        EOF;
839
    }
840
841
    /**
842
     * Add new property
843
     * @param class-string $value
844
     * @return $this
845
     */
846
    protected function addProperty(string $value): self
847
    {
848
        $shortClass = $this->getClassBaseName($value);
849
        $name = Str::camel($shortClass, true);
850
        //replace"interface", "abstract"
851
        $nameClean = str_ireplace(['interface', 'abstract'], '', $name);
852
853
        $this->properties[$value] = [
854
            'name' => $nameClean,
855
            'short' => $shortClass,
856
        ];
857
858
        return $this;
859
    }
860
861
    /**
862
     * Return the property name
863
     * @param class-string $value
864
     * @return string
865
     */
866
    protected function getPropertyName(string $value): string
867
    {
868
        if (!isset($this->properties[$value])) {
869
            return '';
870
        }
871
872
        return $this->properties[$value]['name'];
873
    }
874
875
876
    /**
877
     * Return the route prefix
878
     * @return string
879
     */
880
    protected function getTemplatePrefix(): string
881
    {
882
        $templatePrefix = $this->getOptionValue('templatePrefix');
883
        if ($templatePrefix === null) {
884
            $actionName = $this->getShortClassName($this->className);
885
            $templatePrefix = Str::snake(str_ireplace('action', '', $actionName));
886
        }
887
888
        return $templatePrefix;
889
    }
890
891
    /**
892
     * Return the route prefix
893
     * @return string
894
     */
895
    protected function getRoutePrefix(): string
896
    {
897
        $routePrefix = $this->getOptionValue('routePrefix');
898
        if ($routePrefix === null) {
899
            $actionName = $this->getShortClassName($this->className);
900
            $routePrefix = Str::snake(str_ireplace('action', '', $actionName));
901
        }
902
903
        return $routePrefix;
904
    }
905
906
    /**
907
     * Return the route name
908
     * @param string $value
909
     * @return string
910
     */
911
    protected function getRouteName(string $value): string
912
    {
913
        $routePrefix = $this->getRoutePrefix();
914
        return sprintf('%s_%s', $routePrefix, $value);
915
    }
916
917
    /**
918
     * Return the form parameter method name of the given name
919
     * @param string $field
920
     * @return string
921
     */
922
    protected function getFormParamMethodName(string $field): string
923
    {
924
        return sprintf('get%s', Str::camel($field, false));
925
    }
926
927
    /**
928
     * Return the base classes
929
     * @return array<class-string>
930
     */
931
    protected function getBaseClasses(): array
932
    {
933
        return [
934
            Lang::class,
935
            Pagination::class,
936
            Template::class,
937
            Flash::class,
938
            RouteHelper::class,
939
            LoggerInterface::class,
940
        ];
941
    }
942
943
    /**
944
     * Return the message
945
     * @param string $option
946
     * @return string|null
947
     */
948
    protected function getMessage(string $option): ?string
949
    {
950
        $message = $this->getOptionValue($option);
951
        if (!empty($message)) {
952
            $message = addslashes($message);
953
        }
954
955
        return $message;
956
    }
957
958
    /**
959
     * Return the template for entity record fields
960
     * @param string $field
961
     * @param string $param
962
     * @param bool $isLast
963
     * @param bool $create
964
     * @return string
965
     */
966
    protected function getEntityRecordFieldTemplate(
967
        string $field,
968
        string $param,
969
        bool $isLast = false,
970
        $create = true
971
    ): string {
972
        $fieldMethodName = $this->getFormParamMethodName($param);
973
        if ($create) {
974
            return sprintf('\'%s\' => $formParam->%s(),', $field, $fieldMethodName) . ($isLast ? PHP_EOL : '');
975
        }
976
977
        return sprintf('$entity->%s = $formParam->%s();', $field, $fieldMethodName) . ($isLast ? PHP_EOL : '');
978
    }
979
980
    /**
981
     * Return the template for form parameter entity field
982
     * @param string $field
983
     * @param string $param
984
     * @param bool $isLast
985
     * @param bool $isArray
986
     * @return string
987
     */
988
    protected function getFormParamEntityFieldTemplate(
989
        string $field,
990
        string $param,
991
        bool $isLast = false
992
    ): string {
993
        $fieldMethodName = $this->getFormParamMethodName($param);
994
        return sprintf('\'%s\' => $formParam->%s(),', $field, $fieldMethodName) . ($isLast ? PHP_EOL : '');
995
    }
996
997
    /**
998
     * Load JSON configuration file if exist
999
     * @return void
1000
     */
1001
    protected function loadConfig(): void
1002
    {
1003
        $filename = $this->getOptionValue('config');
1004
        if (!empty($filename)) {
1005
            $file = $this->filesystem->file($filename);
1006
            if ($file->exists() && $file->isReadable()) {
1007
                $content = $file->read();
1008
                $config = Json::decode($content, true);
1009
                foreach ($config as $option => $value) {
1010
                    $optionKey = Str::camel($option, true);
1011
                    $this->values[$optionKey] = $value;
1012
                }
1013
            }
1014
        }
1015
    }
1016
}
1017