Test Failed
Push — master ( 052ffd...55ff26 )
by Jodie
02:51
created

MakeTransformerCommand   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 257
rs 10
c 0
b 0
f 0
wmc 26

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getModelInput() 0 3 1
A getModel() 0 5 1
A generateAllTransformers() 0 9 2
A buildClass() 0 4 1
A __construct() 0 7 1
A getTransformerName() 0 6 1
A getTemplateData() 0 16 1
A getNameInput() 0 3 1
A getTransformerNamespace() 0 3 1
A getStub() 0 3 1
A getModelNamespace() 0 3 1
B handle() 0 29 6
A getTransformerClass() 0 3 1
A getModelDirectory() 0 10 3
A getModelName() 0 3 1
A getModelClass() 0 17 3
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('make:transformer', [
97
                'model' => $model,
98
                '--force' => $this->option('force'),
99
            ]);
100
        }
101
    }
102
103
    /**
104
     * Retrieve the models directory.
105
     *
106
     * @return string
107
     */
108
    protected function getModelDirectory() : string
109
    {
110
        $relativePath = $this->option('directory') ?: config('smokescreen.models_directory', 'app');
111
112
        if (!file_exists($absolutePath = base_path($relativePath))) {
0 ignored issues
show
Bug introduced by
It seems like $relativePath can also be of type array; however, parameter $path of base_path() 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

112
        if (!file_exists($absolutePath = base_path(/** @scrutinizer ignore-type */ $relativePath))) {
Loading history...
113
            $this->error("The specified models directory does not exist: {$absolutePath}");
114
            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...
115
        }
116
117
        return $absolutePath;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    protected function getStub()
124
    {
125
        return 'smokescreen::transformer';
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    protected function buildClass($name)
132
    {
133
        return $this->viewFactory->make($this->getStub(), $this->getTemplateData())
134
            ->render();
135
    }
136
137
    /**
138
     * @throws \ReflectionException
139
     *
140
     * @return array
141
     */
142
    protected function getTemplateData()
143
    {
144
        $modelInspector = new ModelMapper($this->getModel());
145
146
        return [
147
            'rootNamespace' => $this->rootNamespace(),
148
            'model' => $this->getModel(),
149
            'modelClass' => $this->getModelClass(),
150
            'modelNamespace' => $this->getModelNamespace(),
151
            'modelName' => $this->getModelName(),
152
            'transformerClass' => $this->getTransformerClass(),
153
            'transformerNamespace' => $this->getTransformerNamespace(),
154
            'transformerName' => $this->getTransformerName(),
155
            'includes' => $modelInspector->getIncludes(),
156
            'properties' => $modelInspector->getDeclaredProperties(),
157
            'defaultProperties' => $modelInspector->getDefaultProperties(),
158
        ];
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164
    protected function getNameInput()
165
    {
166
        return $this->getTransformerClass();
167
    }
168
169
    /**
170
     * Get the transformer class name.
171
     *
172
     * @return string
173
     */
174
    protected function getTransformerName()
175
    {
176
        return preg_replace(
177
            '/{ModelName}/i',
178
            $this->getModelName(),
179
            config('smokescreen.transformer_name', '{ModelName}Transformer')
180
        );
181
    }
182
183
    /**
184
     * Retrieve the transformer namespace.
185
     *
186
     * @return \Illuminate\Config\Repository|mixed
187
     */
188
    protected function getTransformerNamespace()
189
    {
190
        return config('smokescreen.transformer_namespace', 'App\Transformers');
191
    }
192
193
    /**
194
     * Retrieve the transformer class including namespace.
195
     *
196
     * @return string
197
     */
198
    protected function getTransformerClass()
199
    {
200
        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

200
        return /** @scrutinizer ignore-type */ $this->getTransformerNamespace() . '\\' . $this->getTransformerName();
Loading history...
201
    }
202
203
    /**
204
     * Retrieve the model class including namespace.
205
     *
206
     * @return string
207
     */
208
    protected function getModelClass(): string
209
    {
210
        $class = $this->getModelInput();
211
        if (!str_contains($class, '\\')) {
212
            $directory = $this->getModelDirectory();
213
            $file = str_finish($directory, '/') . $this->getModelInput();
214
            $segments = array_map('ucfirst', explode('/', $file));
215
216
            $class = implode('\\', $segments);
217
218
            // Make relevant to the root namespace
219
            if (($pos = strpos($class, $this->rootNamespace())) !== false) {
220
                $class = substr($class, $pos);
221
            }
222
        }
223
224
        return $class;
225
    }
226
227
    /**
228
     * Get the eloquent model instance.
229
     *
230
     * @return Model
231
     */
232
    protected function getModel(): Model
233
    {
234
        $class = $this->getModelClass();
235
236
        return new $class();
237
    }
238
239
    /**
240
     * Retrieve the model class name.
241
     *
242
     * @return string
243
     */
244
    protected function getModelInput(): string
245
    {
246
        return ucfirst($this->argument('model'));
0 ignored issues
show
Bug introduced by
It seems like $this->argument('model') can also be of type array; however, parameter $str 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

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