agentsclimarketplace

Laravel expert

Skill Akayashuu/agent-skills/skills/laravel-expert

Framework & language expert skills for Claude Code — idiomatic best practices for TypeScript, React, Vue, Svelte, Solid, Angular, Astro

Install
npx -y skills add Akayashuu/agent-skills --skill laravel-expert

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when building or reviewing Laravel apps or packages — service providers, container bindings, Eloquent/N+1, form requests, queued jobs, events, config vs env, facades vs DI, or Testbench/Pest tests.

SKILL.md

8.9 KB, as published. Nobody here has run it

Laravel Expert

Overview

Idiomatic Laravel leans on the framework's conventions — the container, providers, Eloquent — instead of fighting them, while staying explicit where implicit magic becomes a footgun (mass assignment, env() after caching, N+1). These are the judgment calls that separate a clean package or app from one that leaks queries and stale config. This skill is written from the package-author perspective: most rules apply to apps too, but provider wiring, auto-discovery, publishing, and Testbench are where packages differ.

Quick Reference

GoalDoAvoid
Read configuration at runtimeconfig('x.y')env('X') outside config/*.php (returns null when config is cached)
Bind an interfacebind/singleton in register()resolving other services in register()
Same instance everywheresingletonbind (new instance per resolve) when state/connection is shared
Register routes/migrations/publishboot()doing it in register()
Ship a package providerextra.laravel.providers auto-discoverytelling users to edit config/app.php
Merge package configmergeConfigFrom (shallow, user wins)overwriting the published config
Read request input$request->validated() from a Form Request$request->all() into Model::create()
Mass-assignment safetyexplicit $fillable allow-list$guarded = []
Load relationswith([...]) / withCount (eager)lazy access in a loop (N+1)
Background workShouldQueue + idempotent handle()assuming a job runs exactly once
Cross-cutting hookevent + listenerfat controller calling everything inline
Need a fresh/decorated instance per callinject the contracta facade (harder to swap/mock per-call)

Core Patterns

Service provider: register binds, boot wires. register() may only put things into the container — never resolve another service there, it might not be registered yet. Everything that touches the booted framework (routes, migrations, publishing, views, listeners) goes in boot(). A provider can be deferred (provides() + no eager work in register()) so it only loads when one of its bindings is resolved.

public function register(): void {
    $this->mergeConfigFrom(__DIR__.'/../config/package.php', 'package');
    $this->app->singleton(Reporter::class, HttpReporter::class);
}
public function boot(): void {
    $this->loadRoutesFrom(__DIR__.'/../routes/package.php');
    $this->publishes([__DIR__.'/../config/package.php' => config_path('package.php')], 'package-config');
}

Runnable: examples/service-provider.php

Container: bind contracts, choose singleton vs bind deliberately. bind gives a new instance every resolve; singleton gives one shared instance for the lifetime of the request/worker. Type-hinted dependencies are auto-resolved; use contextual binding (when()->needs()->give()) when one consumer needs a different implementation or a config-driven scalar.

$this->app->when(HttpReporter::class)->needs('$timeout')->giveConfig('package.timeout');

Runnable: examples/service-provider.php

Config over env() — the cached-config gotcha. php artisan config:cache (standard in production) freezes config and unsets $_ENV for app code: any env() call outside a config/*.php file then returns null. Read env only inside config files; everywhere else read config().

Eloquent: explicit mass assignment, native casts, eager loading. Prefer an explicit $fillable allow-list over $guarded = [] (an empty guard makes every column settable from input). Push enum/date conversion into casts(). Encapsulate reusable filters in query scopes. Kill N+1 by eager-loading the relations you'll touch with with()/withCount; in dev, Model::preventLazyLoading() turns an accidental lazy load into a thrown error.

$posts = Post::published()->with(['author', 'comments'])->withCount('comments')->get();

Runnable: examples/eager-loading.php

Form Requests: validate + authorize at the edge, read validated(). Move authorize() and rules() out of the controller; the action only runs on valid, authorized input. Reading $request->validated() returns only the rule-listed subset, which is what you should hand to create()/update() — closing the hole that $request->all() opens.

Runnable: examples/form-request.php

Queued jobs must be idempotent. Queues retry on failure ($tries, $backoff), so a job can run more than once. Make handle() safe under re-execution — claim state atomically (transaction + lockForUpdate) rather than trusting a "did we already do it" flag. Use ShouldBeUnique to avoid duplicate dispatch, failed() to compensate, and let SerializesModels re-fetch fresh model state instead of carrying a stale snapshot.

Runnable: examples/queued-job.php

Events/listeners decouple side effects. Emit a domain event (OrderPaid) and let listeners (email, ledger, webhook) react. Listeners can be queued (implements ShouldQueue) so slow side effects don't block the request. This keeps controllers thin and side effects independently testable.

Facades vs dependency injection. Facades are fine for framework globals in app code and read well (Cache::get). For your own collaborators — especially in package code and anything you want to swap or mock per call — inject the contract via the constructor: it's explicit, testable without Facade::swap, and works with contextual binding.

Test packages with Testbench, apps with Pest/PHPUnit. A package has no host app, so extend Orchestra Testbench's TestCase and register your provider via getPackageProviders(); it boots a minimal app so the container, config, and migrations behave realistically. App test suites instead extend the framework TestCase with RefreshDatabase.

Runnable: examples/testbench-test.php

Common Mistakes

  • env() outside config files — returns null once config:cache runs. The bug only shows in production. Route every value through a config/*.php file.
  • Resolving services in register() — order isn't guaranteed; the dependency may be unbound. Defer that work to boot() or a resolve-time closure.
  • $guarded = [] — a mass-assignment hole. Combine with $request->all() and an attacker sets is_admin. Use $fillable + validated().
  • N+1 from lazy loading in loops — accessing $post->author inside foreach fires a query per row. Eager load up front.
  • Assuming a job runs once — retries, duplicate dispatch, and at-least-once delivery mean it may run twice. Design for idempotency.
  • singleton for stateful-per-request things — sharing one instance across a queue worker's many jobs can leak state between jobs. Match lifetime to intent.
  • Editing config/app.php providers for a package — use extra.laravel.providers auto-discovery instead; only the app's own providers belong there.
  • Overwriting published config in boot() — use mergeConfigFrom (shallow merge) so the user's published values win.

When NOT to over-engineer

Not every class needs an interface and a binding — bind a contract when you actually have (or will have) more than one implementation or need to mock it; otherwise inject the concrete. Don't queue a job that finishes in microseconds, don't add an event for a one-listener side effect you'll never reuse, and don't reach for a custom cast or repository layer when an Eloquent model and a scope already say it clearly. Convention first; abstraction only where it earns its weight.

Sources

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.