tutorigo/laravel-ide-macros
Bring IDE autocompletion to your Laravel macros. This package scans your application for registered macros and generates helper files, so PhpStorm and other IDEs recognize macro methods with full type hints and suggestions.
kristofmorva/laravel-ide-macros is a dev-only companion to barryvdh/laravel-ide-helper. Where ide-helper describes the framework as shipped, this package describes the framework as your app extends it — the macros you register on Request, Collection, Response, TestResponse, facades, and your own macroable classes. Without it, PhpStorm/Psalm flag every Request::macro() call site as an undefined method.
composer require --dev kristofmorva/laravel-ide-macros
php artisan vendor:publish --tag=ide-macros-config
php artisan ide-macros:generate
_ide_helper_macros.php — sibling to ide-helper's output, so both are usually already excluded from runtime autoload via composer.json exclude-from-classmap / .gitignore).Where to look first: the generated file itself. It is a plain PHP file of class stubs with @method annotations; reading it tells you in seconds whether a macro you registered actually got picked up, and with which signature. That file, not the package source, is your primary debugging surface.
First use case: you have Request::macro('isApiRequest', fn () => $this->is('api/*')) in a service provider and every controller using $request->isApiRequest() is underlined in red. Run the generator, and the annotation appears — no more @var gymnastics or inline @phpstan-ignore.
Version 1.6.0 is the release that makes the two most common macro targets work out of the box: Illuminate\Http\Request (previously missing entirely, which meant most real-world request macros were invisible to the IDE) and the testing classes TestResponse and TestView.
Register macros in one conventional place, then regenerate. Keep all ::macro() calls in a dedicated provider (e.g. App\Providers\MacroServiceProvider) so the generator's scan is deterministic and reviewers can see macro surface in a single diff:
// app/Providers/MacroServiceProvider.php
Request::macro('isApiRequest', function (): bool {
/** @var \Illuminate\Http\Request $this */
return $this->is('api/*');
});
Type the closure, not the annotation. The generator reflects the closure signature. Add real parameter and return types (or @param/@return docblocks on the closure) and they propagate into the helper file — which is what gives you working completion and static analysis:
Collection::macro('toUpperKeys', function (): static {
return $this->mapWithKeys(fn ($v, $k) => [strtoupper($k) => $v]);
});
Commit the generated file, regenerate in CI. Treating _ide_helper_macros.php as a committed artifact (the same way most teams treat _ide_helper.php) means a developer who pulls and doesn't run the command still gets correct completion. Then add a cheap staleness gate so it can't silently drift:
"scripts": {
"ide-macros": "@php artisan ide-macros:generate",
"ide-macros:check": [
"@ide-macros",
"git diff --exit-code _ide_helper_macros.php"
]
}
Run composer ide-macros in a pre-commit hook or a CI job. The failure mode this prevents — a macro renamed in a provider, helper file stale, IDE happily autocompleting a method that no longer exists — is the single most costly way this package bites teams.
Custom macroable classes. If you have your own Macroable-using class (a custom Builder, a Money value object, a domain-specific collection), register it in the published config so the generator emits stubs for it. This is the main reason to publish the config at all; without it you only get the framework classes.
Test-specific macros (new in 1.6.0). TestResponse macros are typically registered in TestCase::setUp() or a dedicated testing provider. With 1.6.0 those now produce annotations, so fluent test assertions stay typed:
TestResponse::macro('assertJsonHasItems', function (int $count) {
/** @var \Illuminate\Testing\TestResponse $this */
return $this->assertJsonCount($count, 'data');
});
Regenerate after touching test macros too — they're in the same file.
Pair with ide-helper, don't replace it. The two packages write different files and serve different purposes. Run ide-helper:generate, ide-helper:models, and ide-macros:generate in that order in one script so contributors have a single command to remember.
require rather than require-dev, you ship a generator and its config to production for zero benefit. Verify with composer why kristofmorva/laravel-ide-macros.require it manually — it exists purely for static analysis. Duplicate stub class declarations against ide-helper's output will cause confusing "cannot redeclare class" errors if you get this wrong.$this inside macro closures is untyped. PHPStan/Psalm will complain about $this->foo() in the closure body. Add the inline /** @var \Illuminate\Http\Request $this */ shown above, or the closure body reports errors even though the call sites are now clean.Illuminate\Support\Str is reached as Str::macroName(); registered on a Collection instance, as $collection->macroName(). The generator mirrors the class you registered against, so if you register on the facade's accessor but call it through the facade (or vice versa), the annotation won't line up. Register against the underlying class.if ($this->app->runningUnitTests()) won't be seen by a normal CLI run)._ide_helper_macros.php is not — your edits are destroyed on the next run.composer update. A framework minor bump can add or change macroable classes; regenerating keeps the stubs honest and surfaces any new type conflicts early.How can I help you explore Laravel packages today?