Passed
Push — master ( 65b7f9...30151a )
by Chris
15:10
created

ModelCommand::getOutputPaths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 8
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
namespace Leonidas\Console\Command\Make;
4
5
use DirectoryIterator;
6
use Leonidas\Console\Command\HopliteCommand;
7
use Leonidas\Console\Library\ModelComponentFactory;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputOption;
10
11
class ModelCommand extends HopliteCommand
12
{
13
    protected const CORE_FILE_METHODS = [
14
        'makeModelFiles',
15
        'makeCollectionFiles',
16
        'makeRepositoryFiles',
17
    ];
18
19
    protected const SUPPORT_FILE_METHODS = [
20
        'makeFactoryFiles',
21
        'makeAccessProviderFiles',
22
    ];
23
24
    protected const VALID_TEMPLATES = [
25
        'post',
26
        'post:h',
27
        'attachment',
28
        'term',
29
        'term:h',
30
        'user',
31
    ];
32
33
    protected static $defaultName = 'make:model';
34
35
    protected function configure()
36
    {
37
        $this
38
            ->addArgument('model', InputArgument::REQUIRED, '')
39
            ->addArgument('entity', InputArgument::REQUIRED, '')
40
            ->addArgument('single', InputArgument::REQUIRED, '')
41
            ->addArgument('plural', InputArgument::REQUIRED, '')
42
            ->addOption('namespace', 'l', InputOption::VALUE_OPTIONAL, '')
43
            ->addOption('contracts', 'c', InputOption::VALUE_OPTIONAL, '')
44
            ->addOption('abstracts', 'a', InputOption::VALUE_OPTIONAL, '')
45
            ->addOption('template', 't', InputOption::VALUE_REQUIRED, '', 'post')
46
            ->addOption('components', 'p', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The component set to generate')
47
            ->addOption('design', 'd', InputOption::VALUE_NONE, 'Generate interfaces only')
48
            ->addOption('build', 'b', InputOption::VALUE_NONE, 'Generate classes using interfaces as guide')
49
            ->addOption('replace', 'r', InputOption::VALUE_OPTIONAL, '');
50
    }
51
52
    protected function handle(): int
53
    {
54
        $template = $this->input->getOption('template');
55
56
        if ($this->isValidTemplate($template)) {
57
            $status = $this->makeFiles($template);
58
        } else {
59
            $this->output->error("Value \"{$template}\" is not an accepted value for template");
60
61
            $status = self::FAILURE;
62
        }
63
64
        return $status;
65
    }
66
67
    protected function isValidTemplate(string $template): bool
68
    {
69
        return in_array($template, static::VALID_TEMPLATES);
70
    }
71
72
    protected function makeFiles(string $template): int
73
    {
74
        $factory = $this->getComponentFactory($template);
75
        $action = $this->resolveRequestedAction();
76
        $paths = $this->getOutputPaths();
77
78
        foreach (static::CORE_FILE_METHODS as $method) {
79
            $status = $this->$method($factory, $action, $paths);
80
81
            if (self::SUCCESS !== $status) {
82
                return $status;
83
            }
84
        }
85
86
        if ('interfaces' !== $action) {
87
            foreach (static::SUPPORT_FILE_METHODS as $method) {
88
                $status = $this->$method($factory, $paths);
89
90
                if (self::SUCCESS !== $status) {
91
                    return $status;
92
                }
93
            }
94
        }
95
96
        $this->output->success("Successfully created model files");
97
98
        return self::SUCCESS;
99
    }
100
101
    protected function getComponentFactory(string $template): ModelComponentFactory
102
    {
103
        $model = $this->input->getArgument('model');
104
        $entity = $this->input->getArgument('entity');
105
        $single = $this->input->getArgument('single');
106
        $plural = $this->input->getArgument('plural');
107
108
        $model = $this->convert($model)->toPascal();
109
110
        $namespace = $this->getNamespaceFromPath($this->configuredOption(
0 ignored issues
show
Bug introduced by
It seems like $this->configuredOption(...'make.model.namespace') can also be of type null; however, parameter $path of Leonidas\Console\Command...:getNamespaceFromPath() 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

110
        $namespace = $this->getNamespaceFromPath(/** @scrutinizer ignore-type */ $this->configuredOption(
Loading history...
111
            'namespace',
112
            'make.model.namespace'
113
        )) . '\\' . $model;
114
115
        $contracts = $this->getNamespaceFromPath($this->configuredOption(
116
            'contracts',
117
            'make.model.contracts'
118
        )) . '\\' . $model;
119
120
        $abstracts = $this->resolveAbstractNamespace($namespace);
121
122
        $factory = ModelComponentFactory::build([
123
            'model' => $model,
124
            'namespace' => $namespace,
125
            'contracts' => $contracts,
126
            'abstracts' => $abstracts,
127
            'entity' => $entity,
128
            'single' => $single,
129
            'plural' => $plural,
130
            'template' => $template,
131
        ]);
132
133
        return $factory;
134
    }
135
136
    protected function resolveRequestedAction(): string
137
    {
138
        if ($this->input->getOption('design')) {
139
            return 'interfaces';
140
        } elseif ($this->input->getOption('build')) {
141
            return 'classes';
142
        } else {
143
            return 'complete';
144
        }
145
    }
146
147
    protected function getOutputPaths(): array
148
    {
149
        $playground = $this->setupTestDir();
150
151
        return [
152
            'interfaces' => $playground,
153
            'abstracts' => $playground,
154
            'classes' => $playground,
155
        ];
156
    }
157
158
    protected function setupTestDir(): string
159
    {
160
        $playground = $this->external('/.playground/model');
161
162
        if ($this->filesystem->exists($playground)) {
163
            // $this->filesystem->remove($playground);
164
        } else {
165
            $this->filesystem->mkdir($playground);
166
        }
167
168
        foreach (new DirectoryIterator($playground) as $file) {
169
            if (!$file->isFile()) {
170
                continue;
171
            }
172
173
            if (!str_ends_with($file->getBasename(), 'Interface.php')) {
174
                $this->filesystem->remove($file->getPathname());
175
            } else {
176
                require $file->getPathname();
177
            }
178
        }
179
180
        return $playground;
181
    }
182
183
    protected function makeModelFiles(ModelComponentFactory $factory, string $action, array $paths): int
184
    {
185
        $interface = $factory->getModelInterfacePrinter();
186
        $class = $factory->getModelPrinter();
187
188
        $interfaceFile = $this->phpFile($paths['interfaces'], $interface->getClass());
189
        $classFile = $this->phpFile($paths['classes'], $class->getClass());
190
191
        if ('interfaces' === $action || 'complete' === $action) {
192
            $this->writeFile($interfaceFile, $interface->printFile());
193
        } elseif ('classes' === $action) {
194
            if (!interface_exists($interfaceFqn = $interface->getClassFqn())) {
195
                $this->output->error("Interface {$interfaceFqn} does not exist");
196
197
                return self::INVALID;
198
            }
199
200
            $this->writeFile($classFile, $class->printFromType());
201
        }
202
203
        if ('complete' === $action) {
204
            $this->writeFile($classFile, $class->printFile());
205
        }
206
207
        return self::SUCCESS;
208
    }
209
210
    protected function makeCollectionFiles(ModelComponentFactory $factory, string $action, array $paths): int
211
    {
212
        $isPost = $factory->isPostTemplate();
213
214
        $interface = $factory->getCollectionInterfacePrinter();
215
        $collection = $isPost
216
            ? $factory->getChildCollectionPrinter()
217
            : $factory->getCollectionPrinter();
218
        $abstract = $factory->getAbstractCollectionPrinter();
219
        $query = $factory->getChildQueryPrinter();
220
221
        $interfaceFile = $this->phpFile($paths['interfaces'], $interface->getClass());
222
        $collectionFile = $this->phpFile($paths['classes'], $collection->getClass());
223
        $abstractFile = $this->phpFile($paths['abstracts'], $abstract->getClass());
224
        $queryFile = $this->phpFile($paths['classes'], $query->getClass());
225
226
        if ('interfaces' === $action || 'complete' === $action) {
227
            $this->writeFile($interfaceFile, $interface->printFile());
228
        } elseif ('classes' === $action) {
229
            if (!interface_exists($interface->getClassFqn())) {
230
                $this->output->error("Interface {$interface->getClassFqn()} does not exist");
231
232
                return self::INVALID;
233
            }
234
235
            if ($isPost) {
236
                $this->writeFile($abstractFile, $abstract->printFromType());
237
                $this->writeFile($collectionFile, $collection->printFile());
238
                $this->writeFile($queryFile, $query->printFile());
239
            } else {
240
                $this->writeFile($collectionFile, $collection->printFromType());
0 ignored issues
show
Bug introduced by
The method printFromType() does not exist on Leonidas\Console\Library...ollectionAsChildPrinter. ( Ignorable by Annotation )

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

240
                $this->writeFile($collectionFile, $collection->/** @scrutinizer ignore-call */ printFromType());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
241
            }
242
        }
243
244
        if ('complete' === $action) {
245
            $this->writeFile($collectionFile, $collection->printFile());
246
247
            if ($isPost) {
248
                $this->writeFile($abstractFile, $abstract->printFile());
249
                $this->writeFile($queryFile, $query->printFile());
250
            }
251
        }
252
253
        return self::SUCCESS;
254
    }
255
256
    protected function makeRepositoryFiles(ModelComponentFactory $factory, string $action, array $paths): int
257
    {
258
        $interface = $factory->getRepositoryInterfacePrinter();
259
        $class = $factory->getRepositoryPrinter();
260
261
        $interfaceFile = $this->phpFile($paths['interfaces'], $interface->getClass());
262
        $classFile = $this->phpFile($paths['classes'], $class->getClass());
263
264
        if ('interfaces' === $action || 'complete' === $action) {
265
            $this->writeFile($interfaceFile, $interface->printFile());
266
        } elseif ('classes' === $action) {
267
            if (!interface_exists($interfaceFqn = $interface->getClassFqn())) {
268
                $this->output->error("Interface {$interfaceFqn} does not exist");
269
270
                return self::INVALID;
271
            }
272
273
            $this->writeFile($classFile, $class->printFromType());
274
        }
275
276
        if ('complete' === $action) {
277
            $this->writeFile($classFile, $class->printFile());
278
        }
279
280
        return self::SUCCESS;
281
    }
282
283
    protected function makeFactoryFiles(ModelComponentFactory $factory, array $paths): int
284
    {
285
        $model = $factory->getModelConverterPrinter();
286
        $collection = $factory->getCollectionFactoryPrinter();
287
288
        $modelFile = $this->phpFile($paths['classes'], $model->getClass());
289
        $collectionFile = $this->phpFile($paths['classes'], $collection->getClass());
290
291
        $this->writeFile($modelFile, $model->printFile());
292
        $this->writeFile($collectionFile, $collection->printFile());
293
294
        if ($factory->isPostTemplate()) {
295
            $query = $factory->getQueryFactoryPrinter();
296
            $queryFile = $this->phpFile($paths['classes'], $query->getClass());
297
298
            $this->writeFile($queryFile, $query->printFile());
299
        }
300
301
        return self::SUCCESS;
302
    }
303
304
    protected function makeAccessProviderFiles(ModelComponentFactory $factory, array $paths): int
305
    {
306
        $get = $factory->getGetAccessProviderPrinter();
307
        $set = $factory->getSetAccessProviderPrinter();
308
309
        $getFile = $this->phpFile($paths['classes'], $get->getClass());
310
        $setFile = $this->phpFile($paths['classes'], $set->getClass());
311
312
        $this->writeFile($getFile, $get->printFile());
313
        $this->writeFile($setFile, $set->printFile());
314
315
        if ($factory->isPostTemplate()) {
316
            $tag = $factory->getTagAccessProviderPrinter();
317
            $tagFile = $this->phpFile($paths['classes'], $tag->getClass());
318
319
            $this->writeFile($tagFile, $tag->printFile());
320
        }
321
322
        return self::SUCCESS;
323
    }
324
}
325