Test Setup Failed
Pull Request — master (#150)
by Nick
14:47 queued 06:28
created

BuildPageFilesCommand::getOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Build Page Files Command.
4
 *
5
 * @package App\Console\Commands\BuildPageFiles
6
 *
7
 * @author    Nick Menke <[email protected]>
8
 * @copyright 2018-2020 Nick Menke
9
 *
10
 * @link https://github.com/nlmenke/vertebrae
11
 */
12
13
declare(strict_types=1);
14
15
namespace App\Console\Commands\BuildPageFiles;
16
17
use File;
18
use Illuminate\Console\Command;
19
use Illuminate\Contracts\Filesystem\FileNotFoundException;
20
use Illuminate\Support\Composer;
21
use Illuminate\Support\Str;
22
use Symfony\Component\Console\Input\InputArgument;
23
use Symfony\Component\Console\Input\InputOption;
24
25
/**
26
 * Builds class files required for a new API/page.
27
 *
28
 * This command will generate the classes and other files needed to make a new
29
 * API/page for your application. You can choose whether you're building an API
30
 * or a page - this will tell the generator which classes need to be created.
31
 *
32
 * @since x.x.x introduced
33
 */
34
class BuildPageFilesCommand extends Command
35
{
36
    /**
37
     * The Composer instance.
38
     *
39
     * @var Composer
40
     */
41
    protected $composer;
42
43
    /**
44
     * The default feature type.
45
     *
46
     * @var string
47
     */
48
    protected $defaultType = 'api';
49
50
    /**
51
     * The console command description.
52
     *
53
     * @var string
54
     */
55
    protected $description = 'Build the files needed for a new API/page.';
56
57
    /**
58
     * The console command name.
59
     *
60
     * @var string
61
     */
62
    protected $name = 'build:files';
63
64
    /**
65
     * The selected feature type.
66
     *
67
     * @var string
68
     */
69
    protected $selectedType;
70
71
    /**
72
     * Create a new command instance.
73
     *
74
     * @param Composer $composer
75
     *
76
     * @return void
77
     */
78 33
    public function __construct(Composer $composer)
79
    {
80 33
        parent::__construct();
81
82 33
        $this->composer = $composer;
83 33
    }
84
85
    /**
86
     * Execute the console command.
87
     *
88
     * @throws FileNotFoundException
89
     *
90
     * @return void
91
     */
92
    public function handle(): void
93
    {
94
        if ($this->laravel->environment() !== 'local') {
95
            $this->error('Files should only be generated in a local environment');
96
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
97
        }
98
99
        if ($this->option('type') === null || !in_array($this->option('type'), ['api', 'page'], true)) {
100
            $this->selectedType = $this->choice('What type of feature will this be?', ['api', 'page'], $this->defaultType);
101
        } else {
102
            $this->selectedType = $this->option('type');
103
        }
104
105
        $name = Str::studly(Str::singular($this->argument('name')));
106
107
        $controllerPath = app_path('Http/Controllers');
108
        if ($this->isApi()) {
109
            $controllerPath = $controllerPath . '/Api';
110
            $controllerFile = $name . 'ApiController.php';
111
        } else {
112
            $controllerFile = $name . 'Controller.php';
113
        }
114
115
        $modelPath = app_path('Entities/' . $name);
116
        $modelFile = $name . '.php';
117
118
        $requestPath = app_path('Http/Requests/' . $name);
119
        $createRequestFile = 'Create' . $name . 'Request.php';
120
        $updateRequestFile = 'Update' . $name . 'Request.php';
121
122
        $resourcePath = app_path('Http/Resources/' . $name);
123
        $resourceFile = $name . 'Resource.php';
124
125
        $stylePath = resource_path('assets/sass/pages');
126
        $styleFile = '_' . Str::plural(Str::snake($name, '-')) . '.scss';
127
128
        $languageFile = Str::plural(Str::snake($name, '-')) . '.php';
129
130
        $featureTestPath = base_path('tests/Feature/Controllers');
131
        if ($this->isApi()) {
132
            $featureTestPath = $featureTestPath . '/Api';
133
            $featureTestFile = $name . 'ApiControllerTest.php';
134
        } else {
135
            $featureTestFile = $name . 'ControllerTest.php';
136
        }
137
138
        $unitTestPath = base_path('tests/Unit');
139
        $unitTestFile = $name . 'Test.php';
140
141
        // check for existing model; no need to create if files exist
142
        if (File::exists($modelPath . '/' . $modelFile)) {
143
            $this->error($name . ' already exists!');
144
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
145
        }
146
147
        // create controller and add to Git
148
        $controller = $this->buildFile($name, ($this->isApi() ? 'controller.api' : 'controller'));
149
        $this->saveFile($controllerPath, $controllerFile, $controller);
150
        $this->gitAdd($controllerPath . '/' . $controllerFile);
151
152
        // create model and add to Git
153
        $model = $this->buildFile($name, 'model');
154
        $this->saveFile($modelPath, $modelFile, $model);
155
        $this->gitAdd($modelPath . '/' . $modelFile);
156
157
        // create create request and add to Git
158
        $createRequest = $this->buildFile($name, 'request.create');
159
        $this->saveFile($requestPath, $createRequestFile, $createRequest);
160
        $this->gitAdd($requestPath . '/' . $createRequestFile);
161
162
        // create update request and add to Git
163
        $updateRequest = $this->buildFile($name, 'request.update');
164
        $this->saveFile($requestPath, $updateRequestFile, $updateRequest);
165
        $this->gitAdd($requestPath . '/' . $updateRequestFile);
166
167
        if ($this->isApi()) {
168
            // create API resource and add to Git
169
            $resource = $this->buildFile($name, 'resource');
170
            $this->saveFile($resourcePath, $resourceFile, $resource);
171
            $this->gitAdd($resourcePath . '/' . $resourceFile);
172
        }
173
174
        // create stylesheet and add to Git
175
        $this->saveFile($stylePath, $styleFile, '');
176
        $this->gitAdd($stylePath . '/' . $styleFile);
177
178
        // create languages and add to Git
179
        foreach (File::directories(resource_path('lang')) as $languagePath) {
180
            $language = $this->buildfile(Str::snake($name, '-'), 'lang');
181
            $this->saveFile($languagePath, $languageFile, $language);
182
            $this->gitAdd($languagePath . '/' . $languageFile);
183
        }
184
185
        // create feature test and add to Git
186
        $featureTest = $this->buildFile($name, ($this->isApi() ? 'test.feature.controller.api' : 'test.feature.controller'));
187
        $this->saveFile($featureTestPath, $featureTestFile, $featureTest);
188
        $this->gitAdd($featureTestPath . '/' . $featureTestFile);
189
190
        // create unit test and add to Git
191
        $unitTest = $this->buildFile($name, 'test.unit');
192
        $this->saveFile($unitTestPath, $unitTestFile, $unitTest);
193
        $this->gitAdd($unitTestPath . '/' . $unitTestFile);
194
195
        if ($this->option('migration')) {
196
            // create migration and add to Git
197
            $this->buildMigration($name);
198
            $this->gitAdd('$(git ls-files database/migrations --other --exclude-standard)');
199
        }
200
201
        if ($this->option('seeder')) {
202
            // create seeder and add to Git
203
            $seederPath = database_path('seeds');
204
            $seederFile = Str::plural($name) . 'TableSeeder.php';
205
206
            $seeder = $this->buildFile($name, 'seeder');
207
            $this->saveFile($seederPath, $seederFile, $seeder);
208
            $this->gitAdd($seederPath . '/' . $seederFile);
209
        }
210
211
        if ($this->option('factory')) {
212
            // create factory and add to Git
213
            $factoryPath = database_path('factories');
214
            $factoryFile = $name . 'Factory.php';
215
216
            $factory = $this->buildFile($name, 'factory');
217
            $this->saveFile($factoryPath, $factoryFile, $factory);
218
            $this->gitAdd($factoryPath . '/' . $factoryFile);
219
        }
220
221
        // dump composer autoload so everything gets loaded properly
222
        $this->composer->dumpAutoloads();
223
224
        $this->info('File generation complete!');
225
    }
226
227
    /**
228
     * Get the console command arguments.
229
     *
230
     * @return array
231
     */
232 33
    protected function getArguments(): array
233
    {
234
        return [
235 33
            ['name', InputArgument::REQUIRED, 'The name of the API/page being created'],
236
        ];
237
    }
238
239
    /**
240
     * Get the console command options.
241
     *
242
     * @return array
243
     */
244 33
    protected function getOptions(): array
245
    {
246
        return [
247 33
            ['type', '', InputOption::VALUE_OPTIONAL, 'Specify the feature type: `api` or `page`'],
248 33
            ['migration', 'm', InputOption::VALUE_NONE, 'Generate a migration'],
249 33
            ['seeder', 's', InputOption::VALUE_NONE, 'Generate a seeder'],
250 33
            ['factory', 'f', InputOption::VALUE_NONE, 'Generate a factory'],
251
        ];
252
    }
253
254
    /**
255
     * Gets the file stub and fills in the contents.
256
     *
257
     * @param string $name
258
     * @param string $type
259
     *
260
     * @throws FileNotFoundException
261
     *
262
     * @return string
263
     */
264
    private function buildFile(string $name, string $type): string
265
    {
266
        $file = File::get($this->getStub($type));
267
268
        $replacements = [
269
            'LowerDummyRootNamespace' => mb_strtolower($this->laravel->getNamespace()),
270
            'DummyRootNamespace' => $this->laravel->getNamespace(),
271
            'LowerDummy' => mb_strtolower($name),
272
            'Dummy' => $name,
273
            'LowerDummies' => mb_strtolower(Str::plural($name)),
274
            'Dummies' => Str::plural($name),
275
        ];
276
277
        return str_replace(array_keys($replacements), array_values($replacements), $file);
278
    }
279
280
    /**
281
     * Generates a migration file.
282
     *
283
     * @param string $name
284
     *
285
     * @return void
286
     */
287
    private function buildMigration(string $name): void
288
    {
289
        $table = Str::plural(Str::snake($name));
290
        $migration = 'create_' . $table . '_table';
291
292
        $this->call('make:migration', [
293
            'name' => $migration,
294
            '--create' => $table,
295
        ]);
296
    }
297
298
    /**
299
     * Pulls the specified stub file.
300
     *
301
     * @param string $type
302
     *
303
     * @return string
304
     */
305
    private function getStub(string $type): string
306
    {
307
        return __DIR__ . '/stubs/' . $type . '.stub';
308
    }
309
310
    /**
311
     * Adds the file to Git.
312
     *
313
     * @param string $file
314
     *
315
     * @return void
316
     */
317
    private function gitAdd(string $file): void
318
    {
319
        exec('git add ' . $file, $output);
320
321
        $this->output->write($output, true);
322
    }
323
324
    /**
325
     * Checks if the feature should be an API.
326
     *
327
     * @return bool
328
     */
329
    private function isApi(): bool
330
    {
331
        return $this->selectedType === 'api';
332
    }
333
334
    /**
335
     * Saves the generated file.
336
     *
337
     * @param string $path
338
     * @param string $filename
339
     * @param string $file
340
     *
341
     * @return void
342
     */
343
    private function saveFile(string $path, string $filename, string $file): void
344
    {
345
        if (!File::isDirectory($path)) {
346
            File::makeDirectory($path, 493, true, true);
347
        }
348
349
        File::put($path . '/' . $filename, $file);
350
351
        $this->line('<info>Built File:</info> ' . $filename);
352
    }
353
}
354