@famgia/omnify-laravel 0.0.87 → 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,138 @@
1
+ ---
2
+ description: "PEST testing rules - 正常系/異常系, naming conventions"
3
+ globs: ["tests/**", "**/*Test.php"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Testing Rules (PEST)
8
+
9
+ > **Agent:** Act as **@tester** agent
10
+ > - Read `.claude/guides/laravel/testing.md` for full guide
11
+
12
+ ## How to Run Tests
13
+
14
+ ```bash
15
+ # Run ALL tests (from project root - uses Docker wrapper)
16
+ ./artisan test
17
+
18
+ # Run specific test file
19
+ ./artisan test --filter=UserControllerTest
20
+
21
+ # Run specific test method
22
+ ./artisan test --filter="creates user with valid data"
23
+
24
+ # Run with coverage (if xdebug installed)
25
+ ./artisan test --coverage
26
+ ```
27
+
28
+ > **Note:** The root `./artisan` script is a wrapper that runs commands inside Docker container.
29
+
30
+ ## Testing Process
31
+
32
+ 1. **Before writing tests:**
33
+ - Read the Controller to understand endpoints
34
+ - Read the Request to understand validation rules
35
+ - Read the Resource to understand response structure
36
+
37
+ 2. **Write tests covering:**
38
+ - 正常系 (Normal cases) - success scenarios
39
+ - 異常系 (Abnormal cases) - failure scenarios
40
+
41
+ 3. **Run tests to verify:**
42
+ ```bash
43
+ cd backend && ./artisan test
44
+ ```
45
+
46
+ 4. **All tests must pass before committing**
47
+
48
+ ## Critical Rules
49
+
50
+ 1. **PEST Syntax** - Use `describe()` + `it()`, not PHPUnit
51
+ 2. **正常系 + 異常系** - Cover both success and failure
52
+ 3. **Naming** - Use `正常:` and `異常:` prefixes
53
+ 4. **Coverage** - All endpoints, all validation rules
54
+ 5. **RefreshDatabase** - MUST use for SQLite in-memory
55
+
56
+ ## Database Trait - IMPORTANT
57
+
58
+ ```php
59
+ // ✅ CORRECT - Use RefreshDatabase for SQLite in-memory
60
+ use Illuminate\Foundation\Testing\RefreshDatabase;
61
+ uses(RefreshDatabase::class);
62
+
63
+ // ❌ WRONG - DatabaseTransactions doesn't run migrations
64
+ use Illuminate\Foundation\Testing\DatabaseTransactions;
65
+ uses(DatabaseTransactions::class); // Will fail with "no such table"
66
+ ```
67
+
68
+ | Trait | When to Use | Notes |
69
+ | ---------------------- | --------------------------------- | ------------------------------- |
70
+ | `RefreshDatabase` | SQLite in-memory (default) | Runs migrations, then truncates |
71
+ | `DatabaseTransactions` | MySQL/PostgreSQL with existing DB | Only wraps in transaction |
72
+
73
+ ## Test Naming
74
+
75
+ ```php
76
+ // 正常系 (Normal) - success behavior
77
+ it('正常: returns paginated users')
78
+ it('正常: creates user with valid data')
79
+ it('正常: deletes user')
80
+
81
+ // 異常系 (Abnormal) - failure behavior
82
+ it('異常: fails to create user with missing email')
83
+ it('異常: fails to create user with invalid kana format')
84
+ it('異常: returns 404 when user not found')
85
+ it('異常: returns 401 when not authenticated')
86
+ ```
87
+
88
+ ## Coverage Matrix
89
+
90
+ | Endpoint | 正常系 | 異常系 |
91
+ | -------- | ------------------ | --------------------- |
92
+ | index | List, filter, sort | Empty, invalid params |
93
+ | store | Creates → 201 | 422 (each field) |
94
+ | show | Returns → 200 | 404 |
95
+ | update | Updates → 200 | 404, 422 |
96
+ | destroy | Deletes → 204 | 404 |
97
+
98
+ ## Template
99
+
100
+ ```php
101
+ describe('POST /api/users', function () {
102
+ // 正常系
103
+ it('正常: creates user with valid data', function () {
104
+ $response = $this->postJson('/api/users', validUserData());
105
+
106
+ $response->assertCreated();
107
+ $this->assertDatabaseHas('users', ['email' => 'test@example.com']);
108
+ });
109
+
110
+ // 異常系 - Required fields
111
+ it('異常: fails to create user with missing email', function () {
112
+ $data = validUserData();
113
+ unset($data['email']);
114
+
115
+ $this->postJson('/api/users', $data)
116
+ ->assertUnprocessable()
117
+ ->assertJsonValidationErrors(['email']);
118
+ });
119
+
120
+ // 異常系 - Format validation
121
+ it('異常: fails to create user with invalid email format', function () {
122
+ $this->postJson('/api/users', validUserData(['email' => 'invalid']))
123
+ ->assertUnprocessable()
124
+ ->assertJsonValidationErrors(['email']);
125
+ });
126
+ });
127
+ ```
128
+
129
+ ## Japanese Field Tests
130
+
131
+ ```php
132
+ it('異常: fails with hiragana in kana field', function () {
133
+ $this->postJson('/api/users', validUserData([
134
+ 'name_kana_lastname' => 'たなか' // hiragana - should fail
135
+ ]))->assertUnprocessable()
136
+ ->assertJsonValidationErrors(['name_kana_lastname']);
137
+ });
138
+ ```
@@ -0,0 +1,82 @@
1
+ ---
2
+ description: "Laravel backend development rules - security, performance, patterns"
3
+ globs: ["{{LARAVEL_BASE}}/**"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Laravel Rules
8
+
9
+ > **Documentation:** `.claude/guides/laravel/`
10
+ > **Specific Rules:** See also `laravel-controller.mdc`, `laravel-resource.mdc`, `laravel-request.mdc`, `laravel-testing.mdc`
11
+
12
+ ## Critical Rules
13
+
14
+ 1. **Thin Controller** - Validate → Delegate → Respond (see `laravel-controller.mdc`)
15
+ 2. **Schema-First** - Use Omnify schemas, don't create migrations manually
16
+ 3. **Security** - Always use `$request->validated()`, never `$request->all()`
17
+ 4. **Performance** - Use `with()` for relations, `paginate()` for lists
18
+ 5. **Dates** - Store UTC, return `->toISOString()`
19
+ 6. **Testing** - Write 正常系 + 異常系 tests (see `laravel-testing.mdc`)
20
+ 7. **Imports** - Always `use` and short class names, never FQCN inline
21
+
22
+ ## File-Specific Rules
23
+
24
+ | File Type | Rule File | Key Focus |
25
+ | ---------- | ----------------------- | ---------------------------- |
26
+ | Controller | `laravel-controller.mdc` | Thin, Query Builder, OpenAPI |
27
+ | Resource | `laravel-resource.mdc` | Schema matches output |
28
+ | Request | `laravel-request.mdc` | Schema matches validation |
29
+ | Test | `laravel-testing.mdc` | 正常系 + 異常系 coverage |
30
+
31
+ ## Security Checklist
32
+
33
+ - [ ] `$fillable` defined in Model
34
+ - [ ] `$hidden` for sensitive fields
35
+ - [ ] `$request->validated()` not `$request->all()`
36
+ - [ ] No raw SQL with user input
37
+ - [ ] `with()` for eager loading
38
+ - [ ] `whenLoaded()` in Resources
39
+ - [ ] `paginate()` for list endpoints
40
+
41
+ ## When to Use What
42
+
43
+ | Scenario | Solution |
44
+ | ---------------- | ------------------ |
45
+ | Simple CRUD | Controller + Model |
46
+ | Multi-step logic | Service |
47
+ | Reusable action | Action class |
48
+ | Async task | Job |
49
+
50
+ ## Pre-Edit Checklist
51
+
52
+ **BEFORE editing any file, MUST:**
53
+
54
+ 1. **Read the file first** - Check existing imports, patterns, style
55
+ 2. **Check imports** - Add `use` if class not imported
56
+ 3. **Follow existing style** - Match indentation, naming, patterns
57
+ 4. **Never use FQCN inline** - Always import first
58
+
59
+ ## Imports Rule
60
+
61
+ ```php
62
+ // ❌ WRONG: Inline FQCN
63
+ public function store(): \Illuminate\Http\JsonResponse
64
+
65
+ // ✅ CORRECT: Import and use short name
66
+ use Illuminate\Http\JsonResponse;
67
+ public function store(): JsonResponse
68
+ ```
69
+
70
+ ## Don't Over-Engineer
71
+
72
+ | ❌ DON'T | ✅ DO |
73
+ | ----------------------------------- | ------------------------- |
74
+ | Add workaround to hide bugs | Fix root cause or report |
75
+ | Repository for simple CRUD | Use Eloquent directly |
76
+ | Service that just wraps Model | Controller + Model |
77
+ | Interface with 1 implementation | Concrete class |
78
+ | Manual 404 check with route binding | Trust framework |
79
+ | "Improve" unrelated code | Change only what's needed |
80
+ | Build for future "just in case" | YAGNI - build when needed |
81
+
82
+ **Full examples:** `.claude/guides/laravel/architecture.md`
@@ -0,0 +1,59 @@
1
+ # Backend Guides
2
+
3
+ > Laravel 12, PHP 8.4, MySQL 8
4
+
5
+ ## Quick Navigation
6
+
7
+ | Guide | Description |
8
+ | ------------------------------------ | --------------------------------------------- |
9
+ | [architecture.md](./architecture.md) | Design philosophy, when to use Service/Action |
10
+ | [controller.md](./controller.md) | Thin controller pattern, CRUD template |
11
+ | [request.md](./request.md) | Form validation, FormRequest |
12
+ | [resource.md](./resource.md) | API response format, dates |
13
+ | [service.md](./service.md) | When & how to use services |
14
+ | [testing.md](./testing.md) | PEST, 正常系/異常系 |
15
+ | [openapi.md](./openapi.md) | Swagger documentation |
16
+ | [datetime.md](./datetime.md) | Carbon, UTC handling |
17
+
18
+ ## Related
19
+
20
+ | Topic | Location |
21
+ | ------------------------ | ----------------------------------------------------------- |
22
+ | **Security rules** | [/rules/security.md](../../rules/security.md) |
23
+ | **Performance rules** | [/rules/performance.md](../../rules/performance.md) |
24
+ | **Naming conventions** | [/rules/naming.md](../../rules/naming.md) |
25
+ | **Checklist** | [/checklists/backend.md](../../checklists/backend.md) |
26
+ | **New feature workflow** | [/workflows/new-feature.md](../../workflows/new-feature.md) |
27
+
28
+ ## Quick Patterns
29
+
30
+ ### Thin Controller
31
+
32
+ ```php
33
+ public function store(UserStoreRequest $request): UserResource
34
+ {
35
+ return new UserResource(User::create($request->validated()));
36
+ }
37
+ ```
38
+
39
+ ### Resource with Dates
40
+
41
+ ```php
42
+ public function toArray($request): array
43
+ {
44
+ return [
45
+ 'id' => $this->id,
46
+ 'name' => $this->name,
47
+ 'created_at' => $this->created_at?->toISOString(),
48
+ ];
49
+ }
50
+ ```
51
+
52
+ ### When to Use What
53
+
54
+ | Question | Answer |
55
+ | -------------------------- | --------------- |
56
+ | Simple CRUD? | Controller only |
57
+ | Multi-step business logic? | Service |
58
+ | Single reusable action? | Action class |
59
+ | Async task? | Job |
@@ -0,0 +1,424 @@
1
+ # Design Philosophy
2
+
3
+ > This document explains the architectural decisions and design principles for this Laravel backend.
4
+
5
+ ## Architecture: Thin Controller + Service (When Needed)
6
+
7
+ ```mermaid
8
+ flowchart TD
9
+ subgraph HTTP["HTTP Layer"]
10
+ Route[Route] --> Middleware --> FormRequest
11
+ end
12
+
13
+ FormRequest --> Controller
14
+
15
+ subgraph Controller["Controller Layer (Thin)"]
16
+ C[Orchestrate request → response]
17
+ end
18
+
19
+ Controller --> Model
20
+ Controller --> Service
21
+
22
+ subgraph Data["Data Layer"]
23
+ Model["Model (Simple CRUD)"]
24
+ Service["Service (Complex ops)"]
25
+ end
26
+
27
+ Model --> Resource
28
+ Service --> Resource
29
+
30
+ subgraph Output["Response Layer"]
31
+ Resource[Resource - JSON format]
32
+ end
33
+ ```
34
+
35
+ | Layer | Responsibility | Rules |
36
+ | ---------- | ------------------------------ | ----------------------------------- |
37
+ | HTTP | Routing, auth, validation | No business logic |
38
+ | Controller | Orchestrate request → response | Delegate to Model or Service |
39
+ | Model | Simple CRUD | `User::create()`, `$user->update()` |
40
+ | Service | Complex operations | `OrderService`, `PaymentService` |
41
+ | Resource | Format JSON response | Dates to ISO 8601 |
42
+
43
+ ---
44
+
45
+ ## Core Principle: Don't Over-Engineer
46
+
47
+ ### ❌ BAD: Over-Engineering Simple CRUD
48
+
49
+ ```php
50
+ // DON'T create all these for simple CRUD:
51
+ app/
52
+ ├── Repositories/
53
+ │ ├── UserRepositoryInterface.php
54
+ │ └── UserRepository.php
55
+ ├── Services/
56
+ │ └── UserService.php
57
+ ├── DTOs/
58
+ │ └── UserDTO.php
59
+ └── Contracts/
60
+ └── UserServiceInterface.php
61
+
62
+ // Controller calls Service calls Repository calls Model
63
+ // 4 layers for a simple User::create()!
64
+ ```
65
+
66
+ ### ✅ GOOD: Simple CRUD = Controller + Model
67
+
68
+ ```php
69
+ // Simple CRUD - Controller is enough
70
+ class UserController extends Controller
71
+ {
72
+ public function store(UserStoreRequest $request): UserResource
73
+ {
74
+ $user = User::create($request->validated());
75
+ return new UserResource($user);
76
+ }
77
+ }
78
+
79
+ // That's it! No extra layers needed.
80
+ ```
81
+
82
+ ---
83
+
84
+ ## When to Add Each Layer
85
+
86
+ ### Layer Decision Matrix
87
+
88
+ | Scenario | Controller | Service | Action | Job |
89
+ | ------------------------- | ---------- | ------- | ------ | --- |
90
+ | Simple CRUD | ✅ | ❌ | ❌ | ❌ |
91
+ | CRUD + send email | ✅ | ❌ | ❌ | ✅ |
92
+ | Multi-step business logic | ✅ | ✅ | ❌ | ❌ |
93
+ | Reusable single operation | ✅ | ❌ | ✅ | ❌ |
94
+ | Long-running task | ✅ | ❌ | ❌ | ✅ |
95
+
96
+ ### Detailed Examples
97
+
98
+ #### 1. Simple CRUD → Controller Only
99
+
100
+ ```php
101
+ // ✅ Direct model operations
102
+ public function store(UserStoreRequest $request): UserResource
103
+ {
104
+ $user = User::create($request->validated());
105
+ return new UserResource($user);
106
+ }
107
+
108
+ public function update(UserUpdateRequest $request, User $user): UserResource
109
+ {
110
+ $user->update($request->validated());
111
+ return new UserResource($user);
112
+ }
113
+ ```
114
+
115
+ #### 2. CRUD + Side Effects → Controller + Job/Event
116
+
117
+ ```php
118
+ // ✅ Use Job for async operations
119
+ public function store(UserStoreRequest $request): UserResource
120
+ {
121
+ $user = User::create($request->validated());
122
+
123
+ SendWelcomeEmail::dispatch($user); // Async job
124
+ event(new UserRegistered($user)); // Or event
125
+
126
+ return new UserResource($user);
127
+ }
128
+ ```
129
+
130
+ #### 3. Complex Business Logic → Service
131
+
132
+ ```php
133
+ // ✅ Use Service for multi-step operations
134
+ class OrderController extends Controller
135
+ {
136
+ public function __construct(
137
+ private OrderService $orderService
138
+ ) {}
139
+
140
+ public function store(OrderRequest $request): OrderResource
141
+ {
142
+ $order = $this->orderService->checkout(
143
+ cart: Cart::find($request->cart_id),
144
+ user: $request->user(),
145
+ paymentMethod: $request->payment_method
146
+ );
147
+
148
+ return new OrderResource($order);
149
+ }
150
+ }
151
+
152
+ // Service handles complex logic
153
+ class OrderService
154
+ {
155
+ public function checkout(Cart $cart, User $user, string $paymentMethod): Order
156
+ {
157
+ // 1. Validate cart items in stock
158
+ $this->validateInventory($cart);
159
+
160
+ // 2. Calculate totals
161
+ $totals = $this->calculateTotals($cart);
162
+
163
+ // 3. Process payment
164
+ $payment = $this->paymentService->charge($user, $totals, $paymentMethod);
165
+
166
+ // 4. Create order
167
+ $order = Order::create([...]);
168
+
169
+ // 5. Update inventory
170
+ $this->updateInventory($cart);
171
+
172
+ // 6. Send notifications
173
+ event(new OrderPlaced($order));
174
+
175
+ return $order;
176
+ }
177
+ }
178
+ ```
179
+
180
+ #### 4. Single Reusable Operation → Action
181
+
182
+ ```php
183
+ // ✅ Use Action for reusable single-purpose operations
184
+ class CreateInvoiceAction
185
+ {
186
+ public function execute(Order $order): Invoice
187
+ {
188
+ $invoice = Invoice::create([
189
+ 'order_id' => $order->id,
190
+ 'number' => $this->generateNumber(),
191
+ 'total' => $order->total,
192
+ ]);
193
+
194
+ GenerateInvoicePdf::dispatch($invoice);
195
+
196
+ return $invoice;
197
+ }
198
+ }
199
+
200
+ // Used in multiple places
201
+ class OrderController
202
+ {
203
+ public function store(OrderRequest $request, CreateInvoiceAction $createInvoice)
204
+ {
205
+ $order = Order::create($request->validated());
206
+ $createInvoice->execute($order);
207
+ return new OrderResource($order);
208
+ }
209
+ }
210
+
211
+ class RecurringBillingJob
212
+ {
213
+ public function handle(CreateInvoiceAction $createInvoice)
214
+ {
215
+ foreach ($this->getSubscriptions() as $subscription) {
216
+ $order = $subscription->createRenewalOrder();
217
+ $createInvoice->execute($order);
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Why NOT Repository Pattern?
226
+
227
+ ### Problem: Eloquent IS Already a Repository
228
+
229
+ ```php
230
+ // Eloquent provides repository-like methods
231
+ User::find($id);
232
+ User::where('email', $email)->first();
233
+ User::create($data);
234
+ $user->update($data);
235
+ $user->delete();
236
+
237
+ // Adding Repository layer = duplicate abstraction
238
+ interface UserRepositoryInterface {
239
+ public function find(int $id): ?User;
240
+ public function findByEmail(string $email): ?User;
241
+ public function create(array $data): User;
242
+ // ... same methods as Eloquent!
243
+ }
244
+ ```
245
+
246
+ ### When Repository MIGHT Make Sense
247
+
248
+ ```php
249
+ // Only if you genuinely need to swap data sources
250
+ // (rare in real projects)
251
+
252
+ interface ProductCatalogInterface {
253
+ public function search(string $query): Collection;
254
+ }
255
+
256
+ class EloquentProductCatalog implements ProductCatalogInterface { ... }
257
+ class ElasticsearchProductCatalog implements ProductCatalogInterface { ... }
258
+ ```
259
+
260
+ **Reality**: 99% of Laravel projects never swap databases. Don't add abstraction for hypothetical future needs.
261
+
262
+ ---
263
+
264
+ ## Design Principles
265
+
266
+ ### 1. YAGNI (You Aren't Gonna Need It)
267
+
268
+ ```php
269
+ // ❌ DON'T: Create interfaces "just in case"
270
+ interface UserServiceInterface { ... }
271
+ class UserService implements UserServiceInterface { ... }
272
+
273
+ // ✅ DO: Add abstraction only when needed
274
+ class UserService { ... } // Concrete class is fine
275
+ ```
276
+
277
+ ### 2. Single Responsibility
278
+
279
+ ```php
280
+ // ❌ DON'T: Fat controller
281
+ class UserController
282
+ {
283
+ public function store(Request $request)
284
+ {
285
+ $validated = $request->validate([...]); // Validation in controller
286
+ $user = User::create($validated);
287
+ Mail::send(...); // Email logic in controller
288
+ $this->calculatePoints($user); // Business logic in controller
289
+ return response()->json($user); // Manual JSON formatting
290
+ }
291
+ }
292
+
293
+ // ✅ DO: Each layer has one job
294
+ class UserController
295
+ {
296
+ public function store(UserStoreRequest $request): UserResource // Type-hinted
297
+ {
298
+ $user = User::create($request->validated()); // FormRequest validates
299
+ event(new UserRegistered($user)); // Event handles side effects
300
+ return new UserResource($user); // Resource formats output
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### 3. Explicit Over Implicit
306
+
307
+ ```php
308
+ // ❌ DON'T: Magic methods, hidden behavior
309
+ class User extends Model
310
+ {
311
+ public function __call($method, $args) { ... } // Magic
312
+ }
313
+
314
+ // ✅ DO: Clear, readable code
315
+ class User extends Model
316
+ {
317
+ public function orders(): HasMany { ... } // Explicit relationship
318
+ public function scopeActive($query) { ... } // Clear scope
319
+ }
320
+ ```
321
+
322
+ ### 4. Fail Fast
323
+
324
+ ```php
325
+ // ✅ Validate early with FormRequest
326
+ class OrderStoreRequest extends FormRequest
327
+ {
328
+ public function rules(): array
329
+ {
330
+ return [
331
+ 'cart_id' => ['required', 'exists:carts,id'],
332
+ 'payment_method' => ['required', 'in:card,bank'],
333
+ ];
334
+ }
335
+ }
336
+
337
+ // ✅ Throw exceptions for invalid states
338
+ class OrderService
339
+ {
340
+ public function checkout(Cart $cart): Order
341
+ {
342
+ if ($cart->items->isEmpty()) {
343
+ throw new EmptyCartException();
344
+ }
345
+
346
+ if (!$this->hasInventory($cart)) {
347
+ throw new InsufficientInventoryException();
348
+ }
349
+
350
+ // ... proceed with valid cart
351
+ }
352
+ }
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Anti-Patterns to Avoid
358
+
359
+ ### 1. ❌ Repository for Everything
360
+
361
+ ```php
362
+ // DON'T
363
+ class UserRepository {
364
+ public function all() { return User::all(); }
365
+ public function find($id) { return User::find($id); }
366
+ // Pointless wrapper around Eloquent
367
+ }
368
+ ```
369
+
370
+ ### 2. ❌ Service for Simple CRUD
371
+
372
+ ```php
373
+ // DON'T
374
+ class UserService {
375
+ public function create(array $data) {
376
+ return User::create($data); // Just wrapping model method
377
+ }
378
+ }
379
+ ```
380
+
381
+ ### 3. ❌ DTOs for Request Data
382
+
383
+ ```php
384
+ // DON'T
385
+ class UserDTO {
386
+ public function __construct(
387
+ public string $name,
388
+ public string $email,
389
+ ) {}
390
+ }
391
+
392
+ // FormRequest already does this!
393
+ $request->validated(); // Returns validated array
394
+ ```
395
+
396
+ ### 4. ❌ Interfaces Without Implementations
397
+
398
+ ```php
399
+ // DON'T create interface for single implementation
400
+ interface UserServiceInterface { ... }
401
+ class UserService implements UserServiceInterface { ... }
402
+
403
+ // DO use interface only when you have multiple implementations
404
+ interface PaymentGatewayInterface { ... }
405
+ class StripePaymentGateway implements PaymentGatewayInterface { ... }
406
+ class PayPalPaymentGateway implements PaymentGatewayInterface { ... }
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Summary
412
+
413
+ | Principle | Guideline |
414
+ | ------------------- | --------------------------------- |
415
+ | **Simple CRUD** | Controller + Model + Resource |
416
+ | **Side effects** | Events, Jobs, Observers |
417
+ | **Complex logic** | Service (only when truly complex) |
418
+ | **Reusable action** | Action class |
419
+ | **Validation** | FormRequest (always) |
420
+ | **Response format** | Resource (always) |
421
+ | **Repository** | ❌ Don't use (Eloquent is enough) |
422
+ | **Interfaces** | Only for multiple implementations |
423
+
424
+ **Golden Rule**: Start simple. Add layers only when complexity demands it.