ConvertCommand::prepareMethods()   C
last analyzed

Complexity

Conditions 7
Paths 9

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 18
nc 9
nop 1
1
<?php
2
3
namespace Realshadow\RequestDeserializer\Console\Commands\Schema;
4
5
use Illuminate\Filesystem\Filesystem;
6
use Symfony\Component\Console\Input\InputArgument;
7
8
9
/**
10
 * Command for generating request entity from provided JSON schema definition
11
 *
12
 * @package Realshadow\RequestDeserializer\Console\Commands\Schema
13
 * @author Lukáš Homza <[email protected]>
14
 */
15
class ConvertCommand extends AbstractSchemaCommand
16
{
17
18
    /**
19
     * Filesystem handler
20
     *
21
     * @var Filesystem $filesystem
22
     */
23
    private $filesystem;
24
25
    /**
26
     * Path to request entity templates
27
     *
28
     * @var string $templatePath
29
     */
30
    private $templatePath;
31
32
    /**
33
     * Default path to folder with schemas
34
     *
35
     * @var string $schemaPath
36
     */
37
    private $schemaPath;
38
39
    /**
40
     * Default path to folder where generated requests will be stored
41
     *
42
     * @var string $publishPath
43
     */
44
    private $publishPath;
45
46
    /**
47
     * Default namespace
48
     *
49
     * @var string $namespace
50
     */
51
    private $namespace;
52
53
    /**
54
     * The name and signature of the console command
55
     *
56
     * @var string
57
     */
58
    protected $signature = 'schema:convert';
59
60
    /**
61
     * The console command description
62
     *
63
     * @var string
64
     */
65
    protected $description = 'Converts JSON schema to request entity';
66
67
    /**
68
     * Maps JSON schema type to PHP internal type
69
     *
70
     * @param string[]|string $type
71
     *
72
     * @return string
73
     */
74
    private function toPhpInternalType($type)
75
    {
76
        if (is_array($type)) {
77
            $type = end($type);
78
        }
79
80
        switch ($type) {
81
            case 'integer':
82
                $type = 'int';
83
84
                break;
85
            case 'boolean':
86
                $type = 'bool';
87
88
                break;
89
            case 'double':
90
                $type = 'float';
91
92
                break;
93
        }
94
95
        return $type;
96
    }
97
98
    /**
99
     * Maps PHP internal type to JMS type
100
     *
101
     * @param string[]|string $type
102
     *
103
     * @return string
104
     */
105
    private function toJmsType($type)
106
    {
107
        if (is_array($type)) {
108
            $type = end($type);
109
        }
110
111
        switch ($type) {
112
            case 'float':
113
                $type = 'double';
114
115
                break;
116
        }
117
118
        return $type;
119
    }
120
121
    /**
122
     * Handles all properties specified in JSON schema
123
     *
124
     * @param array $schema
125
     *
126
     * @return string
127
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
128
     */
129
    private function prepareProperties(array $schema)
130
    {
131
        $template = $this->filesystem->get($this->templatePath . DIRECTORY_SEPARATOR . 'property.tpl');
132
133
        $total = count($schema['properties']);
134
135
        $i = 1;
136
        $output = '';
137
        foreach ($schema['properties'] as $property => $data) {
138
            $replace = [
139
                'schemaProperty' => $property,
140
                'property' => camel_case($property),
141
                'version' => '1.x',
142
                'phpType' => $this->toPhpInternalType($data['type']),
143
                'jmsType' => $this->toJmsType($data['type']),
144
            ];
145
146
            $output .= $this->replace($replace, $template . str_repeat(PHP_EOL, $i !== $total ? 2 : 0));
147
148
            $i++;
149
        }
150
151
        return $output;
152
    }
153
154
    /**
155
     * Handles all getter methods for properties specified in JSON schema
156
     *
157
     * @param array $schema
158
     *
159
     * @return string
160
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
161
     */
162
    private function prepareMethods(array $schema)
163
    {
164
        $template = $this->filesystem->get($this->templatePath . DIRECTORY_SEPARATOR . 'method.tpl');
165
166
        $total = count($schema['properties']);
167
168
        $i = 1;
169
        $output = '';
170
        foreach ($schema['properties'] as $property => $data) {
171
            $isNullable = true;
172
            if (isset($schema['required']) && in_array($property, $schema['required'], true)) {
173
                $isNullable = false;
174
            }
175
176
            $type = $this->toPhpInternalType($data['type']);
177
178
            $replace = [
179
                'property' => camel_case($property),
180
                'method' => studly_case($property),
181
                'phpType' => ($isNullable ? 'null|' : '') . $type,
182
                'returnType' => ($isNullable ? '?' : '') . $type,
183
            ];
184
185
            $output .= $this->replace($replace, $template . str_repeat(PHP_EOL, $i !== $total ? 2 : 0));
186
187
            $i++;
188
        }
189
190
        return $output;
191
    }
192
193
    /**
194
     * Replaces data in templates to actual code
195
     *
196
     * @param array $what
197
     * @param string $template
198
     *
199
     * @return string
200
     */
201
    private function replace(array $what, $template)
202
    {
203
        foreach ($what as $property => $value) {
204
            $template = str_replace(static::PATTERN_PREFIX . $property . static::PATTERN_SUFFIX, $value, $template);
205
        }
206
207
        return $template;
208
    }
209
210
    /**
211
     * @inheritdoc
212
     */
213
    public function __construct(Filesystem $filesystem)
214
    {
215
        parent::__construct();
216
217
        $this->filesystem = $filesystem;
218
219
        $this->templatePath = $this->path(__DIR__, '..', '..', '..', '..', 'resources', 'templates', 'request');
220
        $this->schemaPath = $this->config('request.schema_path');
221
        $this->publishPath = $this->config('request.publish_path');
222
        $this->namespace = $this->config('request.namespace');
223
224
        $this->addArgument('schema', InputArgument::REQUIRED, 'Path JSON schema');
225
        $this->addArgument('request', InputArgument::REQUIRED, 'Path to resulting request object');
226
    }
227
228
    /**
229
     * Converts JSON schema to request entity
230
     *
231
     * @throws \InvalidArgumentException
232
     * @throws \UnexpectedValueException
233
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
234
     */
235
    public function handle()
236
    {
237
        $schemaPath = $this->path($this->schemaPath, $this->argument('schema'));
238
239
        $requestPath = $this->path($this->publishPath, $this->argument('request'));
240
        $className = pathinfo($requestPath, PATHINFO_FILENAME);
241
242
        if ( ! $this->filesystem->exists($schemaPath)) {
243
            throw new \InvalidArgumentException('Provided JSON schema does not exist.');
244
        }
245
246
        $schema = json_decode($this->filesystem->get($schemaPath), true);
247
        if ( ! isset($schema['properties'])) {
248
            throw new \UnexpectedValueException('Provided JSON schema does not have properties.');
249
        }
250
251
        $this->info('Preparing new <options=bold>' . $className . '</> class');
252
253
        $template = $this->filesystem->get(
254
            $this->path($this->templatePath, 'class.tpl')
255
        );
256
257
        $data = [
258
            'className' => $className,
259
            'namespace' => $this->namespace,
260
            'schemaPath' => app()->environment() === 'testing' ? '' : $schemaPath,
261
            'properties' => $this->prepareProperties($schema),
262
            'methods' => $this->prepareMethods($schema),
263
        ];
264
265
        if ( ! $this->filesystem->exists($this->publishPath)) {
266
            $this->filesystem->makeDirectory($this->publishPath, 0755, true);
267
        }
268
269
        $this->filesystem->put(
270
            $requestPath,
271
            $this->replace($data, $template)
272
        );
273
    }
274
275
}
276