SetupReacterable::handle()   B
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 90
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 53
c 1
b 0
f 0
dl 0
loc 90
rs 8.4032
cc 6
nc 7
nop 3

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
 * This file is part of Laravel Love.
5
 *
6
 * (c) Anton Komarev <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cog\Laravel\Love\Console\Commands;
15
16
use Cog\Contracts\Love\Reacterable\Models\Reacterable as ReacterableInterface;
17
use Cog\Laravel\Love\Reacter\Models\Reacter;
18
use Cog\Laravel\Love\Support\Database\AddForeignColumnStub;
19
use Cog\Laravel\Love\Support\Database\MigrationCreator;
20
use Illuminate\Console\Command;
21
use Illuminate\Contracts\Filesystem\FileNotFoundException;
22
use Illuminate\Database\Eloquent\Model;
23
use Illuminate\Filesystem\Filesystem;
24
use Illuminate\Support\Composer;
25
use Illuminate\Support\Facades\Schema;
26
use Illuminate\Support\Str;
27
use Symfony\Component\Console\Attribute\AsCommand;
28
use Symfony\Component\Console\Input\InputOption;
29
30
#[AsCommand(name: 'love:setup-reacterable', description: 'Set up reacterable model')]
31
final class SetupReacterable extends Command
32
{
33
    private Filesystem $files;
34
35
    private MigrationCreator $creator;
36
37
    private Composer $composer;
38
39
    public function handle(
40
        Filesystem $files,
41
        MigrationCreator $creator,
42
        Composer $composer,
43
    ): int {
44
        $this->files = $files;
45
        $this->creator = $creator;
46
        $this->composer = $composer;
47
48
        $model = $this->resolveModel();
49
        $model = $this->sanitizeName($model);
50
        $foreignColumn = 'love_reacter_id';
51
        $isForeignColumnNullable = boolval($this->option('not-nullable')) === false;
52
53
        if (!class_exists($model)) {
54
            $this->error(
55
                sprintf(
56
                    'Model `%s` not exists.',
57
                    $model,
58
                ),
59
            );
60
61
            return self::FAILURE;
62
        }
63
64
        /** @var \Illuminate\Database\Eloquent\Model $model */
65
        $model = new $model();
66
67
        if ($this->isModelInvalid($model)) {
68
            $this->error(
69
                sprintf(
70
                    'Model `%s` does not implements Reacterable interface.',
71
                    get_class($model),
72
                ),
73
            );
74
75
            return self::FAILURE;
76
        }
77
78
        $table = $model->getTable();
79
        $referencedModel = new Reacter();
80
        $referencedSchema = Schema::connection($referencedModel->getConnectionName());
81
        $referencedTable = $referencedModel->getTable();
82
        $referencedColumn = $referencedModel->getKeyName();
83
84
        if (!$referencedSchema->hasTable($referencedTable)) {
85
            $this->error(
86
                sprintf(
87
                    'Referenced table `%s` does not exists in database.',
88
                    $referencedTable,
89
                ),
90
            );
91
92
            return self::FAILURE;
93
        }
94
95
        if (Schema::hasColumn($table, $foreignColumn)) {
96
            $this->error(
97
                sprintf(
98
                    'Foreign column `%s` already exists in `%s` database table.',
99
                    $foreignColumn,
100
                    $table,
101
                ),
102
            );
103
104
            return self::FAILURE;
105
        }
106
107
        try {
108
            $stub = new AddForeignColumnStub(
109
                $this->files,
110
                $table,
111
                $referencedTable,
112
                $foreignColumn,
113
                $referencedColumn,
114
                $isForeignColumnNullable
115
            );
116
117
            $this->creator->create($this->getMigrationsPath(), $stub);
118
        } catch (FileNotFoundException $exception) {
119
            $this->error($exception->getMessage());
120
121
            return self::FAILURE;
122
        }
123
124
        $this->info('Migration created successfully!');
125
126
        $this->composer->dumpAutoloads();
127
128
        return self::SUCCESS;
129
    }
130
131
    /**
132
     * @return array<int, InputOption>
133
     */
134
    protected function getOptions(): array
135
    {
136
        return [
137
            new InputOption(
138
                name: 'model',
139
                mode: InputOption::VALUE_OPTIONAL,
140
                description: 'The name of the reacterable model',
141
            ),
142
            new InputOption(
143
                name: 'not-nullable',
144
                mode: InputOption::VALUE_NONE,
145
                description: 'Indicate if foreign column does not allow null values',
146
            ),
147
        ];
148
    }
149
150
    private function resolveModel(): string
151
    {
152
        return $this->option('model')
153
            ?? $this->ask('What model should be reacterable?')
154
            ?? $this->resolveModel();
155
    }
156
157
    private function sanitizeName(
158
        string $name,
159
    ): string {
160
        $name = trim($name);
161
        $name = Str::studly($name);
162
163
        return $name;
164
    }
165
166
    private function isModelInvalid(
167
        Model $model,
168
    ): bool {
169
        return !$model instanceof ReacterableInterface;
170
    }
171
172
    private function getMigrationsPath(): string
173
    {
174
        return $this->laravel->databasePath('migrations');
175
    }
176
}
177