@famgia/omnify-laravel 0.0.88 → 0.0.90

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 (49) hide show
  1. package/dist/{chunk-YVVAJA3T.js → chunk-2QSKZS63.js} +188 -12
  2. package/dist/chunk-2QSKZS63.js.map +1 -0
  3. package/dist/index.cjs +190 -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 +186 -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/README.md.stub +95 -0
  14. package/stubs/ai-guides/claude-agents/architect.md.stub +150 -0
  15. package/stubs/ai-guides/claude-agents/developer.md.stub +190 -0
  16. package/stubs/ai-guides/claude-agents/reviewer.md.stub +134 -0
  17. package/stubs/ai-guides/claude-agents/tester.md.stub +196 -0
  18. package/stubs/ai-guides/claude-checklists/backend.md.stub +112 -0
  19. package/stubs/ai-guides/claude-omnify/antdesign-guide.md.stub +401 -0
  20. package/stubs/ai-guides/claude-omnify/config-guide.md.stub +253 -0
  21. package/stubs/ai-guides/claude-omnify/japan-guide.md.stub +186 -0
  22. package/stubs/ai-guides/claude-omnify/laravel-guide.md.stub +61 -0
  23. package/stubs/ai-guides/claude-omnify/react-form-guide.md.stub +259 -0
  24. package/stubs/ai-guides/claude-omnify/schema-guide.md.stub +115 -0
  25. package/stubs/ai-guides/claude-omnify/typescript-guide.md.stub +310 -0
  26. package/stubs/ai-guides/claude-rules/naming.md.stub +364 -0
  27. package/stubs/ai-guides/claude-rules/performance.md.stub +251 -0
  28. package/stubs/ai-guides/claude-rules/security.md.stub +159 -0
  29. package/stubs/ai-guides/claude-workflows/bug-fix.md.stub +201 -0
  30. package/stubs/ai-guides/claude-workflows/code-review.md.stub +164 -0
  31. package/stubs/ai-guides/claude-workflows/new-feature.md.stub +327 -0
  32. package/stubs/ai-guides/cursor/laravel-controller.mdc.stub +391 -0
  33. package/stubs/ai-guides/cursor/laravel-request.mdc.stub +112 -0
  34. package/stubs/ai-guides/cursor/laravel-resource.mdc.stub +73 -0
  35. package/stubs/ai-guides/cursor/laravel-review.mdc.stub +69 -0
  36. package/stubs/ai-guides/cursor/laravel-testing.mdc.stub +138 -0
  37. package/stubs/ai-guides/cursor/laravel.mdc.stub +82 -0
  38. package/stubs/ai-guides/cursor/omnify.mdc.stub +58 -0
  39. package/stubs/ai-guides/laravel/README.md.stub +59 -0
  40. package/stubs/ai-guides/laravel/architecture.md.stub +424 -0
  41. package/stubs/ai-guides/laravel/controller.md.stub +484 -0
  42. package/stubs/ai-guides/laravel/datetime.md.stub +334 -0
  43. package/stubs/ai-guides/laravel/openapi.md.stub +369 -0
  44. package/stubs/ai-guides/laravel/request.md.stub +450 -0
  45. package/stubs/ai-guides/laravel/resource.md.stub +516 -0
  46. package/stubs/ai-guides/laravel/service.md.stub +503 -0
  47. package/stubs/ai-guides/laravel/testing.md.stub +1504 -0
  48. package/ai-guides/laravel-guide.md +0 -461
  49. package/dist/chunk-YVVAJA3T.js.map +0 -1
@@ -0,0 +1,484 @@
1
+ # Controller Guide
2
+
3
+ > **Related:** [README](./README.md) | [Service Guide](./service-guide.md) | [Request Guide](./request-guide.md)
4
+
5
+ ## Golden Rule: Thin Controller
6
+
7
+ Controllers should only:
8
+ 1. **Receive** validated input (via FormRequest)
9
+ 2. **Delegate** to Model or Service
10
+ 3. **Return** formatted response (via Resource)
11
+
12
+ ```php
13
+ // ✅ PERFECT: Thin controller
14
+ public function store(UserStoreRequest $request): UserResource
15
+ {
16
+ $user = User::create($request->validated());
17
+ return new UserResource($user);
18
+ }
19
+
20
+ // ❌ BAD: Fat controller
21
+ public function store(Request $request)
22
+ {
23
+ $validated = $request->validate([...]); // Validation here
24
+ $user = new User();
25
+ $user->name = $validated['name']; // Manual assignment
26
+ $user->password = bcrypt($validated['password']); // Logic here
27
+ $user->save();
28
+ Mail::to($user)->send(new WelcomeMail()); // Side effects here
29
+ return response()->json([ // Manual formatting
30
+ 'id' => $user->id,
31
+ 'name' => $user->name,
32
+ ]);
33
+ }
34
+ ```
35
+
36
+ ---
37
+
38
+ ## CRUD Controller Template
39
+
40
+ ```php
41
+ <?php
42
+
43
+ namespace App\Http\Controllers;
44
+
45
+ use App\Http\Requests\UserStoreRequest;
46
+ use App\Http\Requests\UserUpdateRequest;
47
+ use App\Http\Resources\UserResource;
48
+ use App\Models\User;
49
+ use Illuminate\Http\JsonResponse;
50
+ use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
51
+ use Spatie\QueryBuilder\AllowedFilter;
52
+ use Spatie\QueryBuilder\QueryBuilder;
53
+
54
+ class UserController extends Controller
55
+ {
56
+ /**
57
+ * Display a listing of the resource.
58
+ * GET /api/users?filter[search]=keyword&sort=-created_at&per_page=10
59
+ */
60
+ public function index(): AnonymousResourceCollection
61
+ {
62
+ $users = QueryBuilder::for(User::class)
63
+ ->allowedFilters([
64
+ AllowedFilter::callback('search', function ($query, $value) {
65
+ $query->where(function ($q) use ($value) {
66
+ $q->where('name', 'like', "%{$value}%")
67
+ ->orWhere('email', 'like', "%{$value}%");
68
+ });
69
+ }),
70
+ ])
71
+ ->allowedSorts(['id', 'name', 'email', 'created_at', 'updated_at'])
72
+ ->defaultSort('-id')
73
+ ->paginate(request()->input('per_page', 10));
74
+
75
+ return UserResource::collection($users);
76
+ }
77
+
78
+ /**
79
+ * Store a newly created resource in storage.
80
+ * POST /api/users
81
+ */
82
+ public function store(UserStoreRequest $request): JsonResponse
83
+ {
84
+ $user = User::create($request->validated());
85
+
86
+ return (new UserResource($user))
87
+ ->response()
88
+ ->setStatusCode(201);
89
+ }
90
+
91
+ /**
92
+ * Display the specified resource.
93
+ * GET /api/users/{user}
94
+ */
95
+ public function show(User $user): UserResource
96
+ {
97
+ return new UserResource($user);
98
+ }
99
+
100
+ /**
101
+ * Update the specified resource in storage.
102
+ * PUT /api/users/{user}
103
+ */
104
+ public function update(UserUpdateRequest $request, User $user): UserResource
105
+ {
106
+ $user->update($request->validated());
107
+
108
+ return new UserResource($user);
109
+ }
110
+
111
+ /**
112
+ * Remove the specified resource from storage.
113
+ * DELETE /api/users/{user}
114
+ */
115
+ public function destroy(User $user): JsonResponse
116
+ {
117
+ $user->delete();
118
+
119
+ return response()->json(null, 204);
120
+ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Controller Rules
127
+
128
+ ### 1. Always Type-Hint Return Types
129
+
130
+ ```php
131
+ // ✅ DO: Type-hint everything
132
+ public function index(): AnonymousResourceCollection
133
+ public function store(UserStoreRequest $request): UserResource
134
+ public function show(User $user): UserResource
135
+ public function destroy(User $user): JsonResponse
136
+
137
+ // ❌ DON'T: Missing return types
138
+ public function index()
139
+ public function store(Request $request)
140
+ ```
141
+
142
+ ### 2. Use Route Model Binding
143
+
144
+ ```php
145
+ // ✅ DO: Auto-resolve model from route parameter
146
+ public function show(User $user): UserResource
147
+ {
148
+ return new UserResource($user);
149
+ }
150
+
151
+ // ❌ DON'T: Manual find
152
+ public function show(int $id): UserResource
153
+ {
154
+ $user = User::findOrFail($id);
155
+ return new UserResource($user);
156
+ }
157
+ ```
158
+
159
+ ### 3. Always Use FormRequest for Validation
160
+
161
+ ```php
162
+ // ✅ DO: Separate FormRequest class
163
+ public function store(UserStoreRequest $request): UserResource
164
+ {
165
+ $user = User::create($request->validated());
166
+ return new UserResource($user);
167
+ }
168
+
169
+ // ❌ DON'T: Validate in controller
170
+ public function store(Request $request): UserResource
171
+ {
172
+ $validated = $request->validate([
173
+ 'name' => 'required|string|max:255',
174
+ 'email' => 'required|email|unique:users',
175
+ ]);
176
+ // ...
177
+ }
178
+ ```
179
+
180
+ ### 4. Always Return Resource
181
+
182
+ ```php
183
+ // ✅ DO: Use Resource for consistent formatting
184
+ return new UserResource($user);
185
+ return UserResource::collection($users);
186
+
187
+ // ❌ DON'T: Return model or array directly
188
+ return $user;
189
+ return response()->json(['data' => $user]);
190
+ ```
191
+
192
+ ### 5. Use Dependency Injection for Services
193
+
194
+ ```php
195
+ // ✅ DO: Constructor injection
196
+ class OrderController extends Controller
197
+ {
198
+ public function __construct(
199
+ private OrderService $orderService
200
+ ) {}
201
+
202
+ public function store(OrderRequest $request): OrderResource
203
+ {
204
+ $order = $this->orderService->checkout(...);
205
+ return new OrderResource($order);
206
+ }
207
+ }
208
+
209
+ // ❌ DON'T: Create service in method
210
+ public function store(OrderRequest $request): OrderResource
211
+ {
212
+ $service = new OrderService(); // Hard to test
213
+ // ...
214
+ }
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Query Parameters - List Endpoint
220
+
221
+ ### Template
222
+
223
+ ```php
224
+ use Spatie\QueryBuilder\AllowedFilter;
225
+ use Spatie\QueryBuilder\QueryBuilder;
226
+
227
+ public function index(): AnonymousResourceCollection
228
+ {
229
+ $users = QueryBuilder::for(User::class)
230
+ ->allowedFilters([
231
+ AllowedFilter::callback('search', function ($query, $value) {
232
+ $query->where(function ($q) use ($value) {
233
+ $q->where('name', 'like', "%{$value}%")
234
+ ->orWhere('email', 'like', "%{$value}%");
235
+ });
236
+ }),
237
+ ])
238
+ ->allowedSorts(['id', 'name', 'email', 'created_at'])
239
+ ->defaultSort('-id')
240
+ ->paginate(request()->input('per_page', 10));
241
+
242
+ return UserResource::collection($users);
243
+ }
244
+ ```
245
+
246
+ ### API Example
247
+
248
+ ```bash
249
+ GET /api/users?filter[search]=田中&sort=-created_at&per_page=20
250
+ ```
251
+
252
+ ### OpenAPI - Use Reusable Parameters
253
+
254
+ ```php
255
+ #[OA\Get(
256
+ path: '/api/users',
257
+ summary: 'List users',
258
+ description: 'Allowed sorts: id, name, email, created_at',
259
+ parameters: [
260
+ new OA\Parameter(ref: '#/components/parameters/FilterSearch'),
261
+ new OA\Parameter(ref: '#/components/parameters/QueryPage'),
262
+ new OA\Parameter(ref: '#/components/parameters/QueryPerPage'),
263
+ new OA\Parameter(ref: '#/components/parameters/QuerySort'),
264
+ ],
265
+ )]
266
+ ```
267
+
268
+ ### Keep It Simple
269
+
270
+ ```php
271
+ // ✅ DO: Simple, flat filters
272
+ ->allowedFilters([
273
+ AllowedFilter::callback('search', fn($q, $v) => ...),
274
+ AllowedFilter::exact('status'),
275
+ ])
276
+ ->allowedSorts(['id', 'name', 'created_at'])
277
+
278
+ // ❌ DON'T: Over-engineer
279
+ ->allowedFilters([
280
+ AllowedFilter::exact('author.company.country'), // Too nested!
281
+ ])
282
+ ->allowedIncludes(['a', 'b', 'c', 'd', 'e']) // Too many!
283
+ ```
284
+
285
+ ---
286
+
287
+ ## OpenAPI Schema Location
288
+
289
+ Define schemas close to the code that uses them:
290
+
291
+ | Type | Location | Example |
292
+ | ------------ | ------------------------- | --------------------------------------- |
293
+ | Request body | `*Request.php` | `UserStoreRequest`, `UserUpdateRequest` |
294
+ | Response | `*Resource.php` | `UserResource` |
295
+ | Pagination | `app/OpenApi/Schemas.php` | `PaginationMeta`, `PaginationLinks` |
296
+ | Parameters | `app/OpenApi/Schemas.php` | `FilterSearch`, `QuerySort` |
297
+
298
+ ### Request Schema (on FormRequest class)
299
+
300
+ ```php
301
+ // app/Http/Requests/UserStoreRequest.php
302
+ #[OA\Schema(
303
+ schema: 'UserStoreRequest',
304
+ required: ['name', 'email', 'password'],
305
+ properties: [
306
+ new OA\Property(property: 'name', type: 'string', example: '田中'),
307
+ new OA\Property(property: 'email', type: 'string', format: 'email'),
308
+ new OA\Property(property: 'password', type: 'string', minLength: 8),
309
+ ]
310
+ )]
311
+ class UserStoreRequest extends FormRequest
312
+ ```
313
+
314
+ ### Response Schema (on Resource class)
315
+
316
+ ```php
317
+ // app/Http/Resources/UserResource.php
318
+ #[OA\Schema(
319
+ schema: 'User',
320
+ properties: [
321
+ new OA\Property(property: 'id', type: 'integer'),
322
+ new OA\Property(property: 'name', type: 'string'),
323
+ new OA\Property(property: 'email', type: 'string'),
324
+ new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
325
+ ]
326
+ )]
327
+ class UserResource extends JsonResource
328
+ ```
329
+
330
+ ### Controller uses refs
331
+
332
+ ```php
333
+ #[OA\Post(
334
+ path: '/api/users',
335
+ requestBody: new OA\RequestBody(
336
+ content: new OA\JsonContent(ref: '#/components/schemas/UserStoreRequest')
337
+ ),
338
+ responses: [
339
+ new OA\Response(
340
+ response: 201,
341
+ content: new OA\JsonContent(
342
+ properties: [
343
+ new OA\Property(property: 'data', ref: '#/components/schemas/User'),
344
+ ]
345
+ )
346
+ ),
347
+ ]
348
+ )]
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Don't Mix Concepts
354
+
355
+ | Concept | Use For | DON'T Use For |
356
+ | ---------------- | ---------------- | -------------------- |
357
+ | `*StoreRequest` | POST create | GET list, PUT update |
358
+ | `*UpdateRequest` | PUT/PATCH update | POST create |
359
+ | `*Resource` | Response output | Request input |
360
+ | `index()` | List (no body) | Create/Update |
361
+ | `store()` | Create new | Update existing |
362
+ | `update()` | Update existing | Create new |
363
+
364
+ ---
365
+
366
+ ## Response Status Codes
367
+
368
+ | Action | Success Code | Resource |
369
+ | ------- | -------------- | ---------- |
370
+ | index | 200 OK | Collection |
371
+ | store | 201 Created | Resource |
372
+ | show | 200 OK | Resource |
373
+ | update | 200 OK | Resource |
374
+ | destroy | 204 No Content | null |
375
+
376
+ ```php
377
+ // store returns 201 automatically via Resource
378
+ public function store(UserStoreRequest $request): UserResource
379
+ {
380
+ $user = User::create($request->validated());
381
+
382
+ return (new UserResource($user))
383
+ ->response()
384
+ ->setStatusCode(201); // Explicit 201
385
+ }
386
+
387
+ // Or simpler (Laravel auto-returns 201 for new resources)
388
+ public function store(UserStoreRequest $request): UserResource
389
+ {
390
+ return new UserResource(User::create($request->validated()));
391
+ }
392
+
393
+ // destroy returns 204
394
+ public function destroy(User $user): JsonResponse
395
+ {
396
+ $user->delete();
397
+ return response()->json(null, 204);
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ## When to Add Logic to Controller
404
+
405
+ ### ✅ OK in Controller
406
+
407
+ ```php
408
+ // Simple query building
409
+ $query = User::query();
410
+ if ($request->input('search')) {
411
+ $query->where('name', 'like', '%'.$request->input('search').'%');
412
+ }
413
+
414
+ // Dispatching events/jobs (one-liners)
415
+ event(new UserRegistered($user));
416
+ SendWelcomeEmail::dispatch($user);
417
+
418
+ // Simple eager loading
419
+ $user->load('orders', 'profile');
420
+ ```
421
+
422
+ ### ❌ Move to Service
423
+
424
+ ```php
425
+ // Multi-step business logic
426
+ // - Calculate pricing
427
+ // - Process payment
428
+ // - Create order
429
+ // - Update inventory
430
+ // - Send notifications
431
+ // → Create OrderService
432
+
433
+ // Complex validation beyond FormRequest
434
+ // - Check external API
435
+ // - Validate business rules
436
+ // → Create Service or Action
437
+
438
+ // Reusable across multiple controllers
439
+ // → Create Action class
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Anti-Patterns
445
+
446
+ ```php
447
+ // ❌ DON'T: Business logic in controller
448
+ public function store(OrderRequest $request)
449
+ {
450
+ $cart = Cart::find($request->cart_id);
451
+
452
+ // 50 lines of pricing calculation
453
+ // 30 lines of payment processing
454
+ // 20 lines of inventory update
455
+
456
+ return new OrderResource($order);
457
+ }
458
+
459
+ // ❌ DON'T: Validation in controller
460
+ public function store(Request $request)
461
+ {
462
+ $request->validate([...]); // Use FormRequest instead
463
+ }
464
+
465
+ // ❌ DON'T: Manual JSON response
466
+ public function show(User $user)
467
+ {
468
+ return response()->json([
469
+ 'id' => $user->id,
470
+ 'name' => $user->name,
471
+ ]); // Use Resource instead
472
+ }
473
+
474
+ // ❌ DON'T: Try-catch for expected exceptions
475
+ public function show(int $id)
476
+ {
477
+ try {
478
+ $user = User::findOrFail($id);
479
+ } catch (ModelNotFoundException $e) {
480
+ return response()->json(['error' => 'Not found'], 404);
481
+ }
482
+ // Laravel handles this automatically with route model binding
483
+ }
484
+ ```