BaseMakeActionCommand::formatFields()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 3
b 0
f 0
nc 3
nop 1
dl 0
loc 17
rs 9.9332
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 BaseMakeActionCommand.php
34
 *
35
 *  The action base make command class
36
 *
37
 *  @package    Platine\Framework\Console
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;
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\Stdlib\Helper\Json;
56
use Platine\Stdlib\Helper\Str;
57
58
/**
59
 * @class BaseMakeActionCommand
60
 * @package Platine\Framework\Console
61
 */
62
abstract class BaseMakeActionCommand extends MakeCommand
63
{
64
    /**
65
     * The form parameter class name
66
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
67
     */
68
    protected string $paramClass;
69
70
    /**
71
     * The form validation class name
72
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
73
     */
74
    protected string $validatorClass;
75
76
    /**
77
     * The entity class name
78
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
79
     */
80
    protected string $entityClass;
81
82
    /**
83
     * The repository class name
84
     * @var class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
85
     */
86
    protected string $repositoryClass;
87
88
    /**
89
     * Create new instance
90
     * @param Application $application
91
     * @param Filesystem $filesystem
92
     */
93
    public function __construct(
94
        Application $application,
95
        Filesystem $filesystem
96
    ) {
97
        parent::__construct($application, $filesystem);
98
99
        $this->addOption(
100
            '-c|--fields',
101
            'The entity fields. Example field1:param1,field2:param2,field3',
102
            null,
103
            false
104
        );
105
106
        $this->addOption(
107
            '-i|--fields-unique',
108
            'The entity unique fields. Example field1:param1,field2:param2,field3',
109
            null,
110
            false
111
        );
112
113
        $this->addOption(
114
            '-o|--fields-order',
115
            'The entity orders fields. Example field1:ASC,field2:DESC,field3',
116
            null,
117
            false
118
        );
119
120
        $this->addOption(
121
            '-t|--template-prefix',
122
            'The template prefix',
123
            null,
124
            false
125
        );
126
127
        $this->addOption(
128
            '-r|--route-prefix',
129
            'The route name prefix',
130
            null,
131
            false
132
        );
133
134
        $this->addOption(
135
            '-e|--message-not-found',
136
            'The entity not found error message',
137
            'This record doesn\'t exist',
138
            false
139
        );
140
141
        $this->addOption(
142
            '-l|--message-duplicate',
143
            'The entity duplicate error message',
144
            'This record already exist',
145
            false
146
        );
147
148
        $this->addOption(
149
            '-a|--message-create',
150
            'The entity successfully create message',
151
            'Data successfully created',
152
            false
153
        );
154
155
        $this->addOption(
156
            '-u|--message-update',
157
            'The entity successfully update message',
158
            'Data successfully updated',
159
            false
160
        );
161
162
        $this->addOption(
163
            '-d|--message-delete',
164
            'The entity successfully delete message',
165
            'Data successfully deleted',
166
            false
167
        );
168
169
        $this->addOption(
170
            '-p|--message-process-error',
171
            'The entity processing error message',
172
            'Data processing error',
173
            false
174
        );
175
176
        $this->addOption(
177
            '-j|--config',
178
            'Use JSON config file for options',
179
            null,
180
            false
181
        );
182
183
        $this->addOption(
184
            '-b|--entity-context-key',
185
            'The entity context key name',
186
            'entity',
187
            false
188
        );
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function interact(Reader $reader, Writer $writer): void
195
    {
196
        parent::interact($reader, $writer);
197
198
        // Load configuration file if exist
199
        $this->loadConfig();
200
201
        $this->recordResourceClasses();
202
    }
203
204
    /**
205
     * Record class properties
206
     * @return void
207
     */
208
    protected function recordProperties(): void
209
    {
210
        $io = $this->io();
211
212
        $writer = $io->writer();
213
214
        $writer->boldYellow('Enter the properties list (empty value to finish):', true);
215
        $value = '';
216
        while ($value !== null) {
217
            $value = $io->prompt('Property full class name', null, null, false);
218
219
            if (!empty($value)) {
220
                $value = trim($value);
221
                if (!class_exists($value) && !interface_exists($value)) {
222
                    $writer->boldWhiteBgRed(sprintf('The class [%s] does not exists', $value), true);
223
                } else {
224
                    $shortClass = $this->getClassBaseName($value);
225
                    $name = Str::camel($shortClass, true);
226
                    //replace "interface", "abstract"
227
                    $nameClean = str_ireplace(['interface', 'abstract'], '', $name);
228
229
                    $this->properties[$value] = [
230
                        'name' => $nameClean,
231
                        'short' => $shortClass,
232
                    ];
233
                }
234
            }
235
        }
236
    }
237
238
    /**
239
     * Record the resource classes
240
     * @return void
241
     */
242
    protected function recordResourceClasses(): void
243
    {
244
        $io = $this->io();
245
246
        $paramClass = $io->prompt('Enter the form parameter full class name', null);
247
        while (class_exists($paramClass) === false) {
248
            $paramClass = $io->prompt('Class does not exists, please enter the form parameter full class name', null);
249
        }
250
        $this->paramClass = $paramClass;
251
252
        $validatorClass = $io->prompt('Enter the form validator full class name', null);
253
        while (class_exists($validatorClass) === false) {
254
            $validatorClass = $io->prompt(
255
                'Class does not exists, please enter the form validator full class name',
256
                null
257
            );
258
        }
259
        $this->validatorClass = $validatorClass;
260
261
        $entityClass = $io->prompt('Enter the entity full class name', null);
262
        while (class_exists($entityClass) === false) {
263
            $entityClass = $io->prompt('Class does not exists, please enter the entity full class name', null);
264
        }
265
        $this->entityClass = $entityClass;
266
267
        $repositoryClass = $io->prompt('Enter the repository full class name', null);
268
        while (class_exists($repositoryClass) === false) {
269
            $repositoryClass = $io->prompt('Class does not exists, please enter the repository full class name', null);
270
        }
271
        $this->repositoryClass = $repositoryClass;
272
    }
273
274
    /**
275
     * Add new property
276
     * @param class-string $value
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
277
     * @param string|null $name
278
     * @return $this
279
     */
280
    protected function addProperty(string $value, ?string $name = null): self
281
    {
282
        $shortClass = $this->getClassBaseName($value);
283
        if ($name === null) {
284
            $name = Str::camel($shortClass, true);
285
        }
286
287
        //replace "interface", "abstract"
288
        $nameClean = str_ireplace(['interface', 'abstract'], '', $name);
289
290
        $this->properties[$value] = [
291
            'name' => $nameClean,
292
            'short' => $shortClass,
293
        ];
294
295
        return $this;
296
    }
297
298
    /**
299
     * Return the property name
300
     * @param class-string $value
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
301
     * @return string
302
     */
303
    protected function getPropertyName(string $value): string
304
    {
305
        if (!isset($this->properties[$value])) {
306
            return '';
307
        }
308
309
        return $this->properties[$value]['name'];
310
    }
311
312
313
    /**
314
     * Return the route prefix
315
     * @return string
316
     */
317
    protected function getTemplatePrefix(): string
318
    {
319
        $templatePrefix = $this->getOptionValue('templatePrefix');
320
        if ($templatePrefix === null) {
321
            $actionName = $this->getShortClassName($this->className);
322
            $templatePrefix = Str::snake(str_ireplace('action', '', $actionName));
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('action', '', $actionName) can also be of type array; however, parameter $value of Platine\Stdlib\Helper\Str::snake() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

322
            $templatePrefix = Str::snake(/** @scrutinizer ignore-type */ str_ireplace('action', '', $actionName));
Loading history...
323
        }
324
325
        return $templatePrefix;
326
    }
327
328
    /**
329
     * Return the entity context key
330
     * @param bool $isKey
331
     * @return string
332
     */
333
    protected function getEntityContextKey(bool $isKey = true): string
334
    {
335
        $key = (string) $this->getOptionValue('entityContextKey');
336
        if (!empty($key)) {
337
            if ($isKey) {
338
                $key = Str::snake($key, '_');
339
            } else {
340
                $key = Str::camel($key, true);
341
            }
342
        }
343
344
        return $key;
345
    }
346
347
    /**
348
     * Return the route prefix
349
     * @return string
350
     */
351
    protected function getRoutePrefix(): string
352
    {
353
        $routePrefix = $this->getOptionValue('routePrefix');
354
        if ($routePrefix === null) {
355
            $actionName = $this->getShortClassName($this->className);
356
            $routePrefix = Str::snake(str_ireplace('action', '', $actionName));
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('action', '', $actionName) can also be of type array; however, parameter $value of Platine\Stdlib\Helper\Str::snake() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

356
            $routePrefix = Str::snake(/** @scrutinizer ignore-type */ str_ireplace('action', '', $actionName));
Loading history...
357
        }
358
359
        return $routePrefix;
360
    }
361
362
    /**
363
     * Return the route name
364
     * @param string $value
365
     * @return string
366
     */
367
    protected function getRouteName(string $value): string
368
    {
369
        $routePrefix = $this->getRoutePrefix();
370
        return sprintf('%s_%s', $routePrefix, $value);
371
    }
372
373
    /**
374
     * Return the form parameter method name of the given name
375
     * @param string $field
376
     * @return string
377
     */
378
    protected function getFormParamMethodName(string $field): string
379
    {
380
        return sprintf('get%s', Str::camel($field, false));
381
    }
382
383
    /**
384
     * Return the message
385
     * @param string $option
386
     * @return string|null
387
     */
388
    protected function getMessage(string $option): ?string
389
    {
390
        $message = (string) $this->getOptionValue($option);
391
        if (!empty($message)) {
392
            $message = addslashes($message);
393
        }
394
395
        return $message;
396
    }
397
398
    /**
399
     * Return the template for form parameter entity field
400
     * @param string $field
401
     * @param string $param
402
     * @param bool $isLast
403
     * @return string
404
     */
405
    protected function getFormParamEntityFieldTemplate(
406
        string $field,
407
        string $param,
408
        bool $isLast = false
409
    ): string {
410
        $fieldMethodName = $this->getFormParamMethodName($param);
411
        return sprintf(
412
            '\'%s\' => $formParam->%s(),',
413
            $field,
414
            $fieldMethodName
415
        ) . ($isLast ? PHP_EOL : '');
416
    }
417
418
    /**
419
     * Format option fields
420
     * @param string $values
421
     * @return array<string, string>
422
     */
423
    protected function formatFields(string $values): array
424
    {
425
        $result = [];
426
        $fields = (array) explode(',', $values);
427
        foreach ($fields as $field) {
428
            $param = $field;
429
            $value = explode(':', $field);
430
            $column = $value[0];
431
432
            if (isset($value[1])) {
433
                $param = $value[1];
434
            }
435
436
            $result[$column] = $param;
437
        }
438
439
        return $result;
440
    }
441
442
    /**
443
     * Format fields
444
     * @param array<string, string> $fields
445
     * @param bool $orderField
446
     * @return string
447
     */
448
    protected function formatFieldStr(array $fields, bool $orderField = false): string
449
    {
450
        $result = '';
451
        foreach ($fields as $field => $param) {
452
            if ($orderField) {
453
                $param = Str::upper($param);
454
                $order = 'ASC';
455
                if ($param === 'DESC') {
456
                    $order = 'DESC';
457
                }
458
459
                if ($order === 'ASC') {
460
                    $result .= sprintf('\'%s\', ', $field);
461
                } else {
462
                    $result .= sprintf('\'%s\' => \'DESC\', ', $field);
463
                }
464
            } else {
465
                if ($field === $param) {
466
                    $result .= sprintf('\'%s\', ', $field);
467
                } else {
468
                    $result .= sprintf('\'%s\' => \'%s\', ', $field, $param);
469
                }
470
            }
471
        }
472
473
        return rtrim($result, ', ');
474
    }
475
476
    /**
477
     * Load JSON configuration file if exist
478
     * @return void
479
     */
480
    protected function loadConfig(): void
481
    {
482
        $filename = $this->getOptionValue('config');
483
        if (!empty($filename)) {
484
            $file = $this->filesystem->file($filename);
485
            if ($file->exists() && $file->isReadable()) {
486
                $content = $file->read();
487
488
                /** @var array<string, string> $config */
489
                $config = Json::decode($content, true);
490
                foreach ($config as $option => $value) {
491
                    $optionKey = Str::camel($option, true);
492
                    $this->values[$optionKey] = $value;
493
                }
494
            }
495
        }
496
    }
497
}
498