Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,6 +27,10 @@
*/
final class CollectFieldsForGridClass extends AbstractGridClassCollector implements Collector
{
public function __construct(private readonly FieldRegistry $fieldRegistry)
{
}

/**
* @return class-string
*/
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

use bitExpert\PHPStan\Util\PropertyName;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;

final readonly class CallableFieldNode implements FieldNode
{
private const FIELD_TYPE = 'Sylius\\Bundle\\GridBundle\\Builder\\Field\\CallableField';

public function supports(FullyQualified $nodeClass): bool
{
return self::FIELD_TYPE === $nodeClass->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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

use bitExpert\PHPStan\Util\PropertyName;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;

final readonly class DateTimeFieldNode implements FieldNode
{
private const FIELD_TYPE = 'Sylius\\Bundle\\GridBundle\\Builder\\Field\\DateTimeField';

public function supports(FullyQualified $nodeClass): bool
{
return self::FIELD_TYPE === $nodeClass->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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

final readonly class DefaultFieldRegistry implements FieldRegistry
{
/** @param FieldNode[] $fields */
public function __construct(private readonly array $fields)
{
}

/** @return FieldNode[] */
public function getFields(): array
{
return $this->fields;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

use bitExpert\PHPStan\Util\PropertyName;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;

final readonly class EnumFieldNode implements FieldNode
{
private const FIELD_TYPE = 'Sylius\\Bundle\\GridBundle\\Builder\\Field\\EnumField';

public function supports(FullyQualified $nodeClass): bool
{
return self::FIELD_TYPE === $nodeClass->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;
}
}
29 changes: 29 additions & 0 deletions src/bitExpert/PHPStan/Sylius/Collector/Grid/Field/FieldNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;

interface FieldNode
{
/** Returns true if this node handles the concrete Sylius field class. */
public function supports(FullyQualified $nodeClass): bool;

/**
* Extracts the field name(s) from the StaticCall node.
*
* @return string[] list of field identifiers (already converted to camelCase)
*/
public function getFieldNames(StaticCall $node): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

interface FieldRegistry
{
/** @return FieldNode[] */
public function getFields(): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/*
* This file is part of the phpstan-sylius package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\PHPStan\Sylius\Collector\Grid\Field;

use PHPStan\DependencyInjection\Container;

class FieldRegistryFactory
{
public const TYPE_DESCRIPTOR_TAG = 'phpstan.sylius.grid.field';

private Container $container;

public function __construct(Container $container)
{
$this->container = $container;
}

public function createRegistry(): FieldRegistry
{
/** @var FieldNode[] $fields */
$fields = $this->container->getServicesByTag(self::TYPE_DESCRIPTOR_TAG);

return new DefaultFieldRegistry($fields);
}
}
Loading
Loading