Skip to content

Commit

Permalink
Adds the EloquentOrderByToLatestOrOldestRector rule (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
peterfox committed Sep 22, 2023
1 parent 845e8a0 commit 2960c46
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,29 @@ The EloquentMagicMethodToQueryBuilderRule is designed to automatically transform
- class: [`RectorLaravel\Rector\StaticCall\EloquentMagicMethodToQueryBuilderRector`](../src/Rector/StaticCall/EloquentMagicMethodToQueryBuilderRector.php)

```diff
-User::find(1);
-User::where('email', 'test@test.com')->first();
+User::query()->find(1);
+User::query()->where('email', 'test@test.com')->first();

<br>

## EloquentOrderByToLatestOrOldestRector

Changes `orderBy()` to `latest()` or `oldest()`

- class: [`RectorLaravel\Rector\MethodCall\EloquentOrderByToLatestOrOldestRector`](../src/Rector/MethodCall/EloquentOrderByToLatestOrOldestRector.php)

```diff
use Illuminate\Database\Eloquent\Builder;

-$builder->orderBy('created_at');
-$builder->orderBy('created_at', 'desc');
-$builder->orderBy('deleted_at');
+$builder->latest();
+$builder->oldest();
+$builder->latest('deleted_at');

use App\Models\User;

-$user = User::find(1);
Expand Down
110 changes: 110 additions & 0 deletions src/Rector/MethodCall/EloquentOrderByToLatestOrOldestRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

declare(strict_types=1);

namespace RectorLaravel\Rector\MethodCall;

use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \RectorLaravel\Tests\Rector\MethodCall\EloquentOrderByToLatestOrOldestRector\EloquentOrderByToLatestOrOldestRectorTest
*/
class EloquentOrderByToLatestOrOldestRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Changes orderBy() to latest() or oldest()',
[
new CodeSample(
<<<'CODE_SAMPLE'
use Illuminate\Database\Eloquent\Builder;
$builder->orderBy('created_at');
$builder->orderBy('created_at', 'desc');
$builder->orderBy('deleted_at');
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use Illuminate\Database\Eloquent\Builder;
$builder->latest();
$builder->oldest();
$builder->latest('deleted_at');
CODE_SAMPLE
,
),
]
);
}

public function getNodeTypes(): array
{
return [MethodCall::class];
}

public function refactor(Node $node): ?Node
{
if (! $node instanceof MethodCall) {
return null;
}

if ($this->isOrderByMethodCall($node)) {
return $this->convertOrderByToLatest($node);
}

return null;
}

private function isOrderByMethodCall(MethodCall $methodCall): bool
{
// Check if it's a method call to `orderBy`

return $this->isObjectType($methodCall->var, new ObjectType('Illuminate\Database\Query\Builder'))
&& $methodCall->name instanceof Node\Identifier
&& ($methodCall->name->name === 'orderBy' || $methodCall->name->name === 'orderByDesc')
&& count($methodCall->args) > 0;
}

private function convertOrderByToLatest(MethodCall $methodCall): MethodCall
{
if (! isset($methodCall->args[0]) && ! $methodCall->args[0] instanceof Node\VariadicPlaceholder) {
return $methodCall;
}

$columnVar = $methodCall->args[0]->value ?? null;
if ($columnVar === null) {
return $methodCall;
}

$direction = $methodCall->args[1]->value->value ?? 'asc';
if ($this->isName($methodCall->name, 'orderByDesc')) {
$newMethod = 'oldest';
} else {
$newMethod = $direction === 'asc' ? 'latest' : 'oldest';
}
if ($columnVar instanceof Node\Scalar\String_ && $columnVar->value === 'created_at') {
$methodCall->name = new Node\Identifier($newMethod);
$methodCall->args = [];

return $methodCall;
}

if ($columnVar instanceof Node\Scalar\String_) {
$methodCall->name = new Node\Identifier($newMethod);
$methodCall->args = [new Node\Arg(new Node\Scalar\String_($columnVar->value))];

return $methodCall;
}

$methodCall->name = new Node\Identifier($newMethod);
$methodCall->args = [new Node\Arg($columnVar)];

return $methodCall;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace RectorLaravel\Tests\Rector\MethodCall\EloquentOrderByToLatestOrOldestRector;

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

final class EloquentOrderByToLatestOrOldestRectorTest 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';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace RectorLaravel\Tests\Rector\Cast\DatabaseExpressionCastsToMethodCall\Fixture;

use Illuminate\Database\Query\Builder;

$column = 'tested_at';

/** @var Builder $query */
$query->orderBy('created_at');
$query->orderBy('created_at', 'desc');
$query->orderBy('submitted_at');
$query->orderByDesc('submitted_at');
$query->orderBy($column);

?>
-----
<?php

namespace RectorLaravel\Tests\Rector\Cast\DatabaseExpressionCastsToMethodCall\Fixture;

use Illuminate\Database\Query\Builder;

$column = 'tested_at';

/** @var Builder $query */
$query->latest();
$query->oldest();
$query->latest('submitted_at');
$query->oldest('submitted_at');
$query->latest($column);

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

namespace RectorLaravel\Tests\Rector\Cast\DatabaseExpressionCastsToMethodCall\Fixture;

$query->orderBy('created_at');

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

namespace RectorLaravel\Tests\Rector\Cast\DatabaseExpressionCastsToMethodCall\Fixture;

use Illuminate\Database\Query\Builder;

/** @var Builder $query */
$query->orderBy();

?>
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\MethodCall\EloquentOrderByToLatestOrOldestRector;

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

$rectorConfig->rule(EloquentOrderByToLatestOrOldestRector::class);
};

0 comments on commit 2960c46

Please sign in to comment.