|
1 | | -<?php |
2 | | -/** |
3 | | - * Copyright © Magento, Inc. All rights reserved. |
4 | | - * See COPYING.txt for license details. |
5 | | - */ |
6 | | - |
7 | | -declare(strict_types=1); |
8 | | - |
9 | | -namespace Magento\CatalogExport\Console\Command; |
10 | | - |
11 | | -use Magento\Framework\Console\Cli; |
12 | | -use Magento\Framework\Exception\FileSystemException; |
13 | | -use Magento\Framework\Exception\LocalizedException; |
14 | | -use Magento\Framework\Exception\RuntimeException; |
15 | | -use Magento\Framework\Filesystem\Driver\File; |
16 | | -use Magento\Framework\Phrase; |
17 | | -use Magento\Framework\Xml\Parser; |
18 | | -use Nette\PhpGenerator\PhpFile; |
19 | | -use Nette\PhpGenerator\PsrPrinter; |
20 | | -use Symfony\Component\Console\Command\Command; |
21 | | -use Symfony\Component\Console\Input\InputInterface; |
22 | | -use Symfony\Component\Console\Input\InputOption; |
23 | | -use Symfony\Component\Console\Output\OutputInterface; |
24 | | -use Magento\DataExporter\Config\ConfigInterface; |
25 | | - |
26 | | -/** |
27 | | - * Class Generate |
28 | | - * |
29 | | - * Nette is required for this module - 'composer require nette/php-generator' |
30 | | - * |
31 | | - * A destination folder is required to define where the files will be generated. |
32 | | - * |
33 | | - * php bin/magento dto:generate --destination-folder |
34 | | - * /var/www/commerce-data-export/app/code/Magento/CatalogExportApi/Api/Data |
35 | | - * |
36 | | - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
37 | | - * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
38 | | - * @SuppressWarnings(PHPMD.NPathComplexity) |
39 | | - * |
40 | | - */ |
41 | | -class GenerateDTOFiles extends Command |
42 | | -{ |
43 | | - /** |
44 | | - * Command Option |
45 | | - * @var string |
46 | | - */ |
47 | | - private const DESTINATION_FOLDER = 'destination-folder'; |
48 | | - |
49 | | - /** |
50 | | - * @var Parser |
51 | | - */ |
52 | | - private $parser; |
53 | | - |
54 | | - /** |
55 | | - * @var File |
56 | | - */ |
57 | | - private $fileDriver; |
58 | | - |
59 | | - /** |
60 | | - * @var ConfigInterface |
61 | | - */ |
62 | | - private $config; |
63 | | - |
64 | | - /** |
65 | | - * @var string[] |
66 | | - */ |
67 | | - private $baseConfigEntities = [ |
68 | | - 'Product', |
69 | | - 'Category', |
70 | | - 'ProductVariant', |
71 | | - 'Review', |
72 | | - 'RatingMetadata', |
73 | | - ]; |
74 | | - |
75 | | - /** |
76 | | - * @param Parser $parser |
77 | | - * @param File $fileDriver |
78 | | - * @param ConfigInterface $config |
79 | | - * @param $string|null $name |
80 | | - */ |
81 | | - public function __construct( |
82 | | - Parser $parser, |
83 | | - File $fileDriver, |
84 | | - ConfigInterface $config, |
85 | | - $name = null |
86 | | - ) { |
87 | | - parent::__construct($name); |
88 | | - $this->parser = $parser; |
89 | | - $this->fileDriver = $fileDriver; |
90 | | - $this->config = $config; |
91 | | - } |
92 | | - |
93 | | - /** |
94 | | - * @inheritdoc |
95 | | - */ |
96 | | - protected function configure() |
97 | | - { |
98 | | - $this->setName('dto:generate'); |
99 | | - $this->setDescription( |
100 | | - 'This will generate the provider class for a module or file. A output path must be defined.' |
101 | | - ); |
102 | | - $this->addOption( |
103 | | - self::DESTINATION_FOLDER, |
104 | | - null, |
105 | | - InputOption::VALUE_REQUIRED, |
106 | | - __('Destination Folder') |
107 | | - ); |
108 | | - parent::configure(); |
109 | | - } |
110 | | - |
111 | | - /** |
112 | | - * Generate classes |
113 | | - * |
114 | | - * @param InputInterface $input |
115 | | - * @param OutputInterface $output |
116 | | - * @throws LocalizedException |
117 | | - * @throws RuntimeException |
118 | | - * @throws FileSystemException |
119 | | - * @return int|void |
120 | | - */ |
121 | | - protected function execute(InputInterface $input, OutputInterface $output): ?int |
122 | | - { |
123 | | - if (!$outPutLocation = $input->getOption(self::DESTINATION_FOLDER)) { |
124 | | - throw new RuntimeException(new Phrase('Destination not defined')); |
125 | | - } |
126 | | - |
127 | | - $baseNamespace = $this->resolveNameSpace($outPutLocation); |
128 | | - $parsedEntities = []; |
129 | | - foreach ($this->baseConfigEntities as $node) { |
130 | | - $parsedEntities[] = $this->getConfig($node); |
131 | | - } |
132 | | - $parsedArray = \array_merge(...$parsedEntities); |
133 | | - |
134 | | - $classesData = $this->prepareDtoClassData($parsedArray, $baseNamespace); |
135 | | - |
136 | | - $this->createDirectory($outPutLocation); |
137 | | - try { |
138 | | - $this->generateFiles($classesData, $baseNamespace, $outPutLocation); |
139 | | - } catch (\Throwable $e) { |
140 | | - $output->writeln('Error: ' . $e->getMessage()); |
141 | | - return Cli::RETURN_FAILURE; |
142 | | - } |
143 | | - $output->writeln('Files has been generated'); |
144 | | - |
145 | | - return Cli::RETURN_SUCCESS; |
146 | | - } |
147 | | - |
148 | | - /** |
149 | | - * Return entities config. |
150 | | - * |
151 | | - * @param string $entity |
152 | | - * @param array $parsedArray |
153 | | - * @return array |
154 | | - */ |
155 | | - private function getConfig(string $entity, $parsedArray = []) |
156 | | - { |
157 | | - $parsedEntity = $this->config->get($entity); |
158 | | - if ($parsedEntity) { |
159 | | - $parsedArray[$entity] = $parsedEntity; |
160 | | - |
161 | | - foreach ($parsedEntity['field'] as $field) { |
162 | | - if (!$this->config->isScalar($field['type'])) { |
163 | | - $parsedArray = $this->getConfig($field['type'], $parsedArray); |
164 | | - } |
165 | | - } |
166 | | - } |
167 | | - return $parsedArray; |
168 | | - } |
169 | | - |
170 | | - /** |
171 | | - * Build structure required to build DTO |
172 | | - * |
173 | | - * @param array $parsedArray |
174 | | - * @param string $baseNamespace |
175 | | - * @return array |
176 | | - */ |
177 | | - private function prepareDtoClassData(array $parsedArray, string $baseNamespace) |
178 | | - { |
179 | | - $result = []; |
180 | | - if (empty($parsedArray)) { |
181 | | - return $result; |
182 | | - } |
183 | | - |
184 | | - foreach ($parsedArray as $schemaConfig) { |
185 | | - foreach ($schemaConfig['field'] as &$field) { |
186 | | - $field['type'] = $this->mapType($field['type'], $baseNamespace); |
187 | | - $field['name'] = lcfirst(str_replace('_', '', ucwords($field['name'], '_'))); |
188 | | - } |
189 | | - |
190 | | - $result[$schemaConfig['name']] = $schemaConfig['field']; |
191 | | - } |
192 | | - |
193 | | - return $result; |
194 | | - } |
195 | | - |
196 | | - /** |
197 | | - * Resolve namespace |
198 | | - * |
199 | | - * @param string $filePath |
200 | | - * @return string |
201 | | - */ |
202 | | - private function resolveNameSpace(string $filePath): string |
203 | | - { |
204 | | - $filePath = trim($filePath, DIRECTORY_SEPARATOR); |
205 | | - return str_replace('/', '\\', strstr($filePath, 'Magento')); |
206 | | - } |
207 | | - |
208 | | - /** |
209 | | - * Map type |
210 | | - * |
211 | | - * @param string $type |
212 | | - * @param string $baseNameSpace |
213 | | - * @return string |
214 | | - */ |
215 | | - private function mapType(string $type, string $baseNameSpace): string |
216 | | - { |
217 | | - switch ($type) { |
218 | | - case 'Int': |
219 | | - $type = 'int'; |
220 | | - break; |
221 | | - case 'ID': |
222 | | - case 'String': |
223 | | - $type = 'string'; |
224 | | - break; |
225 | | - case 'Boolean': |
226 | | - $type = 'bool'; |
227 | | - break; |
228 | | - case 'Float': |
229 | | - $type = 'float'; |
230 | | - break; |
231 | | - default: |
232 | | - $type = '\\' . $baseNameSpace . '\\' . $type . '[]|null'; |
233 | | - } |
234 | | - |
235 | | - return $type; |
236 | | - } |
237 | | - |
238 | | - /** |
239 | | - * Generate files |
240 | | - * |
241 | | - * @param array $generateArray |
242 | | - * @param string $baseNameSpace |
243 | | - * @param string $baseFileLocation |
244 | | - * @throws FileSystemException |
245 | | - * @return void |
246 | | - */ |
247 | | - private function generateFiles(array $generateArray, string $baseNameSpace, string $baseFileLocation): void |
248 | | - { |
249 | | - $nonRequiredMethods = ['id']; |
250 | | - foreach ($generateArray as $className => $phpClassFields) { |
251 | | - // phpstan:ignore "Class Nette\PhpGenerator\PhpFile not found." |
252 | | - $file = new PhpFile(); |
253 | | - $file->addComment('Copyright © Magento, Inc. All rights reserved.'); |
254 | | - $file->addComment('See COPYING.txt for license details.'); |
255 | | - $file->addComment(''); |
256 | | - $file->addComment('Generated from et_schema.xml. DO NOT EDIT!'); |
257 | | - $file->setStrictTypes(); |
258 | | - $namespace = $file->addNamespace($baseNameSpace); |
259 | | - $class = $namespace->addClass($className); |
260 | | - $class->addComment($className . ' entity'); |
261 | | - $class->addComment(''); |
262 | | - $class->addComment('phpcs:disable Magento2.PHP.FinalImplementation'); |
263 | | - $class->addComment('@SuppressWarnings(PHPMD.BooleanGetMethodName)'); |
264 | | - $class->addComment('@SuppressWarnings(PHPMD.TooManyFields)'); |
265 | | - $class->addComment('@SuppressWarnings(PHPMD.ExcessivePublicCount)'); |
266 | | - $class->addComment('@SuppressWarnings(PHPMD.ExcessiveClassComplexity)'); |
267 | | - $class->addComment('@SuppressWarnings(PHPMD.CouplingBetweenObjects)'); |
268 | | - foreach ($phpClassFields as $field) { |
269 | | - $repeated = $field['repeated']; |
270 | | - $name = $field['name']; |
271 | | - $type = $field['type']; |
272 | | - |
273 | | - $commentName = preg_replace('/(?<!\ )[A-Z]/', ' $0', $field['name']); |
274 | | - $property = $class->addProperty($field['name'])->setPrivate(); |
275 | | - |
276 | | - if (true === $repeated) { |
277 | | - if (substr($type, -4) !== 'null') { |
278 | | - $property->addComment('@var ' . 'array'); |
279 | | - } else { |
280 | | - $property->addComment('@var ' . $type); |
281 | | - } |
282 | | - } else { |
283 | | - $property->addComment('@var ' . str_replace('[]|null', '', $type)); |
284 | | - } |
285 | | - $method = $class->addMethod('get' . ucfirst($name)); |
286 | | - $method->addComment('Get ' . strtolower($commentName)); |
287 | | - $method->addComment(''); |
288 | | - if (true === $repeated) { |
289 | | - if (substr($type, -4) !== 'null') { |
290 | | - $method->addComment('@return ' . $type . '[]'); |
291 | | - } else { |
292 | | - $method->addComment('@return ' . $type); |
293 | | - } |
294 | | - } else { |
295 | | - $method->addComment('@return ' . str_replace('[]|null', '', $type)); |
296 | | - } |
297 | | - if (true === $repeated) { |
298 | | - $method->setReturnType('array'); |
299 | | - } else { |
300 | | - $method->setReturnType(str_replace('[]|null', '', $type)); |
301 | | - } |
302 | | - if (!in_array($name, $nonRequiredMethods)) { |
303 | | - $method->setReturnNullable(); |
304 | | - } |
305 | | - $method->addBody('return $this->' . $name . ';'); |
306 | | - $method = $class->addMethod('set' . ucfirst($name)); |
307 | | - $method->addComment('Set ' . strtolower($commentName)); |
308 | | - $method->addComment(''); |
309 | | - if (true === $repeated) { |
310 | | - $method->addComment( |
311 | | - '@param ' . str_replace('[]|null', '', $type) . '[] $' . $name |
312 | | - ); |
313 | | - } else { |
314 | | - $method->addComment( |
315 | | - '@param ' . str_replace('[]|null', '', $type) . ' $' . $name |
316 | | - ); |
317 | | - } |
318 | | - $method->addComment('@return void'); |
319 | | - if (true === $repeated) { |
320 | | - if (!in_array($name, $nonRequiredMethods)) { |
321 | | - $method->addParameter($name, null)->setType('array')->setNullable(); |
322 | | - } else { |
323 | | - $method->addParameter($name, null)->setType('array'); |
324 | | - } |
325 | | - } else { |
326 | | - if (!in_array($name, $nonRequiredMethods)) { |
327 | | - $method->addParameter($name) |
328 | | - ->setType(str_replace('[]|null', '', $type)) |
329 | | - ->setNullable(); |
330 | | - } else { |
331 | | - $method->addParameter($name)->setType(str_replace('[]|null', '', $type)); |
332 | | - } |
333 | | - } |
334 | | - $method->setReturnType('void'); |
335 | | - $method->addBody('$this->' . $name . ' = $' . $name . ';'); |
336 | | - } |
337 | | - // phpstan:ignore "Class Nette\PhpGenerator\PsrPrinter not found." |
338 | | - $print = new PsrPrinter(); |
339 | | - $this->writeToFile($baseFileLocation . '/' . $className . '.php', $print->printFile($file)); |
340 | | - } |
341 | | - } |
342 | | - |
343 | | - /** |
344 | | - * Create directory |
345 | | - * |
346 | | - * @param string $outPutLocation |
347 | | - * @throws FileSystemException |
348 | | - * @return void |
349 | | - */ |
350 | | - private function createDirectory(string $outPutLocation): void |
351 | | - { |
352 | | - if (!$this->fileDriver->isExists($outPutLocation)) { |
353 | | - $this->fileDriver->createDirectory($outPutLocation, 0755); |
354 | | - } |
355 | | - } |
356 | | - |
357 | | - /** |
358 | | - * Write to file |
359 | | - * |
360 | | - * @param string $fileLocation |
361 | | - * @param string $output |
362 | | - * @throws FileSystemException |
363 | | - * @return void |
364 | | - */ |
365 | | - private function writeToFile(string $fileLocation, string $output): void |
366 | | - { |
367 | | - $resource = $this->fileDriver->fileOpen($fileLocation, 'w'); |
368 | | - $this->fileDriver->fileWrite($resource, $output); |
369 | | - $this->fileDriver->fileClose($resource); |
370 | | - } |
371 | | -} |
0 commit comments