laravel-providers
Installation
SKILL.md
Laravel Providers
Application setup has two homes. AppServiceProvider sets the app-wide defaults: how models behave, which date class the app uses, the morph map, rate limiters and local debug tooling. Booters, small invokable classes in app/Booters/, replace the closures Laravel puts in bootstrap/app.php for middleware, the schedule and exceptions. Both stay readable because each concern sits in its own named method or class.
Rules
- Named methods.
register()andboot()only call privateregister{Thing}()andboot{Thing}()methods, one per concern:bootMorphMaps(),bootModelConfig(),bootDates(),bootRateLimiters(),bootDebug(). Thenboot()reads as a table of contents. - Models are unguarded. Call
Model::unguard()once, and never put$fillableor$guardedon a model. Mass-assignment safety comes from typed input (Form Request, then DTO), never from raw request arrays. - Lazy loading is prevented locally. Call
Model::preventLazyLoading($this->app->isLocal())and report violations to Ray (or the log, where the project doesn't use Ray) rather than throwing, so you see an N+1 while you work without the page breaking. Production pays nothing. - The morph map is enforced. Use
Relation::enforceMorphMap()and key every entry by the model's PascalCase class basename:'Order' => Order::class. An unmapped model then throws instead of storing its class name, and a namespace move can't orphan stored rows. A model goes in as soon as it joins any polymorphic relation, including the ones packages create: Sanctum'stokenable, notifications, activity or media logs. - Dates are immutable. Call
Date::use(CarbonImmutable::class), sonow(),today()and every date cast returnCarbonImmutable. As a result, type-hint dates asCarbonInterfaceorCarbonImmutable, neverCarbon, which throws aTypeErroron the first cast value it receives. Create dates withnow(),today()orDate::parse().Carbon::parse()returns a mutable instance. - Rate limiters live in
AppServiceProvider::bootRateLimiters(). Every limiter goes there, including a service's own, so they're all registered on every request and in every worker. - Macros live in one
MacroServiceProvider. One place to find every method the app adds to framework classes. - Debug tooling is local only. Ray query watchers and dev-only providers are guarded by
$this->app->isLocal()or an environment check. bootstrap/app.phpwires booters, not closures.withMiddleware,withScheduleandwithExceptionseach take an invokable booter. Route surfaces are registered throughwithRouting(), which laravel-routing owns.- Name every scheduled callback. Every
->call()gets a->name().withoutOverlapping()andonOneServer()throw on an unnamed callback, and the name is whatschedule:listshows. Follow the project's naming style, whether class names or descriptions.