MakeTransformerCommand   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 26
eloc 71
c 4
b 0
f 1
dl 0
loc 260
ccs 0
cts 88
cp 0
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A handle() 0 29 6
A getModelInput() 0 3 1
A getModel() 0 5 1
A buildClass() 0 4 1
A getTransformerName() 0 6 1
A getNameInput() 0 3 1
A getTransformerNamespace() 0 3 1
A getStub() 0 3 1
A getModelNamespace() 0 3 1
A getTransformerClass() 0 3 1
A getModelDirectory() 0 10 3
A getModelName() 0 3 1
A getModelClass() 0 17 3
A generateAllTransformers() 0 11 2
A getTemplateData() 0 16 1
1
<?php
2
3
namespace Rexlabs\Laravel\Smokescreen\Console;
4
5
use Illuminate\Console\GeneratorCommand;
6
use Illuminate\Contracts\View\Factory;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Filesystem\Filesystem;
9
10
class MakeTransformerCommand extends GeneratorCommand
11
{
12
    /**
13
     * The name and signature of the console command.
14
     * make:transformer User
15
     *
16
     * @var string
17
     */
18
    protected $signature = 'make:transformer
19
        {model? : The name of the model to transform. e.g. User} 
20
        {--f|force : Overwrite an existing transformer}
21
        {--d|directory= : Specify the models directory}
22
        {--a|all : Generate a transformer for all models.}';
23
24
    /**
25
     * The console command description.
26
     *
27
     * @var string
28
     */
29
    protected $description = 'Create a new smokescreen transformer class';
30
31
    /**
32
     * The view factory.
33
     *
34
     * @var \Illuminate\Contracts\View\Factory
35
     */
36
    protected $viewFactory;
37
38
    /**
39
     * Inject the dependencies.
40
     *
41
     * @param \Illuminate\Filesystem\Filesystem  $files
42
     * @param \Illuminate\Contracts\View\Factory $viewFactory
43
     */
44
    public function __construct(
45
        Filesystem $files,
46
        Factory $viewFactory
47
    ) {
48
        parent::__construct($files);
49
50
        $this->viewFactory = $viewFactory;
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function handle()
57
    {
58
        $model = $this->getModelInput();
59
        $this->type = "{$model} transformer";
60
61
        if ($this->option('all')) {
62
            $this->line('Generating all transformers');
63
            $this->generateAllTransformers();
64
            return;
65
        }
66
67
        if (empty($model)) {
68
            $this->error('Must specify a model or the --all option');
69
            return;
70
        }
71
72
        // TODO: Given a models directory like app/ determine the namespace
73
        if (!class_exists($modelClass = $this->getModelClass())) {
74
            $this->error("The model [{$modelClass}] does not exist.");
75
            return;
76
        }
77
78
        if (!$this->option('force') && class_exists($this->getTransformerClass())) {
79
            $this->warn("{$this->type} already exists.");
80
            return;
81
        }
82
83
84
        parent::handle();
85
    }
86
87
    /**
88
     * Generate a transformer for every model.
89
     */
90
    protected function generateAllTransformers()
91
    {
92
        $directory = $this->getModelDirectory();
93
        $models = (new ModelsFinder())->findInDirectory($directory);
94
95
        foreach ($models as $model) {
96
            $this->call(
97
                'make:transformer',
98
                [
99
                'model' => $model,
100
                '--force' => $this->option('force'),
101
                ]
102
            );
103
        }
104
    }
105
106
    /**
107
     * Retrieve the models directory.
108
     *
109
     * @return string
110
     */
111
    protected function getModelDirectory(): string
112
    {
113
        $relativePath = $this->option('directory') ?: config('smokescreen.models_directory', 'app');
114
115
        if (!file_exists($absolutePath = base_path($relativePath))) {
116
            $this->error("The specified models directory does not exist: {$absolutePath}");
117
            exit();
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
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...
118
        }
119
120
        return $absolutePath;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    protected function getStub()
127
    {
128
        return 'smokescreen::transformer';
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    protected function buildClass($name)
135
    {
136
        return $this->viewFactory->make($this->getStub(), $this->getTemplateData())
137
            ->render();
138
    }
139
140
    /**
141
     * @throws \ReflectionException
142
     *
143
     * @return array
144
     */
145
    protected function getTemplateData()
146
    {
147
        $modelInspector = new ModelMapper($this->getModel());
148
149
        return [
150
            'rootNamespace' => $this->rootNamespace(),
151
            'model' => $this->getModel(),
152
            'modelClass' => $this->getModelClass(),
153
            'modelNamespace' => $this->getModelNamespace(),
154
            'modelName' => $this->getModelName(),
155
            'transformerClass' => $this->getTransformerClass(),
156
            'transformerNamespace' => $this->getTransformerNamespace(),
157
            'transformerName' => $this->getTransformerName(),
158
            'includes' => $modelInspector->getIncludes(),
159
            'properties' => $modelInspector->getDeclaredProperties(),
160
            'defaultProperties' => $modelInspector->getDefaultProperties(),
161
        ];
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    protected function getNameInput()
168
    {
169
        return $this->getTransformerClass();
170
    }
171
172
    /**
173
     * Get the transformer class name.
174
     *
175
     * @return string
176
     */
177
    protected function getTransformerName()
178
    {
179
        return preg_replace(
180
            '/{ModelName}/i',
181
            $this->getModelName(),
182
            config('smokescreen.transformer_name', '{ModelName}Transformer')
183
        );
184
    }
185
186
    /**
187
     * Retrieve the transformer namespace.
188
     *
189
     * @return \Illuminate\Config\Repository|mixed
190
     */
191
    protected function getTransformerNamespace()
192
    {
193
        return config('smokescreen.transformer_namespace', 'App\Transformers');
194
    }
195
196
    /**
197
     * Retrieve the transformer class including namespace.
198
     *
199
     * @return string
200
     */
201
    protected function getTransformerClass()
202
    {
203
        return $this->getTransformerNamespace() . '\\' . $this->getTransformerName();
0 ignored issues
show
Bug introduced by
Are you sure $this->getTransformerNamespace() of type Illuminate\Config\Repository|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

203
        return /** @scrutinizer ignore-type */ $this->getTransformerNamespace() . '\\' . $this->getTransformerName();
Loading history...
204
    }
205
206
    /**
207
     * Retrieve the model class including namespace.
208
     *
209
     * @return string
210
     */
211
    protected function getModelClass(): string
212
    {
213
        $class = $this->getModelInput();
214
        if (!str_contains($class, '\\')) {
215
            $directory = $this->getModelDirectory();
216
            $file = str_finish($directory, '/') . $this->getModelInput();
0 ignored issues
show
Bug introduced by
The function str_finish was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

216
            $file = /** @scrutinizer ignore-call */ str_finish($directory, '/') . $this->getModelInput();
Loading history...
217
            $segments = array_map('ucfirst', explode('/', $file));
218
219
            $class = implode('\\', $segments);
220
221
            // Make relevant to the root namespace
222
            if (($pos = strpos($class, $this->rootNamespace())) !== false) {
223
                $class = substr($class, $pos);
224
            }
225
        }
226
227
        return $class;
228
    }
229
230
    /**
231
     * Get the eloquent model instance.
232
     *
233
     * @return Model
234
     */
235
    protected function getModel(): Model
236
    {
237
        $class = $this->getModelClass();
238
239
        return new $class();
240
    }
241
242
    /**
243
     * Retrieve the model class name.
244
     *
245
     * @return string
246
     */
247
    protected function getModelInput(): string
248
    {
249
        return ucfirst($this->argument('model'));
0 ignored issues
show
Bug introduced by
It seems like $this->argument('model') can also be of type array and null; however, parameter $string of ucfirst() does only seem to accept string, 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

249
        return ucfirst(/** @scrutinizer ignore-type */ $this->argument('model'));
Loading history...
250
    }
251
252
    /**
253
     * Retrieve the model class name.
254
     *
255
     * @return string
256
     */
257
    protected function getModelName(): string
258
    {
259
        return class_basename($this->getModelInput());
260
    }
261
262
    /**
263
     * Get the namespace of the model class.
264
     *
265
     * @return string
266
     */
267
    protected function getModelNamespace()
268
    {
269
        return $this->getNamespace($this->getModelClass());
270
    }
271
}
272