-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
0 parents
commit 852f0b8
Showing
26 changed files
with
8,630 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
end_of_line = lf | ||
indent_size = 4 | ||
indent_style = space | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false | ||
|
||
[*.{yml,yaml}] | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
* text=auto | ||
|
||
.github/ export-ignore | ||
tests/ export-ignore | ||
.editorconfig export-ignore | ||
.gitattributes export-ignore | ||
.gitignore export-ignore | ||
phpunit.xml.dist export-ignore | ||
phpstan.neon.dist export-ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/vendor/ | ||
.phpunit.result.cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
## Installation | ||
|
||
```bash | ||
composer require --dev devfrey/rector-eloquent-generics | ||
``` | ||
|
||
```php | ||
return RectorConfig::configure() | ||
// ... | ||
->withRules([ | ||
Devfrey\RectorLaravel\Eloquent\AddBuilderPropertyRector::class, | ||
Devfrey\RectorLaravel\Eloquent\AddGenericHasBuilderTraitRector::class, | ||
Devfrey\RectorLaravel\Eloquent\DocumentRelationGenericsRector::class, | ||
]); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "devfrey/rector-eloquent-generics", | ||
"description": "Small set of Rector rules for Eloquent's new generics.", | ||
"license": "MIT", | ||
"type": "library", | ||
"authors": [ | ||
{ | ||
"name": "Jeffrey Angenent", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"require": { | ||
"php": "^8.2", | ||
"rector/rector": "^1.2", | ||
"symplify/rule-doc-generator-contracts": "^11.2" | ||
}, | ||
"require-dev": { | ||
"laravel/framework": "^11.15.0", | ||
"phpstan/extension-installer": "^1.4.1", | ||
"phpstan/phpstan": "^1.11.7", | ||
"phpunit/phpunit": "^11.2.6", | ||
"symplify/phpstan-rules": "^13.0" | ||
}, | ||
"minimum-stability": "stable", | ||
"autoload": { | ||
"psr-4": { | ||
"Devfrey\\RectorLaravel\\": "src/" | ||
} | ||
}, | ||
"autoload-dev": { | ||
"psr-4": { | ||
"Tests\\": "tests/" | ||
} | ||
}, | ||
"config": { | ||
"allow-plugins": { | ||
"phpstan/extension-installer": true | ||
}, | ||
"sort-packages": true | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
includes: | ||
- vendor/symplify/phpstan-rules/config/rector-rules.neon | ||
|
||
parameters: | ||
level: 9 | ||
paths: | ||
- src | ||
- tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<phpunit | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd" | ||
bootstrap="vendor/autoload.php" | ||
colors="true" | ||
> | ||
<testsuites> | ||
<testsuite name="Tests"> | ||
<directory>./tests</directory> | ||
</testsuite> | ||
</testsuites> | ||
<source> | ||
<include> | ||
<directory>./src</directory> | ||
</include> | ||
</source> | ||
</phpunit> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
|
||
namespace Devfrey\RectorLaravel\Eloquent; | ||
|
||
use PhpParser\Builder\Property; | ||
use PhpParser\Node; | ||
use PhpParser\Node\Name; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\ClassMethod; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
/** | ||
* @see \Tests\Eloquent\AddBuilderPropertyRector\AddBuilderPropertyRectorTest | ||
*/ | ||
final class AddBuilderPropertyRector extends ModelRector | ||
{ | ||
/** | ||
* Get the rule definition for the Rector rule. | ||
* | ||
* @return \Symplify\RuleDocGenerator\ValueObject\RuleDefinition | ||
*/ | ||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('Refactor newEloquentBuilder() to $builder', [ | ||
new CodeSample( | ||
<<<'CODE_SAMPLE' | ||
public function newEloquentBuilder($query): UserBuilder | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
CODE_SAMPLE | ||
, | ||
<<<'CODE_SAMPLE' | ||
protected static string $builder = UserBuilder::class; | ||
CODE_SAMPLE, | ||
), | ||
]); | ||
} | ||
|
||
/** | ||
* Refactor the Eloquent model. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $model | ||
* @return \PhpParser\Node\Stmt\Class_|null | ||
*/ | ||
public function refactorModel(Class_ $model): ?Class_ | ||
{ | ||
foreach ($model->stmts as $key => $stmt) { | ||
if (!$stmt instanceof ClassMethod) { | ||
continue; | ||
} | ||
|
||
if (!$this->isName($stmt, 'newEloquentBuilder')) { | ||
continue; | ||
} | ||
|
||
if (!$stmt->getReturnType() instanceof Name\FullyQualified) { | ||
// If the method does not have a native return type, we cannot | ||
// determine the builder class and therefore cannot refactor | ||
// the method to a property. | ||
continue; | ||
} | ||
|
||
// Remove the newEloquentBuilder() method | ||
unset($model->stmts[$key]); | ||
|
||
// Add the $builder property | ||
$this->insertAfterTraitUses( | ||
$model, | ||
$this->createProtectedStaticBuilderProperty($stmt->getReturnType()), | ||
); | ||
|
||
return $model; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Insert the property after the trait uses. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $class | ||
* @param \PhpParser\Node\Stmt\Property $property | ||
* @return void | ||
*/ | ||
private function insertAfterTraitUses(Class_ $class, Node\Stmt\Property $property): void | ||
{ | ||
$traitUseIndex = -1; | ||
|
||
foreach ($class->stmts as $index => $stmt) { | ||
if ($stmt instanceof Node\Stmt\TraitUse) { | ||
$traitUseIndex = $index; | ||
} | ||
} | ||
|
||
array_splice($class->stmts, $traitUseIndex + 1, 0, [$property]); | ||
} | ||
|
||
/** | ||
* Create a protected static $builder property. | ||
* | ||
* @param \PhpParser\Node\Name\FullyQualified $builderClass | ||
* @return \PhpParser\Node\Stmt\Property | ||
*/ | ||
private function createProtectedStaticBuilderProperty(Name\FullyQualified $builderClass): Node\Stmt\Property | ||
{ | ||
// \App\Models\Builders\UserBuilder::class | ||
$default = $this->nodeFactory->createClassConstReference($builderClass->toString()); | ||
|
||
// protected static $builder = \App\Models\Builders\UserBuilder::class | ||
return (new Property('builder')) | ||
->makeProtected() | ||
->makeStatic() | ||
->setType('string') | ||
->setDefault($default) | ||
->getNode(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<?php | ||
|
||
namespace Devfrey\RectorLaravel\Eloquent; | ||
|
||
use PhpParser\Comment\Doc; | ||
use PhpParser\Node\Expr\ClassConstFetch; | ||
use PhpParser\Node\Name\FullyQualified; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\TraitUse; | ||
use Rector\NodeManipulator\ClassManipulator; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
/** | ||
* @see \Tests\Eloquent\AddGenericHasBuilderTraitRector\AddGenericHasBuilderTraitRectorTest | ||
*/ | ||
final class AddGenericHasBuilderTraitRector extends ModelRector | ||
{ | ||
/** | ||
* Create a new Rector rule instance. | ||
* | ||
* @param \Rector\NodeManipulator\ClassManipulator $classManipulator | ||
* @return void | ||
*/ | ||
public function __construct( | ||
private readonly ClassManipulator $classManipulator, | ||
) { | ||
} | ||
|
||
/** | ||
* Get the rule definition for the Rector rule. | ||
* | ||
* @return \Symplify\RuleDocGenerator\ValueObject\RuleDefinition | ||
*/ | ||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('// @todo fill the description', [ | ||
new CodeSample( | ||
<<<'CODE_SAMPLE' | ||
// @todo fill code before | ||
CODE_SAMPLE | ||
, | ||
<<<'CODE_SAMPLE' | ||
// @todo fill code after | ||
CODE_SAMPLE, | ||
), | ||
]); | ||
} | ||
|
||
/** | ||
* Refactor the Eloquent model. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $model | ||
* @return \PhpParser\Node\Stmt\Class_|null | ||
*/ | ||
public function refactorModel(Class_ $model): ?Class_ | ||
{ | ||
if ($this->classManipulator->hasTrait($model, 'Illuminate\Database\Eloquent\HasBuilder')) { | ||
return null; | ||
} | ||
|
||
$builderClassName = $this->detectBuilderClassName($model); | ||
|
||
if (is_null($builderClassName)) { | ||
return null; | ||
} | ||
|
||
$model->stmts = [ | ||
$this->createTraitUse($builderClassName), | ||
...$model->stmts, | ||
]; | ||
|
||
return $model; | ||
} | ||
|
||
/** | ||
* Detect the given model's builder name. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $model | ||
* @return \PhpParser\Node\Name\FullyQualified|null | ||
*/ | ||
private function detectBuilderClassName(Class_ $model): ?FullyQualified | ||
{ | ||
// First we check if the model has a newEloquentBuilder() method | ||
$newEloquentBuilder = $model->getMethod('newEloquentBuilder'); | ||
|
||
if (!is_null($newEloquentBuilder)) { | ||
$returnType = $newEloquentBuilder->getReturnType(); | ||
|
||
if ($returnType instanceof FullyQualified) { | ||
return $returnType; | ||
} | ||
|
||
// If the return type cannot be determined, we immediately abort | ||
// and skip looking for the $builder property. Eloquent only uses | ||
// the $builder property if the newEloquentBuilder() method | ||
// is missing. | ||
return null; | ||
} | ||
|
||
// Otherwise, we check if the model has a protected static $builder property | ||
foreach ($model->getProperties() as $property) { | ||
if ( | ||
!$property->isProtected() | ||
|| !$property->isStatic() | ||
|| !$this->isName($property, 'builder') | ||
) { | ||
continue; | ||
} | ||
|
||
$default = $property->props[0]->default; | ||
|
||
if ( | ||
$default instanceof ClassConstFetch | ||
&& $default->class instanceof FullyQualified | ||
) { | ||
return $default->class; | ||
} | ||
|
||
// If the $builder property is not a class constant fetch, we | ||
// cannot determine the builder type. | ||
return null; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Create a new trait use statement. | ||
* | ||
* @param \PhpParser\Node\Name\FullyQualified $builderClassName | ||
* @return \PhpParser\Node\Stmt\TraitUse | ||
*/ | ||
private function createTraitUse(FullyQualified $builderClassName): TraitUse | ||
{ | ||
$traitUse = new TraitUse([new FullyQualified('Illuminate\Database\Eloquent\HasBuilder')]); | ||
$traitUse->setDocComment( | ||
new Doc( | ||
<<<PHPDOC | ||
/** | ||
* @use \Illuminate\Database\Eloquent\HasBuilder<{$builderClassName->toCodeString()}<\$this>> | ||
*/ | ||
PHPDOC, | ||
), | ||
); | ||
|
||
return $traitUse; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
<?php | ||
|
||
namespace Devfrey\RectorLaravel\Eloquent; | ||
|
||
use PhpParser\Node\Arg; | ||
use PhpParser\Node\Expr\ClassConstFetch; | ||
use PhpParser\Node\Expr\MethodCall; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\Return_; | ||
use PHPStan\Type\ObjectType; | ||
use PHPStan\Type\ThisType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\TypeWithClassName; | ||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; | ||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; | ||
use Rector\Exception\ShouldNotHappenException; | ||
use Rector\PhpParser\Node\BetterNodeFinder; | ||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType; | ||
use Rector\StaticTypeMapper\ValueObject\Type\SelfStaticType; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
final class DocumentRelationGenericsRector extends ModelRector | ||
{ | ||
private const SIMPLE_RELATIONS = [ | ||
'belongsTo' => 'Illuminate\Database\Eloquent\Relations\BelongsTo', | ||
'belongsToMany' => 'Illuminate\Database\Eloquent\Relations\BelongsToMany', | ||
'hasMany' => 'Illuminate\Database\Eloquent\Relations\HasMany', | ||
'hasOne' => 'Illuminate\Database\Eloquent\Relations\HasOne', | ||
'morphMany' => 'Illuminate\Database\Eloquent\Relations\MorphMany', | ||
'morphOne' => 'Illuminate\Database\Eloquent\Relations\MorphOne', | ||
// 'morphTo' => 'Illuminate\Database\Eloquent\Relations\MorphTo', | ||
'morphToMany' => 'Illuminate\Database\Eloquent\Relations\MorphToMany', | ||
]; | ||
|
||
private const INTERMEDIATE_RELATIONS = [ | ||
'hasManyThrough' => 'Illuminate\Database\Eloquent\Relations\HasManyThrough', | ||
'hasOneThrough' => 'Illuminate\Database\Eloquent\Relations\HasOneThrough', | ||
]; | ||
|
||
/** | ||
* Create a new Rector rule instance. | ||
* | ||
* @param \Rector\PhpParser\Node\BetterNodeFinder $betterNodeFinder | ||
* @param \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory $phpDocInfoFactory | ||
* @param \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger $phpDocTypeChanger | ||
* @return void | ||
*/ | ||
public function __construct( | ||
private readonly BetterNodeFinder $betterNodeFinder, | ||
private readonly PhpDocInfoFactory $phpDocInfoFactory, | ||
private readonly PhpDocTypeChanger $phpDocTypeChanger, | ||
) { | ||
} | ||
|
||
/** | ||
* Get the rule definition for the Rector rule. | ||
* | ||
* @return \Symplify\RuleDocGenerator\ValueObject\RuleDefinition | ||
*/ | ||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition('// @todo fill the description', [ | ||
new CodeSample( | ||
<<<'CODE_SAMPLE' | ||
// @todo fill code before | ||
CODE_SAMPLE | ||
, | ||
<<<'CODE_SAMPLE' | ||
// @todo fill code after | ||
CODE_SAMPLE, | ||
), | ||
]); | ||
} | ||
|
||
/** | ||
* Refactor the Eloquent model. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $model | ||
* @return \PhpParser\Node\Stmt\Class_|null | ||
*/ | ||
public function refactorModel(Class_ $model): ?Class_ | ||
{ | ||
$modelType = $this->nodeTypeResolver->getType($model); | ||
|
||
if (!$modelType instanceof TypeWithClassName) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
$hasChanged = false; | ||
|
||
foreach ($model->getMethods() as $method) { | ||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($method, Return_::class); | ||
|
||
if (count($returns) !== 1) { | ||
continue; | ||
} | ||
|
||
$returnExpression = $returns[0]->expr; | ||
|
||
if (!$returnExpression instanceof MethodCall) { | ||
continue; | ||
} | ||
|
||
/// If the method call is chained, we take the first method call | ||
$methodCall = $this->getParentMethodCall($returnExpression); | ||
|
||
$genericRelationType = $this->parseMethodCallIntoGenericObjectTypeIfRelation($modelType, $methodCall); | ||
|
||
if (is_null($genericRelationType)) { | ||
continue; | ||
} | ||
|
||
$hasChanged = $this->phpDocTypeChanger->changeReturnType( | ||
functionLike: $method, | ||
phpDocInfo: $this->phpDocInfoFactory->createFromNodeOrEmpty($method), | ||
newType: $genericRelationType, | ||
) || $hasChanged; | ||
} | ||
|
||
// Return the node if changes were made to avoid unnecessary re-parsing | ||
// of the node, which also causes issues. | ||
return $hasChanged ? $model : null; | ||
} | ||
|
||
/** | ||
* Parse a method call into a generic object type if it is a relation. | ||
* | ||
* @param \PhpParser\Node\Expr\MethodCall $methodCall | ||
* @return \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType|null | ||
*/ | ||
private function parseMethodCallIntoGenericObjectTypeIfRelation( | ||
TypeWithClassName $modelType, | ||
MethodCall $methodCall, | ||
): ?FullyQualifiedGenericObjectType { | ||
$selfType = new SelfStaticType( | ||
$modelType->getClassReflection() ?? throw new ShouldNotHappenException(), | ||
); | ||
|
||
foreach (self::SIMPLE_RELATIONS as $relationMethod => $relationClass) { | ||
if (!$this->isName($methodCall->name, $relationMethod)) { | ||
continue; | ||
} | ||
|
||
$relatedType = $this->resolveFirstArgumentObjectType($methodCall); | ||
|
||
if (is_null($relatedType)) { | ||
return null; | ||
} | ||
|
||
return new FullyQualifiedGenericObjectType( | ||
$relationClass, | ||
[ | ||
$this->normalizeIntoStaticObjectType($relatedType), | ||
$selfType, | ||
], | ||
); | ||
} | ||
|
||
foreach (self::INTERMEDIATE_RELATIONS as $relationMethod => $relationClass) { | ||
if (!$this->isName($methodCall, $relationMethod)) { | ||
continue; | ||
} | ||
|
||
[$relatedType, $throughType] = $this->resolveFirstTwoArgumentObjectTypes($methodCall); | ||
|
||
if (is_null($relatedType) || is_null($throughType)) { | ||
return null; | ||
} | ||
|
||
return new FullyQualifiedGenericObjectType( | ||
$relationClass, | ||
[ | ||
$this->normalizeIntoStaticObjectType($relatedType), | ||
$this->normalizeIntoStaticObjectType($throughType), | ||
$selfType, | ||
], | ||
); | ||
} | ||
|
||
if ($this->isName($methodCall->name, 'morphTo')) { | ||
return new FullyQualifiedGenericObjectType( | ||
'Illuminate\Database\Eloquent\Relations\MorphTo', | ||
[ | ||
new ObjectType('Illuminate\Database\Eloquent\Model'), | ||
$selfType, | ||
], | ||
); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Normalize the given object type into a static object type. | ||
* | ||
* @template T of \PHPStan\Type\Type | ||
* @param \PHPStan\Type\ObjectType $type | ||
* @return ($type is \PHPStan\Type\ThisType ? \PHPStan\Type\ThisType : T) | ||
*/ | ||
private function normalizeIntoStaticObjectType(Type $type): ObjectType | ||
{ | ||
return $type instanceof ThisType ? $type->getStaticObjectType() : $type; | ||
} | ||
|
||
/** | ||
* Resolve the first argument type from the given method call. | ||
* | ||
* @param \PhpParser\Node\Expr\MethodCall $methodCall | ||
* @return \PHPStan\Type\ObjectType|null | ||
*/ | ||
private function resolveFirstArgumentObjectType(MethodCall $methodCall) | ||
{ | ||
$arguments = $methodCall->getArgs(); | ||
|
||
if ($arguments === []) { | ||
return null; | ||
} | ||
|
||
return $this->resolveObjectTypeFromArgument($arguments[0]); | ||
} | ||
|
||
/** | ||
* Resolve the first two argument types from the given method call. | ||
* | ||
* @param \PhpParser\Node\Expr\MethodCall $methodCall | ||
* @return array{\PHPStan\Type\ObjectType|null, \PHPStan\Type\ObjectType|null} | ||
*/ | ||
private function resolveFirstTwoArgumentObjectTypes(MethodCall $methodCall): array | ||
{ | ||
$arguments = $methodCall->getArgs(); | ||
|
||
if (count($arguments) < 2) { | ||
return [null, null]; | ||
} | ||
|
||
$firstArgument = $this->resolveObjectTypeFromArgument($arguments[0]); | ||
$secondArgument = $this->resolveObjectTypeFromArgument($arguments[1]); | ||
|
||
return [$firstArgument, $secondArgument]; | ||
} | ||
|
||
/** | ||
* Resolve the object type from the given argument. | ||
* | ||
* @param \PhpParser\Node\Arg $argument | ||
* @return \PHPStan\Type\ObjectType|null | ||
*/ | ||
private function resolveObjectTypeFromArgument(Arg $argument): ?ObjectType | ||
{ | ||
if (!$argument->value instanceof ClassConstFetch) { | ||
return null; | ||
} | ||
|
||
$resolvedType = $this->nodeTypeResolver->getType($argument->value->class); | ||
|
||
if (!$resolvedType instanceof ObjectType) { | ||
return null; | ||
} | ||
|
||
return $resolvedType; | ||
} | ||
|
||
/** | ||
* Get the parent method call of the given method call. | ||
* | ||
* @param \PhpParser\Node\Expr\MethodCall $methodCall | ||
* @return \PhpParser\Node\Expr\MethodCall | ||
*/ | ||
private function getParentMethodCall(MethodCall $methodCall): MethodCall | ||
{ | ||
if ($methodCall->var instanceof MethodCall) { | ||
return $this->getParentMethodCall($methodCall->var); | ||
} | ||
|
||
return $methodCall; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
namespace Devfrey\RectorLaravel\Eloquent; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PHPStan\Type\ObjectType; | ||
use Rector\Exception\ShouldNotHappenException; | ||
use Rector\Rector\AbstractRector; | ||
|
||
abstract class ModelRector extends AbstractRector | ||
{ | ||
/** | ||
* Get the node types that the Rector rule refactors. | ||
* | ||
* @return array<class-string<\PhpParser\Node>> | ||
*/ | ||
final public function getNodeTypes(): array | ||
{ | ||
return [Class_::class]; | ||
} | ||
|
||
/** | ||
* Refactor the Eloquent model. | ||
* | ||
* @param \PhpParser\Node\Stmt\Class_ $model | ||
* @return \PhpParser\Node\Stmt\Class_|null | ||
*/ | ||
abstract public function refactorModel(Class_ $model): ?Class_; | ||
|
||
/** | ||
* Refactor the node. | ||
* | ||
* @param \PhpParser\Node $node | ||
* @return \PhpParser\Node\Stmt\Class_|null | ||
*/ | ||
final public function refactor(Node $node): ?Class_ | ||
{ | ||
if (!$node instanceof Class_) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
if (!$this->isObjectType($node, new ObjectType('\Illuminate\Database\Eloquent\Model'))) { | ||
return null; | ||
} | ||
|
||
return $this->refactorModel($node); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
tests/Eloquent/AddBuilderPropertyRector/AddBuilderPropertyRectorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector; | ||
|
||
use Iterator; | ||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
final class AddBuilderPropertyRectorTest extends AbstractRectorTestCase | ||
{ | ||
#[DataProvider('provideData')] | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public static function provideData(): Iterator | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/configured_rule.php'; | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
.../Eloquent/AddBuilderPropertyRector/Fixture/model_with_new_eloquent_builder_method.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class User extends Model | ||
{ | ||
use HasFactory; | ||
use SoftDeletes; | ||
|
||
protected $fillable = ['email']; | ||
|
||
public function foo(): BelongsTo | ||
{ | ||
return $this->belongsTo(Foo::class); | ||
} | ||
|
||
public function newEloquentBuilder($query): UserBuilder | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class User extends Model | ||
{ | ||
use HasFactory; | ||
use SoftDeletes; | ||
protected static string $builder = \App\Models\Builders\UserBuilder::class; | ||
|
||
protected $fillable = ['email']; | ||
|
||
public function foo(): BelongsTo | ||
{ | ||
return $this->belongsTo(Foo::class); | ||
} | ||
} | ||
|
||
?> |
33 changes: 33 additions & 0 deletions
33
...t/AddBuilderPropertyRector/Fixture/skip_model_without_new_eloquent_builder_method.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
|
||
class User extends Model | ||
{ | ||
public function foo(): BelongsTo | ||
{ | ||
return $this->belongsTo(Foo::class); | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
|
||
class User extends Model | ||
{ | ||
public function foo(): BelongsTo | ||
{ | ||
return $this->belongsTo(Foo::class); | ||
} | ||
} | ||
|
||
?> |
33 changes: 33 additions & 0 deletions
33
...PropertyRector/Fixture/skip_model_without_new_eloquent_builder_method_return_type.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
public function newEloquentBuilder($query) | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddBuilderPropertyRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
public function newEloquentBuilder($query) | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> |
8 changes: 8 additions & 0 deletions
8
tests/Eloquent/AddBuilderPropertyRector/config/configured_rule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
use Devfrey\RectorLaravel\Eloquent\AddBuilderPropertyRector; | ||
use Rector\Config\RectorConfig; | ||
|
||
return function (RectorConfig $rectorConfig) { | ||
$rectorConfig->rule(AddBuilderPropertyRector::class); | ||
}; |
26 changes: 26 additions & 0 deletions
26
tests/Eloquent/AddGenericHasBuilderTraitRector/AddGenericHasBuilderTraitRectorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector; | ||
|
||
use Iterator; | ||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
final class AddGenericHasBuilderTraitRectorTest extends AbstractRectorTestCase | ||
{ | ||
#[DataProvider('provideData')] | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public static function provideData(): Iterator | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/configured_rule.php'; | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
tests/Eloquent/AddGenericHasBuilderTraitRector/Fixture/model_with_builder_property.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\FooBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class Comment extends Model | ||
{ | ||
protected $table = 'comments'; | ||
|
||
protected static string $builder = FooBuilder::class; | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\FooBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class Comment extends Model | ||
{ | ||
/** | ||
* @use \Illuminate\Database\Eloquent\HasBuilder<\App\Models\Builders\FooBuilder<$this>> | ||
*/ | ||
use \Illuminate\Database\Eloquent\HasBuilder; | ||
protected $table = 'comments'; | ||
|
||
protected static string $builder = FooBuilder::class; | ||
} | ||
|
||
?> |
43 changes: 43 additions & 0 deletions
43
...erTraitRector/Fixture/model_with_new_eloquent_builder_method_and_builder_property.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\FooBuilder; | ||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
protected static string $builder = FooBuilder::class; | ||
|
||
public function newEloquentBuilder($query): UserBuilder | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\FooBuilder; | ||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
/** | ||
* @use \Illuminate\Database\Eloquent\HasBuilder<\App\Models\Builders\UserBuilder<$this>> | ||
*/ | ||
use \Illuminate\Database\Eloquent\HasBuilder; | ||
protected static string $builder = FooBuilder::class; | ||
|
||
public function newEloquentBuilder($query): UserBuilder | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> |
37 changes: 37 additions & 0 deletions
37
...loquent/AddGenericHasBuilderTraitRector/Fixture/skip_model_with_has_builder_trait.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\HasBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class Post extends Model | ||
{ | ||
/** | ||
* @use \Illuminate\Database\Eloquent\HasBuilder<\App\Models\Builders\PostBuilder> | ||
*/ | ||
use HasBuilder; | ||
use SoftDeletes; | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\HasBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class Post extends Model | ||
{ | ||
/** | ||
* @use \Illuminate\Database\Eloquent\HasBuilder<\App\Models\Builders\PostBuilder> | ||
*/ | ||
use HasBuilder; | ||
use SoftDeletes; | ||
} | ||
|
||
?> |
33 changes: 33 additions & 0 deletions
33
...HasBuilderTraitRector/Fixture/skip_model_without_new_eloquent_builder_return_type.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
public function newEloquentBuilder($query) | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\AddGenericHasBuilderTraitRector\Fixture; | ||
|
||
use App\Models\Builders\UserBuilder; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class User extends Model | ||
{ | ||
public function newEloquentBuilder($query) | ||
{ | ||
return new UserBuilder($query); | ||
} | ||
} | ||
|
||
?> |
8 changes: 8 additions & 0 deletions
8
tests/Eloquent/AddGenericHasBuilderTraitRector/config/configured_rule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
use Devfrey\RectorLaravel\Eloquent\AddGenericHasBuilderTraitRector; | ||
use Rector\Config\RectorConfig; | ||
|
||
return function (RectorConfig $rectorConfig) { | ||
$rectorConfig->rule(AddGenericHasBuilderTraitRector::class); | ||
}; |
26 changes: 26 additions & 0 deletions
26
tests/Eloquent/DocumentRelationGenericsRector/DocumentRelationGenericsRectorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Eloquent\DocumentRelationGenericsRector; | ||
|
||
use Iterator; | ||
use PHPUnit\Framework\Attributes\DataProvider; | ||
use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
||
final class DocumentRelationGenericsRectorTest extends AbstractRectorTestCase | ||
{ | ||
#[DataProvider('provideData')] | ||
public function test(string $filePath): void | ||
{ | ||
$this->doTestFile($filePath); | ||
} | ||
|
||
public static function provideData(): Iterator | ||
{ | ||
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
} | ||
|
||
public function provideConfigFilePath(): string | ||
{ | ||
return __DIR__ . '/config/configured_rule.php'; | ||
} | ||
} |
160 changes: 160 additions & 0 deletions
160
tests/Eloquent/DocumentRelationGenericsRector/Fixture/model.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<?php | ||
|
||
namespace Tests\Eloquent\DocumentRelationGenericsRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use Illuminate\Database\Eloquent\Relations\HasMany; | ||
use Illuminate\Database\Eloquent\Relations\MorphOne; | ||
use Illuminate\Database\Eloquent\Relations\Pivot; | ||
use Illuminate\Database\Eloquent\SoftDeletingScope; | ||
|
||
class User extends Model | ||
{ | ||
public function posts(): HasMany | ||
{ | ||
return $this->hasMany(Post::class); | ||
} | ||
|
||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\HasMany | ||
*/ | ||
public function followers() | ||
{ | ||
return $this->hasMany(self::class, 'followers'); | ||
} | ||
|
||
public function teams() | ||
{ | ||
return $this->belongsToMany(Team::class) | ||
->using(UserTeam::class) | ||
->as('user_team') | ||
->withPivot([ | ||
'role', | ||
]); | ||
} | ||
} | ||
|
||
class Post extends Model | ||
{ | ||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo | ||
*/ | ||
public function user(): BelongsTo | ||
{ | ||
return $this->belongsTo(User::class)->withoutGlobalScope(SoftDeletingScope::class); | ||
} | ||
|
||
public function image(): MorphOne | ||
{ | ||
return $this->morphOne(Image::class, 'imageable'); | ||
} | ||
} | ||
|
||
class Image extends Model | ||
{ | ||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo | ||
*/ | ||
public function imageable() | ||
{ | ||
return $this->morphTo(); | ||
} | ||
} | ||
|
||
class Team extends Model | ||
{ | ||
// | ||
} | ||
|
||
class UserTeam extends Pivot | ||
{ | ||
// | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Tests\Eloquent\DocumentRelationGenericsRector\Fixture; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use Illuminate\Database\Eloquent\Relations\HasMany; | ||
use Illuminate\Database\Eloquent\Relations\MorphOne; | ||
use Illuminate\Database\Eloquent\Relations\Pivot; | ||
use Illuminate\Database\Eloquent\SoftDeletingScope; | ||
|
||
class User extends Model | ||
{ | ||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Tests\Eloquent\DocumentRelationGenericsRector\Fixture\Post, $this> | ||
*/ | ||
public function posts(): HasMany | ||
{ | ||
return $this->hasMany(Post::class); | ||
} | ||
|
||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\HasMany<\Tests\Eloquent\DocumentRelationGenericsRector\Fixture\User, $this> | ||
*/ | ||
public function followers() | ||
{ | ||
return $this->hasMany(self::class, 'followers'); | ||
} | ||
|
||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Eloquent\DocumentRelationGenericsRector\Fixture\Team, $this> | ||
*/ | ||
public function teams() | ||
{ | ||
return $this->belongsToMany(Team::class) | ||
->using(UserTeam::class) | ||
->as('user_team') | ||
->withPivot([ | ||
'role', | ||
]); | ||
} | ||
} | ||
|
||
class Post extends Model | ||
{ | ||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Tests\Eloquent\DocumentRelationGenericsRector\Fixture\User, $this> | ||
*/ | ||
public function user(): BelongsTo | ||
{ | ||
return $this->belongsTo(User::class)->withoutGlobalScope(SoftDeletingScope::class); | ||
} | ||
|
||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\MorphOne<\Tests\Eloquent\DocumentRelationGenericsRector\Fixture\Image, $this> | ||
*/ | ||
public function image(): MorphOne | ||
{ | ||
return $this->morphOne(Image::class, 'imageable'); | ||
} | ||
} | ||
|
||
class Image extends Model | ||
{ | ||
/** | ||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model, $this> | ||
*/ | ||
public function imageable() | ||
{ | ||
return $this->morphTo(); | ||
} | ||
} | ||
|
||
class Team extends Model | ||
{ | ||
// | ||
} | ||
|
||
class UserTeam extends Pivot | ||
{ | ||
// | ||
} | ||
|
||
?> |
8 changes: 8 additions & 0 deletions
8
tests/Eloquent/DocumentRelationGenericsRector/config/configured_rule.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
use Devfrey\RectorLaravel\Eloquent\DocumentRelationGenericsRector; | ||
use Rector\Config\RectorConfig; | ||
|
||
return function (RectorConfig $rectorConfig) { | ||
$rectorConfig->rule(DocumentRelationGenericsRector::class); | ||
}; |