@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,364 @@
1
+ # Naming Conventions Guide
2
+
3
+ > **Related:** [README](./README.md) | [Testing Guide](./testing-guide.md)
4
+
5
+ ## Overview
6
+
7
+ Consistent naming is critical for maintainability. This guide defines naming patterns for all backend code.
8
+
9
+ ---
10
+
11
+ ## PHP Imports
12
+
13
+ **Always use `use` statements. Never use FQCN (Fully Qualified Class Name) inline.**
14
+
15
+ ```php
16
+ // ❌ WRONG: Inline FQCN
17
+ public function store(): \Illuminate\Http\JsonResponse
18
+ {
19
+ return new \App\Http\Resources\UserResource($user);
20
+ }
21
+
22
+ // ✅ CORRECT: Import at top, use short names
23
+ use Illuminate\Http\JsonResponse;
24
+ use App\Http\Resources\UserResource;
25
+
26
+ public function store(): JsonResponse
27
+ {
28
+ return new UserResource($user);
29
+ }
30
+ ```
31
+
32
+ | Rule | Description |
33
+ | ------------------ | -------------------------------- |
34
+ | Import all classes | Use `use` at top of file |
35
+ | Short class names | Never `\Full\Path\Class` inline |
36
+ | Group imports | Framework, then App, then Others |
37
+ | No unused imports | Remove unused `use` statements |
38
+
39
+ ---
40
+
41
+ ## File & Class Naming
42
+
43
+ ### Pattern: `{Model}{Type}.php`
44
+
45
+ | Type | Pattern | Example |
46
+ | ---------- | ------------------------ | ------------------------- |
47
+ | Controller | `{Model}Controller` | `UserController.php` |
48
+ | Request | `{Model}{Action}Request` | `UserStoreRequest.php` |
49
+ | Resource | `{Model}Resource` | `UserResource.php` |
50
+ | Model | `{Model}` (singular) | `User.php` |
51
+ | Service | `{Model}Service` | `OrderService.php` |
52
+ | Action | `{Verb}{Noun}Action` | `CreateInvoiceAction.php` |
53
+ | Job | `{Verb}{Noun}Job` | `SendWelcomeEmailJob.php` |
54
+ | Event | `{Model}{PastTense}` | `UserRegistered.php` |
55
+ | Observer | `{Model}Observer` | `UserObserver.php` |
56
+ | Policy | `{Model}Policy` | `UserPolicy.php` |
57
+ | Test | `{Model}ControllerTest` | `UserControllerTest.php` |
58
+
59
+ ### Request Naming
60
+
61
+ | Action | Pattern | Example |
62
+ | ------ | ---------------------- | ----------------------- |
63
+ | Create | `{Model}StoreRequest` | `UserStoreRequest.php` |
64
+ | Update | `{Model}UpdateRequest` | `UserUpdateRequest.php` |
65
+
66
+ > **Note:** Use `Store` (not `Create`) and `Update` (not `Edit`) to match Laravel CRUD conventions.
67
+
68
+ ---
69
+
70
+ ## Method Naming
71
+
72
+ ### Controller Methods (RESTful)
73
+
74
+ | HTTP Method | Controller Method | Route | Description |
75
+ | ----------- | ----------------- | ----------------- | --------------- |
76
+ | GET | `index()` | `/api/users` | List all |
77
+ | POST | `store()` | `/api/users` | Create new |
78
+ | GET | `show()` | `/api/users/{id}` | Get single |
79
+ | PUT/PATCH | `update()` | `/api/users/{id}` | Update existing |
80
+ | DELETE | `destroy()` | `/api/users/{id}` | Delete |
81
+
82
+ ### Service Methods
83
+
84
+ | Pattern | Example |
85
+ | --------------------- | ------------------------- |
86
+ | `{verb}{Noun}` | `processPayment()` |
87
+ | `{verb}{Noun}{State}` | `calculateTotalWithTax()` |
88
+
89
+ ```php
90
+ // ✅ Good
91
+ public function processPayment(Order $order): Payment
92
+ public function calculateDiscount(Cart $cart): float
93
+ public function sendNotification(User $user): void
94
+
95
+ // ❌ Bad
96
+ public function payment(Order $order) // Missing verb
97
+ public function doStuff() // Too vague
98
+ ```
99
+
100
+ ### Model Methods
101
+
102
+ | Type | Pattern | Example |
103
+ | -------- | --------------------- | ----------------------- |
104
+ | Scope | `scope{Name}` | `scopeActive($query)` |
105
+ | Accessor | `{attribute}` (PHP 8) | `fullName(): Attribute` |
106
+ | Mutator | `{attribute}` (PHP 8) | `password(): Attribute` |
107
+ | Relation | `{relation}` (noun) | `posts()`, `author()` |
108
+
109
+ ```php
110
+ // Scope - filter queries
111
+ public function scopeActive(Builder $query): Builder
112
+ {
113
+ return $query->where('status', 'active');
114
+ }
115
+
116
+ // Accessor (PHP 8+)
117
+ protected function fullName(): Attribute
118
+ {
119
+ return Attribute::get(fn () => "{$this->first_name} {$this->last_name}");
120
+ }
121
+
122
+ // Relationship
123
+ public function posts(): HasMany
124
+ {
125
+ return $this->hasMany(Post::class);
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Test Naming (PEST)
132
+
133
+ Use `describe()` to group tests by endpoint, `it()` for individual test cases.
134
+
135
+ ### Naming Rules
136
+
137
+ | Category | Prefix | Pattern | Example |
138
+ | --------------------- | ------- | ---------------------------------------- | ----------------------------------------------------- |
139
+ | **正常系 (Normal)** | `正常:` | `it('正常: {verb} {what}')` | `it('正常: creates user with valid data')` |
140
+ | **異常系 (Abnormal)** | `異常:` | `it('異常: fails to {action} {reason}')` | `it('異常: fails to create user with invalid email')` |
141
+ | **異常系 (404/401)** | `異常:` | `it('異常: returns {status} {reason}')` | `it('異常: returns 404 when user not found')` |
142
+
143
+ ### 正常系 (Normal Cases) - Success Behavior
144
+
145
+ **Pattern:** `it('正常: {verb} {what}')`
146
+
147
+ **Common verbs:** `returns`, `creates`, `updates`, `deletes`, `filters`, `sorts`, `paginates`
148
+
149
+ ```php
150
+ // GET (index)
151
+ it('正常: returns paginated users')
152
+ it('正常: returns users filtered by search')
153
+ it('正常: returns users sorted by created_at')
154
+
155
+ // POST (store)
156
+ it('正常: creates user with valid data')
157
+
158
+ // GET (show)
159
+ it('正常: returns user by id')
160
+
161
+ // PUT (update)
162
+ it('正常: updates user with valid data')
163
+ it('正常: updates user with partial data')
164
+
165
+ // DELETE (destroy)
166
+ it('正常: deletes user')
167
+ ```
168
+
169
+ ### 異常系 (Abnormal Cases) - Failure Behavior
170
+
171
+ **Pattern:** `it('異常: fails to {action} {reason}')` or `it('異常: returns {status} {reason}')`
172
+
173
+ ```php
174
+ // Validation errors (422) - use "fails to"
175
+ it('異常: fails to create user with missing email')
176
+ it('異常: fails to create user with invalid email format')
177
+ it('異常: fails to create user with duplicate email')
178
+ it('異常: fails to create user with short password')
179
+ it('異常: fails to create user with invalid kana format') // Japanese
180
+ it('異常: fails to update user with invalid data')
181
+
182
+ // Not found (404) - use "returns 404"
183
+ it('異常: returns 404 when user not found')
184
+ it('異常: returns 404 when updating nonexistent user')
185
+ it('異常: returns 404 when deleting nonexistent user')
186
+
187
+ // Authentication (401) - use "returns 401"
188
+ it('異常: returns 401 when not authenticated')
189
+
190
+ // Authorization (403) - use "returns 403"
191
+ it('異常: returns 403 when user cannot access resource')
192
+ it('異常: returns 403 when deleting without admin role')
193
+ ```
194
+
195
+ ### Complete Example
196
+
197
+ ```php
198
+ describe('POST /api/users', function () {
199
+ // ================================================================
200
+ // 正常系 (Normal Cases)
201
+ // ================================================================
202
+ it('正常: creates user with valid data', function () { ... });
203
+
204
+ // ================================================================
205
+ // 異常系 (Abnormal Cases)
206
+ // ================================================================
207
+
208
+ // Required fields
209
+ it('異常: fails to create user with missing email', function () { ... });
210
+ it('異常: fails to create user with missing password', function () { ... });
211
+
212
+ // Format validation
213
+ it('異常: fails to create user with invalid email format', function () { ... });
214
+ it('異常: fails to create user with short password', function () { ... });
215
+
216
+ // Unique constraint
217
+ it('異常: fails to create user with duplicate email', function () { ... });
218
+
219
+ // Japanese field validation
220
+ it('異常: fails to create user with invalid kana format', function () { ... });
221
+ });
222
+
223
+ describe('GET /api/users/{id}', function () {
224
+ // 正常系
225
+ it('正常: returns user by id', function () { ... });
226
+
227
+ // 異常系
228
+ it('異常: returns 404 when user not found', function () { ... });
229
+ });
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Database Naming
235
+
236
+ ### Tables
237
+
238
+ | Pattern | Example |
239
+ | ------------------------ | ----------- |
240
+ | Plural, snake_case | `users` |
241
+ | Pivot: singular_singular | `post_tag` |
242
+ | (alphabetical order) | `role_user` |
243
+
244
+ ### Columns
245
+
246
+ | Type | Pattern | Example |
247
+ | ------------- | ------------------ | -------------------- |
248
+ | Primary key | `id` | `id` |
249
+ | Foreign key | `{table}_id` | `user_id` |
250
+ | Boolean | `is_{state}` | `is_active` |
251
+ | Timestamp | `{action}_at` | `verified_at` |
252
+ | Japanese name | `name_{part}` | `name_lastname` |
253
+ | Japanese kana | `name_kana_{part}` | `name_kana_lastname` |
254
+
255
+ ### Japanese Name Fields (JapaneseName type)
256
+
257
+ | Field | Description | Max Length |
258
+ | --------------------- | ---------------- | ---------- |
259
+ | `name_lastname` | Family name (姓) | 50 |
260
+ | `name_firstname` | Given name (名) | 50 |
261
+ | `name_kana_lastname` | Family name kana | 100 |
262
+ | `name_kana_firstname` | Given name kana | 100 |
263
+
264
+ ---
265
+
266
+ ## Route Naming
267
+
268
+ ### API Routes
269
+
270
+ | Pattern | Example |
271
+ | ------------------ | -------------------------- |
272
+ | Plural resource | `/api/users` |
273
+ | Nested resource | `/api/users/{user}/posts` |
274
+ | Action on resource | `/api/orders/{order}/ship` |
275
+
276
+ ```php
277
+ // routes/api.php
278
+ Route::apiResource('users', UserController::class);
279
+ Route::apiResource('posts', PostController::class);
280
+
281
+ // Nested
282
+ Route::apiResource('users.posts', UserPostController::class);
283
+
284
+ // Custom actions
285
+ Route::post('/orders/{order}/ship', [OrderController::class, 'ship']);
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Variable Naming
291
+
292
+ ### PHP Variables
293
+
294
+ | Type | Pattern | Example |
295
+ | -------------- | ------------ | ------------------ |
296
+ | Model instance | `$camelCase` | `$user`, `$post` |
297
+ | Collection | `$plural` | `$users`, `$posts` |
298
+ | Boolean | `$is{State}` | `$isActive` |
299
+ | Query builder | `$query` | `$query` |
300
+
301
+ ```php
302
+ // ✅ Good
303
+ $user = User::find($id);
304
+ $users = User::all();
305
+ $isActive = $user->status === 'active';
306
+
307
+ // ❌ Bad
308
+ $u = User::find($id); // Too short
309
+ $userData = User::find($id); // Redundant "Data"
310
+ $active = true; // Missing "is" prefix for boolean
311
+ ```
312
+
313
+ ---
314
+
315
+ ## OpenAPI/Swagger Naming
316
+
317
+ ### Tag Names
318
+
319
+ | Pattern | Example |
320
+ | ------------------ | ------- |
321
+ | Plural, PascalCase | `Users` |
322
+
323
+ ### Parameter Names
324
+
325
+ | Type | Pattern | Example |
326
+ | ----- | ------------- | ------------- |
327
+ | Query | `Query{Name}` | `QuerySearch` |
328
+ | Path | `Path{Name}` | `PathId` |
329
+
330
+ ### Response Names
331
+
332
+ | Pattern | Example |
333
+ | --------------- | ----------------- |
334
+ | `{Description}` | `Success` |
335
+ | `{HttpStatus}` | `NotFound` |
336
+ | `{Error}Error` | `ValidationError` |
337
+
338
+ ---
339
+
340
+ ## Summary Table
341
+
342
+ | Type | Convention | Example |
343
+ | -------------------- | ---------------------------------------- | ----------------------------------------------------- |
344
+ | **Imports** | | |
345
+ | PHP imports | `use` at top, short name inline | `use JsonResponse;` → `: JsonResponse` |
346
+ | **Files** | | |
347
+ | Controller | `{Model}Controller` | `UserController.php` |
348
+ | Request | `{Model}{Action}Request` | `UserStoreRequest.php` |
349
+ | Resource | `{Model}Resource` | `UserResource.php` |
350
+ | Test | `{Model}ControllerTest` | `UserControllerTest.php` |
351
+ | **Methods** | | |
352
+ | Controller | RESTful verbs | `index`, `store`, `show` |
353
+ | Service | `{verb}{Noun}` | `processPayment()` |
354
+ | Scope | `scope{Name}` | `scopeActive()` |
355
+ | **Test Naming** | | |
356
+ | 正常系 (Normal) | `it('正常: {verb} {what}')` | `it('正常: creates user with valid data')` |
357
+ | 異常系 (Abnormal) | `it('異常: fails to {action} {reason}')` | `it('異常: fails to create user with invalid email')` |
358
+ | 異常系 (404/401/403) | `it('異常: returns {status} {reason}')` | `it('異常: returns 404 when user not found')` |
359
+ | **Database** | | |
360
+ | Table | plural, snake_case | `users`, `order_items` |
361
+ | Column | snake_case | `user_id`, `created_at` |
362
+ | Boolean column | `is_{state}` | `is_verified` |
363
+ | **Routes** | | |
364
+ | API | plural, kebab-case | `/api/users`, `/api/order-items` |
@@ -0,0 +1,251 @@
1
+ # Performance & Quality Rules
2
+
3
+ > **Non-negotiable rules** for Laravel performance and code quality.
4
+
5
+ ## 🟠 N+1 Query Problem
6
+
7
+ **Always eager load relationships.**
8
+
9
+ ```php
10
+ // ❌ N+1 PROBLEM: 1 + N queries (N = number of posts)
11
+ $posts = Post::all();
12
+ foreach ($posts as $post) {
13
+ echo $post->author->name; // Query for each post!
14
+ }
15
+
16
+ // ✅ CORRECT: Eager loading with with()
17
+ $posts = Post::with('author')->get();
18
+ foreach ($posts as $post) {
19
+ echo $post->author->name; // No extra queries
20
+ }
21
+
22
+ // ✅ CORRECT: Multiple relationships
23
+ $posts = Post::with(['author', 'comments', 'tags'])->get();
24
+
25
+ // ✅ CORRECT: Nested eager loading
26
+ $posts = Post::with('comments.author')->get();
27
+ ```
28
+
29
+ **Enable N+1 detection in development:**
30
+
31
+ ```php
32
+ // In AppServiceProvider boot()
33
+ use Illuminate\Database\Eloquent\Model;
34
+
35
+ public function boot(): void
36
+ {
37
+ // Throw exception on lazy loading (dev only)
38
+ Model::preventLazyLoading(!app()->isProduction());
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 🟠 Use `whenLoaded()` in Resources
45
+
46
+ **Prevent queries in Resources.**
47
+
48
+ ```php
49
+ // ❌ ERROR: Triggers query if not eager loaded
50
+ class PostResource extends JsonResource
51
+ {
52
+ public function toArray($request): array
53
+ {
54
+ return [
55
+ 'author' => new UserResource($this->author), // N+1!
56
+ ];
57
+ }
58
+ }
59
+
60
+ // ✅ CORRECT: Only include if already loaded
61
+ class PostResource extends JsonResource
62
+ {
63
+ public function toArray($request): array
64
+ {
65
+ return [
66
+ 'author' => new UserResource($this->whenLoaded('author')),
67
+ ];
68
+ }
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 🟠 Pagination for List Endpoints
75
+
76
+ **Never return all records.**
77
+
78
+ ```php
79
+ // ❌ ERROR: Returns all records (memory + performance)
80
+ public function index()
81
+ {
82
+ return UserResource::collection(User::all()); // 1M users = crash
83
+ }
84
+
85
+ // ✅ CORRECT: Always paginate
86
+ public function index(Request $request)
87
+ {
88
+ $perPage = min($request->input('per_page', 15), 100); // Max 100
89
+ return UserResource::collection(User::paginate($perPage));
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 🟠 Select Only Needed Columns
96
+
97
+ **Don't SELECT * when you need few fields.**
98
+
99
+ ```php
100
+ // ❌ INEFFICIENT: Fetches all columns
101
+ $users = User::all();
102
+ $names = $users->pluck('name');
103
+
104
+ // ✅ EFFICIENT: Select only needed columns
105
+ $names = User::pluck('name');
106
+
107
+ // ✅ EFFICIENT: For multiple columns
108
+ $users = User::select(['id', 'name', 'email'])->get();
109
+ ```
110
+
111
+ ---
112
+
113
+ ## 🟡 Code Quality Rules
114
+
115
+ ### Never Validate in Controller
116
+
117
+ ```php
118
+ // ❌ BAD: Validation in controller
119
+ public function store(Request $request)
120
+ {
121
+ $validated = $request->validate([...]);
122
+ }
123
+
124
+ // ✅ CORRECT: Use FormRequest
125
+ public function store(UserStoreRequest $request)
126
+ {
127
+ $user = User::create($request->validated());
128
+ return new UserResource($user);
129
+ }
130
+ ```
131
+
132
+ ### Always Use Resource for Responses
133
+
134
+ ```php
135
+ // ❌ BAD: Exposes all fields, inconsistent format
136
+ return $user;
137
+ return response()->json($user);
138
+
139
+ // ✅ CORRECT: Use Resource
140
+ return new UserResource($user);
141
+ return UserResource::collection($users);
142
+ ```
143
+
144
+ ### Use Route Model Binding
145
+
146
+ ```php
147
+ // ❌ BAD: Manual find
148
+ public function show(int $id)
149
+ {
150
+ $user = User::findOrFail($id);
151
+ return new UserResource($user);
152
+ }
153
+
154
+ // ✅ CORRECT: Route model binding
155
+ public function show(User $user)
156
+ {
157
+ return new UserResource($user);
158
+ }
159
+ ```
160
+
161
+ ### Use Transactions for Multiple Operations
162
+
163
+ ```php
164
+ // ❌ BAD: Partial failure possible
165
+ $order = Order::create([...]);
166
+ $order->items()->createMany([...]);
167
+
168
+ // ✅ CORRECT: Transaction ensures atomicity
169
+ DB::transaction(function () use ($data) {
170
+ $order = Order::create([...]);
171
+ $order->items()->createMany([...]);
172
+ return $order;
173
+ });
174
+ ```
175
+
176
+ ### Never Use env() Outside Config
177
+
178
+ ```php
179
+ // ❌ BAD: env() won't work with config:cache
180
+ $apiKey = env('API_KEY');
181
+
182
+ // ✅ CORRECT: Use config()
183
+ $apiKey = config('services.api_key');
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 🔵 Date/Time Rules
189
+
190
+ ### Always Store UTC
191
+
192
+ ```php
193
+ // config/app.php
194
+ 'timezone' => 'UTC', // NEVER change this
195
+
196
+ // ❌ BAD: Store local time
197
+ $event->scheduled_at = Carbon::now('Asia/Tokyo');
198
+
199
+ // ✅ CORRECT: Store UTC
200
+ $event->scheduled_at = Carbon::now(); // UTC
201
+ ```
202
+
203
+ ### Always Return ISO 8601 in API
204
+
205
+ ```php
206
+ // ❌ BAD: Local format
207
+ 'created_at' => $this->created_at->format('Y-m-d H:i:s'),
208
+
209
+ // ✅ CORRECT: ISO 8601 UTC
210
+ 'created_at' => $this->created_at?->toISOString(),
211
+ // Output: "2024-01-15T10:30:00.000000Z"
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Quick Reference
217
+
218
+ | Category | ❌ Never Do | ✅ Always Do |
219
+ | --------------- | ----------------------------------- | ------------------------ |
220
+ | **Performance** | `Model::all()` without limit | `Model::paginate()` |
221
+ | | Access relation without `with()` | Eager load with `with()` |
222
+ | | `$this->relation` in Resource | `$this->whenLoaded()` |
223
+ | **Quality** | Validate in Controller | Use FormRequest |
224
+ | | Return Model directly | Return Resource |
225
+ | | `findOrFail($id)` | Route model binding |
226
+ | | Multiple DB ops without transaction | `DB::transaction()` |
227
+ | **Config** | `env()` in code | `config()` |
228
+ | **Dates** | Local timezone | UTC everywhere |
229
+ | | `format('Y-m-d')` | `->toISOString()` |
230
+
231
+ ---
232
+
233
+ ## Development Safeguards
234
+
235
+ Add to `AppServiceProvider::boot()`:
236
+
237
+ ```php
238
+ public function boot(): void
239
+ {
240
+ // Prevent N+1 queries (throws exception on lazy load)
241
+ Model::preventLazyLoading(!app()->isProduction());
242
+
243
+ // Prevent accessing missing attributes
244
+ Model::preventAccessingMissingAttributes(!app()->isProduction());
245
+
246
+ // Prevent silently discarding attributes
247
+ Model::preventSilentlyDiscardingAttributes(!app()->isProduction());
248
+ }
249
+ ```
250
+
251
+ These will throw exceptions during development, helping you catch issues early.