Skip to content

Commit 6a9ad4a

Browse files
authored
[Laravel110] Adds ModelCastsPropertyToCastsMethodRector rule (#173)
* Adds ModelCastsPropertyToCastsMethodRector rule * Set method to protected instead of public
1 parent 30f1bfd commit 6a9ad4a

12 files changed

+254
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use Rector\Config\RectorConfig;
2222

2323
return static function (RectorConfig $rectorConfig): void {
2424
$rectorConfig->sets([
25-
LaravelSetList::LARAVEL_100
25+
LaravelSetList::LARAVEL_110
2626
]);
2727
};
2828
```

config/sets/laravel110.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use RectorLaravel\Rector\Class_\ModelCastsPropertyToCastsMethodRector;
7+
8+
// see https://laravel.com/docs/11.x/upgrade
9+
return static function (RectorConfig $rectorConfig): void {
10+
$rectorConfig->import(__DIR__ . '/../config.php');
11+
12+
// https://github.com/laravel/framework/pull/47237
13+
$rectorConfig->rule(ModelCastsPropertyToCastsMethodRector::class);
14+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use RectorLaravel\Set\LaravelLevelSetList;
7+
use RectorLaravel\Set\LaravelSetList;
8+
9+
return static function (RectorConfig $rectorConfig): void {
10+
$rectorConfig->sets([LaravelSetList::LARAVEL_110, LaravelLevelSetList::UP_TO_LARAVEL_100]);
11+
};

docs/rector_rules_overview.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 50 Rules Overview
1+
# 51 Rules Overview
22

33
## AddArgumentDefaultValueRector
44

@@ -698,6 +698,14 @@ Change minutes argument to seconds in `Illuminate\Contracts\Cache\Store` and Ill
698698

699699
<br>
700700

701+
## ModelCastsPropertyToCastsMethodRector
702+
703+
Refactor Model `$casts` property with `casts()` method
704+
705+
- class: [`RectorLaravel\Rector\Class_\ModelCastsPropertyToCastsMethodRector`](../src/Rector/Class_/ModelCastsPropertyToCastsMethodRector.php)
706+
707+
<br>
708+
701709
## NotFilledBlankFuncCallToBlankFilledFuncCallRector
702710

703711
Swap the use of NotBooleans used with `filled()` and `blank()` to the correct helper.
@@ -1095,4 +1103,4 @@ Use `$this->components` property within commands
10951103
}
10961104
```
10971105

1098-
<br>
1106+
<br>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace RectorLaravel\Rector\Class_;
4+
5+
use PhpParser\BuilderFactory;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Stmt\Class_;
8+
use PhpParser\Node\Stmt\ClassMethod;
9+
use PhpParser\Node\Stmt\Property;
10+
use PhpParser\Node\Stmt\Return_;
11+
use PHPStan\Type\ObjectType;
12+
use Rector\Rector\AbstractRector;
13+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
14+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
15+
16+
/**
17+
* @see \RectorLaravel\Tests\Rector\Class_\ModelCastsPropertyToCastsMethodRector\ModelCastsPropertyToCastsMethodRectorTest
18+
*/
19+
class ModelCastsPropertyToCastsMethodRector extends AbstractRector
20+
{
21+
public function __construct(protected BuilderFactory $builderFactory)
22+
{
23+
}
24+
25+
public function getRuleDefinition(): RuleDefinition
26+
{
27+
return new RuleDefinition('Refactor Model $casts property with casts() method', [
28+
new CodeSample(
29+
<<<'CODE_SAMPLE'
30+
use Illuminate\Database\Eloquent\Model;
31+
32+
class Person extends Model
33+
{
34+
protected $casts = [
35+
'age' => 'integer',
36+
];
37+
}
38+
CODE_SAMPLE,
39+
<<<'CODE_SAMPLE'
40+
use Illuminate\Database\Eloquent\Model;
41+
42+
class Person extends Model
43+
{
44+
protected function casts(): array
45+
{
46+
return [
47+
'age' => 'integer',
48+
];
49+
}
50+
}
51+
CODE_SAMPLE,
52+
),
53+
]);
54+
}
55+
56+
public function getNodeTypes(): array
57+
{
58+
return [Class_::class];
59+
}
60+
61+
/**
62+
* @param Class_ $node
63+
*/
64+
public function refactor(Node $node): ?Class_
65+
{
66+
// Check if it is a Model
67+
if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Model'))) {
68+
69+
return null;
70+
}
71+
72+
// Check if there is already a casts() method
73+
foreach ($node->stmts as $stmt) {
74+
if ($stmt instanceof ClassMethod && $this->isName($stmt, 'casts')) {
75+
return null;
76+
}
77+
}
78+
79+
// Check if there is a protected $casts property
80+
foreach ($node->stmts as $index => $stmt) {
81+
if ($stmt instanceof Property && ($this->isName($stmt, 'casts') && $stmt->isProtected())) {
82+
$method = $this->builderFactory->method('casts')
83+
->setReturnType('array')
84+
->makeProtected();
85+
// convert the property to a return statement
86+
$method->addStmt(new Return_($stmt->props[0]->default));
87+
unset($node->stmts[$index]);
88+
$node->stmts[] = $method->getNode();
89+
90+
return $node;
91+
}
92+
}
93+
94+
return null;
95+
}
96+
}

src/Set/LaravelLevelSetList.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,9 @@ final class LaravelLevelSetList implements SetListInterface
7272
* @var string
7373
*/
7474
final public const UP_TO_LARAVEL_100 = __DIR__ . '/../../config/sets/level/up-to-laravel-100.php';
75+
76+
/**
77+
* @var string
78+
*/
79+
final public const UP_TO_LARAVEL_110 = __DIR__ . '/../../config/sets/level/up-to-laravel-110.php';
7580
}

src/Set/LaravelSetList.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ final class LaravelSetList implements SetListInterface
8383
*/
8484
final public const LARAVEL_100 = __DIR__ . '/../../config/sets/laravel100.php';
8585

86+
/**
87+
* @var string
88+
*/
89+
final public const LARAVEL_110 = __DIR__ . '/../../config/sets/laravel110.php';
90+
8691
/**
8792
* @var string
8893
*/
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\Class_\ModelCastsPropertyToCastsMethodRector\Fixture;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class CastsPropertyExists extends Model
8+
{
9+
protected $casts = [
10+
'birthday' => 'datetime',
11+
'age' => 'integer',
12+
];
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace RectorLaravel\Tests\Rector\Class_\ModelCastsPropertyToCastsMethodRector\Fixture;
20+
21+
use Illuminate\Database\Eloquent\Model;
22+
23+
class CastsPropertyExists extends Model
24+
{
25+
protected function casts(): array
26+
{
27+
return [
28+
'birthday' => 'datetime',
29+
'age' => 'integer',
30+
];
31+
}
32+
}
33+
34+
?>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\Class_\ModelCastsPropertyToCastsMethodRector\Fixture;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class ClassWithCastsMethod extends Model
8+
{
9+
protected $casts = [
10+
'name' => 'string',
11+
];
12+
13+
private function casts(): array
14+
{
15+
return [
16+
'birthday' => 'datetime',
17+
'age' => 'integer',
18+
];
19+
}
20+
}
21+
22+
?>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace RectorLaravel\Tests\Rector\Class_\ModelCastsPropertyToCastsMethodRector\Fixture;
4+
5+
class NonModelClass
6+
{
7+
protected $casts = [
8+
'birthday' => 'datetime',
9+
'age' => 'integer',
10+
];
11+
}
12+
13+
?>
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_\ModelCastsPropertyToCastsMethodRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class ModelCastsPropertyToCastsMethodRectorTest 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: 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_\ModelCastsPropertyToCastsMethodRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');
10+
11+
$rectorConfig->rule(ModelCastsPropertyToCastsMethodRector::class);
12+
};

0 commit comments

Comments
 (0)