From 85366f764fc20704e1524b506c72e742c34eb267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Hochd=C3=B6rfer?= Date: Sat, 26 Sep 2026 10:25:01 +0200 Subject: [PATCH] Allow custom field handling --- README.md | 4 ++ extension.neon | 31 +++++++++++++ .../Grid/CollectFieldsForGridClass.php | 36 +++++++++++----- .../Grid/Field/CallableFieldNode.php | 43 +++++++++++++++++++ .../Grid/Field/DateTimeFieldNode.php | 43 +++++++++++++++++++ .../Grid/Field/DefaultFieldRegistry.php | 27 ++++++++++++ .../Collector/Grid/Field/EnumFieldNode.php | 43 +++++++++++++++++++ .../Sylius/Collector/Grid/Field/FieldNode.php | 29 +++++++++++++ .../Collector/Grid/Field/FieldRegistry.php | 19 ++++++++ .../Grid/Field/FieldRegistryFactory.php | 35 +++++++++++++++ .../Collector/Grid/Field/GenericFieldNode.php | 43 +++++++++++++++++++ .../Collector/Grid/Field/StringFieldNode.php | 43 +++++++++++++++++++ .../Collector/Grid/Field/TwigFieldNode.php | 43 +++++++++++++++++++ ...lderFieldIsPartOfResourceClassUnitTest.php | 17 +++++++- 14 files changed, 444 insertions(+), 12 deletions(-) create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/CallableFieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DateTimeFieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DefaultFieldRegistry.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/EnumFieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldRegistry.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldRegistryFactory.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/GenericFieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/StringFieldNode.php create mode 100644 src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/TwigFieldNode.php diff --git a/README.md b/README.md index 9cdd1cc..0b366ac 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,10 @@ Current assumptions: To include your custom filter type in the checks, implement the `bitExpert\PHPStan\Sylius\Collector\Grid\Filter\FilterNode` interface and add a service to your `phpstan.neon` file and tag it with the `phpstan.sylius.grid.filter` tag. +### Custom field types + +To include your custom filter type in the checks, implement the `bitExpert\PHPStan\Sylius\Collector\Grid\Field\FieldNode` interface and add a service to your `phpstan.neon` file and tag it with the `phpstan.sylius.grid.field` tag. + ## Contribute Please feel free to fork and extend existing or add new features and send a pull request with your changes! To establish diff --git a/extension.neon b/extension.neon index c04820c..fc85a66 100644 --- a/extension.neon +++ b/extension.neon @@ -10,10 +10,41 @@ services: class: bitExpert\PHPStan\Sylius\Collector\Grid\CollectRessourceClassForGridClass tags: - phpstan.collector + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\FieldRegistryFactory + syliusFieldTypeRegistry: + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\DefaultFieldRegistry + factory: @bitExpert\PHPStan\Sylius\Collector\Grid\Field\FieldRegistryFactory::createRegistry - class: bitExpert\PHPStan\Sylius\Collector\Grid\CollectFieldsForGridClass + arguments: + fieldRegistry: @syliusFieldTypeRegistry tags: - phpstan.collector + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\StringFieldNode + tags: + - phpstan.sylius.grid.field + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\DateTimeFieldNode + tags: + - phpstan.sylius.grid.field + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\TwigFieldNode + tags: + - phpstan.sylius.grid.field + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\EnumFieldNode + tags: + - phpstan.sylius.grid.field + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\CallableFieldNode + tags: + - phpstan.sylius.grid.field + - + class: bitExpert\PHPStan\Sylius\Collector\Grid\Field\GenericFieldNode + tags: + - phpstan.sylius.grid.field - class: bitExpert\PHPStan\Sylius\Collector\Grid\Filter\FilterRegistryFactory diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/CollectFieldsForGridClass.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/CollectFieldsForGridClass.php index 3b41a03..a8de486 100644 --- a/src/bitExpert/PHPStan/Sylius/Collector/Grid/CollectFieldsForGridClass.php +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/CollectFieldsForGridClass.php @@ -12,12 +12,11 @@ namespace bitExpert\PHPStan\Sylius\Collector\Grid; -use bitExpert\PHPStan\Util\PropertyName; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\FieldRegistry; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; -use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Name\FullyQualified; use PHPStan\Analyser\Scope; use PHPStan\Collectors\Collector; use PHPStan\Type\ObjectType; @@ -28,6 +27,10 @@ */ final class CollectFieldsForGridClass extends AbstractGridClassCollector implements Collector { + public function __construct(private readonly FieldRegistry $fieldRegistry) + { + } + /** * @return class-string */ @@ -60,17 +63,28 @@ public function processNode(Node $node, Scope $scope): ?array } $classType = new ObjectType($classReflection->getName()); - /** @var Arg $arg */ - $arg = $node->args[0]; - /** @var String_ $fieldName */ - $fieldName = $arg->value; + /** @var FullyQualified $nodeClass */ + $nodeClass = $node->class; - // the . means the resource object is passed to the grid field. That means, we can ignore it - if ('.' === $fieldName->value) { - return null; + // first check if the various field implementations have defined custom fields to check + foreach ($this->fieldRegistry->getFields() as $fieldNode) { + if ($fieldNode->supports($nodeClass)) { + $fieldNames = $fieldNode->getFieldNames($node); + + if (0 === \count($fieldNames)) { + return null; + } + + // the . means the resource object is passed to the grid field. That means, we can ignore it + if ('.' === $fieldNames[0]) { + return null; + } + + return [$classType->getClassName(), $fieldNames[0], $node->getLine()]; + } } - return [$classType->getClassName(), PropertyName::convertSnakeToCamelCase($fieldName->value), $node->getLine()]; + return null; } protected function isFieldInterfaceReturnType(Type $type): bool diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/CallableFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/CallableFieldNode.php new file mode 100644 index 0000000..726f72f --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/CallableFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DateTimeFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DateTimeFieldNode.php new file mode 100644 index 0000000..807bd95 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DateTimeFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DefaultFieldRegistry.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DefaultFieldRegistry.php new file mode 100644 index 0000000..1a3144c --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/DefaultFieldRegistry.php @@ -0,0 +1,27 @@ +fields; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/EnumFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/EnumFieldNode.php new file mode 100644 index 0000000..3263d61 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/EnumFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldNode.php new file mode 100644 index 0000000..cd80df3 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldNode.php @@ -0,0 +1,29 @@ +container = $container; + } + + public function createRegistry(): FieldRegistry + { + /** @var FieldNode[] $fields */ + $fields = $this->container->getServicesByTag(self::TYPE_DESCRIPTOR_TAG); + + return new DefaultFieldRegistry($fields); + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/GenericFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/GenericFieldNode.php new file mode 100644 index 0000000..a2e7652 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/GenericFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/StringFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/StringFieldNode.php new file mode 100644 index 0000000..6fbb719 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/StringFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/TwigFieldNode.php b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/TwigFieldNode.php new file mode 100644 index 0000000..ce71088 --- /dev/null +++ b/src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/TwigFieldNode.php @@ -0,0 +1,43 @@ +name; + } + + public function getFieldNames(StaticCall $node): array + { + $fieldNames = []; + + if (isset($node->args[0])) { + $arg = $node->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + $fieldNames[] = PropertyName::convertSnakeToCamelCase($arg->value->value); + } + } + + return $fieldNames; + } +} diff --git a/tests/bitExpert/PHPStan/Sylius/Rule/Grid/GridBuilderFieldIsPartOfResourceClassUnitTest.php b/tests/bitExpert/PHPStan/Sylius/Rule/Grid/GridBuilderFieldIsPartOfResourceClassUnitTest.php index 6327a23..ecd80e2 100644 --- a/tests/bitExpert/PHPStan/Sylius/Rule/Grid/GridBuilderFieldIsPartOfResourceClassUnitTest.php +++ b/tests/bitExpert/PHPStan/Sylius/Rule/Grid/GridBuilderFieldIsPartOfResourceClassUnitTest.php @@ -14,6 +14,13 @@ use bitExpert\PHPStan\Sylius\Collector\Grid\CollectFieldsForGridClass; use bitExpert\PHPStan\Sylius\Collector\Grid\CollectRessourceClassForGridClass; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\CallableFieldNode; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\DateTimeFieldNode; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\DefaultFieldRegistry; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\EnumFieldNode; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\GenericFieldNode; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\StringFieldNode; +use bitExpert\PHPStan\Sylius\Collector\Grid\Field\TwigFieldNode; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -29,9 +36,17 @@ protected function getRule(): Rule protected function getCollectors(): array { + $fields = []; + $fields[] = new StringFieldNode(); + $fields[] = new DateTimeFieldNode(); + $fields[] = new TwigFieldNode(); + $fields[] = new EnumFieldNode(); + $fields[] = new CallableFieldNode(); + $fields[] = new GenericFieldNode(); + return [ new CollectRessourceClassForGridClass(), - new CollectFieldsForGridClass(), + new CollectFieldsForGridClass(new DefaultFieldRegistry($fields)), ]; }