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
8 changes: 8 additions & 0 deletions config/sets/sylius/b2b-kit/b2b-kit.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
use PhpParser\Node\Stmt\Class_;
use Rector\Config\RectorConfig;
use Sylius\SyliusRector\Rector\Class_\AddAttributeOverridesToClassExtendingTypeRector;
use Sylius\SyliusRector\Rector\Class_\AddIndexToClassExtendingTypeRector;
use Sylius\SyliusRector\Rector\Class_\AddInterfaceToClassExtendingTypeRector;
use Sylius\SyliusRector\Rector\Class_\AddMethodCallToConstructorForClassesUsingTraitRector;
use Sylius\SyliusRector\Rector\Class_\AddTraitToClassExtendingTypeRector;
use Sylius\SyliusRector\Rector\Dto\AddMethodCallToConstructorForClassesUsingTrait;
use Sylius\SyliusRector\Rector\Dto\AttributeOverride;
use Sylius\SyliusRector\Rector\Dto\Index;
use Sylius\SyliusRector\Rector\TraitUse\AliasTraitMethodRector;

return static function (RectorConfig $rectorConfig): void {
Expand Down Expand Up @@ -72,6 +74,12 @@
'Sylius\Component\Core\Model\Payment' => ['Sylius\B2BKit\TradeCredit\Entity\TradeCreditPaymentTrait'],
], 'sylius/b2b-kit', '>=4.0 <5.0');

$rectorConfig->ruleWithConfigurationComposerVersionBound(AddIndexToClassExtendingTypeRector::class, [
'Sylius\Component\Core\Model\Payment' => [
new Index('idx_payment_credit_approval_state', ['credit_approval_state']),
],
], 'sylius/b2b-kit', '>=4.0 <5.0');

$rectorConfig->ruleWithConfigurationComposerVersionBound(AddInterfaceToClassExtendingTypeRector::class, [
'Sylius\Bundle\CoreBundle\Doctrine\ORM\AddressRepository' => [
'Sylius\B2BKit\Organization\Repository\AddressRepositoryInterface',
Expand Down
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ parameters:
count: 1
path: src/Rector/Class_/AddAttributeOverridesToClassExtendingTypeRector.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Stmt\\\\Class_\\) of method Sylius\\\\SyliusRector\\\\Rector\\\\Class_\\\\AddIndexToClassExtendingTypeRector\\:\\:refactor\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method Rector\\\\Contract\\\\Rector\\\\RectorInterface\\:\\:refactor\\(\\)$#"
count: 3
path: src/Rector/Class_/AddIndexToClassExtendingTypeRector.php

-
message: "#^Method Sylius\\\\SyliusRector\\\\Rector\\\\Class_\\\\AddMethodCallToConstructorForClassesUsingTraitRector\\:\\:refactor\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
Expand Down
186 changes: 186 additions & 0 deletions src/Rector/Class_/AddIndexToClassExtendingTypeRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<?php

declare(strict_types=1);

namespace Sylius\SyliusRector\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Sylius\SyliusRector\NodeManipulator\ClassInheritanceManipulator;
use Sylius\SyliusRector\Rector\Dto\Index;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Sylius\SyliusRector\Tests\Rector\Class_\AddIndexToClassExtendingType\AddIndexToClassExtendingTypeRectorTest
*/
final class AddIndexToClassExtendingTypeRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var array<string, Index[]>
*/
private array $configuration = [];

public function __construct(
private ClassInheritanceManipulator $classInheritanceManipulator,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Adds ORM\Index to classes extending the given type',
[
new CodeSample(
<<<'CODE_SAMPLE'
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
class Order extends BaseOrder
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[ORM\Index(name: 'idx_order_number', columns: ['number'])]
class Order extends BaseOrder
{
}
CODE_SAMPLE
,
),
],
);
}

public function getNodeTypes(): array
{
return [Class_::class];
}

public function configure(array $configuration): void
{
$this->configuration = $configuration;
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
$hasChanges = false;

foreach ($this->configuration as $className => $indexes) {
if (! $this->classInheritanceManipulator->isDerivative($node, $className)) {
continue;
}

foreach ($indexes as $index) {
if ($this->hasIndex($node, $index->name)) {
continue;
}

$this->addIndex($node, $index);
$hasChanges = true;
}
}

return $hasChanges ? $node : null;
}

private function hasIndex(Class_ $node, string $indexName): bool
{
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if (! $this->isIndexAttributeName($attr->name->toString())) {
continue;
}

if ($this->matchesIndexName($attr, $indexName)) {
return true;
}
}
}

return false;
}

private function isIndexAttributeName(string $attributeName): bool
{
return in_array($attributeName, ['Doctrine\ORM\Mapping\Index', 'ORM\Index', 'Index'], true);
}

private function matchesIndexName(Attribute $attribute, string $indexName): bool
{
$nameArg = $this->resolveNameArg($attribute);

return $nameArg instanceof String_ && $nameArg->value === $indexName;
}

/**
* Doctrine\ORM\Mapping\Index takes $name as its first parameter, so an existing index may
* spell the name either as "name:" or positionally.
*/
private function resolveNameArg(Attribute $attribute): ?Expr
{
$firstPositionalArg = null;

foreach ($attribute->args as $arg) {
if ($arg->name instanceof Identifier) {
if ($arg->name->toString() === 'name') {
return $arg->value;
}

continue;
}

$firstPositionalArg ??= $arg->value;
}

return $firstPositionalArg;
}

private function addIndex(Class_ $node, Index $index): void
{
$columnItems = [];
foreach ($index->columns as $column) {
$columnItems[] = new ArrayItem(new String_($column));
}

$attribute = new Attribute(
new FullyQualified('Doctrine\ORM\Mapping\Index'),
[
new Arg(new String_($index->name), false, false, [], new Identifier('name')),
new Arg(
new Array_($columnItems, [
'kind' => Array_::KIND_SHORT,
]),
false,
false,
[],
new Identifier('columns'),
),
],
);

$node->attrGroups[] = new AttributeGroup([$attribute]);
}
}
17 changes: 17 additions & 0 deletions src/Rector/Dto/Index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Sylius\SyliusRector\Rector\Dto;

final class Index
{
/**
* @param string[] $columns
*/
public function __construct(
public readonly string $name,
public readonly array $columns,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Sylius\SyliusRector\Tests\Rector\Class_\AddIndexToClassExtendingType;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AddIndexToClassExtendingTypeRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}

/**
* @return Iterator<string>
*/
public function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Index;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[Index(name: 'idx_order_number', columns: ['number'])]
class Order extends BaseOrder
{
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
class Order extends BaseOrder
{
}

?>
-----
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[\Doctrine\ORM\Mapping\Index(name: 'idx_order_number', columns: ['number'])]
class Order extends BaseOrder
{
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[ORM\Index('idx_order_number', ['number'])]
class Order extends BaseOrder
{
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[ORM\Index(name: 'idx_order_state', columns: ['state'])]
class Order extends BaseOrder
{
}

?>
-----
<?php

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\Order as BaseOrder;

#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
#[ORM\Index(name: 'idx_order_state', columns: ['state'])]
#[\Doctrine\ORM\Mapping\Index(name: 'idx_order_number', columns: ['number'])]
class Order extends BaseOrder
{
}

?>
Loading
Loading