Passed
Pull Request — master (#58)
by Dmitriy
02:35
created

DefaultController::diff()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
c 1
b 0
f 0
dl 0
loc 20
ccs 0
cts 11
cp 0
rs 9.8666
cc 4
nc 4
nop 2
crap 20
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\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 DataResponseFactoryInterface $responseFactory,
31
        private 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)),
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected ')' on line 40 at column 67
Loading history...
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
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