Modern PHP 8.4 and Laravel patterns: architecture, Eloquent, queues, testing. Use when working with Laravel, Eloquent, Blade, artisan, PHPUnit, PHPStan, or building/testing PHP applications with frameworks. Not for PHP internals (php-src) or general PHP language discussion.
From compound-engineeringnpx claudepluginhub iliaal/compound-engineering-plugin --plugin compound-engineeringThis skill uses the workspace's default tool permissions.
references/factories.mdreferences/feature-testing.mdreferences/laravel-ecosystem.mdreferences/mocking-and-faking.mdreferences/testing.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
declare(strict_types=1) in every fileelse.$exception not $e, $request not $r?string not string|null. Always specify void. Import classnames everywhere, never inline FQN.['required', 'email'] for easier custom rule classesphpstan analyse --level=8). Aim for level 9 on new projects. Use @phpstan-type and @phpstan-param for generic collection types.Use these when applicable -- do not add explanatory comments in generated code (Claude and developers know them):
$fn = $obj->method(...)(Stringable&Countable)|null for complex constraintspublic string $name { get => strtoupper($this->name); set => trim($value); }public private(set) string $name -- public read, private writenew without parentheses in chains: new MyService()->handle()array_find(), array_any(), array_all() -- native array search/check without closures wrapping Collection__construct(private readonly PaymentService $payments)toDto() method to convert validated data to typed service parameters.Rule::requiredIf(), sometimes, exclude_if for complex form logicRoute::scopeBindings()->group(fn() => ...)Route::model('conversation', AiConversation::class) for custom binding resolutionRoute::apiResource('posts', PostController::class) -- generates index/store/show/update/destroy without create/edit{ "success": bool, "data": ..., "error": null, "meta": {} }snake_case plural table names matching model convention$table->foreignId('user_id')->constrained()->cascadeOnDelete()Schema::dropIfExists() for new tablesModel::preventLazyLoading(!app()->isProduction()) -- catch N+1 during developmentPost::with(['user:id,name'])->select(['id', 'title', 'user_id'])Post::where('status', 'draft')->update([...]) -- do not load into memory to updateincrement()/decrement() for counters in a single querychunk(1000)), lazy collections for memory-constrained processingscopeActive, scopeRecent) for reusable constraintswithCount('comments') / withExists('approvals') for aggregate subqueries -- never load relations just to count->when($filter, fn($q) => $q->where(...)) for conditional query buildingDB::transaction(fn() => ...) -- automatic rollback on exceptionModel::upsert($rows, ['unique_key'], ['update_cols']) for bulk insert-or-updatePrunable / MassPrunable trait with prunable() query for automatic stale record cleanup$guarded = [] is a mass assignment vulnerability -- always use explicit $fillablewhenLoaded() for relationships -- prevents N+1 in responseswhen() / mergeWhen() for permission-based field inclusionwhenPivotLoaded() for pivot datawithResponse() for custom headers, with() for metadata (version, pagination)toArray() from controllers.@deprecated in OpenAPI/docblock), remove in a later version.{ "success": false, "error": { "code": "...", "message": "..." } } structure. Use Handler::render() or a custom exception handler to normalize ValidationException, ModelNotFoundException, AuthorizationException, and application errors into one format. Callers build error handling once.toDto() ensure services receive typed, pre-validated data. Internal code trusts that input was validated at entry -- no redundant checks scattered through repositories or models.Bus::batch([...])->then()->catch()->finally()->dispatch()Bus::chain([new Step1, new Step2])->dispatch()Redis::throttle('api')->allow(10)->every(60)->then(fn() => ...)ShouldBeUnique interface to prevent duplicate processingfailed() method on jobstests/Feature/): HTTP through the full stack. Use $this->getJson(), $this->postJson(), etc.tests/Unit/): Isolated logic -- services, actions, value objects. No HTTP, minimal database.use RefreshDatabase for full migration reset per test. use DatabaseTransactions for wrapping in transaction (faster, but no migration testing). use DatabaseMigrations to run and rollback migrations per test.DB::table() insertstest_ prefix: test_user_can_update_own_profileactingAs($user) for auth, Sanctum::actingAs($user, ['ability']) for API authQueue::fake() then act then Queue::assertPushed(...)Http::fake() for outbound HTTP: Http::fake(['api.example.com/*' => Http::response([...], 200)]) then Http::assertSent(...)Gate::forUser($user)->allows('update', $post) for authorization assertionsassertDatabaseHas / assertDatabaseMissing to verify persistencepcov or XDEBUG_MODE=coverage in CI
General testing discipline (anti-patterns, rationalization resistance): see writing-tests skill.
See testing patterns and examples for PHPUnit essentials, data providers, and running tests.
See feature testing for auth, validation, API, console, and DB assertions.
See mocking and faking for facade fakes and action mocking.
See factories for states, relationships, sequences, and afterCreating hooks../vendor/bin/phpstan analyse --level=8 && ./vendor/bin/phpunit pass with zero warnings before declaring doneopcache.enable=1), set opcache.memory_consumption=256, opcache.max_accelerated_files=20000. Validate with opcache_get_status().opcache.jit_buffer_size=100M, opcache.jit=1255 (tracing). Biggest gains on CPU-bound code (math, loops), minimal impact on I/O-bound Laravel requests.opcache.preload=preload.php -- preload framework classes and hot app classes. Use composer dumpautoload --classmap-authoritative in production.php artisan config:cache && php artisan route:cache && php artisan view:cache && php artisan event:cache -- run on every deploy. composer install --optimize-autoloader --no-dev for production.