8000 Add rule to add HasFactory to Models by jradtilbrook · Pull Request #353 · driftingly/rector-laravel · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add rule to add HasFactory to Models #353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions config/sets/laravel-legacy-factories-to-classes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector;
use RectorLaravel\Rector\FuncCall\FactoryFuncCallToStaticCallRector;
use RectorLaravel\Rector\MethodCall\FactoryApplyingStatesRector;
use RectorLaravel\Rector\Namespace_\FactoryDefinitionRector;
Expand All @@ -13,6 +14,7 @@
// https://laravel.com/docs/7.x/database-testing#writing-factories
// https://laravel.com/docs/8.x/database-testing#defining-model-factories
$rectorConfig->rule(FactoryDefinitionRector::class);
$rectorConfig->rule(AddHasFactoryToModelsRector::class);

// https://laravel.com/docs/7.x/database-testing#using-factories
// https://laravel.com/docs/8.x/database-testing#creating-models-using-factories
Expand Down
22 changes: 22 additions & 0 deletions docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ Adds the `@extends` annotation to Factories.

<br>

## AddHasFactoryToModelsRector

Adds the `HasFactory` trait to Models.

:wrench: **configure it!**

- class: [`RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector`](../src/Rector/Class_/AddHasFactoryToModelsRector.php)

```diff
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
+ use \Illuminate\Database\Eloquent\Factories\HasFactory;
}
```

<br>

## AddGenericReturnTypeToRelationsRector

Add generic return type to relations in child of `Illuminate\Database\Eloquent\Model`
Expand Down
111 changes: 111 additions & 0 deletions src/Rector/Class_/AddHasFactoryToModelsRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

namespace RectorLaravel\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\TraitUse;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Rector\Reflection\ReflectionResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;

/**
* @changelog https://github.com/laravel/framework/pull/39310
*
* @see \RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\AddHasFactoryToModelsRectorTest
* @see \RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRectorConfiguredTest\AddHasFactoryToModelsRectorConfiguredTest
*/
final class AddHasFactoryToModelsRector extends AbstractRector implements ConfigurableRectorInterface
{
private const string TRAIT_NAME = 'Illuminate\Database\Eloquent\Factories\HasFactory';

/**
* @var string[]
*/
private array $allowList = [];

public function __construct(
private readonly ReflectionResolver $reflectionResolver,
) {}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Adds the HasFactory trait to Models.', [
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
}
CODE_SAMPLE

,
<<<'CODE_SAMPLE'
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
}
CODE_SAMPLE, ['App\Models\User']
),
]);
}

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

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}

/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipClass($node)) {
return null;
}

$traitUse = new TraitUse([new FullyQualified(self::TRAIT_NAME)]);

$node->stmts = array_merge([$traitUse], $node->stmts);

return $node;
}

private function shouldSkipClass(Class_ $class): bool
{
if (! $this->isObjectType($class, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
return true;
}

if ($this->allowList !== [] && ! $this->isNames($class, $this->allowList)) {
return true;
}

$classReflection = $this->reflectionResolver->resolveClassReflection($class);
if (! $classReflection instanceof ClassReflection) {
return true;
}

return $classReflection->hasTraitUse(self::TRAIT_NAME);
}
}
9 changes: 9 additions & 0 deletions stubs/Illuminate/Database/Eloquent/Factories/HasFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Illuminate\Database\Eloquent\Factories;

if (trait_exists('Illuminate\Database\Eloquent\Factories\HasFactory')) {
return;
}

trait HasFactory {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AddHasFactoryToModelsRectorConfiguredTest extends AbstractRectorTestCase
{
public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture/Configured');
}

/**
* @test
*/
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

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

declare(strict_types=1);

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AddHasFactoryToModelsRectorTest extends AbstractRectorTestCase
{
public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture/WithoutConfiguration');
}

/**
* @test
*/
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

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

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
}

?>
-----
<?php

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
}

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

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured;

use Illuminate\Database\Eloquent\Model;

class Person extends Model {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured;

use Illuminate\Database\Eloquent\Model;

class SkipIfAlreadyPresent extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
}

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

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\WithoutConfiguration;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
}

?>
-----
<?php

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\WithoutConfiguration;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
}

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

namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\WithoutConfiguration;

use Illuminate\Database\Eloquent\Model;

class SkipIfAlreadyPresent extends Model
{
use \Illuminate\Database\Eloquent\Factories\HasFactory;
}

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

declare(strict_types=1);

use Rector\Config\RectorConfig;
use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');

$rectorConfig->ruleWithConfiguration(AddHasFactoryToModelsRector::class, [
'RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector\Fixture\Configured\User',
'RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRectorConfigured\Fixture\SkipIfAlreadyPresent',
]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');

$rectorConfig->rule(AddHasFactoryToModelsRector::class);
};
0