@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.
- package/dist/{chunk-YVVAJA3T.js → chunk-V7LWJ6OM.js} +178 -12
- package/dist/chunk-V7LWJ6OM.js.map +1 -0
- package/dist/index.cjs +180 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +5 -1
- package/dist/plugin.cjs +176 -11
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +5 -5
- package/scripts/postinstall.js +29 -36
- package/stubs/ai-guides/claude-agents/architect.md.stub +150 -0
- package/stubs/ai-guides/claude-agents/developer.md.stub +190 -0
- package/stubs/ai-guides/claude-agents/reviewer.md.stub +134 -0
- package/stubs/ai-guides/claude-agents/tester.md.stub +196 -0
- package/stubs/ai-guides/claude-checklists/backend.md.stub +112 -0
- package/stubs/ai-guides/claude-omnify/antdesign-guide.md.stub +401 -0
- package/stubs/ai-guides/claude-omnify/config-guide.md.stub +253 -0
- package/stubs/ai-guides/claude-omnify/japan-guide.md.stub +186 -0
- package/stubs/ai-guides/claude-omnify/laravel-guide.md.stub +61 -0
- package/stubs/ai-guides/claude-omnify/schema-guide.md.stub +115 -0
- package/stubs/ai-guides/claude-omnify/typescript-guide.md.stub +310 -0
- package/stubs/ai-guides/claude-rules/naming.md.stub +364 -0
- package/stubs/ai-guides/claude-rules/performance.md.stub +251 -0
- package/stubs/ai-guides/claude-rules/security.md.stub +159 -0
- package/stubs/ai-guides/claude-workflows/bug-fix.md.stub +201 -0
- package/stubs/ai-guides/claude-workflows/code-review.md.stub +164 -0
- package/stubs/ai-guides/claude-workflows/new-feature.md.stub +327 -0
- package/stubs/ai-guides/cursor/laravel-controller.mdc.stub +391 -0
- package/stubs/ai-guides/cursor/laravel-request.mdc.stub +112 -0
- package/stubs/ai-guides/cursor/laravel-resource.mdc.stub +73 -0
- package/stubs/ai-guides/cursor/laravel-review.mdc.stub +69 -0
- package/stubs/ai-guides/cursor/laravel-testing.mdc.stub +138 -0
- package/stubs/ai-guides/cursor/laravel.mdc.stub +82 -0
- package/stubs/ai-guides/laravel/README.md.stub +59 -0
- package/stubs/ai-guides/laravel/architecture.md.stub +424 -0
- package/stubs/ai-guides/laravel/controller.md.stub +484 -0
- package/stubs/ai-guides/laravel/datetime.md.stub +334 -0
- package/stubs/ai-guides/laravel/openapi.md.stub +369 -0
- package/stubs/ai-guides/laravel/request.md.stub +450 -0
- package/stubs/ai-guides/laravel/resource.md.stub +516 -0
- package/stubs/ai-guides/laravel/service.md.stub +503 -0
- package/stubs/ai-guides/laravel/testing.md.stub +1504 -0
- package/ai-guides/laravel-guide.md +0 -461
- package/dist/chunk-YVVAJA3T.js.map +0 -1
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# API Resource Guide
|
|
2
|
+
|
|
3
|
+
> **Related:** [README](./README.md) | [Controller Guide](./controller-guide.md) | [DateTime Guide](../laravel/datetime-guide.md)
|
|
4
|
+
|
|
5
|
+
## Why Resources?
|
|
6
|
+
|
|
7
|
+
| Without Resources | With Resources |
|
|
8
|
+
| ----------------------- | ------------------------ |
|
|
9
|
+
| Expose all model fields | Control exposed fields |
|
|
10
|
+
| Inconsistent formats | Consistent API format |
|
|
11
|
+
| Manual JSON building | Automatic transformation |
|
|
12
|
+
| Date format varies | ISO 8601 everywhere |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Resource Template
|
|
17
|
+
|
|
18
|
+
```php
|
|
19
|
+
<?php
|
|
20
|
+
|
|
21
|
+
namespace App\Http\Resources;
|
|
22
|
+
|
|
23
|
+
use Illuminate\Http\Request;
|
|
24
|
+
use Illuminate\Http\Resources\Json\JsonResource;
|
|
25
|
+
|
|
26
|
+
class UserResource extends JsonResource
|
|
27
|
+
{
|
|
28
|
+
/**
|
|
29
|
+
* Transform the resource into an array.
|
|
30
|
+
*
|
|
31
|
+
* @return array<string, mixed>
|
|
32
|
+
*/
|
|
33
|
+
public function toArray(Request $request): array
|
|
34
|
+
{
|
|
35
|
+
return [
|
|
36
|
+
'id' => $this->id,
|
|
37
|
+
'name' => $this->name,
|
|
38
|
+
'email' => $this->email,
|
|
39
|
+
'email_verified_at' => $this->email_verified_at?->toISOString(),
|
|
40
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
41
|
+
'updated_at' => $this->updated_at?->toISOString(),
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Response Formats
|
|
50
|
+
|
|
51
|
+
### Single Resource
|
|
52
|
+
|
|
53
|
+
```php
|
|
54
|
+
// Controller
|
|
55
|
+
return new UserResource($user);
|
|
56
|
+
|
|
57
|
+
// Response
|
|
58
|
+
{
|
|
59
|
+
"data": {
|
|
60
|
+
"id": 1,
|
|
61
|
+
"name": "John Doe",
|
|
62
|
+
"email": "john@example.com",
|
|
63
|
+
"created_at": "2024-01-15T10:30:00.000000Z"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Collection (Paginated)
|
|
69
|
+
|
|
70
|
+
```php
|
|
71
|
+
// Controller
|
|
72
|
+
return UserResource::collection($users->paginate(15));
|
|
73
|
+
|
|
74
|
+
// Response
|
|
75
|
+
{
|
|
76
|
+
"data": [
|
|
77
|
+
{ "id": 1, "name": "John", ... },
|
|
78
|
+
{ "id": 2, "name": "Jane", ... }
|
|
79
|
+
],
|
|
80
|
+
"links": {
|
|
81
|
+
"first": "http://api.example.com/users?page=1",
|
|
82
|
+
"last": "http://api.example.com/users?page=10",
|
|
83
|
+
"prev": null,
|
|
84
|
+
"next": "http://api.example.com/users?page=2"
|
|
85
|
+
},
|
|
86
|
+
"meta": {
|
|
87
|
+
"current_page": 1,
|
|
88
|
+
"from": 1,
|
|
89
|
+
"last_page": 10,
|
|
90
|
+
"per_page": 15,
|
|
91
|
+
"to": 15,
|
|
92
|
+
"total": 150
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Date Formatting Rules
|
|
100
|
+
|
|
101
|
+
### Always Use ISO 8601 UTC
|
|
102
|
+
|
|
103
|
+
```php
|
|
104
|
+
// ✅ CORRECT: toISOString() returns UTC with Z suffix
|
|
105
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
106
|
+
// Output: "2024-01-15T10:30:00.000000Z"
|
|
107
|
+
|
|
108
|
+
// ❌ WRONG: Local format
|
|
109
|
+
'created_at' => $this->created_at?->format('Y-m-d H:i:s'),
|
|
110
|
+
// Output: "2024-01-15 10:30:00" (ambiguous timezone)
|
|
111
|
+
|
|
112
|
+
// ❌ WRONG: Local timezone
|
|
113
|
+
'created_at' => $this->created_at?->setTimezone('Asia/Tokyo')->format('Y-m-d H:i:s'),
|
|
114
|
+
// Output: "2024-01-15 19:30:00" (local time, confusing)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Nullable Dates
|
|
118
|
+
|
|
119
|
+
```php
|
|
120
|
+
// ✅ Use null-safe operator
|
|
121
|
+
'email_verified_at' => $this->email_verified_at?->toISOString(),
|
|
122
|
+
// Returns null if not set
|
|
123
|
+
|
|
124
|
+
// ❌ Don't return empty string
|
|
125
|
+
'email_verified_at' => $this->email_verified_at ? $this->email_verified_at->toISOString() : '',
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Relationships
|
|
131
|
+
|
|
132
|
+
### Eager Loading Required
|
|
133
|
+
|
|
134
|
+
```php
|
|
135
|
+
// Controller - always eager load
|
|
136
|
+
public function show(User $user): UserResource
|
|
137
|
+
{
|
|
138
|
+
$user->load('posts', 'profile');
|
|
139
|
+
return new UserResource($user);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Resource
|
|
143
|
+
public function toArray(Request $request): array
|
|
144
|
+
{
|
|
145
|
+
return [
|
|
146
|
+
'id' => $this->id,
|
|
147
|
+
'name' => $this->name,
|
|
148
|
+
'profile' => new ProfileResource($this->whenLoaded('profile')),
|
|
149
|
+
'posts' => PostResource::collection($this->whenLoaded('posts')),
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Conditional Relationships
|
|
155
|
+
|
|
156
|
+
```php
|
|
157
|
+
public function toArray(Request $request): array
|
|
158
|
+
{
|
|
159
|
+
return [
|
|
160
|
+
'id' => $this->id,
|
|
161
|
+
'name' => $this->name,
|
|
162
|
+
|
|
163
|
+
// Only include if loaded (prevents N+1)
|
|
164
|
+
'profile' => new ProfileResource($this->whenLoaded('profile')),
|
|
165
|
+
|
|
166
|
+
// Only include if loaded
|
|
167
|
+
'posts' => PostResource::collection($this->whenLoaded('posts')),
|
|
168
|
+
|
|
169
|
+
// Only include count if loaded
|
|
170
|
+
'posts_count' => $this->whenCounted('posts'),
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Nested Resources
|
|
176
|
+
|
|
177
|
+
```php
|
|
178
|
+
// PostResource
|
|
179
|
+
public function toArray(Request $request): array
|
|
180
|
+
{
|
|
181
|
+
return [
|
|
182
|
+
'id' => $this->id,
|
|
183
|
+
'title' => $this->title,
|
|
184
|
+
'author' => new UserResource($this->whenLoaded('author')),
|
|
185
|
+
'comments' => CommentResource::collection($this->whenLoaded('comments')),
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Controller with nested eager loading
|
|
190
|
+
public function show(Post $post): PostResource
|
|
191
|
+
{
|
|
192
|
+
$post->load(['author', 'comments.user']);
|
|
193
|
+
return new PostResource($post);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Conditional Fields
|
|
200
|
+
|
|
201
|
+
### Based on Auth
|
|
202
|
+
|
|
203
|
+
```php
|
|
204
|
+
public function toArray(Request $request): array
|
|
205
|
+
{
|
|
206
|
+
return [
|
|
207
|
+
'id' => $this->id,
|
|
208
|
+
'name' => $this->name,
|
|
209
|
+
'email' => $this->email,
|
|
210
|
+
|
|
211
|
+
// Only for admin
|
|
212
|
+
'admin_notes' => $this->when(
|
|
213
|
+
$request->user()?->isAdmin(),
|
|
214
|
+
$this->admin_notes
|
|
215
|
+
),
|
|
216
|
+
|
|
217
|
+
// Only for owner
|
|
218
|
+
'api_token' => $this->when(
|
|
219
|
+
$request->user()?->id === $this->id,
|
|
220
|
+
$this->api_token
|
|
221
|
+
),
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Based on Model State
|
|
227
|
+
|
|
228
|
+
```php
|
|
229
|
+
public function toArray(Request $request): array
|
|
230
|
+
{
|
|
231
|
+
return [
|
|
232
|
+
'id' => $this->id,
|
|
233
|
+
'title' => $this->title,
|
|
234
|
+
'status' => $this->status,
|
|
235
|
+
|
|
236
|
+
// Only if published
|
|
237
|
+
'published_at' => $this->when(
|
|
238
|
+
$this->status === 'published',
|
|
239
|
+
$this->published_at?->toISOString()
|
|
240
|
+
),
|
|
241
|
+
|
|
242
|
+
// Only if has discount
|
|
243
|
+
'discount' => $this->when($this->discount > 0, [
|
|
244
|
+
'amount' => $this->discount,
|
|
245
|
+
'percentage' => $this->discount_percentage,
|
|
246
|
+
]),
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Computed Fields
|
|
254
|
+
|
|
255
|
+
```php
|
|
256
|
+
public function toArray(Request $request): array
|
|
257
|
+
{
|
|
258
|
+
return [
|
|
259
|
+
'id' => $this->id,
|
|
260
|
+
'first_name' => $this->first_name,
|
|
261
|
+
'last_name' => $this->last_name,
|
|
262
|
+
|
|
263
|
+
// Computed
|
|
264
|
+
'full_name' => $this->first_name . ' ' . $this->last_name,
|
|
265
|
+
|
|
266
|
+
// From accessor
|
|
267
|
+
'avatar_url' => $this->avatar_url, // Model accessor
|
|
268
|
+
|
|
269
|
+
// Boolean flags
|
|
270
|
+
'is_verified' => $this->email_verified_at !== null,
|
|
271
|
+
'is_active' => $this->status === 'active',
|
|
272
|
+
|
|
273
|
+
// Formatted values
|
|
274
|
+
'price_formatted' => '$' . number_format($this->price, 2),
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Hiding Fields
|
|
282
|
+
|
|
283
|
+
### Using Resource
|
|
284
|
+
|
|
285
|
+
```php
|
|
286
|
+
public function toArray(Request $request): array
|
|
287
|
+
{
|
|
288
|
+
return [
|
|
289
|
+
'id' => $this->id,
|
|
290
|
+
'name' => $this->name,
|
|
291
|
+
'email' => $this->email,
|
|
292
|
+
// Don't include: password, remember_token
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Using Model $hidden
|
|
298
|
+
|
|
299
|
+
```php
|
|
300
|
+
// Model
|
|
301
|
+
class User extends Model
|
|
302
|
+
{
|
|
303
|
+
protected $hidden = [
|
|
304
|
+
'password',
|
|
305
|
+
'remember_token',
|
|
306
|
+
];
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Additional Meta
|
|
313
|
+
|
|
314
|
+
### For Single Resource
|
|
315
|
+
|
|
316
|
+
```php
|
|
317
|
+
class OrderResource extends JsonResource
|
|
318
|
+
{
|
|
319
|
+
public function toArray(Request $request): array
|
|
320
|
+
{
|
|
321
|
+
return [
|
|
322
|
+
'id' => $this->id,
|
|
323
|
+
'total' => $this->total,
|
|
324
|
+
];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public function with(Request $request): array
|
|
328
|
+
{
|
|
329
|
+
return [
|
|
330
|
+
'meta' => [
|
|
331
|
+
'currency' => 'USD',
|
|
332
|
+
'tax_rate' => 0.1,
|
|
333
|
+
],
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Response
|
|
339
|
+
{
|
|
340
|
+
"data": { "id": 1, "total": 100 },
|
|
341
|
+
"meta": { "currency": "USD", "tax_rate": 0.1 }
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Custom Wrapper
|
|
346
|
+
|
|
347
|
+
```php
|
|
348
|
+
// Disable "data" wrapper
|
|
349
|
+
class PlainResource extends JsonResource
|
|
350
|
+
{
|
|
351
|
+
public static $wrap = null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Response (no "data" key)
|
|
355
|
+
{ "id": 1, "name": "John" }
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Resource Collection
|
|
361
|
+
|
|
362
|
+
### Custom Collection Class
|
|
363
|
+
|
|
364
|
+
```php
|
|
365
|
+
<?php
|
|
366
|
+
|
|
367
|
+
namespace App\Http\Resources;
|
|
368
|
+
|
|
369
|
+
use Illuminate\Http\Resources\Json\ResourceCollection;
|
|
370
|
+
|
|
371
|
+
class UserCollection extends ResourceCollection
|
|
372
|
+
{
|
|
373
|
+
public $collects = UserResource::class;
|
|
374
|
+
|
|
375
|
+
public function toArray(Request $request): array
|
|
376
|
+
{
|
|
377
|
+
return [
|
|
378
|
+
'data' => $this->collection,
|
|
379
|
+
'meta' => [
|
|
380
|
+
'total_active' => $this->collection->where('status', 'active')->count(),
|
|
381
|
+
],
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Usage
|
|
387
|
+
return new UserCollection($users->paginate(15));
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Common Patterns
|
|
393
|
+
|
|
394
|
+
### User Resource
|
|
395
|
+
|
|
396
|
+
```php
|
|
397
|
+
class UserResource extends JsonResource
|
|
398
|
+
{
|
|
399
|
+
public function toArray(Request $request): array
|
|
400
|
+
{
|
|
401
|
+
return [
|
|
402
|
+
'id' => $this->id,
|
|
403
|
+
'name' => $this->name,
|
|
404
|
+
'email' => $this->email,
|
|
405
|
+
'avatar_url' => $this->avatar_url,
|
|
406
|
+
'email_verified_at' => $this->email_verified_at?->toISOString(),
|
|
407
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
408
|
+
'updated_at' => $this->updated_at?->toISOString(),
|
|
409
|
+
|
|
410
|
+
// Relations
|
|
411
|
+
'profile' => new ProfileResource($this->whenLoaded('profile')),
|
|
412
|
+
'roles' => RoleResource::collection($this->whenLoaded('roles')),
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Post Resource (with Author)
|
|
419
|
+
|
|
420
|
+
```php
|
|
421
|
+
class PostResource extends JsonResource
|
|
422
|
+
{
|
|
423
|
+
public function toArray(Request $request): array
|
|
424
|
+
{
|
|
425
|
+
return [
|
|
426
|
+
'id' => $this->id,
|
|
427
|
+
'title' => $this->title,
|
|
428
|
+
'slug' => $this->slug,
|
|
429
|
+
'content' => $this->content,
|
|
430
|
+
'excerpt' => Str::limit($this->content, 150),
|
|
431
|
+
'status' => $this->status,
|
|
432
|
+
'published_at' => $this->published_at?->toISOString(),
|
|
433
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
434
|
+
'updated_at' => $this->updated_at?->toISOString(),
|
|
435
|
+
|
|
436
|
+
// Author (always loaded)
|
|
437
|
+
'author' => new UserResource($this->whenLoaded('author')),
|
|
438
|
+
|
|
439
|
+
// Comments (conditional)
|
|
440
|
+
'comments' => CommentResource::collection($this->whenLoaded('comments')),
|
|
441
|
+
'comments_count' => $this->whenCounted('comments'),
|
|
442
|
+
|
|
443
|
+
// Computed
|
|
444
|
+
'is_published' => $this->status === 'published',
|
|
445
|
+
'reading_time' => ceil(str_word_count($this->content) / 200) . ' min',
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Order Resource (with Items)
|
|
452
|
+
|
|
453
|
+
```php
|
|
454
|
+
class OrderResource extends JsonResource
|
|
455
|
+
{
|
|
456
|
+
public function toArray(Request $request): array
|
|
457
|
+
{
|
|
458
|
+
return [
|
|
459
|
+
'id' => $this->id,
|
|
460
|
+
'order_number' => $this->order_number,
|
|
461
|
+
'status' => $this->status,
|
|
462
|
+
'subtotal' => $this->subtotal,
|
|
463
|
+
'tax' => $this->tax,
|
|
464
|
+
'total' => $this->total,
|
|
465
|
+
'currency' => 'JPY',
|
|
466
|
+
'paid_at' => $this->paid_at?->toISOString(),
|
|
467
|
+
'shipped_at' => $this->shipped_at?->toISOString(),
|
|
468
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
469
|
+
|
|
470
|
+
// Relations
|
|
471
|
+
'customer' => new UserResource($this->whenLoaded('customer')),
|
|
472
|
+
'items' => OrderItemResource::collection($this->whenLoaded('items')),
|
|
473
|
+
|
|
474
|
+
// Computed
|
|
475
|
+
'items_count' => $this->whenCounted('items'),
|
|
476
|
+
'can_cancel' => $this->status === 'pending',
|
|
477
|
+
'can_refund' => in_array($this->status, ['paid', 'shipped']),
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Best Practices
|
|
486
|
+
|
|
487
|
+
### ✅ DO
|
|
488
|
+
|
|
489
|
+
```php
|
|
490
|
+
// Always use toISOString() for dates
|
|
491
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
492
|
+
|
|
493
|
+
// Use whenLoaded() for relationships
|
|
494
|
+
'posts' => PostResource::collection($this->whenLoaded('posts')),
|
|
495
|
+
|
|
496
|
+
// Use when() for conditional fields
|
|
497
|
+
'admin_notes' => $this->when($request->user()?->isAdmin(), $this->admin_notes),
|
|
498
|
+
|
|
499
|
+
// Use null-safe operator
|
|
500
|
+
'profile' => new ProfileResource($this->whenLoaded('profile')),
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### ❌ DON'T
|
|
504
|
+
|
|
505
|
+
```php
|
|
506
|
+
// Don't expose sensitive fields
|
|
507
|
+
'password' => $this->password,
|
|
508
|
+
'remember_token' => $this->remember_token,
|
|
509
|
+
|
|
510
|
+
// Don't use model directly (causes N+1)
|
|
511
|
+
'posts' => PostResource::collection($this->posts), // Load in controller!
|
|
512
|
+
|
|
513
|
+
// Don't format dates inconsistently
|
|
514
|
+
'created_at' => $this->created_at->format('Y-m-d'),
|
|
515
|
+
'updated_at' => $this->updated_at->toDateTimeString(),
|
|
516
|
+
```
|