{"id":"57674ac9-dfcf-48ff-8d27-5995acfdbb70","shortId":"qVge4s","kind":"skill","title":"php-laravel","tagline":">-","description":"# PHP & Laravel Development\n\n## Code Style\n\n- `declare(strict_types=1)` in every file\n- Happy path last -- handle errors/guards first, success at the end. Use early returns; avoid `else`.\n- Comments only explain *why*, never *what*. Never comment tests. If code needs a \"what\" comment, rename or restructure instead.\n- No single-letter variables -- `$exception` not `$e`, `$request` not `$r`\n- `?string` not `string|null`. Always specify `void`. Import classnames everywhere, never inline FQN.\n- Validation uses array notation `['required', 'email']` for easier custom rule classes\n- Static analysis: run PHPStan at level 8+ (`phpstan analyse --level=8`). Aim for level 9 on new projects. Use `@phpstan-type` and `@phpstan-param` for generic collection types.\n\n## Modern PHP (8.4)\n\nUse these when applicable -- do not add explanatory comments in generated code (Claude and developers know them):\n- Readonly classes and properties for immutable data\n- Enums with methods and interfaces for domain constants\n- Match expressions over switch\n- Constructor promotion with readonly\n- First-class callable syntax `$fn = $obj->method(...)`\n- Fibers for cooperative async when Swoole/ReactPHP not available\n- DNF types `(Stringable&Countable)|null` for complex constraints\n- Property hooks: `public string $name { get => strtoupper($this->name); set => trim($value); }`\n- Asymmetric visibility: `public private(set) string $name` -- public read, private write\n- `new` without parentheses in chains: `new MyService()->handle()`\n- `array_find()`, `array_any()`, `array_all()` -- native array search/check without closures wrapping Collection\n\n## Laravel Architecture\n\n- **Thin controllers** -- controllers only: validate, call service/action, return response. Domain behavior (scopes, accessors, relationships) lives in models; cross-cutting orchestration lives in service classes.\n- **Service classes** for business logic with readonly DI: `__construct(private readonly PaymentService $payments)`\n- **Action classes** (single-purpose invokable) for operations that cross service boundaries\n- **Form Requests** for all validation -- never validate inline in controllers. Add `toDto()` method to convert validated data to typed service parameters.\n- Conditional validation: `Rule::requiredIf()`, `sometimes`, `exclude_if` for complex form logic\n- **Events + Listeners** for side effects (notifications, logging, cache invalidation). Do not put side effects in services.\n- Feature folder organization over type-based when project exceeds ~20 models\n\n## Production Resilience\n\n- **Fail-fast config validation**: validate critical config values in a service provider's `boot()` method. Missing API keys, invalid DSNs, or misconfigured queues should crash the app on startup, not on the first request that hits the code path.\n- **Health endpoints**: expose `/health` (shallow, returns 200 if the process responds) and `/ready` (deep, checks database, Redis, and critical service connectivity). Use Laravel's built-in health checks (`Illuminate\\Health`) or a simple route that queries each dependency.\n\n## Routing\n\n- Scoped route model binding to prevent cross-tenant access: `Route::scopeBindings()->group(fn() => ...)`\n- `Route::model('conversation', AiConversation::class)` for custom binding resolution\n- API resource routes: `Route::apiResource('posts', PostController::class)` -- generates index/store/show/update/destroy without create/edit\n- Standardized JSON response envelope: `{ \"success\": bool, \"data\": ..., \"error\": null, \"meta\": {} }`\n\n## Migrations\n\n- Anonymous class migrations -- no class name collisions\n- `snake_case` plural table names matching model convention\n- Foreign keys: `$table->foreignId('user_id')->constrained()->cascadeOnDelete()`\n- Always add index on foreign keys and frequently filtered columns\n- Down method: include rollback logic or `Schema::dropIfExists()` for new tables\n- Separate schema and data migrations -- data backfills in their own migration file, not mixed with DDL\n- Renames/removals use expand-contract: add new column → backfill → switch reads → drop old (see `ia-postgresql` skill for the full pattern)\n- Never edit a migration that has run in a shared environment -- write a new one\n\n## Eloquent\n\n- `Model::preventLazyLoading(!app()->isProduction())` -- catch N+1 during development\n- Select only needed columns: `Post::with(['user:id,name'])->select(['id', 'title', 'user_id'])`\n- Bulk operations at database level: `Post::where('status', 'draft')->update([...])` -- do not load into memory to update\n- `increment()`/`decrement()` for counters in a single query\n- Composite indexes for common query combinations\n- Chunking for large datasets (`chunk(1000)`), lazy collections for memory-constrained processing\n- Query scopes (`scopeActive`, `scopeRecent`) for reusable constraints\n- `withCount('comments')` / `withExists('approvals')` for aggregate subqueries -- never load relations just to count\n- `->when($filter, fn($q) => $q->where(...))` for conditional query building\n- `DB::transaction(fn() => ...)` -- automatic rollback on exception\n- `Model::upsert($rows, ['unique_key'], ['update_cols'])` for bulk insert-or-update\n- `Prunable` / `MassPrunable` trait with `prunable()` query for automatic stale record cleanup\n- `$guarded = []` is a mass assignment vulnerability -- always use explicit `$fillable`\n\n## API Resources\n\n- `whenLoaded()` for relationships -- prevents N+1 in responses\n- `when()` / `mergeWhen()` for permission-based field inclusion\n- `whenPivotLoaded()` for pivot data\n- `withResponse()` for custom headers, `with()` for metadata (version, pagination)\n\n## API Design\n\n- **Contract-first**: define the API Resource and Form Request before writing the controller. The resource is the response contract, the Form Request is the input contract -- implementation follows.\n- **Hyrum's Law awareness**: every observable response field, ordering, or timing becomes a dependency for callers. Use API Resources to control exactly what's serialized -- never return raw models or `toArray()` from controllers.\n- **Addition over modification**: add new fields/endpoints rather than changing or removing existing ones. Removing a field from an API Resource breaks callers silently. Deprecate first (`@deprecated` in OpenAPI/docblock), remove in a later version.\n- **Consistent error envelope**: all exceptions should produce the same `{ \"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.\n- **Boundary validation via Form Requests**: validate at the HTTP boundary, not inside services. Form Requests with `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.\n- **Third-party responses are untrusted data**: validate shape and content of external API responses before using them in logic, rendering, or decision-making. A compromised or misbehaving service can return unexpected types, malicious content, or missing fields. Wrap in a DTO or validate through a dedicated response class before use.\n\n## Queues & Jobs\n\n- Job batching with `Bus::batch([...])->then()->catch()->finally()->dispatch()`\n- Job chaining for sequential ops: `Bus::chain([new Step1, new Step2])->dispatch()`\n- Rate limiting: `Redis::throttle('api')->allow(10)->every(60)->then(fn() => ...)`\n- `ShouldBeUnique` interface to prevent duplicate processing\n- Always handle failures -- implement `failed()` method on jobs\n\n## Testing (PHPUnit)\n\n- **Feature tests** (`tests/Feature/`): HTTP through the full stack. Use `$this->getJson()`, `$this->postJson()`, etc.\n- **Unit tests** (`tests/Unit/`): Isolated logic -- services, actions, value objects. No HTTP, minimal database.\n- Default to feature tests for anything touching routes, controllers, or models\n- `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.\n- Model factories for all test data -- never raw `DB::table()` inserts\n- One behavior per test. Name with `test_` prefix: `test_user_can_update_own_profile`\n- Assert both response status AND side effects (DB state, dispatched jobs, sent notifications)\n- `actingAs($user)` for auth, `Sanctum::actingAs($user, ['ability'])` for API auth\n- Fake facades BEFORE the action: `Queue::fake()` then act then `Queue::assertPushed(...)`\n- `Http::fake()` for outbound HTTP: `Http::fake(['api.example.com/*' => Http::response([...], 200)])` then `Http::assertSent(...)`\n- `Gate::forUser($user)->allows('update', $post)` for authorization assertions\n- `assertDatabaseHas` / `assertDatabaseMissing` to verify persistence\n- Coverage target: 80%+ with `pcov` or `XDEBUG_MODE=coverage` in CI\nFor generic test discipline (anti-patterns, mock rules, rationalization resistance), see the `ia-writing-tests` skill — this skill covers Laravel-specific patterns that sit on top of that foundation.\nSee [testing patterns and examples](./references/testing.md) for PHPUnit essentials, data providers, and running tests.\nSee [feature testing](./references/feature-testing.md) for auth, validation, API, console, and DB assertions.\nSee [mocking and faking](./references/mocking-and-faking.md) for facade fakes and action mocking.\nSee [factories](./references/factories.md) for states, relationships, sequences, and afterCreating hooks.\n\n## Common Pitfalls\n\nConcrete Laravel footguns that recur across projects. Each is a real class of bug caught in production review; all are invisible to PHPStan and feature tests alone.\n\n**Query-builder `update()` silently skips observers and audit events.** `Model::query()->where(...)->update([...])` and `Relation::update([...])` are query-builder operations — they do NOT fire Eloquent model events. Any observer registered via `#[ObservedBy]`, OwenIt Auditable trait, or `static::saving/updating` callback is bypassed. No audit row, no cascading cleanup, no dispatched jobs. Fix: `lockForUpdate() + save()` inside a transaction gives the same idempotent-atomic semantics while still firing events. Reach for raw mass update only with a `// intentionally bypasses <Observer>` comment documenting the bypass.\n\n**Observer `deleting()` cleanup at parent scope nukes siblings.** If a `DocumentObserver::deleting()` calls `Storage::deleteDirectory($parent->uploadPath)` and the parent has a `hasMany` of Documents, deleting one child wipes storage for all siblings while their DB rows remain pointing at non-existent keys. Detection: when any single-row `$model->delete()` has an Observer, open `app/Observers/{Model}Observer.php` and check whether `deleting()` / `deleted()` hooks operate at parent scope or single-row scope. Fix: scope cleanup to the row's own storage paths, or move cleanup out of the observer into an Action class that knows the sibling count.\n\n**`chunkById + json_decode + mutate + json_encode + update` loses concurrent writes on jsonb columns.** The window between the SELECT populating `$row->metadata` and the per-row UPDATE is milliseconds; any user save in that window is silently overwritten by the migration's stale snapshot. Fix: use in-place `DB::raw(\"jsonb_set(metadata, '{path}', ...)\")` for shallow edits, or `lockForUpdate()` inside the chunk for arbitrary PHP logic. Default `chunkById + decode/encode` is only safe during a maintenance window with writes blocked.\n\n**`date:<fmt>` cast format only reaches `$model->toArray()`, NOT `JsonResource::resolve()`.** A `JsonResource` that does `return ['started_at' => $this->resource->started_at]` emits ISO 8601 from Carbon's own `JsonSerializable`, ignoring the cast format entirely. Changing `date` to `date:m/d/Y` is NOT an API contract change unless the code path uses `$model->toArray()` directly (Filament admin, DTOs pulling from `toArray()`, direct `json_encode($model)`). Verify with a live reproducer before flagging as wire-format regression.\n\n**Nested-array validation accepts scalar elements when only `*.field` rules are set.** Rules like `'items.*.name' => 'string'` and `'items.*.date' => 'date'` do NOT enforce that each `items.*` is itself an array. Scalar elements pass validation; the handler's `$data['items'][0]['name']` then yields `null` (string indexed as array — PHP warning, blank row written) or `TypeError` (int indexed as array — 500 to the caller). Always pair per-key rules with an explicit `'items.*' => 'array'` constraint.\n\n**`DB::afterCommit` closes the rollback half but not the post-commit-failure half.** Wrapping an external mutation (S3, search index, third-party webhook) in `DB::afterCommit($closure)` prevents the external work from running when the transaction rolls back. It does NOT retry the external op when it fails after commit — the closure runs once, exceptions bubble out of the response cycle, the operation drops, and the DB row now advertises a state the external system doesn't reflect. Closing patterns: (a) queued job with `tries` + exponential backoff + `failed(Throwable $e)` handler that reverts the DB precondition the job was supposed to make true; (b) external-op-first-then-DB when the op is idempotent on the destination key (works for `Storage::copy`, fails for `Storage::move` after first attempt); (c) reconciler scheduled command that walks rows with stuck \"in-flight\" flags. Pattern (a) is the general-purpose default; queue retry semantics already model the transient/permanent split.\n\n## Discipline\n\n- Simplicity first -- every change as simple as possible, impact minimal code\n- Only touch what's necessary -- avoid introducing unrelated changes\n- No hacky workarounds -- if a fix feels wrong, step back and implement the clean solution\n- Before adding a new abstraction, verify it appears in 3+ places. If not, inline it.\n- No empty catch blocks -- log or rethrow, never swallow exceptions\n- Verify: `./vendor/bin/phpstan analyse --level=8 && ./vendor/bin/phpunit` pass with zero warnings before declaring done\n\n## Production Performance\n\nFor OPcache + JIT + preloading configuration and Laravel-specific deploy caches (`config:cache`, `route:cache`, etc.), load [production-performance.md](./references/production-performance.md).\n\n## References\n\n- [laravel-ecosystem.md](./references/laravel-ecosystem.md) -- Notifications, Task Scheduling, Custom Casts\n- [testing.md](./references/testing.md) -- PHPUnit essentials, data providers, running tests\n- [feature-testing.md](./references/feature-testing.md) -- Auth, validation, API, console, DB assertions\n- [mocking-and-faking.md](./references/mocking-and-faking.md) -- Facade fakes, action mocking, Mockery\n- [factories.md](./references/factories.md) -- States, relationships, sequences, afterCreating hooks\n- [production-performance.md](./references/production-performance.md) -- OPcache, JIT, preloading, Laravel deploy caches","tags":["php","laravel","skills","iliaal","agent-skills","ai-coding-assistant","ai-tools","claude-code"],"capabilities":["skill","source-iliaal","skill-php-laravel","topic-agent-skills","topic-ai-coding-assistant","topic-ai-tools","topic-claude-code","topic-skills"],"categories":["ai-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/iliaal/ai-skills/php-laravel","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add iliaal/ai-skills","source_repo":"https://github.com/iliaal/ai-skills","install_from":"skills.sh"}},"qualityScore":"0.456","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 13 github stars · SKILL.md body (15,100 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:07:02.922Z","embedding":null,"createdAt":"2026-05-09T01:05:35.927Z","updatedAt":"2026-05-18T19:07:02.922Z","lastSeenAt":"2026-05-18T19:07:02.922Z","tsv":"'+1':570,709 '/*''':1149 '/health':383 '/ready':392 '/references/factories.md':1252,1992 '/references/feature-testing.md':1230,1977 '/references/laravel-ecosystem.md':1962 '/references/mocking-and-faking.md':1243,1985 '/references/production-performance.md':1959,1999 '/references/testing.md':1218,1969 '/vendor/bin/phpstan':1927 '/vendor/bin/phpunit':1931 '0':1668 '1':12 '10':992 '1000':623 '20':336 '200':386,1152 '3':1910 '500':1688 '60':994 '8':91,95,1930 '8.4':117 '80':1172 '8601':1575 '9':99 'abil':1124 'abstract':1905 'accept':1631 'access':429 'accessor':240 'across':1267 'act':1136 'actinga':1117,1122 'action':266,1033,1132,1248,1465,1988 'ad':1902 'add':124,288,490,531,800 'addit':797 'admin':1606 'advertis':1775 'aftercommit':1705,1731 'aftercr':1258,1996 'aggreg':643 'aiconvers':437 'aim':96 'allow':991,1159 'alon':1288 'alreadi':1860 'alway':65,489,698,1003,1692 'analys':93,1928 'analysi':86 'anonym':466 'anti':1186 'anti-pattern':1185 'anyth':1045 'api':357,443,702,733,740,781,815,924,990,1126,1234,1594,1980 'api.example.com':1148 'api.example.com/*''':1147 'apiresourc':447 'app':367,566 'app/observers':1428 'appear':1908 'applic':121,859 'approv':641 'arbitrari':1536 'architectur':227 'array':76,213,215,217,220,1629,1658,1676,1687,1702 'assert':1104,1164,1238,1983 'assertdatabaseha':1165 'assertdatabasemiss':1166 'assertpush':1139 'asserts':1155 'assign':696 'asymmetr':194 'async':169 'atom':1352 'attempt':1835 'audit':1297,1324,1333 'auth':1120,1127,1232,1978 'author':1163 'authorizationexcept':857 'automat':664,688 'avail':173 'avoid':29,1882 'awar':767 'b':1809 'back':1743,1895 'backfil':516,534 'backoff':1792 'base':332,717 'batch':966,969 'becom':775 'behavior':238,1091 'bind':423,441 'blank':1679 'block':1551,1919 'bool':460 'boot':354 'boundari':277,869,878 'break':817 'bubbl':1761 'bug':1275 'build':660,865 'builder':1291,1309 'built':405 'built-in':404 'bulk':587,676 'bus':968,979 'busi':256 'bypass':1331,1367,1371 'c':1836 'cach':317,1951,1953,1955,2005 'call':233,1384 'callabl':161 'callback':1329 'caller':779,818,864,1691 'carbon':1577 'cascad':1336 'cascadeondelet':488 'case':474 'cast':1553,1583,1967 'catch':568,971,1918 'caught':1276 'chain':209,975,980 'chang':805,1586,1596,1869,1885 'check':394,408,905,1432 'child':1399 'chunk':618,622,1534 'chunkbyid':1472,1540 'ci':1180 'class':84,136,160,252,254,267,438,450,467,470,960,1273,1466 'classnam':69 'claud':130 'clean':1899 'cleanup':691,1337,1374,1448,1458 'close':1706,1784 'closur':223,1732,1757 'code':7,41,129,378,842,895,1599,1876 'col':674 'collect':113,225,625 'collis':472 'column':498,533,576,1484 'combin':617 'command':1839 'comment':31,38,45,126,639,1368 'commit':1715,1755 'common':615,1260 'complex':180,307 'composit':612 'compromis':937 'concret':1262 'concurr':1480 'condit':299,658 'config':343,347,1952 'configur':1945 'connect':400 'consist':830 'consol':1235,1981 'constant':149 'constrain':487,629 'constraint':181,637,1703 'construct':261 'constructor':154 'content':921,946 'contract':530,736,754,761,1595 'contract-first':735 'control':229,230,287,748,784,796,1048 'convent':480 'convers':436 'convert':292 'cooper':168 'copi':1828 'count':650,1471 'countabl':177 'counter':607 'cover':1201 'coverag':1170,1178 'crash':365 'create/edit':454 'critic':346,398 'cross':246,275,427 'cross-cut':245 'cross-ten':426 'custom':82,440,726,850,1966 'cut':247 'cycl':1766 'data':141,294,461,513,515,723,893,917,1084,1222,1666,1972 'databas':395,590,1039 'databasemigr':1071 'databasetransact':1060 'dataset':621 'date':1552,1587,1589,1647,1648 'db':661,1087,1111,1237,1407,1521,1704,1730,1772,1800,1815,1982 'ddl':525 'decis':934 'decision-mak':933 'declar':9,1937 'decod':1474 'decode/encode':1541 'decrement':605 'dedic':958 'deep':393 'default':1040,1539,1856 'defin':738 'delet':1373,1383,1397,1423,1434,1435 'deletedirectori':1386 'depend':418,777 'deploy':1950,2004 'deprec':820,822 'design':734 'destin':1823 'detect':1416 'develop':6,132,572 'di':260 'direct':1604,1611 'disciplin':1184,1865 'dispatch':973,985,1113,1339 'dnf':174 'document':1369,1396 'documentobserv':1382 'doesn':1781 'domain':148,237 'done':1938 'draft':595 'drop':537,1769 'dropifexist':506 'dsns':360 'dto':953 'dtos':1607 'duplic':1001 'e':57,1795 'earli':27 'easier':81 'edit':549,1529 'effect':314,323,1110 'element':1633,1660 'eloqu':563,1315 'els':30 'email':79 'emit':1573 'empti':1917 'encod':1477,1613 'end':25 'endpoint':381 'enforc':1651 'ensur':886 'entir':1585 'entri':902 'enum':142 'envelop':458,832 'environ':558 'error':462,831,841,860,866 'errors/guards':20 'essenti':1221,1971 'etc':1026,1956 'event':310,1298,1317,1357 'everi':14,768,993,1868 'everywher':70 'exact':785 'exampl':1217 'exceed':335 'except':55,667,834,851,1760,1925 'exclud':304 'exist':808,1414 'expand':529 'expand-contract':528 'explain':33 'explanatori':125 'explicit':700,1700 'exponenti':1791 'expos':382 'express':151 'extern':923,1720,1735,1749,1779,1811 'external-op-first-then-db':1810 'facad':1129,1245,1986 'factori':1080,1251 'factories.md':1991 'fail':341,1007,1753,1793,1829 'fail-fast':340 'failur':1005,1716 'fake':1128,1134,1141,1146,1242,1246,1987 'fals':840 'fast':342 'faster':1065 'featur':326,1013,1042,1228,1286 'feature-testing.md':1976 'feel':1892 'fiber':166 'field':718,771,812,949,1636 'fields/endpoints':802 'filament':1605 'file':15,521 'fillabl':701 'filter':497,652 'final':972 'find':214 'fire':1314,1356 'first':21,159,373,737,821,1813,1834,1867 'first-class':158 'fix':1341,1446,1516,1891 'flag':1621,1848 'flight':1847 'fn':163,433,653,663,996 'folder':327 'follow':763 'footgun':1264 'foreign':481,493 'foreignid':484 'form':278,308,743,756,872,882 'format':863,1554,1584,1625 'forus':1157 'foundat':1212 'fqn':73 'frequent':496 'full':546,1019,1054 'gate':1156 'general':1854 'general-purpos':1853 'generat':128,451 'generic':112,1182 'get':187 'getjson':1023 'give':1347 'group':432 'guard':692 'hacki':1887 'half':1709,1717 'handl':19,212,867,1004 'handler':846,852,1664,1796 'happi':16 'hasmani':1394 'header':727 'health':380,407,410 'hit':376 'hook':183,1259,1436,1997 'http':877,1016,1037,1140,1144,1145,1150,1154 'hyrum':764 'ia':541,1195 'ia-postgresql':540 'ia-writing-test':1194 'id':486,580,583,586 'idempot':1351,1820 'idempotent-atom':1350 'ignor':1581 'illumin':409 'immut':140 'impact':1874 'implement':762,1006,1897 'import':68 'in-flight':1845 'in-plac':1518 'includ':501 'inclus':719 'increment':604 'index':491,613,1674,1685,1724 'index/store/show/update/destroy':452 'inlin':72,285,1914 'input':760,898 'insert':678,1089 'insert-or-upd':677 'insid':880,1344,1532 'instead':49 'int':1684 'intent':1366 'interfac':146,998 'intern':894 'introduc':1883 'invalid':318,359 'invis':1282 'invok':271 'iso':1574 'isol':1030 'isproduct':567 'item':1642,1646,1654,1667,1701 'jit':1943,2001 'job':964,965,974,1010,1114,1340,1788,1803 'json':456,1473,1476,1612 'jsonb':1483,1523 'jsonresourc':1560,1563 'jsonserializ':1580 'key':358,482,494,672,1415,1696,1824 'know':133,1468 'laravel':3,5,226,402,1203,1263,1948,2003 'laravel-ecosystem.md':1961 'laravel-specif':1202,1947 'larg':620 'last':18 'later':828 'law':766 'lazi':624 'letter':53 'level':90,94,98,591,1929 'like':1641 'limit':987 'listen':311 'live':242,249,1618 'load':599,646,1957 'lockforupd':1342,1531 'log':316,1920 'logic':257,309,503,930,1031,1538 'lose':1479 'm/d/y':1590 'mainten':1547 'make':935,1807 'malici':945 'mass':695,1361 'massprun':682 'match':150,478 'memori':601,628 'memory-constrain':627 'mergewhen':713 'messag':843 'meta':464 'metadata':730,1492,1525 'method':144,165,290,355,500,1008 'migrat':465,468,514,520,551,1055,1068,1076,1512 'millisecond':1500 'minim':1038,1875 'misbehav':939 'misconfigur':362 'miss':356,948 'mix':523 'mock':1188,1240,1249,1989 'mockeri':1990 'mocking-and-faking.md':1984 'mode':1177 'model':244,337,422,435,479,564,668,792,910,1050,1079,1299,1316,1422,1429,1557,1602,1614,1861 'modelnotfoundexcept':856 'modern':115 'modif':799 'move':1457,1832 'mutat':1475,1721 'myservic':211 'n':569,708 'name':186,190,200,471,477,581,1094,1643,1669 'nativ':219 'necessari':1881 'need':42,575 'nest':1628 'nested-array':1627 'never':35,37,71,283,548,645,789,1085,1923 'new':101,205,210,508,532,561,801,981,983,1904 'non':1413 'non-exist':1412 'normal':854 'notat':77 'notif':315,1116,1963 'nuke':1378 'null':64,178,463,1672 'obj':164 'object':1035 'observ':769,1295,1319,1372,1426,1462 'observedbi':1322 'observer.php':1430 'old':538 'one':562,809,862,1090,1398 'op':978,1750,1812,1818 'opcach':1942,2000 'open':1427 'openapi/docblock':824 'oper':273,588,1310,1437,1768 'orchestr':248 'order':772 'organ':328 'outbound':1143 'overwritten':1509 'owenit':1323 'pagin':732 'pair':1693 'param':110 'paramet':298 'parent':1376,1387,1391,1439 'parenthes':207 'parti':913,1727 'pass':1661,1932 'path':17,379,1455,1526,1600 'pattern':547,1187,1205,1215,1785,1849 'payment':265 'paymentservic':264 'pcov':1174 'per':1057,1077,1092,1496,1695 'per-key':1694 'per-row':1495 'perform':1940 'permiss':716 'permission-bas':715 'persist':1169 'php':2,4,116,1537,1677 'php-laravel':1 'phpstan':88,92,105,109,1284 'phpstan-param':108 'phpstan-typ':104 'phpunit':1012,1220,1970 'pitfal':1261 'pivot':722 'place':1520,1911 'plural':475 'point':1410 'popul':1490 'possibl':1873 'post':448,577,592,1161,1714 'post-commit-failur':1713 'postcontrol':449 'postgresql':542 'postjson':1025 'pre':891 'pre-valid':890 'precondit':1801 'prefix':1097 'preload':1944,2002 'prevent':425,707,1000,1733 'preventlazyload':565 'privat':197,203,262 'process':389,630,1002 'produc':836 'product':338,1278,1939 'production-performance.md':1958,1998 'profil':1103 'project':102,334,1268 'promot':155 'properti':138,182 'provid':352,1223,1973 'prunabl':681,685 'public':184,196,201 'pull':1608 'purpos':270,1855 'put':321 'q':654,655 'queri':416,611,616,631,659,686,1290,1300,1308 'query-build':1289,1307 'queu':1787 'queue':363,963,1133,1138,1857 'r':60 'rate':986 'rather':803 'ration':1190 'raw':791,1086,1360,1522 'reach':1358,1556 'read':202,536 'readon':135,157,259,263 'real':1272 'receiv':888 'reconcil':1837 'record':690 'recur':1266 'redi':396,988 'redund':904 'refer':1960 'reflect':1783 'refreshdatabas':1052 'regist':1320 'regress':1626 'relat':647,1304 'relationship':241,706,1255,1994 'remain':1409 'remov':807,810,825 'renam':46 'renames/removals':526 'render':847,931 'repositori':908 'reproduc':1619 'request':58,279,374,744,757,873,883 'requir':78 'requiredif':302 'reset':1056 'resili':339 'resist':1191 'resolut':442 'resolv':1561 'resourc':444,703,741,750,782,816,1570 'respond':390 'respons':236,457,711,753,770,914,925,959,1106,1151,1765 'restructur':48 'rethrow':1922 'retri':1747,1858 'return':28,235,385,790,942,1566 'reusabl':636 'revert':1798 'review':1279 'roll':1742 'rollback':502,665,1075,1708 'rout':414,419,421,430,434,445,446,1047,1954 'row':670,1334,1408,1421,1444,1451,1491,1497,1680,1773,1842 'rule':83,301,1189,1637,1640,1697 'run':87,554,1073,1225,1738,1758,1974 's3':1722 'safe':1544 'sanctum':1121 'save':1343,1503 'saving/updating':1328 'scalar':1632,1659 'scatter':906 'schedul':1838,1965 'schema':505,511 'scope':239,420,632,1377,1440,1445,1447 'scopeact':633 'scopebind':431 'scoperec':634 'search':1723 'search/check':221 'see':539,1192,1213,1227,1239,1250 'select':573,582,1489 'semant':1353,1859 'sent':1115 'separ':510 'sequenc':1256,1995 'sequenti':977 'serial':788 'servic':251,253,276,297,325,351,399,881,887,940,1032 'service/action':234 'set':191,198,1524,1639 'shallow':384,1528 'shape':919 'share':557 'shouldbeuniqu':997 'sibl':1379,1404,1470 'side':313,322,1109 'silent':819,1293,1508 'simpl':413,1871 'simplic':1866 'singl':52,269,610,1420,1443 'single-lett':51 'single-purpos':268 'single-row':1419,1442 'sit':1207 'skill':543,1198,1200 'skill-php-laravel' 'skip':1294 'snake':473 'snapshot':1515 'solut':1900 'sometim':303 'source-iliaal' 'specif':1204,1949 'specifi':66 'split':1864 'stack':1020 'stale':689,1514 'standard':455 'start':1567,1571 'startup':369 'state':1112,1254,1777,1993 'static':85,1327 'status':594,1107 'step':1894 'step1':982 'step2':984 'still':1355 'storag':1385,1401,1454,1827,1831 'strict':10 'string':61,63,185,199,1644,1673 'stringabl':176 'strtoupper':188 'structur':844 'stuck':1844 'style':8 'subqueri':644 'success':22,459,839 'suppos':1805 'swallow':1924 'switch':153,535 'swoole/reactphp':171 'syntax':162 'system':1780 'tabl':476,483,509,1088 'target':1171 'task':1964 'tenant':428 'test':39,1011,1014,1028,1043,1058,1069,1078,1083,1093,1096,1098,1183,1197,1214,1226,1229,1287,1975 'testing.md':1968 'tests/feature':1015 'tests/unit':1029 'thin':228 'third':912,1726 'third-parti':911,1725 'throttl':989 'throwabl':1794 'time':774 'titl':584 'toarray':794,1558,1603,1610 'todto':289,885 'top':1209 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-ai-tools' 'topic-claude-code' 'topic-skills' 'touch':1046,1878 'trait':683,1325 'transact':662,1064,1346,1741 'transient/permanent':1863 'tri':1790 'trim':192 'true':1808 'trust':896 'type':11,106,114,175,296,331,889,944 'type-bas':330 'typeerror':1683 'unexpect':943 'uniqu':671 'unit':1027 'unless':1597 'unrel':1884 'untrust':916 'updat':596,603,673,680,1101,1160,1292,1302,1305,1362,1478,1498 'uploadpath':1388 'upsert':669 'use':26,75,103,118,401,527,699,780,845,927,962,1021,1051,1059,1070,1517,1601 'user':485,579,585,1099,1118,1123,1158,1502 'valid':74,232,282,284,293,300,344,345,870,874,892,900,918,955,1233,1630,1662,1979 'validationexcept':855 'valu':193,348,1034 'variabl':54 'verifi':1168,1615,1906,1926 'version':731,829 'via':871,1321 'visibl':195 'void':67 'vulner':697 'walk':1841 'warn':1678,1935 'webhook':1728 'whenload':704 'whenpivotload':720 'whether':1433 'window':1486,1506,1548 'wipe':1400 'wire':1624 'wire-format':1623 'withcount':638 'withexist':640 'without':206,222,453 'withrespons':724 'work':1736,1825 'workaround':1888 'wrap':224,950,1062,1718 'write':204,559,746,1196,1481,1550 'written':1681 'wrong':1893 'xdebug':1176 'yield':1671 'zero':1934","prices":[{"id":"e3d54a31-1d4f-4c1f-8c15-ad6db040b293","listingId":"57674ac9-dfcf-48ff-8d27-5995acfdbb70","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"iliaal","category":"ai-skills","install_from":"skills.sh"},"createdAt":"2026-05-09T01:05:35.927Z"}],"sources":[{"listingId":"57674ac9-dfcf-48ff-8d27-5995acfdbb70","source":"github","sourceId":"iliaal/ai-skills/php-laravel","sourceUrl":"https://github.com/iliaal/ai-skills/tree/master/skills/php-laravel","isPrimary":false,"firstSeenAt":"2026-05-09T01:05:35.927Z","lastSeenAt":"2026-05-18T19:07:02.922Z"}],"details":{"listingId":"57674ac9-dfcf-48ff-8d27-5995acfdbb70","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"iliaal","slug":"php-laravel","github":{"repo":"iliaal/ai-skills","stars":13,"topics":["agent-skills","ai-coding-assistant","ai-tools","claude-code","skills"],"license":"mit","html_url":"https://github.com/iliaal/ai-skills","pushed_at":"2026-05-16T13:15:17Z","description":"Curated collection of agent skills for AI coding assistants.","skill_md_sha":"ea8d05ac1204d76331f6758dff0d661d32856d1d","skill_md_path":"skills/php-laravel/SKILL.md","default_branch":"master","skill_tree_url":"https://github.com/iliaal/ai-skills/tree/master/skills/php-laravel"},"layout":"multi","source":"github","category":"ai-skills","frontmatter":{"name":"php-laravel","description":">-"},"skills_sh_url":"https://skills.sh/iliaal/ai-skills/php-laravel"},"updatedAt":"2026-05-18T19:07:02.922Z"}}