Test Failed
Pull Request — master (#106)
by Alexander
17:17 queued 14:21
created

DefaultController::generate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 16
ccs 0
cts 9
cp 0
rs 9.9666
cc 2
nc 2
nop 3
crap 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\RequestModel\Attribute\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\GiiInterface;
24
use Yiisoft\Yii\Gii\ParametersProvider;
25
use Yiisoft\Yii\Gii\Request\GeneratorRequest;
26
27
final class DefaultController
28
{
29
    public function __construct(
30
        private readonly DataResponseFactoryInterface $responseFactory,
31
        private readonly ParametersProvider $parametersProvider,
32
    ) {
33
    }
34
35
    public function list(GiiInterface $gii): ResponseInterface
36
    {
37
        $generators = $gii->getGenerators();
38
39
        return $this->responseFactory->createResponse([
40
            'generators' => array_map($this->serializeGenerator(...), array_values($generators)),
41
        ]);
42
    }
43
44
    public function get(GeneratorRequest $request): ResponseInterface
45
    {
46
        $generator = $request->getGenerator();
47
48
        return $this->responseFactory->createResponse(
49
            $this->serializeGenerator($generator)
50
        );
51
    }
52
53
    public function generate(
54
        GeneratorRequest $request,
55
        CodeFileWriter $codeFileWriter,
56
        CommandHydrator $commandHydrator
57
    ): ResponseInterface {
58
        $generator = $request->getGenerator();
59
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
60
        $answers = $request->getAnswers();
61
        try {
62
            $files = $generator->generate($command);
63
        } catch (InvalidGeneratorCommandException $e) {
64
            return $this->createErrorResponse($e);
65
        }
66
        $result = $codeFileWriter->write($files, $answers);
67
68
        return $this->responseFactory->createResponse(array_values($result->getResults()));
69
    }
70
71
    public function preview(
72
        GeneratorRequest $request,
73
        CommandHydrator $commandHydrator,
74
        #[Query('file')] ?string $file = null
75
    ): ResponseInterface {
76
        $generator = $request->getGenerator();
77
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
78
79
        try {
80
            $files = $generator->generate($command);
81
        } catch (InvalidGeneratorCommandException $e) {
82
            return $this->createErrorResponse($e);
83
        }
84
        if ($file === null) {
85
            return $this->responseFactory->createResponse([
86
                'files' => array_map($this->serializeCodeFile(...), array_values($files)),
87
                'operations' => CodeFileWriteOperationEnum::getLabels(),
88
            ]);
89
        }
90
91
        foreach ($files as $generatedFile) {
92
            if ($generatedFile->getId() === $file) {
93
                $content = $generatedFile->preview();
94
                return $this->responseFactory->createResponse(
95
                    ['content' => $content ?: 'Preview is not available for this file type.']
96
                );
97
            }
98
        }
99
100
        return $this->responseFactory->createResponse(
101
            ['message' => "Code file not found: $file"],
102
            Status::UNPROCESSABLE_ENTITY
103
        );
104
    }
105
106
    public function diff(
107
        GeneratorRequest $request,
108
        CommandHydrator $commandHydrator,
109
        #[Query('file')] string $file
110
    ): ResponseInterface {
111
        $generator = $request->getGenerator();
112
        $command = $commandHydrator->hydrate($generator::getCommandClass(), $request->getBody());
113
114
        try {
115
            $files = $generator->generate($command);
116
        } catch (InvalidGeneratorCommandException $e) {
117
            return $this->createErrorResponse($e);
118
        }
119
120
        foreach ($files as $generatedFile) {
121
            if ($generatedFile->getId() === $file) {
122
                return $this->responseFactory->createResponse(['diff' => $generatedFile->diff()]);
123
            }
124
        }
125
        return $this->responseFactory->createResponse(
126
            ['message' => "Code file not found: $file"],
127
            Status::UNPROCESSABLE_ENTITY
128
        );
129
    }
130
131
    private function createErrorResponse(InvalidGeneratorCommandException $e): DataResponse
132
    {
133
        return $this->responseFactory->createResponse(
134
            ['errors' => $e->getResult()->getErrorMessagesIndexedByAttribute()],
135
            Status::UNPROCESSABLE_ENTITY
136
        );
137
    }
138
139
    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...
140
    {
141
        return [
142
            'id' => $file->getId(),
143
            'content' => $file->getContent(),
144
            'operation' => $file->getOperation()->value,
145
            'state' => $file->getState()->value,
146
            'path' => $file->getPath(),
147
            'relativePath' => $file->getRelativePath(),
148
            'type' => $file->getType(),
149
            'preview' => $file->preview(),
150
        ];
151
    }
152
153
    private function serializeGenerator(GeneratorInterface $generator): array
154
    {
155
        /**
156
         * @psalm-var class-string<GeneratorCommandInterface> $commandClass
157
         */
158
        $commandClass = $generator::getCommandClass();
159
160
        $dataset = new AttributesRulesProvider($commandClass);
161
        $rules = $dataset->getRules();
162
        $dumpedRules = (new RulesDumper())->asArray($rules);
163
        $attributes = $commandClass::getAttributes();
164
        $hints = $commandClass::getHints();
165
        $labels = $commandClass::getAttributeLabels();
166
167
        $reflection = new ReflectionClass($commandClass);
168
        $constructorParameters = $reflection->getConstructor()?->getParameters() ?? [];
169
170
        $attributesResult = [];
171
        foreach ($attributes as $attributeName) {
172
            $reflectionProperty = $reflection->getProperty($attributeName);
173
            $defaultValue = $reflectionProperty->hasDefaultValue()
174
                ? $reflectionProperty->getDefaultValue()
175
                : $this->findReflectionParameter($attributeName, $constructorParameters)?->getDefaultValue();
176
            $attributesResult[$attributeName] = [
177
                'defaultValue' => $defaultValue,
178
                'hint' => $hints[$attributeName] ?? null,
179
                'label' => $labels[$attributeName] ?? null,
180
                'rules' => $dumpedRules[$attributeName] ?? [],
181
            ];
182
        }
183
184
        return [
185
            'id' => $generator::getId(),
186
            'name' => $generator::getName(),
187
            'description' => $generator::getDescription(),
188
            'commandClass' => $commandClass,
189
            'attributes' => $attributesResult,
190
            'templates' => $this->parametersProvider->getTemplates($generator::getId()),
191
        ];
192
    }
193
194
    /**
195
     * @param ReflectionParameter[] $constructorParameters
196
     */
197
    private function findReflectionParameter(string $name, array $constructorParameters): ReflectionParameter|null
198
    {
199
        foreach ($constructorParameters as $parameter) {
200
            if ($parameter->getName() === $name) {
201
                return $parameter;
202
            }
203
        }
204
        return null;
205
    }
206
}
207