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

DefaultController::generate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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