Completed
Push — master ( 8f08c7...7bd638 )
by Mohamed
02:25
created

ResourceCommand::makeLangFileTo()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 1
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
1
<?php
2
3
namespace Microboard\Commands;
4
5
use Exception;
6
use Illuminate\Console\Command;
7
use Illuminate\Contracts\Filesystem\FileNotFoundException;
8
use Illuminate\Filesystem\Filesystem;
9
use Illuminate\Support\Str;
10
use Microboard\Factory;
11
use Microboard\Foundations\Traits\MicroboardPathResolver;
12
use Microboard\Models\Role;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputOption;
15
16
class ResourceCommand extends Command
17
{
18
    use MicroboardPathResolver;
19
20
    /**
21
     * The console command name.
22
     *
23
     * @var string
24
     */
25
    protected $name = 'microboard:resource';
26
27
    /**
28
     * The console command description.
29
     *
30
     * @var string
31
     */
32
    protected $description = 'Create an admin resource for given model';
33
34
    /**
35
     * @var Factory
36
     */
37
    protected $microboard;
38
39
    /**
40
     * @var Filesystem
41
     */
42
    protected $files;
43
44
    /**
45
     * ResourceCommand constructor.
46
     *
47
     * @param Factory $microboard
48
     * @param Filesystem $files
49
     */
50
    public function __construct(Factory $microboard, Filesystem $files)
51
    {
52
        parent::__construct();
53
54
        $this->microboard = $microboard;
55
        $this->files = $files;
56
    }
57
58
    /**
59
     * Execute the console command.
60
     *
61
     * @throws Exception
62
     */
63
    public function handle()
64
    {
65
        $namespace = $this->option('namespace') ? $this->option('namespace') . '\\' : null;
66
        $modelName = $this->argument('model');
67
        $model = $this->getNamespacedModel($modelName);
68
        $options = $this->getAvailableOptions();
69
        $abilities = $this->getAvailableAbilities();
70
71
        // Create permissions and make a policy
72
        if (in_array('policy', $options)) {
73
            $this->microboard->createPermissionsFor(
74
                Role::where('name', config('microboard.resources.default_role', 'admin'))->firstOrFail(),
75
                $modelName,
0 ignored issues
show
Bug introduced by
It seems like $modelName defined by $this->argument('model') on line 66 can also be of type array or null; however, Microboard\Factory::createPermissionsFor() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
76
                $abilities
77
            );
78
79
            // Make a policy
80
            $this->call('microboard:policy', [
81
                'name' => "{$modelName}Policy",
82
                '--model' => $model,
83
                '--abilities' => $this->option('abilities'),
84
                '--base_path' => $this->option('base_path'),
85
            ]);
86
        }
87
88
        // Make controller and datatable classes
89
        if (in_array('controller', $options)) {
90
            // Make a controller
91
            $this->call('microboard:controller', [
92
                'name' => "{$namespace}{$modelName}Controller",
93
                '--model' => $model,
94
                '--base_path' => $this->option('base_path'),
95
                '--namespaced' => true
96
            ]);
97
98
            // Make a datatable
99 View Code Duplication
            if (is_array($abilities) && in_array('viewAny', $abilities)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
                $this->call('microboard:datatable', [
101
                    'name' => $modelName,
102
                    '--model' => trim($model, '/\\'),
103
                    '--columns' => $this->getModelFillableColumns($model),
104
                    '--base_path' => $this->option('base_path')
105
                ]);
106
            }
107
108
            // Make a store form request
109 View Code Duplication
            if (is_array($abilities) && in_array('create', $abilities)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
110
                $this->call('microboard:request', [
111
                    'name' => "{$modelName}\\StoreFormRequest",
112
                    '--model' => $model,
113
                    '--columns' => $this->getModelFillableColumns($model),
114
                    '--base_path' => $this->option('base_path')
115
                ]);
116
            }
117
118
            // Make a update form request
119 View Code Duplication
            if (is_array($abilities) && in_array('update', $abilities)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
                $this->call('microboard:request', [
121
                    'name' => "{$modelName}\\UpdateFormRequest",
122
                    '--model' => $model,
123
                    '--columns' => $this->getModelFillableColumns($model),
124
                    '--base_path' => $this->option('base_path')
125
                ]);
126
            }
127
        }
128
129
        // Make views and lang
130
        if (in_array('views', $options)) {
131
            $this->makeFormBladeFile()
132
                ->makeTableBladeFile();
133
134
            foreach (config('microboard.localizations', []) as $lang) {
135
                if (!isset($lang['code'])) {
136
                    throw new \InvalidArgumentException('Lang file must contains "code" index!');
137
                }
138
139
                $this->makeLangFileTo($lang['code']);
140
            }
141
142
            $this->updateRoutesAndNavLinks();
143
        }
144
    }
145
146
    /**
147
     * @param $modelName
148
     * @return \Illuminate\Contracts\Foundation\Application|mixed
149
     */
150
    protected function getNamespacedModel($modelName)
151
    {
152
        $model = ($this->option('model-namespace') ? ('/' . rtrim($this->option('model-namespace'), '/\\') . '/') : '') . $modelName;
153
        $model = str_replace('/', '\\', $model);
154
        if (Str::startsWith($model, '\\')) {
155
            $namespacedModel = $model;
156
        } else {
157
            $namespacedModel = $this->laravel->getNamespace() . $model;
158
        }
159
160
        return $namespacedModel;
161
    }
162
163
    /**
164
     * @return array
165
     */
166
    protected function getAvailableOptions()
167
    {
168
        return $this->option('only') ?
169
            explode(',', $this->option('only')) :
170
            ['controller', 'policy', 'views'];
171
    }
172
173
    /**
174
     * @return array
175
     */
176
    protected function getAvailableAbilities()
177
    {
178
        $abilities = ['viewAny', 'view', 'create', 'update', 'delete'];
179
180
        if ($this->option('abilities')) {
181
            $abilities = array_map(function ($ability) {
182
                return trim($ability);
183
            }, explode(',', $this->option('abilities')));
184
        }
185
186
        return $abilities;
187
    }
188
189
    /**
190
     *
191
     *
192
     * @param string $model
193
     * @param bool $filter
194
     * @return array
195
     */
196
    protected function getModelFillableColumns(string $model, $filter = false)
197
    {
198
        $model = resolve($model);
199
200
        return collect($model->getFillable())
201
            ->filter(function ($column) use ($model, $filter) {
202
                return $filter || !in_array($column, $model->getHidden());
203
            })
204
            ->all();
205
    }
206
207
    /**
208
     * Create table.blade.php file
209
     *
210
     * @return $this
211
     * @throws FileNotFoundException
212
     */
213
    protected function makeTableBladeFile()
214
    {
215
        $this->makeBladeFile('table');
216
217
        return $this;
218
    }
219
220
    /**
221
     * Create a new blade file with given name.
222
     *
223
     * @param $file
224
     * @return $this
225
     * @throws FileNotFoundException
226
     */
227
    protected function makeBladeFile($file)
228
    {
229
        $name = Str::of($this->argument('model'))->slug()->plural();
0 ignored issues
show
Bug introduced by
It seems like $this->argument('model') targeting Illuminate\Console\Conce...ractsWithIO::argument() can also be of type array or null; however, Illuminate\Support\Str::of() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
230
        $stub = $this->resolveStubPath("/stubs/views/{$file}.stub");
231
        $path = $this->getViewsPath($file, $name);
232
233
        $this->makeDirectory($path);
234
235
        $this->files->put($path, $this->replaceVariablesForm($file, $stub, $name));
236
237
        return $this;
238
    }
239
240
    /**
241
     * @param $view
242
     * @param $name
243
     * @return string
244
     */
245
    protected function getViewsPath($view, $name): string
246
    {
247
        $path = Str::lower("{$this->option('namespace')}/{$name}/{$view}.blade.php");
248
249
        return $this->option('base_path') ?
250
            $this->option('base_path') . '/resources/views/' . $path :
251
            $this->laravel->resourcePath("views/{$path}");
252
    }
253
254
    /**
255
     * Build the directory for the class if necessary.
256
     *
257
     * @param string $path
258
     * @return string
259
     */
260
    protected function makeDirectory($path)
261
    {
262
        if (!$this->files->isDirectory(dirname($path))) {
263
            $this->files->makeDirectory(dirname($path), 0777, true, true);
264
        }
265
266
        return $path;
267
    }
268
269
    /**
270
     * Replace views variables.
271
     *
272
     * @param $file
273
     * @param string $stub
274
     * @param $name
275
     * @return string
276
     * @throws FileNotFoundException
277
     */
278
    protected function replaceVariablesForm($file, string $stub, $name)
279
    {
280
        $stub = $this->files->get($stub);
281
        $replaces = [];
282
283 View Code Duplication
        if ($file === 'form') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284
            $columns = $this->getModelFillableColumns(
285
                $this->getNamespacedModel($this->argument('model')),
286
                true
287
            );
288
            $html = '';
289
290
            foreach ($columns as $column) {
291
                $html .= str_replace([
292
                    '{{ column }}', '{{ model }}', '{{ variable }}'
293
                ], [
294
                    $column, $name, Str::singular($name)
295
                ], $this->files->get(
296
                    $this->resolveStubPath('/stubs/views/form-input.stub')
297
                ));
298
            }
299
300
            $replaces = [
301
                '{{ columns }}' => $html
302
            ];
303
        }
304
305 View Code Duplication
        if ($file === 'table') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
            $columns = $this->getModelFillableColumns(
307
                $this->getNamespacedModel($this->argument('model'))
308
            );
309
            $html = '';
310
311
            foreach ($columns as $column) {
312
                $html .= str_replace([
313
                    '{{ column }}', '{{ model }}', '{{ variable }}'
314
                ], [
315
                    $column, $name, Str::singular($name)
316
                ], $this->files->get(
317
                    $this->resolveStubPath('/stubs/views/table-column.stub')
318
                ));
319
            }
320
321
            $replaces = [
322
                '{{ columns }}' => $html
323
            ];
324
        }
325
326
        return str_replace(array_keys($replaces), array_values($replaces), $stub);
327
    }
328
329
    /**
330
     * Create form.blade.php
331
     *
332
     * @return $this
333
     * @throws FileNotFoundException
334
     */
335
    protected function makeFormBladeFile()
336
    {
337
        $this->makeBladeFile('form');
338
339
        return $this;
340
    }
341
342
    /**
343
     * It makes lang files to this model
344
     *
345
     * @param $code
346
     * @return $this
347
     * @throws FileNotFoundException
348
     */
349
    protected function makeLangFileTo($code)
350
    {
351
        $columns = '';
352
        $name = Str::of($this->argument('model'))->slug()->plural();
0 ignored issues
show
Bug introduced by
It seems like $this->argument('model') targeting Illuminate\Console\Conce...ractsWithIO::argument() can also be of type array or null; however, Illuminate\Support\Str::of() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
353
        $stub = $this->resolveStubPath("/stubs/lang/{$code}.stub");
354
        $modelName = $this->argument('model');
355
        $path = Str::lower("{$code}/{$name}.php");
356
        $path = $this->option('base_path') ?
357
            $this->option('base_path') . '/resources/lang/' . $path :
358
            $this->laravel->resourcePath("lang/{$path}");
359
360
        $this->makeDirectory($path);
361
362
        foreach ($this->getModelFillableColumns($this->getNamespacedModel($modelName), false) as $column) {
363
            $name = Str::title($column);
364
            $columns .= "\r\n\t\t'{$column}' => '{$name}',";
365
        }
366
367
        $this->files->put($path, str_replace([
368
            '{{ model }}', '{{ columns }}'
369
        ], [
370
            Str::title($name), $columns
0 ignored issues
show
Bug introduced by
It seems like $name defined by \Illuminate\Support\Str:...el'))->slug()->plural() on line 352 can also be of type object<Illuminate\Support\Stringable>; however, Illuminate\Support\Str::title() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
371
        ], $this->files->get($stub)));
372
373
        return $this;
374
    }
375
376
    /**
377
     * @throws FileNotFoundException
378
     */
379
    protected function updateRoutesAndNavLinks()
380
    {
381
        $navPath = ($this->option('base_path') ?
382
                $this->option('base_path') . '/resources/views/' :
383
                $this->laravel->resourcePath('views/'))
384
            . 'vendor/microboard/layouts/partials/navbar-links.blade.php';
385
        $routePath = ($this->option('base_path') ?
386
                $this->option('base_path') . '/routes/' :
387
                $this->laravel->basePath('routes/'))
388
            . 'microboard.php';
389
390
        $navLink = $this->files->get(
391
            $this->resolveStubPath('/stubs/views/nav-link.stub')
392
        );
393
394
        if (
395
            !$this->files->exists($navPath) ||
396
            !$this->files->exists($routePath)
397
        ) {
398
            throw new \InvalidArgumentException('You must do microboard:install first!');
399
        }
400
401
        $this->files->append(
402
            $navPath,
403
            str_replace([
404
                '{{ model }}', '{{ route }}', '{{ trans }}'
405
            ], [
406
                $this->getNamespacedModel($name = $this->argument('model')),
407
                $trans = Str::of($name)->slug()->plural(),
0 ignored issues
show
Bug introduced by
It seems like $name defined by $this->argument('model') on line 406 can also be of type array or null; however, Illuminate\Support\Str::of() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
408
                $trans
409
            ], $navLink)
410
        );
411
412
        $this->files->append(
413
            $routePath,
414
            "\r\nRoute::resource('$trans', '{$name}Controller');"
415
        );
416
    }
417
418
    /**
419
     * Get the console command arguments.
420
     *
421
     * @return array
422
     */
423
    protected function getArguments()
424
    {
425
        return [
426
            ['model', InputArgument::REQUIRED, 'The model name']
427
        ];
428
    }
429
430
    /**
431
     * Get the console command arguments.
432
     *
433
     * @return array
434
     */
435
    protected function getOptions()
436
    {
437
        // TODO:: add description
438
        return [
439
            ['namespace', '', InputOption::VALUE_OPTIONAL, '', config('microboard.routes.namespace.admin_directory', 'Admin')],
440
            ['model-namespace', '', InputOption::VALUE_REQUIRED, ''],
441
            ['base_path', 'p', InputOption::VALUE_OPTIONAL, ''],
442
            ['abilities', 'a', InputOption::VALUE_OPTIONAL, ''],
443
            ['only', 'o', InputOption::VALUE_OPTIONAL, ''],
444
        ];
445
    }
446
}
447