Skip to content

Commit

Permalink
feat: add Laravel database expressions rector
Browse files Browse the repository at this point in the history
  • Loading branch information
remarkablemark committed Dec 20, 2023
1 parent 9b8bdf8 commit 36d64b7
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 96 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ composer.phar

# PHPUnit
.phpunit.result.cache
.phpunit.cache/

# PHP CS Fixer
.php-cs-fixer.cache
Expand Down
42 changes: 35 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
# rector-template
# rector-laravel-database-expressions

[![packagist](https://img.shields.io/packagist/v/remarkablemark/rector-template)](https://packagist.org/packages/remarkablemark/rector-template)
[![test](https://github.com/remarkablemark/rector-template/actions/workflows/test.yml/badge.svg)](https://github.com/remarkablemark/rector-template/actions/workflows/test.yml)
[![packagist](https://img.shields.io/packagist/v/remarkablemark/rector-laravel-database-expressions)](https://packagist.org/packages/remarkablemark/rector-laravel-database-expressions)
[![test](https://github.com/remarkablemark/rector-laravel-database-expressions/actions/workflows/test.yml/badge.svg)](https://github.com/remarkablemark/rector-laravel-database-expressions/actions/workflows/test.yml)

[Rector](https://github.com/rectorphp/rector) template
[Rector](https://github.com/rectorphp/rector) to refactor Laravel 10 database expressions.

From [Laravel 10](https://laravel.com/docs/10.x/upgrade#database-expressions):

> Database "expressions" (typically generated via `DB::raw`) have been rewritten in Laravel 10.x to offer additional functionality in the future. Notably, the grammar's raw string value must now be retrieved via the expression's `getValue(Grammar $grammar)` method. Casting an expression to a string using `(string)` is no longer supported.
>
> If your application is manually casting database expressions to strings using `(string)` or invoking the `__toString` method on the expression directly, you should update your code to invoke the `getValue` method instead:
>
> ```php
> use Illuminate\Support\Facades\DB;
>
> $expression = DB::raw('select 1');
>
> $string = $expression->getValue(DB::connection()->getQueryGrammar());
> ```
## Requirements
Expand All @@ -14,7 +28,7 @@ PHP >=7.2
Install with [Composer](http://getcomposer.org/):
```sh
composer require --dev rector/rector remarkablemark/rector-template
composer require --dev rector/rector remarkablemark/rector-laravel-database-expressions
```
## Usage
Expand All @@ -27,10 +41,10 @@ Register rule in `rector.php`:
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Remarkablemark\RectorTemplate\ExampleRector;
use Remarkablemark\RectorLaravelDatabaseExpressions\LaravelDatabaseExpressionsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(ExampleRector::class);
$rectorConfig->rule(LaravelDatabaseExpressionsRector::class);
};
```

Expand All @@ -52,6 +66,20 @@ Clear the cache and apply the rule:
vendor/bin/rector process --clear-cache
```

## Rule

### Before

```php
DB::select(DB::raw('select 1'));
```

### After

```php
DB::select(DB::raw('select 1')->getValue(DB::getQueryGrammar()));
```

## License

[MIT](LICENSE)
11 changes: 7 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"name": "remarkablemark/rector-template",
"name": "remarkablemark/rector-laravel-database-expressions",
"type": "library",
"description": "Rector template",
"description": "Rector to fix Laravel 10 database expressions",
"keywords": [
"rector",
"template"
"refactor",
"laravel",
"database",
"expressions"
],
"license": "MIT",
"authors": [
Expand All @@ -20,7 +23,7 @@
},
"autoload": {
"psr-4": {
"Remarkablemark\\RectorTemplate\\": "src"
"Remarkablemark\\RectorLaravelDatabaseExpressions\\": "src"
}
},
"require-dev": {
Expand Down
55 changes: 0 additions & 55 deletions src/ExampleRector.php

This file was deleted.

71 changes: 71 additions & 0 deletions src/LaravelDatabaseExpressionsRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Remarkablemark\RectorLaravelDatabaseExpressions;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

final class LaravelDatabaseExpressionsRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Fix Laravel 10 database expressions', [
new CodeSample(
"DB::select(DB::raw('select 1'));",
"DB::select(DB::raw('select 1')->getValue(DB::getQueryGrammar()));"
)
]
);
}

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

/**
* @param MethodCall|StaticCall $node
*/
public function refactor(Node $node): ?Node
{
/** @var Node */
$subNode = $node->args[0]->value ?? null;

if (
! isset($subNode->class) ||
$this->getName($node->name) !== 'select' ||
strpos($this->getName($subNode->class), 'DB') === false ||
$this->getName($subNode->name) !== 'raw'
) {
return null;
}

$arguments[] = new Arg(
new StaticCall(
new Name('DB'),
'getQueryGrammar'
)
);

$node->args[0]->value = new MethodCall(
$subNode,
new Identifier('getValue'),
$arguments
);

return $node;
}
}
4 changes: 2 additions & 2 deletions tests/config/rector-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Remarkablemark\RectorTemplate\ExampleRector;
use Remarkablemark\RectorLaravelDatabaseExpressions\LaravelDatabaseExpressionsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(ExampleRector::class);
$rectorConfig->rule(LaravelDatabaseExpressionsRector::class);
};
27 changes: 17 additions & 10 deletions tests/fixture/SkipRuleTestFixture.php.inc
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<?php

namespace Package\Tests\Rector\MyFirstRector\Fixture;

class SomeClass
{
public function handleLogin(User $user, string $password)
{
return $user->isCorrectPassword($password);
}
}
declare(strict_types=1);

use Illuminate\Support\Facades\DB;

DB::select('select 1');

DB::select(
'select 2'
);

?>
$db->select('select 3');

DB::raw('select 4');

function select5() {
DB::select('select 5');
$db->select('select 5');
}
36 changes: 18 additions & 18 deletions tests/fixture/TestFixture.php.inc
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<?php

namespace Package\Tests\Rector\MyFirstRector\Fixture;
declare(strict_types=1);

class SomeClass
{
public function handlePasswordChange(User $user, string $password)
{
$user->setPassword($password);
}
}
use Illuminate\Support\Facades\DB;

?>
DB::select(DB::raw('select 1'));

DB::select(
DB::raw('select 2')
);

$db->select(DB::raw('select 3'));
-----
<?php

namespace Package\Tests\Rector\MyFirstRector\Fixture;
declare(strict_types=1);

use Illuminate\Support\Facades\DB;

DB::select(DB::raw('select 1')->getValue(DB::getQueryGrammar()));

class SomeClass
{
public function handlePasswordChange(User $user, string $password)
{
$user->changePassword($password);
}
}
DB::select(
DB::raw('select 2')->getValue(DB::getQueryGrammar())
);

?>
$db->select(DB::raw('select 3')->getValue(DB::getQueryGrammar()));

0 comments on commit 36d64b7

Please sign in to comment.