Completed
Push — master ( 7d628f...613755 )
by Anton
05:15
created

src/Console/Commands/RegisterReacters.php (1 issue)

Labels
Severity
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\Exceptions\ReacterableInvalid;
17
use Cog\Contracts\Love\Reacterable\Models\Reacterable as ReacterableContract;
18
use Illuminate\Console\Command;
19
use Illuminate\Database\Eloquent\Relations\Relation;
20
use Symfony\Component\Console\Input\InputOption;
21
22
final class RegisterReacters extends Command
23
{
24
    /**
25
     * The console command name.
26
     *
27
     * @var string
28
     */
29
    protected $name = 'love:register-reacters';
30
31
    /**
32
     * The console command description.
33
     *
34
     * @var string
35
     */
36
    protected $description = 'Register Reacterable models as Reacters';
37
38
    protected function getOptions(): array
39
    {
40
        return [
41
            ['model', null, InputOption::VALUE_REQUIRED, 'The name of the Reacterable model'],
42
            ['ids', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '(optional) Comma-separated list of model IDs (e.g. `--ids=1,2,16,34`)'],
43
        ];
44
    }
45
46
    /**
47
     * Execute the console command.
48
     *
49
     * @return int
50
     */
51
    public function handle(): int
52
    {
53
        $reacterableType = $this->option('model');
54
        if ($reacterableType === null) {
55
            $this->error('Option `--model` is required!');
56
57
            return 1;
58
        }
59
60
        try {
61
            $reacterableModel = $this->reacterableModelFromType($reacterableType);
62
63
            $modelIds = $this->option('ids');
64
            $modelIds = $this->normalizeIds($modelIds);
0 ignored issues
show
It seems like $modelIds can also be of type boolean and null and string; however, parameter $modelIds of Cog\Laravel\Love\Console...eacters::normalizeIds() does only seem to accept array, 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

64
            $modelIds = $this->normalizeIds(/** @scrutinizer ignore-type */ $modelIds);
Loading history...
65
66
            $models = $this->collectModels($reacterableModel, $modelIds);
67
68
            $this->info(sprintf('Models registering as Reacters %s', PHP_EOL));
69
            $this->line(sprintf('Model Type: <fg=Cyan>%s</>', get_class($reacterableModel)));
70
71
            $this->registerModelsAsReacters($models);
72
73
            $this->info('Models has been registered as Reacters');
74
        } catch (ReacterableInvalid $exception) {
75
            $this->error($exception->getMessage());
76
77
            return 1;
78
        }
79
80
        return 0;
81
    }
82
83
    /**
84
     * Instantiate model from type or morph map value.
85
     *
86
     * @param string $modelType
87
     * @return \Cog\Contracts\Love\Reacterable\Models\Reacterable|\Illuminate\Database\Eloquent\Model
88
     *
89
     * @throws \Cog\Contracts\Love\Reacterable\Exceptions\ReacterableInvalid
90
     */
91
    private function reacterableModelFromType(
92
        string $modelType
93
    ): ReacterableContract {
94
        if (!class_exists($modelType)) {
95
            $modelType = $this->findModelTypeInMorphMap($modelType);
96
        }
97
98
        $model = new $modelType();
99
100
        if (!$model instanceof ReacterableContract) {
101
            throw ReacterableInvalid::notImplementInterface($modelType);
102
        }
103
104
        return $model;
105
    }
106
107
    /**
108
     * Find model type in morph mappings registry.
109
     *
110
     * @param string $modelType
111
     * @return string
112
     *
113
     * @throws \Cog\Contracts\Love\Reacterable\Exceptions\ReacterableInvalid
114
     */
115
    private function findModelTypeInMorphMap(
116
        string $modelType
117
    ): string {
118
        $morphMap = Relation::morphMap();
119
120
        if (!isset($morphMap[$modelType])) {
121
            throw ReacterableInvalid::classNotExists($modelType);
122
        }
123
124
        return $morphMap[$modelType];
125
    }
126
127
    private function normalizeIds(array $modelIds): array
128
    {
129
        if (!isset($modelIds[0])) {
130
            return $modelIds;
131
        }
132
133
        $firstItem = $modelIds[0];
134
135
        if (is_string($firstItem) && strpos($firstItem, ',')) {
136
            $modelIds = explode(',', $firstItem);
137
        }
138
139
        return $modelIds;
140
    }
141
142
    /**
143
     * @param \Cog\Contracts\Love\Reacterable\Models\Reacterable|\Illuminate\Database\Eloquent\Model $reacterableModel
144
     * @param array $modelIds
145
     * @return iterable
146
     */
147
    private function collectModels(
148
        ReacterableContract $reacterableModel,
149
        array $modelIds
150
    ): iterable {
151
        $query = $reacterableModel
152
            ->query()
153
            ->whereNull('love_reacter_id');
154
155
        if (!empty($modelIds)) {
156
            $query->whereKey($modelIds);
157
        }
158
159
        return $query->get();
160
    }
161
162
    private function registerModelsAsReacters(iterable $models): void
163
    {
164
        $collectedCount = $models->count();
165
        $progressBar = $this->output->createProgressBar($collectedCount);
166
167
        foreach ($models as $model) {
168
            $model->registerAsLoveReacter();
169
            $progressBar->advance();
170
        }
171
172
        $progressBar->finish();
173
        $this->line(PHP_EOL);
174
    }
175
}
176