Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Rector Laravel Laravel Package

driftingly/rector-laravel

Rector rules for Laravel and first-party packages like Cashier and Livewire. Automate Laravel upgrades with version-aware rule sets that detect your installed version from composer.json, plus dozens of individual rules for modernizing legacy code.

View on GitHub
Deep Wiki
Context7

Getting Started

Install as a dev dependency — this is tooling, never a runtime dependency:

composer require --dev driftingly/rector-laravel

Create (or update) rector.php at the project root. The one-line, set-it-and-forget-it configuration is the composer-based set, which reads your composer.json and applies every Laravel version set up to the version you actually have installed (and Faker, Livewire, and Cashier sets too, when those packages are present):

<?php declare(strict_types=1);

use Rector\Config\RectorConfig;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/app', __DIR__ . '/database', __DIR__ . '/routes', __DIR__ . '/tests'])
    ->withComposerBased(laravel: true);

Where to look first:

  1. docs/rector_rules_overview.md in the repo — the full rule catalogue, including unreleased rules on dev-main.
  2. The Rector Find Rule page — filter by the Laravel set group and see a before/after diff for each rule.
  3. The config sets under config/sets/ — each LaravelSetList::* constant maps to an explicit list of rules you can read.

Your first use case should be a read-only survey of an existing codebase, not an automatic rewrite:

vendor/bin/rector process --dry-run

That gives you a diff of everything the configured sets would change. If the diff is huge, you have not configured too much — you have simply discovered how much stale Laravel idiom exists in the codebase. Trim withPaths() or temporarily enable one set at a time before committing anything.

The second use case is the headline feature: a framework upgrade. Bump laravel/framework in composer.json, run Rector, and let it apply the mechanical parts of the upgrade (renamed methods, moved classes, changed signatures) so you only hand-review the behavioural parts.

Implementation Patterns

Set selection over individual rules. Prefer the named sets unless you have a specific reason. The version sets (LaravelSetList::LARAVEL_130 = the 12→13 upgrade) are narrow and safe to run during an upgrade. The cross-cutting sets are broader and deserve their own commits:

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
    ->withComposerBased(laravel: true)
    ->withSets([
        LaravelSetList::LARAVEL_CODE_QUALITY,
        LaravelSetList::LARAVEL_COLLECTION,
        LaravelSetList::LARAVEL_TESTING,
        LaravelSetList::LARAVEL_TYPE_DECLARATIONS,
    ]);

Land one set per pull request. A PR that only applies LARAVEL_COLLECTION (e.g. ->filter()->first() → ->first(fn ($x) => ...), pluck()->unique() → unique('col'), ->where(...)->count() shortcuts) is reviewable. A PR that mixes collection rewrites, facade-to-DI conversion, and type declarations is not. Use git add -p or, better, one Rector run per branch.

Split mechanical from semantic. Treat LARAVEL_STATIC_TO_INJECTION (Facade → constructor injection) and LARAVEL_ELOQUENT_MAGIC_METHOD_TO_QUERY_BUILDER as semantic refactors, not formatting: they change constructor signatures and can alter static-analysis assumptions. Run them separately from cosmetic sets like LARAVEL_FACADE_ALIASES_TO_FULL_NAMES or LARAVEL_ARRAY_STR_FUNCTION_TO_STATIC_CALL.

Gate in CI with --dry-run. Once you have burned down the backlog, add a pipeline step so drift never re-accumulates:

vendor/bin/rector process --dry-run --no-progress-bar

Exit code is non-zero when changes are pending, which makes it a natural quality gate alongside Pint and PHPStan. Cache storage/rector (or wherever your cache dir points) between runs to keep it fast on large repos.

Configure the rules that demand it. A handful of rules are configurable and must be added explicitly with withConfiguredRule():

use RectorLaravel\Rector\FuncCall\RemoveDumpDataDeadCodeRector;
use RectorLaravel\Rector\MethodCall\WhereToWhereLikeRector;

->withConfiguredRule(RemoveDumpDataDeadCodeRector::class, ['dd', 'dump', 'var_dump', 'ray'])
->withConfiguredRule(WhereToWhereLikeRector::class, [
    WhereToWhereLikeRector::USING_POSTGRES_DRIVER => true,
])

WhereToWhereLikeRector is the one to get right — whereLike() handles case sensitivity differently on Postgres versus MySQL, so the boolean is not cosmetic.

Hold opinionated rules behind a deliberate opt-in. ResponseHelperCallToJsonResponseRector, RemoveModelPropertyFromFactoriesRector, EmptyToBlankAndFilledFuncRector, UseForwardsCallsTraitRector, MinutesToSecondsInCacheRector are excluded from every set by design. Add them via withRules() when your team agrees with the opinion, and expect reviewers to question each one the first time.

Compose with core Rector. RectorLaravel is an extension, so it plays well with the standard sets and level configuration. A pragmatic ordering is: framework upgrade sets first, then LaravelSetList::LARAVEL_CODE_QUALITY, then core CODE_QUALITY/DEAD_CODE, then TYPE_DECLARATION. Run Pint afterwards — Rector does not guarantee formatting.

Write a custom rule when you have house idiom. The repo ships a scaffold:

composer make:rule -- If_/ConvertIfToWhen
composer make:rule -- --configurable My/SomeRule

This generates the rule plus its fixture-based test. If you fork the package to add internal rules, mirror the existing pattern: AbstractRector, getNodeTypes(), refactor(), getRuleDefinition(), and a tests/Rector/.../Fixture/ directory. Keeping rules in a fork (or a private companion package) is often cleaner than sprinkling ad-hoc regex scripts across the codebase.

Gotchas and Tips

  • Version detection is only as good as your constraints. withComposerBased(laravel: true) reads composer.json, not composer.lock. If your constraint is ^10.0|^11.0|^12.0, the detected "installed version" may be lower than what production runs, and you will silently skip upgrade rules. Be explicit, or check what got registered.
  • The manual level sets are deprecated. LaravelLevelSetList::UP_TO_LARAVEL_130 still works but is on the way out. Migrate to withComposerBased() now rather than later — the deprecation will bite you in a future major.
  • Always dry-run before writing. On a first run against a legacy app, run --dry-run and read the diff. Rector's output is only as safe as your set selection; the version sets are conservative, the quality sets are not.
  • Exclude vendors, storage, bootstrap cache, and generated code. Rector will happily walk into vendor/ if withPaths() is too broad. Keep paths narrow and add withSkip() for directories like app/Console/Kernel.php legacy files, generated IDE helpers (_ide_helper.php, _ide_helper_models.php), and migration fixtures you do not want rewritten.
  • Clear the cache when rules seem to be ignored. Rector caches aggressively. If you add a rule and nothing changes, run vendor/bin/rector process --clear-cache. Also verify the rule is actually in a set — plenty of rules live only in dev-main and are not yet released.
  • Facade-to-injection is not a pure refactor. LARAVEL_STATIC_TO_INJECTION alters constructor signatures, which breaks any subclass, new-ed-by-hand service, or queue job that did not previously take arguments. Run it class-by-class, and let PHPStan/your test suite catch the fallout.
  • Type declarations can surface real bugs. LARAVEL_TYPE_DECLARATIONS adds return types and generics; it will happily expose methods that return null where you claimed a value. Treat the resulting static-analysis errors as findings, not noise.
  • RemoveDumpDataDeadCodeRector deletes, not comments. Configure it with the full list of debug functions you actually use (including Ray's ray(), ds(), dumpp()) or a stray dd() in a controller survives to production. Run it last, after your other rules, and grep the diff for removed lines before merging.
  • RouteActionCallableRector needs your namespace. The string-to-callable conversion ('UserController@index' → [UserController::class, 'index']) cannot resolve the right FQCN without the NAMESPACE config and, for split route files, the ROUTES per-file overrides. Wrong namespace yields broken imports rather than a clean skip.
  • Postgres is a config decision, not a default. WhereToWhereLikeRector behaves differently across drivers. Get the boolean wrong and you trade readable code for subtly case-insensitive queries.
  • Review the opinionated rules by hand. ResponseHelperCallToJsonResponseRector changes the response object type — code asserting on Illuminate\Http\JsonResponse or on response() helpers may still pass, but IDE and static analysis contracts shift. Same for EmptyToBlankAndFilledFuncRector: empty('0') and blank('0') agree, but empty() on objects/collections is a semantic minefield.
  • Extension points worth knowing: every rule is a standard AbstractRector, so you can temporarily isolate one with a throwaway withRules([SomeRector::class]) config, or via the CLI --rule option, which is the fastest way to debug "which rule made this change?" Combine with -vv for verbose output. Fixture tests run with vendor/bin/phpunit (or composer test) and are the right place to pin down an edge case before proposing a rule upstream.
  • Keep the package current, but not on dev-main in production configs. The rules overview page documents unreleased rules; those can change behaviour or vanish. Pin to a tagged release and read the release notes on upgrade — the 2026-10-27 release is recent enough that you should not assume the pre-1.0 API is frozen.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager