[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FDQ2QFRVF32BSH0Y58CBEM":3,"skill-same:01M6FDQ2QFRVF32BSH0Y58CBEM":58},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":27,"publishedAt":31,"linkOnly":32,"markdownContent":33,"markdownBody":33,"frontMatter":34,"refName":35,"commitSha":36,"skillDirectory":37,"forksCount":38,"repositoryHtmlUrl":39,"repositoryHomepageUrl":40,"ownerHtmlUrl":41,"ownerBlogUrl":6,"ownerDescription":42,"updatedAt":43,"install":44,"resources":57},"01M6FDQ2QFRVF32BSH0Y58CBEM","laravel-api",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnoartem\u002Fskills\u002Flaravel-api\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":25,"taxonomyTypeDesc":26},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[28,29,30],"api","laravel","laravel api","2026-11-10 10:12:30",false,"# Laravel API - Steve's Architecture\n\nBuild Laravel REST APIs with clean, stateless, resource-scoped architecture.\n\n## Quick Start\n\nWhen user requests a Laravel API, follow this workflow:\n\n1. **Understand requirements** - What resources? What operations? Authentication needed?\n2. **Initialize project structure** - Set up routing, remove frontend bloat\n3. **Build first resource** - Complete CRUD to establish pattern\n4. **Add authentication** - JWT via PHP Open Source Saver\n5. **Iterate on remaining resources** - Follow established pattern\n\n## Core Architecture Principles\n\nRead `references\u002Farchitecture.md` for comprehensive details. Key principles:\n\n1. **Stateless by design** - No hidden dependencies, explicit data flow\n2. **Boundary-first** - Clear separation of HTTP, business logic, data layers\n3. **Resource-scoped** - Routes, controllers organized by resource\n4. **Version discipline** - Namespace-based versioning, HTTP Sunset headers\n\n## Code Quality Standards\n\nAll code must follow Laravel best practices and PSR-12 standards:\n\n1. **Preserve Functionality** - Refactorings change HOW code works, never WHAT it does\n2. **Explicit Over Implicit** - Prefer clear, readable code over clever shortcuts\n3. **Type Declarations** - Always use return types on methods, parameter types where beneficial\n4. **Avoid Nested Ternaries** - Use match expressions, switch, or if\u002Felse for clarity\n5. **Consistent Naming** - Follow PSR-12 and Laravel conventions strictly\n6. **Proper Namespacing** - Organize imports logically, use full type hints\n\n**When reviewing or refactoring code:**\n- Focus on clarity and maintainability over cleverness\n- Simplify complex nested logic into readable structures\n- Extract magic values into named constants or config\n- Remove unnecessary complexity while preserving exact behavior\n\n## Project Structure\n\n```\nroutes\u002Fapi\u002F\n  routes.php              # Main entry point, version grouping\n  tasks.php               # All task routes, all versions\n  projects.php            # All project routes, all versions\n\napp\u002FHttp\u002F\n  Controllers\u002F{Resource}\u002FV1\u002F\n    StoreController.php   # Always invokable\n    IndexController.php\n    ShowController.php\n  Requests\u002F{Resource}\u002FV1\u002F\n    StoreTaskRequest.php  # Validation + payload() method\n  Payloads\u002F{Resource}\u002F\n    StoreTaskPayload.php  # Simple DTOs with toArray()\n  Responses\u002F\n    JsonDataResponse.php  # Implements Responsable\n    JsonErrorResponse.php\n  Middleware\u002F\n    HttpSunset.php\n\napp\u002FActions\u002F{Resource}\u002F\n  CreateTask.php          # Single-purpose business logic\n\napp\u002FServices\u002F             # Only when logic too complex for Actions\n\napp\u002FModels\u002F\n  Task.php                # HasUlids trait, simple data access\n```\n\n## Building a New Resource Endpoint\n\n### Step 1: Model\n\nAlways use ULIDs. Keep models simple - data access only.\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Concerns\\HasUlids;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\n\nfinal class Task extends Model\n{\n    use HasFactory;\n    use HasUlids;\n    \n    protected $fillable = [\n        'title',\n        'description',\n        'status',\n        'project_id',\n    ];\n\n    protected $casts = [\n        'created_at' => 'datetime',\n        'updated_at' => 'datetime',\n    ];\n\n    public function project(): BelongsTo\n    {\n        return $this->belongsTo(Project::class);\n    }\n}\n```\n\n### Step 2: Routes\n\nCreate resource route file at `routes\u002Fapi\u002F{resource}.php`:\n\n```php\nuse App\\Http\\Controllers\\Tasks\\V1;\n\nRoute::middleware(['auth:api'])->group(function () {\n    Route::get('\u002Ftasks', V1\\IndexController::class);\n    Route::post('\u002Ftasks', V1\\StoreController::class);\n    Route::get('\u002Ftasks\u002F{task}', V1\\ShowController::class);\n    Route::patch('\u002Ftasks\u002F{task}', V1\\UpdateController::class);\n    Route::delete('\u002Ftasks\u002F{task}', V1\\DestroyController::class);\n});\n```\n\nInclude in `routes\u002Fapi\u002Froutes.php`:\n\n```php\nRoute::prefix('v1')->group(function () {\n    require __DIR__ . '\u002Ftasks.php';\n});\n```\n\n### Step 3: DTO (Payload)\n\nCreate at `app\u002FHttp\u002FPayloads\u002F{Resource}\u002F{Operation}Payload.php`:\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Http\\Payloads\\Tasks;\n\nfinal readonly class StoreTaskPayload\n{\n    public function __construct(\n        public string $title,\n        public ?string $description,\n        public string $status,\n        public string $projectId,\n    ) {}\n\n    public function toArray(): array\n    {\n        return [\n            'title' => $this->title,\n            'description' => $this->description,\n            'status' => $this->status,\n            'project_id' => $this->projectId,\n        ];\n    }\n}\n```\n\n### Step 4: Form Request\n\nCreate at `app\u002FHttp\u002FRequests\u002F{Resource}\u002FV1\u002F{Operation}Request.php`:\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Http\\Requests\\Tasks\\V1;\n\nuse App\\Http\\Payloads\\Tasks\\StoreTaskPayload;\nuse Illuminate\\Foundation\\Http\\FormRequest;\nuse Illuminate\\Validation\\Rule;\n\nfinal class StoreTaskRequest extends FormRequest\n{\n    public function authorize(): bool\n    {\n        return true;\n    }\n\n    public function rules(): array\n    {\n        return [\n            'title' => ['required', 'string', 'max:255'],\n            'description' => ['nullable', 'string', 'max:1000'],\n            'status' => ['required', Rule::in(['pending', 'in_progress', 'completed'])],\n            'project_id' => ['required', 'string', 'exists:projects,id'],\n        ];\n    }\n\n    public function payload(): StoreTaskPayload\n    {\n        return new StoreTaskPayload(\n            title: $this->string('title')->toString(),\n            description: $this->string('description')->toString(),\n            status: $this->string('status')->toString(),\n            projectId: $this->string('project_id')->toString(),\n        );\n    }\n}\n```\n\n### Step 5: Action\n\nCreate at `app\u002FActions\u002F{Resource}\u002F{Operation}.php`:\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Actions\\Tasks;\n\nuse App\\Http\\Payloads\\Tasks\\StoreTaskPayload;\nuse App\\Models\\Task;\n\nfinal readonly class CreateTask\n{\n    public function handle(StoreTaskPayload $payload): Task\n    {\n        return Task::create($payload->toArray());\n    }\n}\n```\n\n### Step 6: Controller\n\nCreate invokable controller at `app\u002FHttp\u002FControllers\u002F{Resource}\u002FV1\u002F{Operation}Controller.php`:\n\n```php\n\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Http\\Controllers\\Tasks\\V1;\n\nuse App\\Actions\\Tasks\\CreateTask;\nuse App\\Http\\Requests\\Tasks\\V1\\StoreTaskRequest;\nuse App\\Http\\Responses\\JsonDataResponse;\nuse Illuminate\\Http\\JsonResponse;\n\nfinal readonly class StoreController\n{\n    public function __construct(\n        private CreateTask $createTask,\n    ) {}\n\n    public function __invoke(StoreTaskRequest $request): JsonResponse\n    {\n        $task = $this->createTask->handle(\n            payload: $request->payload(),\n        );\n\n        return new JsonDataResponse(\n            data: $task,\n            status: 201,\n        );\n    }\n}\n```\n\n## Response Format\n\nStandard format for all responses:\n\n**Success:**\n```json\n{\n    \"data\": {...},\n    \"meta\": {...}\n}\n```\n\n**Error (Problem+JSON):**\n```json\n{\n    \"type\": \"about:blank\",\n    \"title\": \"Validation Failed\",\n    \"status\": 422,\n    \"detail\": \"The given data was invalid\",\n    \"errors\": {...}\n}\n```\n\n## Query Building\n\nUse Spatie Query Builder for filtering, sorting, includes:\n\n```php\nuse Spatie\\QueryBuilder\\QueryBuilder;\n\n$tasks = QueryBuilder::for(Task::class)\n    ->allowedFilters(['status', 'priority'])\n    ->allowedSorts(['created_at', 'due_date'])\n    ->allowedIncludes(['project', 'assignee'])\n    ->paginate();\n```\n\n## Versioning Endpoints\n\nWhen creating V2:\n\n1. Create V2 namespace: `App\\Http\\Controllers\\Tasks\\V2\\`\n2. Add V2 route group in resource file\n3. Add Sunset middleware to V1 routes:\n\n```php\nRoute::middleware(['auth:api', 'http.sunset:2025-12-31'])->group(function () {\n    \u002F\u002F V1 routes\n});\n```\n\n## Authentication Setup\n\nUse PHP Open Source Saver JWT package:\n\n```bash\ncomposer require php-open-source-saver\u002Fjwt-auth\nphp artisan vendor:publish --provider=\"PHPOpenSourceSaver\\JWTAuth\\Providers\\LaravelServiceProvider\"\nphp artisan jwt:secret\n```\n\nConfigure in `config\u002Fauth.php`:\n\n```php\n'guards' => [\n    'api' => [\n        'driver' => 'jwt',\n        'provider' => 'users',\n    ],\n],\n```\n\n## Essential Setup\n\nAdd to `app\u002FProviders\u002FAppServiceProvider.php`:\n\n```php\nuse Illuminate\\Database\\Eloquent\\Model;\n\npublic function boot(): void\n{\n    Model::shouldBeStrict(); \u002F\u002F Prevent N+1 queries\n}\n```\n\nRegister HttpSunset middleware in `app\u002FHttp\u002FKernel.php`:\n\n```php\nprotected $middlewareAliases = [\n    'http.sunset' => \\App\\Http\\Middleware\\HttpSunset::class,\n];\n```\n\n## Anti-Patterns to Avoid\n\n- Using auto-increment IDs instead of ULIDs\n- Business logic in models\n- Multiple actions per controller\n- Accessing request data directly in controllers\u002Factions\n- Hidden query scopes\n- Service classes when an Action would suffice\n- Breaking changes without versioning\n- Inconsistent response formats\n- Nested ternary operators (use match expressions instead)\n- Missing type declarations on methods and parameters\n- Overly compact \"clever\" code that sacrifices readability\n\n## Code Review & Refactoring\n\nWhen reviewing or refactoring Laravel API code, apply these principles:\n\n### Simplification Checklist\n\n1. **Preserve Functionality** - Ensure refactorings don't change behavior\n2. **Check Type Safety** - Add missing return types and parameter types\n3. **Simplify Logic** - Replace nested ternaries with match expressions\n4. **Extract Complexity** - Move complex conditions into named methods\n5. **Verify Standards** - Ensure PSR-12 compliance with declare(strict_types=1)\n6. **Improve Naming** - Use descriptive names that reveal intent\n\n### Match Expression Pattern\n\nReplace nested ternaries with match for clarity:\n\n```php\n\u002F\u002F ❌ Avoid: Nested ternary\n$status = $task->completed_at \n    ? ($task->verified ? 'verified' : 'completed')\n    : ($task->started_at ? 'in_progress' : 'pending');\n\n\u002F\u002F ✅ Prefer: Match expression\n$status = match (true) {\n    $task->completed_at && $task->verified => 'verified',\n    $task->completed_at => 'completed',\n    $task->started_at => 'in_progress',\n    default => 'pending',\n};\n```\n\n## References\n\n- **architecture.md** - Comprehensive architectural patterns and principles\n- **code-examples.md** - Complete working examples for every component\n- **code-quality.md** - Laravel best practices, refactoring patterns, and PSR-12 standards\n\n## Templates\n\nTemplate files in `assets\u002Ftemplates\u002F` for quick scaffolding:\n- Controller.php\n- FormRequest.php\n- Payload.php\n- Action.php\n- Model.php\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fnoartem\u002Fskills\u002Flaravel-api",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill laravel-api",{"tool":50,"command":51},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill laravel-api",{"tool":53,"command":54},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill laravel-api","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnoartem\u002Fskills\u002Flaravel-api\u002FSKILL.md",true,[],[59,71,81,90,96,103],{"skillNo":60,"name":61,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":67,"publishedAt":31},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[64],{"categoryNo":65,"categoryName":66,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[68,69,70],"model","model hierarchy","model hierarchy skill",{"skillNo":72,"name":73,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":77,"publishedAt":31},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[76],{"categoryNo":65,"categoryName":66,"taxonomyType":20,"taxonomyTypeDesc":21},[78,79,80],"MCP","zoom","zoom mcp",{"skillNo":82,"name":83,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":31},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[87,88,89,79],"sdk","zoom apps sdk","apps",{"skillNo":91,"name":92,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":95,"publishedAt":31},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[92],{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":102,"publishedAt":31},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[101],{"categoryNo":65,"categoryName":66,"taxonomyType":20,"taxonomyTypeDesc":21},[98],{"skillNo":104,"name":105,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":31},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[105]]