From 988f9eca267461cca6641899b4f1d211787f8864 Mon Sep 17 00:00:00 2001 From: jb cr <51637606+jbcr@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:39:22 +0100 Subject: [PATCH 1/2] improve convert --- README.md | 2 +- bin/config-converter | 30 +++++++++-- composer.json | 3 +- src/ClassConfigConverter.php | 101 +++++++++++++++++++++++------------ src/FieldConverter.php | 7 +-- src/FilterConverter.php | 20 ++++++- src/GridBuilderCalls.php | 67 +++++++++++++++-------- 7 files changed, 163 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index e4d01b1..4d5dc29 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Converts a yaml configuration file from the [SyliusGridBundle](https://github.co ## How to use: * Clone the repository * `composer install` -* `bin/config-converter [namespace] [--functional]` +* `bin/config-converter [namespace] [--functional] [--output-directory ] [-q]` This will print the generated code out to the screen and produce a file that contains the new configuration. diff --git a/bin/config-converter b/bin/config-converter index e50405c..5c0d37a 100755 --- a/bin/config-converter +++ b/bin/config-converter @@ -3,15 +3,17 @@ use Mamazu\ConfigConverter\ClassConfigConverter; -require_once __DIR__."/../vendor/autoload.php"; -require_once __DIR__."/../src/ClassConfigConverter.php"; -require_once __DIR__."/../src/GridBuilderCalls.php"; +require_once __DIR__ . "/../vendor/autoload.php"; +require_once __DIR__ . "/../src/ClassConfigConverter.php"; +require_once __DIR__ . "/../src/GridBuilderCalls.php"; if (count($argv) < 2) { echo "Usage: bin/config-transformer [grid namespace]\n"; echo "\t: must be in the yaml / yml format\n"; echo "\t[grid namespace]: (optional) Namespace of the grid class that is generated"; - echo "\t[--function]: (optional) Generating the functional version of it"; + echo "\t[--output-directory ]: (optional) Save the file in directory"; + echo "\t[--functional]: (optional) Generating the functional version of it"; + echo "\t[-q]: (optional) Quiet"; echo "\n\n"; echo "Example: bin/config-transformer order.yaml \"App\\Env\"\n"; die(1); @@ -35,4 +37,22 @@ if (in_array('--functional', $argv, true)) { $configConverter->setFunctional(); } -$configConverter->convert($argv[1]); +$outputDirectory = ''; +if (in_array('--output-directory', $argv, true)) { + $key = array_search('--output-directory', $argv, true); + if (array_key_exists($key + 1, $argv) === false) { + echo 'Please add the output directory after the --output-directory argument'; + die(1); + } + $outputDirectory = $argv[$key + 1]; + if ( + is_dir($outputDirectory) === false + && mkdir($outputDirectory, 0777, true) + && is_dir($outputDirectory) === false + ) { + echo 'Unable to create the directory defined by "--output-directory" argument: "'.$outputDirectory.'"'; + die(1); + } +} + +$configConverter->convert($argv[1], $outputDirectory); diff --git a/composer.json b/composer.json index e107eff..5d776a3 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,9 @@ { "require": { + "php": ">=7.4", "symplify/php-config-printer": "^9.4.70", "symfony/yaml": "^v5.4.14", - "sylius/grid-bundle": "dev-master", + "sylius/grid-bundle": "1.12.x-dev", "phpunit/phpunit": "^9.5.26" }, "autoload": { diff --git a/src/ClassConfigConverter.php b/src/ClassConfigConverter.php index ce8adb9..6d239e6 100644 --- a/src/ClassConfigConverter.php +++ b/src/ClassConfigConverter.php @@ -3,42 +3,44 @@ namespace Mamazu\ConfigConverter; use InvalidArgumentException; +use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; +use PhpParser\Node\Param; +use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; -use PhpParser\PrettyPrinter\Standard; -use Sylius\Bundle\GridBundle\Builder\ActionGroup\SubItemActionGroup; +use PhpParser\Node\Stmt\Throw_; +use PhpParser\Node\Stmt\Use_; +use PhpParser\Node\Stmt\UseUse; use Sylius\Bundle\GridBundle\Builder\Action\Action; +use Sylius\Bundle\GridBundle\Builder\Action\CreateAction; use Sylius\Bundle\GridBundle\Builder\Action\DeleteAction; use Sylius\Bundle\GridBundle\Builder\Action\ShowAction; use Sylius\Bundle\GridBundle\Builder\Action\UpdateAction; -use Sylius\Bundle\GridBundle\Builder\Action\CreateAction; -use Sylius\Bundle\GridBundle\Builder\Field\Field; -use Sylius\Bundle\GridBundle\Builder\Filter\Filter; -use Sylius\Bundle\GridBundle\Builder\GridBuilder; -use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface; -use Sylius\Bundle\GridBundle\Builder\ActionGroup\MainActionGroup; -use Sylius\Bundle\GridBundle\Builder\ActionGroup\ItemActionGroup; use Sylius\Bundle\GridBundle\Builder\ActionGroup\BulkActionGroup; +use Sylius\Bundle\GridBundle\Builder\ActionGroup\ItemActionGroup; +use Sylius\Bundle\GridBundle\Builder\ActionGroup\MainActionGroup; +use Sylius\Bundle\GridBundle\Builder\ActionGroup\SubItemActionGroup; use Sylius\Bundle\GridBundle\Builder\Field\DateTimeField; +use Sylius\Bundle\GridBundle\Builder\Field\Field; use Sylius\Bundle\GridBundle\Builder\Field\StringField; use Sylius\Bundle\GridBundle\Builder\Field\TwigField; -use PhpParser\Node; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; -use PhpParser\Node\Param; -use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Use_; -use PhpParser\Node\Stmt\UseUse; +use Sylius\Bundle\GridBundle\Builder\Filter\Filter; +use Sylius\Bundle\GridBundle\Builder\GridBuilder; +use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface; use Sylius\Bundle\GridBundle\Config\GridConfig; use Sylius\Bundle\GridBundle\Grid\AbstractGrid; +use Sylius\Bundle\GridBundle\Grid\ResourceAwareGridInterface; use Symfony\Component\Yaml\Yaml; class ClassConfigConverter { + const NO_CLASS = 'To be replaced with the correct class.'; private ?string $namespace = null; private bool $functional = false; private bool $verbose = true; @@ -66,7 +68,7 @@ public function makeQuiet(): void $this->verbose = false; } - public function convert(string $fileName): void + public function convert(string $fileName, string $outputDirectory): void { $allGrids = Yaml::parse(file_get_contents($fileName))['sylius_grid']['grids'] ?? []; @@ -84,23 +86,33 @@ public function convert(string $fileName): void * Functional mode: filename = name of the grid */ foreach ($allGrids as $gridName => $gridConfiguration) { - [$className, $phpCode] = $this->handleGrid($gridName, $gridConfiguration); + echo "************* PROCESS GRID $gridName *************" . PHP_EOL; + try { + [$className, $phpCode] = $this->handleGrid($gridName, $gridConfiguration); + } catch (\Throwable $e) { + printf('EXCEPTION "%s" on grid "%s": %s', get_class($e), $gridName, $e->getMessage()); + continue; + } $new_content = $this->codeOutputter->printCode($phpCode); - $newFileName = $className.'.php'; - if($this->verbose) { - echo "==============================$newFileName================".PHP_EOL; + $newFileName = $className . '.php'; + if ($this->verbose) { + echo "==============================$newFileName================" . PHP_EOL; echo $new_content; } else { - echo "Writing the content to ".realpath($newFileName); + echo "Writing the content to " . realpath($outputDirectory . DIRECTORY_SEPARATOR . $newFileName) . PHP_EOL; } - file_put_contents($newFileName, $new_content); + file_put_contents($outputDirectory . DIRECTORY_SEPARATOR . $newFileName, $new_content); } } public function handleGrid(string $gridName, array $gridConfiguration): array { + $phpNodes = []; + $phpNodes[] = new Node\Stmt\Declare_([ + new Node\Stmt\DeclareDeclare(new Identifier('strict_types'), new Node\Scalar\LNumber(1)) + ]); if ($this->namespace) { $phpNodes[] = new Node\Stmt\Namespace_(new Name($this->namespace)); } @@ -123,27 +135,35 @@ public function handleGrid(string $gridName, array $gridConfiguration): array DateTimeField::class, StringField::class, TwigField::class, + ResourceAwareGridInterface::class, ])); - $resourceClass = $gridConfiguration['driver']['options']['class'] ?? 'To be replaced with the correct class.'; - + $resourceClass = $gridConfiguration['driver']['options']['class'] ?? self::NO_CLASS; + $isClassName = false; if (strpos($resourceClass, '%') !== false || strpos($resourceClass, 'expr:param') !== false) { - trigger_error('You are using the parameter based syntax which does not work. Either provide a class directly in the getResourceClass or pass it by the constructor.', E_USER_WARNING); + trigger_error('You are using the parameter based syntax which does not work. Either provide a class directly in the getResourceClass or pass it by the constructor.', + E_USER_WARNING); + } elseif ($resourceClass !== self::NO_CLASS) { + $phpNodes[] = new Use_([new UseUse(new Name($resourceClass))]); + $isClassName = true; + $part = explode('\\', $resourceClass); + $resourceClass = end($part); } if (!$this->functional) { $className = ucfirst(preg_replace_callback('#_\w#', static fn($a) => strtoupper($a[0][1]), $gridName)); + $phpNodes[] = new Class_( new Identifier($className), [ 'extends' => new Identifier('AbstractGrid'), + 'implements' => [new Identifier('ResourceAwareGridInterface')], 'stmts' => [ $this->createStaticFunction('getName', $gridName), $this->createGridBuildFunction($gridConfiguration), - $this->createStaticFunction('getResourceClass', $resourceClass), + $this->createResourceFunction($resourceClass, $isClassName), ], - ], - [ + ] ); } else { $className = $gridName; @@ -170,7 +190,6 @@ private function createStaticFunction(string $functionName, string $returnValue) public function createGridBuildFunction(array $configuration): Node { $gridBuilder = new Variable('gridBuilder'); - return new ClassMethod( new Identifier('buildGrid'), [ @@ -219,4 +238,20 @@ private function generateUseStatements(array $useStatement): array ); } + private function createResourceFunction(string $returnValue, bool $isClassName): Node + { + $expr = $isClassName ? new Expr\ConstFetch(new Name($returnValue . '::class')) : new String_($returnValue); + return new ClassMethod( + new Identifier('getResourceClass'), + [ + 'flags' => Class_::MODIFIER_PUBLIC, + 'returnType' => 'string', + 'stmts' => [ + $returnValue === self::NO_CLASS ? new Throw_(new Expr\New_(new Name('\InvalidArgumentException'), + ['arguments' => $expr])) : new Return_($expr), + ], + ] + ); + } + } diff --git a/src/FieldConverter.php b/src/FieldConverter.php index ff27cd8..56273b2 100644 --- a/src/FieldConverter.php +++ b/src/FieldConverter.php @@ -47,6 +47,7 @@ public function convertField(Expr $gridBuilder, string $fieldName, array $fieldC // Only add the options if the value is not empty. This can happen for the twig field for example. The template is now // part of the create call and not an option anymore if (isset($fieldConfig['options'])) { + unset($fieldConfig['options']['template']); if (count($fieldConfig['options']) > 0) { $field = new MethodCall($field, 'setOptions', [$this->convertValue($fieldConfig['options'])]); } @@ -60,7 +61,7 @@ public function convertField(Expr $gridBuilder, string $fieldName, array $fieldC private function createField(array $fieldConfig, string $fieldName): Expr { - switch ($fieldConfig['type']) { + switch ($fieldConfig['type']??'string') { case 'datetime': $field = new StaticCall(new Name('DateTimeField'), 'create', [ $this->convertValue($fieldName), @@ -80,9 +81,9 @@ private function createField(array $fieldConfig, string $fieldName): Expr default: $field = new StaticCall(new Name('Field'), 'create', [ $this->convertValue($fieldName), - $this->convertValue($fieldConfig['type']), + $this->convertValue($fieldConfig['type']??'string'), ]); } return $field; } -} \ No newline at end of file +} diff --git a/src/FilterConverter.php b/src/FilterConverter.php index 2919aee..f53fac9 100644 --- a/src/FilterConverter.php +++ b/src/FilterConverter.php @@ -7,6 +7,7 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; +use Sylius\Bundle\GridBundle\Builder\Filter\Filter; class FilterConverter { @@ -25,9 +26,24 @@ public function convertFilter(Expr $gridBuilder, string $filterName, array $conf $this->convertToFunctionCall($filter, $configuration, 'label'); $this->convertToFunctionCall($filter, $configuration, 'options'); $this->convertToFunctionCall($filter, $configuration, 'form_options'); - + if (\array_key_exists('default_value', $configuration)) { + if (method_exists(Filter::class, 'setDefaultValue') === false) { + trigger_error(sprintf( + 'The "%s" class dont have the "%s" method. Please use GridBundle 1.13+. This option is lost in convertion.', + Filter::class, + 'setDefaultValue' + )); + } else { + $filter = new MethodCall( + $filter, + 'setDefaultValue', + [$this->convertValue($configuration['default_value'])] + ); + } + unset($configuration['default_value']); + } $this->checkUnconsumedConfiguration('filter', $configuration); return new MethodCall($gridBuilder, 'addFilter', [new Arg($filter)]); } -} \ No newline at end of file +} diff --git a/src/GridBuilderCalls.php b/src/GridBuilderCalls.php index 3d13346..fe7f64d 100644 --- a/src/GridBuilderCalls.php +++ b/src/GridBuilderCalls.php @@ -8,6 +8,7 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Throw_; class GridBuilderCalls { @@ -23,7 +24,7 @@ public function __construct() $this->fieldConverter = new FieldConverter(); } - public function getGridBuilderBody(Expr $gridBuilder, array $gridConfiguration): Expression + public function getGridBuilderBody(Expr $gridBuilder, array $gridConfiguration): Node\Stmt { if (array_key_exists('driver', $gridConfiguration)) { $gridBuilder = $this->handleDriver($gridBuilder, $gridConfiguration['driver']); @@ -69,31 +70,41 @@ public function getGridBuilderBody(Expr $gridBuilder, array $gridConfiguration): // Handle actions if (array_key_exists('actions', $gridConfiguration)) { - foreach ($gridConfiguration['actions'] as $type => $configuredTypes) { - $mappings = [ - 'main' => 'MainActionGroup', - 'item' => 'ItemActionGroup', - 'subitem' => 'SubItemActionGroup', - 'bulk' => 'BulkActionGroup', - ]; - - $gridBuilder = new MethodCall( - $gridBuilder, - 'addActionGroup', - [ - new Arg(new Node\Expr\StaticCall( - new Name($mappings[$type]), - 'create', - $this->convertActionsToFunctionParameters($configuredTypes) - )), - ] - ); + if ($gridConfiguration['actions'] instanceof \iterable) { + foreach ($gridConfiguration['actions'] as $type => $configuredTypes) { + $mappings = [ + 'main' => 'MainActionGroup', + 'item' => 'ItemActionGroup', + 'subitem' => 'SubItemActionGroup', + 'bulk' => 'BulkActionGroup', + ]; + + $gridBuilder = new MethodCall( + $gridBuilder, + 'addActionGroup', + [ + new Arg(new Node\Expr\StaticCall( + new Name($mappings[$type]), + 'create', + $this->convertActionsToFunctionParameters($configuredTypes) + )), + ] + ); + } } unset($gridConfiguration['actions']); } $this->checkUnconsumedConfiguration('.', $gridConfiguration); + if ($gridBuilder instanceof Expr\Variable && $gridBuilder->name === 'gridBuilder') { + return new Throw_( + new Expr\New_(new Name( + '\InvalidArgumentException'), + ['arguments' => new Node\Scalar\String_('No configuration for this grid')] + ) + ); + } return new Expression($gridBuilder); } @@ -140,17 +151,29 @@ public function convertActionsToFunctionParameters(array $actions): array private function handleDriver(Expr $gridBuilder, array $driverConfiguration): Expr { if (array_key_exists('name', $driverConfiguration)) { - $gridBuilder = new MethodCall($gridBuilder, 'setDriver', [$this->convertValue($driverConfiguration['name'])]); + if ($driverConfiguration['name'] !== 'doctrine/orm') { + $gridBuilder = new MethodCall( + $gridBuilder, + 'setDriver', + [$this->convertValue($driverConfiguration['name'])] + ); + } unset($driverConfiguration['name']); } if (array_key_exists('repository', $driverConfiguration['options'])) { - $gridBuilder = $this->handleRepositoryConfiguration($gridBuilder, $driverConfiguration['options']['repository']); + $gridBuilder = $this->handleRepositoryConfiguration( + $gridBuilder, + $driverConfiguration['options']['repository'] + ); unset($driverConfiguration['options']['repository']); } if (array_key_exists('options', $driverConfiguration)) { foreach ($driverConfiguration['options'] as $option => $optionValue) { + if ($option === 'class') { + continue; + } $gridBuilder = new MethodCall($gridBuilder, 'setDriverOption', [ $this->convertValue($option), $this->convertValue($optionValue) From c100af076ba54acc83755c569ba1b57b05a93b0f Mon Sep 17 00:00:00 2001 From: jb cr <51637606+jbcr@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:13:49 +0100 Subject: [PATCH 2/2] review change --- bin/config-converter | 2 +- src/ClassConfigConverter.php | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/config-converter b/bin/config-converter index 5c0d37a..4ef0a77 100755 --- a/bin/config-converter +++ b/bin/config-converter @@ -37,7 +37,7 @@ if (in_array('--functional', $argv, true)) { $configConverter->setFunctional(); } -$outputDirectory = ''; +$outputDirectory = getcwd(); if (in_array('--output-directory', $argv, true)) { $key = array_search('--output-directory', $argv, true); if (array_key_exists($key + 1, $argv) === false) { diff --git a/src/ClassConfigConverter.php b/src/ClassConfigConverter.php index 6d239e6..6beff6e 100644 --- a/src/ClassConfigConverter.php +++ b/src/ClassConfigConverter.php @@ -96,13 +96,14 @@ public function convert(string $fileName, string $outputDirectory): void $new_content = $this->codeOutputter->printCode($phpCode); $newFileName = $className . '.php'; + $newFilePath = realpath($outputDirectory . DIRECTORY_SEPARATOR . $newFileName); if ($this->verbose) { - echo "==============================$newFileName================" . PHP_EOL; + echo "==============================$newFilePath================" . PHP_EOL; echo $new_content; } else { - echo "Writing the content to " . realpath($outputDirectory . DIRECTORY_SEPARATOR . $newFileName) . PHP_EOL; + echo "Writing the content to " . $newFilePath . PHP_EOL; } - file_put_contents($outputDirectory . DIRECTORY_SEPARATOR . $newFileName, $new_content); + file_put_contents($newFilePath, $new_content); } }