Skip to content

Commit 09ebd2d

Browse files
committed
Add rule to add HasFactory to Models
1 parent 4378a24 commit 09ebd2d

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RectorLaravel\Rector\Class_;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Name\FullyQualified;
9+
use PhpParser\Node\Stmt\Class_;
10+
use PhpParser\Node\Stmt\TraitUse;
11+
use PHPStan\Reflection\ClassReflection;
12+
use PHPStan\Type\ObjectType;
13+
use Rector\Rector\AbstractRector;
14+
use Rector\Reflection\ReflectionResolver;
15+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
16+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
17+
18+
/**
19+
* @changelog https://github.com/laravel/framework/pull/39310
20+
*
21+
* @see \RectorLaravel\Tests\Rector\Class_\RemoveModelPropertyFromFactoriesRector\RemoveModelPropertyFromFactoriesRectorTest
22+
*/
23+
final class AddHasFactoryToModelsRector extends AbstractRector
24+
{
25+
private const string TRAIT_NAME = 'Illuminate\Database\Eloquent\Factories\HasFactory';
26+
27+
public function __construct(
28+
private readonly ReflectionResolver $reflectionResolver,
29+
) {}
30+
31+
public function getRuleDefinition(): RuleDefinition
32+
{
33+
return new RuleDefinition('Adds the HasFactory trait to Models.', [
34+
new CodeSample(
35+
<<<'CODE_SAMPLE'
36+
use Illuminate\Database\Eloquent\Model;
37+
38+
class User extends Model
39+
{
40+
}
41+
CODE_SAMPLE
42+
43+
,
44+
<<<'CODE_SAMPLE'
45+
use Illuminate\Database\Eloquent\Model;
46+
47+
class User extends Model
48+
{
49+
use \Illuminate\Database\Eloquent\Factories\HasFactory;
50+
}
51+
CODE_SAMPLE
52+
),
53+
]);
54+
}
55+
56+
/**
57+
* @return array<class-string<Node>>
58+
*/
59+
public function getNodeTypes(): array
60+
{
61+
return [Class_::class];
62+
}
63+
64+
/**
65+
* @param Class_ $node
66+
*/
67+
public function refactor(Node $node): ?Node
68+
{
69+
if ($this->shouldSkipClass($node)) {
70+
return null;
71+
}
72+
73+
$traitUse = new TraitUse([new FullyQualified(self::TRAIT_NAME)]);
74+
75+
$node->stmts = array_merge([$traitUse], $node->stmts);
76+
77+
return $node;
78+
}
79+
80+
private function shouldSkipClass(Class_ $class): bool
81+
{
82+
if (! $this->isObjectType($class, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
83+
return null;
84+
}
85+
86+
$classReflection = $this->reflectionResolver->resolveClassReflection($class);
87+
if (! $classReflection instanceof ClassReflection) {
88+
return false;
89+
}
90+
91+
return $classReflection->hasTraitUse(self::TRAIT_NAME);
92+
}
93+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent\Factories;
4+
5+
if (trait_exists('Illuminate\Database\Eloquent\Factories\HasFactory')) {
6+
return;
7+
}
8+
9+
trait HasFactory {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RectorLaravel\Tests\Rector\Class_\AddHasFactoryToModelsRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddHasFactoryToModelsRectorTest extends AbstractRectorTestCase
12+
{
13+
public static function provideData(): Iterator
14+
{
15+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
16+
}
17+
18+
/**
19+
* @test
20+
*/
21+
#[DataProvider('provideData')]
22+
public function test(string $filePath): void
23+
{
24+
$this->doTestFile($filePath);
25+
}
26+
27+
public function provideConfigFilePath(): string
28+
{
29+
return __DIR__ . '/config/configured_rule.php';
30+
}
31+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\Class_\RemoveModelPropertyFromFactoriesRector\Fixture;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class User extends Model
8+
{
9+
}
10+
11+
?>
12+
-----
13+
<?php
14+
15+
namespace RectorLaravel\Tests\Rector\Class_\RemoveModelPropertyFromFactoriesRector\Fixture;
16+
17+
use Illuminate\Database\Eloquent\Model;
18+
19+
class User extends Model
20+
{
21+
use \Illuminate\Database\Eloquent\Factories\HasFactory;
22+
}
23+
24+
?>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\Class_\RemoveModelPropertyFromFactoriesRector\Fixture;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class SkipIfAlreadyPresent extends Model
8+
{
9+
use \Illuminate\Database\Eloquent\Factories\HasFactory;
10+
}
11+
12+
?>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');
10+
11+
$rectorConfig->rule(AddHasFactoryToModelsRector::class);
12+
};

0 commit comments

Comments
 (0)