DefaultController   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 24
eloc 97
c 5
b 0
f 0
dl 0
loc 188
ccs 0
cts 116
cp 0
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A generate() 0 16 2
A get() 0 6 1
A __construct() 0 4 1
A serializeCodeFile() 0 11 1
A serializeGenerator() 0 35 3
A list() 0 13 2
A diff() 0 22 4
A findReflectionParameter() 0 8 3
A createErrorResponse() 0 5 1
A preview() 0 32 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Gii\Controller;
6
7
use Psr\Http\Message\ResponseInterface;
8
use ReflectionClass;
9
use ReflectionParameter;
10
use Yiisoft\DataResponse\DataResponse;
11
use Yiisoft\DataResponse\DataResponseFactoryInterface;
12
use Yiisoft\Http\Status;
13
use Yiisoft\Input\Http\Attribute\Parameter\Query;
14
use Yiisoft\Validator\Helper\RulesDumper;
15
use Yiisoft\Validator\RulesProvider\AttributesRulesProvider;
16
use Yiisoft\Yii\Gii\Component\CodeFile\CodeFile;
17
use Yiisoft\Yii\Gii\Component\CodeFile\CodeFileWriteOperationEnum;
18
use Yiisoft\Yii\Gii\Component\CodeFile\CodeFileWriter;
19
use Yiisoft\Yii\Gii\Exception\InvalidGeneratorCommandException;
20
use Yiisoft\Yii\Gii\Generator\CommandHydrator;
21
use Yiisoft\Yii\Gii\GeneratorCommandInterface;
22
use Yiisoft\Yii\Gii\GeneratorInterface;
23
use Yiisoft\Yii\Gii\GeneratorProxy;
24
use Yiisoft\Yii\Gii\GiiInterface;
25
use Yiisoft\Yii\Gii\ParametersProvider;
26
use Yiisoft\Yii\Gii\Request\GeneratorRequest;
27
28
final class DefaultController
29
{
30
    public function __construct(
31
        private readonly DataResponseFactoryInterface $responseFactory,
32
        private readonly ParametersProvider $parametersProvider,
33
    ) {
34
    }
35
36
    public function list(GiiInterface $gii): ResponseInterface
37
    {
38
        $generators = $gii->getGenerators();
39
40
        return $this->responseFactory->createResponse([
41
            'generators' => array_map(
42
                $this->serializeGenerator(...),
43
                array_values(
44
                    array_map(
45
                        fn (GeneratorInterface|GeneratorProxy $generator) => $generator instanceof GeneratorProxy
46
                            ? $generator->getClass()
47
                            : $generator::class,
48
                        $generators
49
                    )
50
                ),
51
            ),
52
        ]);
53
    }
54
55
    public function get(GeneratorRequest $request): ResponseInterface
56
    {
57
        $generator = $request->getGenerator();
58
59
        return $this->responseFactory->createResponse(
60
            $this->serializeGenerator($generator::class)
61
        );
62
    }
63
64
    public function generate(
65
        GeneratorRequest $request,
66
        CodeFileWriter $codeFileWriter,
67
        CommandHydrator $commandHydrator
68
    ): ResponseInterface {
69
        $generator = $request->getGenerator();
70
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
71
        $answers = $request->getAnswers();
72
        try {
73
            $files = $generator->generate($command);
74
        } catch (InvalidGeneratorCommandException $e) {
75
            return $this->createErrorResponse($e);
76
        }
77
        $result = $codeFileWriter->write($files, $answers);
78
79
        return $this->responseFactory->createResponse(array_values($result->getResults()));
80
    }
81
82
    public function preview(
83
        GeneratorRequest $request,
84
        CommandHydrator $commandHydrator,
85
        #[Query('file')] ?string $file = null
86
    ): ResponseInterface {
87
        $generator = $request->getGenerator();
88
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
89
90
        try {
91
            $files = $generator->generate($command);
92
        } catch (InvalidGeneratorCommandException $e) {
93
            return $this->createErrorResponse($e);
94
        }
95
        if ($file === null) {
96
            return $this->responseFactory->createResponse([
97
                'files' => array_map($this->serializeCodeFile(...), array_values($files)),
98
                'operations' => CodeFileWriteOperationEnum::getLabels(),
99
            ]);
100
        }
101
102
        foreach ($files as $generatedFile) {
103
            if ($generatedFile->getId() === $file) {
104
                $content = $generatedFile->preview();
105
                return $this->responseFactory->createResponse(
106
                    ['content' => $content ?: 'Preview is not available for this file type.']
107
                );
108
            }
109
        }
110
111
        return $this->responseFactory->createResponse(
112
            ['message' => "Code file not found: $file"],
113
            Status::UNPROCESSABLE_ENTITY
114
        );
115
    }
116
117
    public function diff(
118
        GeneratorRequest $request,
119
        CommandHydrator $commandHydrator,
120
        #[Query('file')] string $file
121
    ): ResponseInterface {
122
        $generator = $request->getGenerator();
123
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
124
125
        try {
126
            $files = $generator->generate($command);
127
        } catch (InvalidGeneratorCommandException $e) {
128
            return $this->createErrorResponse($e);
129
        }
130
131
        foreach ($files as $generatedFile) {
132
            if ($generatedFile->getId() === $file) {
133
                return $this->responseFactory->createResponse(['diff' => $generatedFile->diff()]);
134
            }
135
        }
136
        return $this->responseFactory->createResponse(
137
            ['message' => "Code file not found: $file"],
138
            Status::UNPROCESSABLE_ENTITY
139
        );
140
    }
141
142
    private function createErrorResponse(InvalidGeneratorCommandException $e): DataResponse
143
    {
144
        return $this->responseFactory->createResponse(
145
            ['errors' => $e->getResult()->getErrorMessagesIndexedByProperty()],
146
            Status::UNPROCESSABLE_ENTITY
147
        );
148
    }
149
150
    private function serializeCodeFile(CodeFile $file): array
0 ignored issues
show
Unused Code introduced by
The method serializeCodeFile() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
151
    {
152
        return [
153
            'id' => $file->getId(),
154
            'content' => $file->getContent(),
155
            'operation' => $file->getOperation()->value,
156
            'state' => $file->getState()->value,
157
            'path' => $file->getPath(),
158
            'relativePath' => $file->getRelativePath(),
159
            'type' => $file->getType(),
160
            'preview' => $file->preview(),
161
        ];
162
    }
163
164
    /**
165
     * @psalm-param class-string<GeneratorCommandInterface> $generatorClass
166
     */
167
    private function serializeGenerator(string $generatorClass): array
168
    {
169
        $commandClass = $generatorClass::getCommandClass();
170
171
        $dataset = new AttributesRulesProvider($commandClass);
172
        $rules = $dataset->getRules();
173
        $dumpedRules = (new RulesDumper())->asArray($rules);
174
        $attributes = $commandClass::getAttributes();
175
        $hints = $commandClass::getHints();
176
        $labels = $commandClass::getAttributeLabels();
177
178
        $reflection = new ReflectionClass($commandClass);
179
        $constructorParameters = $reflection->getConstructor()?->getParameters() ?? [];
180
181
        $attributesResult = [];
182
        foreach ($attributes as $attributeName) {
183
            $reflectionProperty = $reflection->getProperty($attributeName);
184
            $defaultValue = $reflectionProperty->hasDefaultValue()
185
                ? $reflectionProperty->getDefaultValue()
186
                : $this->findReflectionParameter($attributeName, $constructorParameters)?->getDefaultValue();
187
            $attributesResult[$attributeName] = [
188
                'defaultValue' => $defaultValue,
189
                'hint' => $hints[$attributeName] ?? null,
190
                'label' => $labels[$attributeName] ?? null,
191
                'rules' => $dumpedRules[$attributeName] ?? [],
192
            ];
193
        }
194
195
        return [
196
            'id' => $generatorClass::getId(),
197
            'name' => $generatorClass::getName(),
198
            'description' => $generatorClass::getDescription(),
199
            'commandClass' => $commandClass,
200
            'attributes' => $attributesResult,
201
            'templates' => $this->parametersProvider->getTemplates($generatorClass::getId()),
202
        ];
203
    }
204
205
    /**
206
     * @param ReflectionParameter[] $constructorParameters
207
     */
208
    private function findReflectionParameter(string $name, array $constructorParameters): ReflectionParameter|null
209
    {
210
        foreach ($constructorParameters as $parameter) {
211
            if ($parameter->getName() === $name) {
212
                return $parameter;
213
            }
214
        }
215
        return null;
216
    }
217
}
218