Completed
Push — master ( d1538b...42b84f )
by Anton
03:22
created

SetupReactable::getOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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\Reactable\Models\Reactable as ReactableContract;
17
use Cog\Laravel\Love\Reactant\Models\Reactant;
18
use Cog\Laravel\Love\Support\Database\MigrationCreator;
19
use Cog\Laravel\Love\Support\Database\AddForeignColumnStub;
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\Input\InputOption;
28
29
final class SetupReactable extends Command
30
{
31
    /**
32
     * The console command name.
33
     *
34
     * @var string
35
     */
36
    protected $name = 'love:setup-reactable';
37
38
    /**
39
     * The console command description.
40
     *
41
     * @var string
42
     */
43
    protected $description = 'Set up reactable model';
44
45
    private $files;
46
47
    private $creator;
48
49
    private $composer;
50
51
    public function __construct(Filesystem $files, MigrationCreator $creator, Composer $composer)
52
    {
53
        parent::__construct();
54
55
        $this->files = $files;
56
        $this->creator = $creator;
57
        $this->composer = $composer;
58
    }
59
60
    public function handle(): int
61
    {
62
        $model = $this->resolveModel();
63
        $model = $this->sanitizeName($model);
64
        $foreignColumn = 'love_reactant_id';
65
        $isForeignColumnNullable = boolval($this->option('nullable'));
66
67
        if (!class_exists($model)) {
68
            $this->error(sprintf(
69
                'Model `%s` not exists.',
70
                $model
71
            ));
72
73
            return 1;
74
        }
75
76
        /** @var \Illuminate\Database\Eloquent\Model $model */
77
        $model = new $model();
78
79
        if ($this->isModelInvalid($model)) {
80
            $this->error(sprintf(
81
                'Model `%s` does not implements Reactable interface.',
82
                get_class($model)
83
            ));
84
85
            return 1;
86
        }
87
88
        $table = $model->getTable();
89
        $referencedModel = new Reactant();
90
        $referencedTable = $referencedModel->getTable();
91
        $referencedColumn = $referencedModel->getKeyName();
92
93
        if (!Schema::hasTable($referencedTable)) {
94
            $this->error(sprintf(
95
                'Referenced table `%s` does not exists in database.',
96
                $referencedTable
97
            ));
98
99
            return 1;
100
        }
101
102
        if (Schema::hasColumn($table, $foreignColumn)) {
103
            $this->error(sprintf(
104
                'Foreign column `%s` already exists in `%s` database table.',
105
                $foreignColumn,
106
                $table
107
            ));
108
109
            return 1;
110
        }
111
112
        try {
113
            $stub = new AddForeignColumnStub(
114
                $this->files,
115
                $table,
116
                $referencedTable,
117
                $foreignColumn,
118
                $referencedColumn,
119
                $isForeignColumnNullable
120
            );
121
122
            $this->creator->create($this->getMigrationsPath(), $stub);
123
        } catch (FileNotFoundException $exception) {
124
            $this->error($exception->getMessage());
125
126
            return 1;
127
        }
128
129
        $this->info('Migration created successfully!');
130
131
        $this->composer->dumpAutoloads();
132
133
        return 0;
134
    }
135
136
    protected function getOptions(): array
137
    {
138
        return [
139
            ['model', null, InputOption::VALUE_OPTIONAL, 'The name of the reactable model'],
140
            ['nullable', null, InputOption::VALUE_NONE, 'Indicate if foreign column allows null values'],
141
        ];
142
    }
143
144
    private function resolveModel(): string
145
    {
146
        return $this->option('model')
147
            ?? $this->ask('What model should be reactable?')
148
            ?? $this->resolveModel();
149
    }
150
151
    private function sanitizeName(string $name): string
152
    {
153
        $name = trim($name);
154
        $name = Str::studly($name);
155
156
        return $name;
157
    }
158
159
    private function isModelInvalid(Model $model): bool
160
    {
161
        return !$model instanceof ReactableContract;
162
    }
163
164
    private function getMigrationsPath(): string
165
    {
166
        return $this->laravel->databasePath('migrations');
167
    }
168
}
169