@famgia/omnify-laravel 0.0.88 → 0.0.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/{chunk-YVVAJA3T.js → chunk-V7LWJ6OM.js} +178 -12
  2. package/dist/chunk-V7LWJ6OM.js.map +1 -0
  3. package/dist/index.cjs +180 -11
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +48 -1
  6. package/dist/index.d.ts +48 -1
  7. package/dist/index.js +5 -1
  8. package/dist/plugin.cjs +176 -11
  9. package/dist/plugin.cjs.map +1 -1
  10. package/dist/plugin.js +1 -1
  11. package/package.json +5 -5
  12. package/scripts/postinstall.js +29 -36
  13. package/stubs/ai-guides/claude-agents/architect.md.stub +150 -0
  14. package/stubs/ai-guides/claude-agents/developer.md.stub +190 -0
  15. package/stubs/ai-guides/claude-agents/reviewer.md.stub +134 -0
  16. package/stubs/ai-guides/claude-agents/tester.md.stub +196 -0
  17. package/stubs/ai-guides/claude-checklists/backend.md.stub +112 -0
  18. package/stubs/ai-guides/claude-omnify/antdesign-guide.md.stub +401 -0
  19. package/stubs/ai-guides/claude-omnify/config-guide.md.stub +253 -0
  20. package/stubs/ai-guides/claude-omnify/japan-guide.md.stub +186 -0
  21. package/stubs/ai-guides/claude-omnify/laravel-guide.md.stub +61 -0
  22. package/stubs/ai-guides/claude-omnify/schema-guide.md.stub +115 -0
  23. package/stubs/ai-guides/claude-omnify/typescript-guide.md.stub +310 -0
  24. package/stubs/ai-guides/claude-rules/naming.md.stub +364 -0
  25. package/stubs/ai-guides/claude-rules/performance.md.stub +251 -0
  26. package/stubs/ai-guides/claude-rules/security.md.stub +159 -0
  27. package/stubs/ai-guides/claude-workflows/bug-fix.md.stub +201 -0
  28. package/stubs/ai-guides/claude-workflows/code-review.md.stub +164 -0
  29. package/stubs/ai-guides/claude-workflows/new-feature.md.stub +327 -0
  30. package/stubs/ai-guides/cursor/laravel-controller.mdc.stub +391 -0
  31. package/stubs/ai-guides/cursor/laravel-request.mdc.stub +112 -0
  32. package/stubs/ai-guides/cursor/laravel-resource.mdc.stub +73 -0
  33. package/stubs/ai-guides/cursor/laravel-review.mdc.stub +69 -0
  34. package/stubs/ai-guides/cursor/laravel-testing.mdc.stub +138 -0
  35. package/stubs/ai-guides/cursor/laravel.mdc.stub +82 -0
  36. package/stubs/ai-guides/laravel/README.md.stub +59 -0
  37. package/stubs/ai-guides/laravel/architecture.md.stub +424 -0
  38. package/stubs/ai-guides/laravel/controller.md.stub +484 -0
  39. package/stubs/ai-guides/laravel/datetime.md.stub +334 -0
  40. package/stubs/ai-guides/laravel/openapi.md.stub +369 -0
  41. package/stubs/ai-guides/laravel/request.md.stub +450 -0
  42. package/stubs/ai-guides/laravel/resource.md.stub +516 -0
  43. package/stubs/ai-guides/laravel/service.md.stub +503 -0
  44. package/stubs/ai-guides/laravel/testing.md.stub +1504 -0
  45. package/ai-guides/laravel-guide.md +0 -461
  46. package/dist/chunk-YVVAJA3T.js.map +0 -1
@@ -0,0 +1,391 @@
1
+ ---
2
+ description: "Controller development rules - thin controller, query builder, OpenAPI"
3
+ globs: ["{{LARAVEL_BASE}}/Http/Controllers/**"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Controller Rules
8
+
9
+ ## Golden Rule: Thin Controller
10
+
11
+ ```php
12
+ // ✅ PERFECT
13
+ public function store(UserStoreRequest $request): UserResource
14
+ {
15
+ return new UserResource(User::create($request->validated()));
16
+ }
17
+
18
+ // ❌ BAD: Fat controller with logic
19
+ public function store(Request $request)
20
+ {
21
+ $validated = $request->validate([...]); // Use FormRequest!
22
+ // 50 lines of business logic... // Use Service!
23
+ }
24
+ ```
25
+
26
+ ## Index Method - Query Builder Template
27
+
28
+ ```php
29
+ use Spatie\QueryBuilder\AllowedFilter;
30
+ use Spatie\QueryBuilder\QueryBuilder;
31
+
32
+ public function index(): AnonymousResourceCollection
33
+ {
34
+ $users = QueryBuilder::for(User::class)
35
+ ->allowedFilters([
36
+ AllowedFilter::callback('search', function ($query, $value) {
37
+ $query->where(function ($q) use ($value) {
38
+ $q->where('name', 'like', "%{$value}%")
39
+ ->orWhere('email', 'like', "%{$value}%");
40
+ });
41
+ }),
42
+ ])
43
+ ->allowedSorts(['id', 'name', 'email', 'created_at'])
44
+ ->defaultSort('-id')
45
+ ->paginate(request()->input('per_page', 10));
46
+
47
+ return UserResource::collection($users);
48
+ }
49
+ ```
50
+
51
+ ## OpenAPI - Schema MUST Match Code
52
+
53
+ Frontend needs exact response/request structure. Always verify against actual code.
54
+
55
+ ### Golden Rule: Verify Before Writing OpenAPI
56
+
57
+ 1. **Response** → Check `*Resource.php` schema
58
+ 2. **Request** → Check `*Request.php` schema
59
+ 3. **Parameters** → Check `allowedFilters()` / `allowedSorts()`
60
+
61
+ ### Schema Location
62
+
63
+ | Type | Define At | Ref In Controller |
64
+ | ------------------------ | ------------------------ | --------------------------------------- |
65
+ | Response | `*Resource.php` | `#/components/schemas/User` |
66
+ | Request body | `*Request.php` | `#/components/schemas/UserStoreRequest` |
67
+ | Pagination | `Schemas.php` | `#/components/schemas/PaginationMeta` |
68
+ | Common params | `Schemas.php` | `#/components/parameters/QueryPage` |
69
+ | Resource-specific params | **Inline in Controller** | N/A |
70
+
71
+ ### Common refs from `Schemas.php`
72
+
73
+ ```php
74
+ // Parameters
75
+ new OA\Parameter(ref: '#/components/parameters/QueryPage'),
76
+ new OA\Parameter(ref: '#/components/parameters/QueryPerPage'),
77
+ new OA\Parameter(ref: '#/components/parameters/PathId'),
78
+
79
+ // Responses
80
+ new OA\Response(ref: '#/components/responses/ValidationError', response: 422),
81
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
82
+ new OA\Response(ref: '#/components/responses/NoContent', response: 204),
83
+
84
+ // Pagination
85
+ new OA\Property(property: 'links', ref: '#/components/schemas/PaginationLinks'),
86
+ new OA\Property(property: 'meta', ref: '#/components/schemas/PaginationMeta'),
87
+ ```
88
+
89
+ ### Filter & Sort - MUST Document Specifically
90
+
91
+ `filter` and `sort` differ per resource → Write inline with specific details.
92
+
93
+ #### Filter Types (Spatie Query Builder)
94
+
95
+ | Type | Code | OpenAPI Description | Example |
96
+ | ----------------- | ------------------------------------------ | -------------------------- | ----------------------- |
97
+ | Callback (search) | `AllowedFilter::callback('search', fn...)` | List all searchable fields | `filter[search]=田中` |
98
+ | Exact | `AllowedFilter::exact('status')` | Exact match | `filter[status]=active` |
99
+ | Partial | `AllowedFilter::partial('name')` | LIKE %value% | `filter[name]=田中` |
100
+ | Scope | `AllowedFilter::scope('active')` | Model scope | `filter[active]=1` |
101
+
102
+ #### Filter Documentation Template
103
+
104
+ ```php
105
+ // CODE: allowedFilters() configuration
106
+ ->allowedFilters([
107
+ AllowedFilter::callback('search', function ($query, $value) {
108
+ $query->where(function ($q) use ($value) {
109
+ $q->where('name_lastname', 'like', "%{$value}%")
110
+ ->orWhere('name_firstname', 'like', "%{$value}%")
111
+ ->orWhere('name_kana_lastname', 'like', "%{$value}%")
112
+ ->orWhere('name_kana_firstname', 'like', "%{$value}%")
113
+ ->orWhere('email', 'like', "%{$value}%");
114
+ });
115
+ }),
116
+ AllowedFilter::exact('status'),
117
+ ])
118
+
119
+ // OPENAPI: Document EACH filter with specific description
120
+ new OA\Parameter(
121
+ name: 'filter[search]',
122
+ in: 'query',
123
+ description: 'Partial match on: name_lastname, name_firstname, name_kana_lastname, name_kana_firstname, email',
124
+ schema: new OA\Schema(type: 'string'),
125
+ example: '田中'
126
+ ),
127
+ new OA\Parameter(
128
+ name: 'filter[status]',
129
+ in: 'query',
130
+ description: 'Exact match on status',
131
+ schema: new OA\Schema(type: 'string', enum: ['active', 'inactive']),
132
+ example: 'active'
133
+ ),
134
+ ```
135
+
136
+ #### Sort Documentation Template
137
+
138
+ ```php
139
+ // CODE: allowedSorts() + defaultSort()
140
+ ->allowedSorts(['id', 'name_lastname', 'name_firstname', 'email', 'created_at', 'updated_at'])
141
+ ->defaultSort('-id')
142
+
143
+ // OPENAPI: enum MUST include both asc and desc for each field
144
+ new OA\Parameter(
145
+ name: 'sort',
146
+ in: 'query',
147
+ description: 'Sort field. Prefix `-` for descending. Default: `-id`',
148
+ schema: new OA\Schema(
149
+ type: 'string',
150
+ enum: [
151
+ 'id', '-id',
152
+ 'name_lastname', '-name_lastname',
153
+ 'name_firstname', '-name_firstname',
154
+ 'email', '-email',
155
+ 'created_at', '-created_at',
156
+ 'updated_at', '-updated_at'
157
+ ]
158
+ ),
159
+ example: '-created_at'
160
+ ),
161
+ ```
162
+
163
+ #### Checklist: Filter & Sort Documentation
164
+
165
+ | Item | Check |
166
+ | ----------------------------- | ----------------------------------------------- |
167
+ | Each `AllowedFilter` | Has corresponding `filter[name]` parameter? |
168
+ | Callback filter | Description lists ALL searchable fields? |
169
+ | Exact filter with enum values | `schema.enum` matches valid values? |
170
+ | `allowedSorts()` | `sort.enum` includes both `field` and `-field`? |
171
+ | `defaultSort()` | Mentioned in `sort.description`? |
172
+
173
+ ### OpenAPI Checklist per Action
174
+
175
+ #### `index()` - List
176
+
177
+ **Verify before writing:**
178
+ 1. `allowedFilters()` → Each filter has `filter[name]` param with specific description
179
+ 2. `allowedSorts()` → `sort.enum` includes all fields + `-field` versions
180
+ 3. `defaultSort()` → Mentioned in sort description
181
+ 4. Response → ref to Resource + PaginationLinks + PaginationMeta
182
+
183
+ ```php
184
+ #[OA\Get(
185
+ parameters: [
186
+ // Each allowedFilter → specific param
187
+ new OA\Parameter(
188
+ name: 'filter[search]',
189
+ in: 'query',
190
+ description: 'Partial match on: name_lastname, name_firstname, email', // ← List actual fields!
191
+ schema: new OA\Schema(type: 'string'),
192
+ example: '田中'
193
+ ),
194
+ // Pagination
195
+ new OA\Parameter(ref: '#/components/parameters/QueryPage'),
196
+ new OA\Parameter(ref: '#/components/parameters/QueryPerPage'),
197
+ // Sort with full enum
198
+ new OA\Parameter(
199
+ name: 'sort',
200
+ in: 'query',
201
+ description: 'Sort field. Prefix `-` for descending. Default: `-id`', // ← Include default!
202
+ schema: new OA\Schema(
203
+ type: 'string',
204
+ enum: ['id', '-id', 'name', '-name', 'created_at', '-created_at'] // ← Match allowedSorts!
205
+ ),
206
+ example: '-created_at'
207
+ ),
208
+ ],
209
+ responses: [
210
+ new OA\Response(response: 200, content: new OA\JsonContent(
211
+ properties: [
212
+ new OA\Property(property: 'data', type: 'array', items: new OA\Items(ref: '#/components/schemas/User')),
213
+ new OA\Property(property: 'links', ref: '#/components/schemas/PaginationLinks'),
214
+ new OA\Property(property: 'meta', ref: '#/components/schemas/PaginationMeta'),
215
+ ]
216
+ )),
217
+ ]
218
+ )]
219
+ ```
220
+
221
+ #### `store()` - Create
222
+
223
+ ```php
224
+ // 1. Request: ref to *StoreRequest schema
225
+ // 2. Response: ref to Resource schema
226
+ // 3. Errors: 422 ValidationError
227
+
228
+ #[OA\Post(
229
+ requestBody: new OA\RequestBody(
230
+ required: true,
231
+ content: new OA\JsonContent(ref: '#/components/schemas/UserStoreRequest') // ← Check UserStoreRequest.php
232
+ ),
233
+ responses: [
234
+ new OA\Response(response: 201, content: new OA\JsonContent(
235
+ properties: [new OA\Property(property: 'data', ref: '#/components/schemas/User')]
236
+ )),
237
+ new OA\Response(ref: '#/components/responses/ValidationError', response: 422),
238
+ ]
239
+ )]
240
+ ```
241
+
242
+ #### `show()` - Get Single
243
+
244
+ ```php
245
+ // 1. Parameter: PathId
246
+ // 2. Response: ref to Resource schema
247
+ // 3. Errors: 404 NotFound
248
+
249
+ #[OA\Get(
250
+ parameters: [new OA\Parameter(ref: '#/components/parameters/PathId')],
251
+ responses: [
252
+ new OA\Response(response: 200, content: new OA\JsonContent(
253
+ properties: [new OA\Property(property: 'data', ref: '#/components/schemas/User')]
254
+ )),
255
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
256
+ ]
257
+ )]
258
+ ```
259
+
260
+ #### `update()` - Update
261
+
262
+ ```php
263
+ // 1. Parameter: PathId
264
+ // 2. Request: ref to *UpdateRequest schema (NOT StoreRequest!)
265
+ // 3. Response: ref to Resource schema
266
+ // 4. Errors: 404, 422
267
+
268
+ #[OA\Put(
269
+ parameters: [new OA\Parameter(ref: '#/components/parameters/PathId')],
270
+ requestBody: new OA\RequestBody(
271
+ content: new OA\JsonContent(ref: '#/components/schemas/UserUpdateRequest') // ← Check UserUpdateRequest.php
272
+ ),
273
+ responses: [
274
+ new OA\Response(response: 200, content: ...),
275
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
276
+ new OA\Response(ref: '#/components/responses/ValidationError', response: 422),
277
+ ]
278
+ )]
279
+ ```
280
+
281
+ #### `destroy()` - Delete
282
+
283
+ ```php
284
+ // 1. Parameter: PathId
285
+ // 2. Response: 204 NoContent
286
+ // 3. Errors: 404 NotFound
287
+
288
+ #[OA\Delete(
289
+ parameters: [new OA\Parameter(ref: '#/components/parameters/PathId')],
290
+ responses: [
291
+ new OA\Response(ref: '#/components/responses/NoContent', response: 204),
292
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
293
+ ]
294
+ )]
295
+ ```
296
+
297
+ ## Response Status Codes
298
+
299
+ | Action | Code | Return Type |
300
+ | ------- | ---- | ----------------------------- |
301
+ | index | 200 | Collection |
302
+ | store | 201 | Resource + status |
303
+ | show | 200 | Resource |
304
+ | update | 200 | Resource |
305
+ | destroy | 204 | `response()->json(null, 204)` |
306
+
307
+ ## Imports - MUST use short names
308
+
309
+ ```php
310
+ // ✅ CORRECT
311
+ use Illuminate\Http\JsonResponse;
312
+ use Spatie\QueryBuilder\QueryBuilder;
313
+
314
+ public function store(): JsonResponse
315
+
316
+ // ❌ WRONG - Never FQCN inline
317
+ public function store(): \Illuminate\Http\JsonResponse
318
+ ```
319
+
320
+ ## Keep It Simple
321
+
322
+ ```php
323
+ // ✅ DO
324
+ ->allowedFilters([
325
+ AllowedFilter::callback('search', fn($q, $v) => ...),
326
+ ])
327
+ ->allowedSorts(['id', 'name', 'created_at'])
328
+
329
+ // ❌ DON'T over-engineer
330
+ ->allowedFilters([
331
+ AllowedFilter::exact('author.company.country'), // Too nested!
332
+ ])
333
+ ```
334
+
335
+ ## Don't Mix Concepts
336
+
337
+ | Concept | Use For | DON'T Use For |
338
+ | ---------------- | ---------------- | -------------------- |
339
+ | `*StoreRequest` | POST create | GET list, PUT update |
340
+ | `*UpdateRequest` | PUT/PATCH update | POST create |
341
+ | `*Resource` | Response output | Request input |
342
+ | `index()` | List (no body) | Create/Update |
343
+ | `store()` | Create new | Update existing |
344
+ | `update()` | Update existing | Create new |
345
+
346
+ ```php
347
+ // ❌ WRONG: Using StoreRequest for update
348
+ public function update(UserStoreRequest $request) // WRONG!
349
+
350
+ // ✅ CORRECT: Each action has its own Request
351
+ public function store(UserStoreRequest $request) // Create
352
+ public function update(UserUpdateRequest $request) // Update
353
+ ```
354
+
355
+ ## After Coding Checklist
356
+
357
+ ### Code
358
+ 1. ✅ Run tests: `./artisan test --filter=ControllerTest`
359
+
360
+ ### OpenAPI (only if annotations changed)
361
+
362
+ | Check | Code | OpenAPI | Match |
363
+ | --------------- | ------------------ | ----------------------- | ------------------------------------- |
364
+ | Response | `*Resource.php` | `ref: User` | Schema exists? Properties match? |
365
+ | Request | `*Request.php` | `ref: UserStoreRequest` | Schema exists? Required fields match? |
366
+ | Filters | `allowedFilters()` | `filter[name]` params | Each filter documented? |
367
+ | Callback filter | Fields in callback | `description` | All fields listed? |
368
+ | Exact filter | Valid values | `schema.enum` | Enum matches valid values? |
369
+ | Sort | `allowedSorts()` | `sort.enum` | Includes `field` AND `-field`? |
370
+ | Default sort | `defaultSort()` | `sort.description` | Default mentioned? |
371
+
372
+ ```bash
373
+ # Generate and verify
374
+ ./artisan l5-swagger:generate
375
+
376
+ # Check json output
377
+ cat backend/storage/api-docs/api-docs.json | jq '.components.schemas.User'
378
+ cat backend/storage/api-docs/api-docs.json | jq '.components.schemas.UserStoreRequest'
379
+ ```
380
+
381
+ ### Common Mistakes
382
+
383
+ | Mistake | Fix |
384
+ | ------------------------------- | ------------------------------------------------------------------------- |
385
+ | `ref: User` missing | Add `#[OA\Schema]` to `UserResource.php` |
386
+ | `ref: UserStoreRequest` missing | Add `#[OA\Schema]` to `UserStoreRequest.php` |
387
+ | sort enum incomplete | Include BOTH `field` AND `-field` for each `allowedSorts()` |
388
+ | sort missing default | Add "Default: `-id`" to sort description |
389
+ | filter description vague | List ALL fields: "Partial match on: name_lastname, name_firstname, email" |
390
+ | filter[status] no enum | Add `enum: ['active', 'inactive']` for exact filters |
391
+ | Using StoreRequest for update | Use UpdateRequest for PUT/PATCH |
@@ -0,0 +1,112 @@
1
+ ---
2
+ description: "FormRequest rules - OpenAPI Schema must match validation rules"
3
+ globs: ["{{LARAVEL_BASE}}/Http/Requests/**"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Request Rules
8
+
9
+ ## Golden Rule: Schema MUST Match Validation Rules
10
+
11
+ OpenAPI Schema properties MUST match the actual `schemaRules()` fields.
12
+
13
+ ## Before Editing Request
14
+
15
+ 1. **Read `OmnifyBase/*RequestBase.php`** to see actual validation rules
16
+ 2. **Compare with `#[OA\Schema]`** properties
17
+ 3. **Ensure `required` array matches** required validation rules
18
+ 4. **Match constraints** (maxLength, minLength, format)
19
+
20
+ ## Checklist
21
+
22
+ ```php
23
+ // Check OmnifyBase/*StoreRequestBase.php → schemaRules()
24
+ return [
25
+ 'name_lastname' => ['required', 'string', 'max:50'],
26
+ 'name_firstname' => ['required', 'string', 'max:50'],
27
+ 'email' => ['required', 'string', 'max:255', 'unique:users'],
28
+ 'password' => ['required', 'string', 'max:255'],
29
+ ];
30
+
31
+ // #[OA\Schema] MUST match
32
+ #[OA\Schema(
33
+ schema: 'UserStoreRequest',
34
+ required: ['name_lastname', 'name_firstname', 'email', 'password'], // ← Match 'required' rules
35
+ properties: [
36
+ new OA\Property(property: 'name_lastname', type: 'string', maxLength: 50), // ← max:50
37
+ new OA\Property(property: 'name_firstname', type: 'string', maxLength: 50),
38
+ new OA\Property(property: 'email', type: 'string', format: 'email', maxLength: 255),
39
+ new OA\Property(property: 'password', type: 'string', format: 'password', maxLength: 255),
40
+ ]
41
+ )]
42
+ ```
43
+
44
+ ## Mapping Validation Rules to OpenAPI
45
+
46
+ | Laravel Rule | OpenAPI Property |
47
+ | ------------ | --------------------------- |
48
+ | `required` | Add to `required: []` array |
49
+ | `max:50` | `maxLength: 50` |
50
+ | `min:8` | `minLength: 8` |
51
+ | `email` | `format: 'email'` |
52
+ | `string` | `type: 'string'` |
53
+ | `integer` | `type: 'integer'` |
54
+ | `boolean` | `type: 'boolean'` |
55
+ | `nullable` | `nullable: true` |
56
+
57
+ ## StoreRequest vs UpdateRequest
58
+
59
+ | Type | `required` | Notes |
60
+ | ---------------- | ------------------- | ---------------------- |
61
+ | `*StoreRequest` | All required fields | Creating new resource |
62
+ | `*UpdateRequest` | Empty or partial | Partial update allowed |
63
+
64
+ ```php
65
+ // StoreRequest - all fields required
66
+ #[OA\Schema(
67
+ schema: 'UserStoreRequest',
68
+ required: ['name_lastname', 'name_firstname', 'email', 'password'],
69
+ ...
70
+ )]
71
+
72
+ // UpdateRequest - no required (partial update)
73
+ #[OA\Schema(
74
+ schema: 'UserUpdateRequest',
75
+ // No 'required' array - all fields optional
76
+ properties: [...]
77
+ )]
78
+ ```
79
+
80
+ ## Common Mistakes
81
+
82
+ | Mistake | Fix |
83
+ | ----------------------- | ------------------------------------ |
84
+ | Missing field in schema | Check *RequestBase.php schemaRules() |
85
+ | Wrong maxLength | Match `max:X` rule |
86
+ | Missing required | Check if rule has `required` |
87
+ | Extra field in schema | Remove if not in rules |
88
+
89
+ ## Custom Rules in Child Class
90
+
91
+ If you override rules in the child Request class, update schema accordingly:
92
+
93
+ ```php
94
+ // UserStoreRequest.php
95
+ public function rules(): array
96
+ {
97
+ return array_merge($this->schemaRules(), [
98
+ 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], // Added 'email' format
99
+ 'password' => ['required', 'string', 'min:8', 'max:255'], // Added min:8
100
+ ]);
101
+ }
102
+
103
+ // Update schema to match
104
+ new OA\Property(property: 'email', type: 'string', format: 'email'), // ← Added format
105
+ new OA\Property(property: 'password', type: 'string', minLength: 8), // ← Added minLength
106
+ ```
107
+
108
+ ## After Editing
109
+
110
+ ```bash
111
+ ./artisan l5-swagger:generate
112
+ ```
@@ -0,0 +1,73 @@
1
+ ---
2
+ description: "Resource class rules - OpenAPI Schema must match actual output"
3
+ globs: ["{{LARAVEL_BASE}}/Http/Resources/**"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Resource Rules
8
+
9
+ ## Golden Rule: Schema MUST Match Output
10
+
11
+ OpenAPI Schema properties MUST match the actual `toArray()` / `schemaArray()` output.
12
+
13
+ ## Before Editing Resource
14
+
15
+ 1. **Read `OmnifyBase/*ResourceBase.php`** to see actual fields
16
+ 2. **Compare with `#[OA\Schema]`** properties
17
+ 3. **Add missing fields** to schema
18
+ 4. **Remove non-existent fields** from schema
19
+
20
+ ## Checklist
21
+
22
+ ```php
23
+ // Check OmnifyBase/*ResourceBase.php → schemaArray()
24
+ return [
25
+ 'id' => $this->id,
26
+ 'name_lastname' => $this->name_lastname,
27
+ 'name_full_name' => $this->name_full_name, // ← Computed field!
28
+ 'email' => $this->email,
29
+ 'created_at' => $this->created_at?->toISOString(),
30
+ ];
31
+
32
+ // #[OA\Schema] MUST have ALL these properties
33
+ #[OA\Schema(
34
+ properties: [
35
+ new OA\Property(property: 'id', ...),
36
+ new OA\Property(property: 'name_lastname', ...),
37
+ new OA\Property(property: 'name_full_name', ...), // ← Don't forget!
38
+ new OA\Property(property: 'email', ...),
39
+ new OA\Property(property: 'created_at', ...),
40
+ ]
41
+ )]
42
+ ```
43
+
44
+ ## Common Mistakes
45
+
46
+ | Mistake | Fix |
47
+ | ---------------------------------------- | ----------------------- |
48
+ | Missing computed fields (name_full_name) | Check *ResourceBase.php |
49
+ | Schema has field not in output | Remove from schema |
50
+ | Field type mismatch | Match actual PHP type |
51
+ | Missing nullable | Check if `?->` used |
52
+
53
+ ## JapaneseName Type Fields
54
+
55
+ JapaneseName expands to multiple fields including computed ones:
56
+
57
+ ```php
58
+ // All these come from JapaneseName type
59
+ 'name_lastname' => $this->name_lastname,
60
+ 'name_firstname' => $this->name_firstname,
61
+ 'name_kana_lastname' => $this->name_kana_lastname,
62
+ 'name_kana_firstname' => $this->name_kana_firstname,
63
+ 'name_full_name' => $this->name_full_name, // Computed!
64
+ 'name_full_name_kana' => $this->name_full_name_kana, // Computed!
65
+ ```
66
+
67
+ **MUST include ALL in OpenAPI Schema!**
68
+
69
+ ## After Editing
70
+
71
+ ```bash
72
+ ./artisan l5-swagger:generate
73
+ ```
@@ -0,0 +1,69 @@
1
+ ---
2
+ description: "Code review checklist - security, performance, quality"
3
+ globs: []
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Code Review Rules
8
+
9
+ > Review process for Laravel code
10
+
11
+ ## Review Process
12
+
13
+ 1. Security Check → 2. Performance Check → 3. Quality Check → 4. Test Check
14
+
15
+ ## Security Checklist
16
+
17
+ | Check | Look For |
18
+ | --------------- | ---------------------------- |
19
+ | Mass Assignment | `$request->validated()` |
20
+ | SQL Injection | No raw SQL |
21
+ | `$fillable` | Defined, no sensitive fields |
22
+ | `$hidden` | Password hidden |
23
+
24
+ ## Performance Checklist
25
+
26
+ | Check | Look For |
27
+ | ---------- | ---------------------- |
28
+ | N+1 | `with()` for relations |
29
+ | Pagination | `paginate()` for lists |
30
+ | Resources | `whenLoaded()` |
31
+
32
+ ## Quality Checklist
33
+
34
+ | Check | Look For |
35
+ | ---------- | --------------- |
36
+ | Validation | FormRequest |
37
+ | Response | Resource |
38
+ | Dates | `toISOString()` |
39
+
40
+ ## Code Style Checklist
41
+
42
+ | Check | Look For |
43
+ | ----------- | -------------------------------------- |
44
+ | **Imports** | No FQCN inline (`\Full\Path\Class`) |
45
+ | Use | All classes imported with `use` at top |
46
+ | Naming | Follows conventions |
47
+
48
+ ## Test Checklist
49
+
50
+ | Endpoint | 正常系 | 異常系 |
51
+ | -------- | ------ | -------- |
52
+ | store | 201 | 422 |
53
+ | show | 200 | 404 |
54
+ | update | 200 | 404, 422 |
55
+ | destroy | 204 | 404 |
56
+
57
+ ## Output Format
58
+
59
+ ```markdown
60
+ ## Security
61
+ ✅ Using `$request->validated()`
62
+ ❌ Missing `$hidden` for password
63
+
64
+ ## Performance
65
+ ✅ Eager loading
66
+ ❌ Missing pagination
67
+
68
+ ## Recommendation: Request Changes
69
+ ```