@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,334 @@
1
+ # DateTime Handling Guide
2
+
3
+ > **Related:** [README](./README.md) | [Resource Guide](./resource-guide.md)
4
+
5
+ ## Golden Rule: "Store UTC, Respond UTC, Accept UTC"
6
+
7
+ ```
8
+ Database (UTC) ← Carbon (UTC) → API Response (ISO 8601 UTC)
9
+
10
+ API Request (UTC)
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Configuration
16
+
17
+ ### 1. Set Application Timezone to UTC
18
+
19
+ **`config/app.php`:**
20
+
21
+ ```php
22
+ 'timezone' => 'UTC',
23
+ ```
24
+
25
+ > ⚠️ NEVER change this to local timezone. Always use UTC.
26
+
27
+ ### 2. Database Timezone
28
+
29
+ **MySQL** - Ensure UTC:
30
+
31
+ ```sql
32
+ SET GLOBAL time_zone = '+00:00';
33
+ SET time_zone = '+00:00';
34
+ ```
35
+
36
+ Or in `my.cnf`:
37
+ ```ini
38
+ [mysqld]
39
+ default-time-zone = '+00:00'
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Carbon Usage Rules
45
+
46
+ ### Always Use Carbon (Never raw DateTime)
47
+
48
+ ```php
49
+ use Illuminate\Support\Carbon;
50
+
51
+ // ✅ Correct
52
+ $now = Carbon::now(); // Current UTC time
53
+ $date = Carbon::parse($input); // Parse with UTC
54
+
55
+ // ❌ Wrong
56
+ $now = new \DateTime(); // Don't use raw DateTime
57
+ $now = date('Y-m-d H:i:s'); // Don't use date()
58
+ ```
59
+
60
+ ### API Response Format
61
+
62
+ **Always return ISO 8601 with Z suffix:**
63
+
64
+ ```php
65
+ // In Model - cast dates properly
66
+ protected $casts = [
67
+ 'email_verified_at' => 'datetime',
68
+ 'scheduled_at' => 'datetime',
69
+ 'created_at' => 'datetime',
70
+ 'updated_at' => 'datetime',
71
+ ];
72
+
73
+ // In Resource - format as ISO 8601 UTC
74
+ public function toArray($request): array
75
+ {
76
+ return [
77
+ 'id' => $this->id,
78
+ 'title' => $this->title,
79
+ 'scheduled_at' => $this->scheduled_at?->toISOString(), // "2024-01-15T10:30:00.000000Z"
80
+ 'created_at' => $this->created_at?->toISOString(),
81
+ 'updated_at' => $this->updated_at?->toISOString(),
82
+ ];
83
+ }
84
+ ```
85
+
86
+ ### Accepting Date Input
87
+
88
+ ```php
89
+ // In FormRequest
90
+ public function rules(): array
91
+ {
92
+ return [
93
+ 'scheduled_at' => ['required', 'date'], // Accepts ISO 8601
94
+ ];
95
+ }
96
+
97
+ // In Controller - Carbon auto-parses UTC
98
+ public function store(StoreEventRequest $request): JsonResponse
99
+ {
100
+ $event = Event::create([
101
+ 'title' => $request->title,
102
+ 'scheduled_at' => Carbon::parse($request->scheduled_at), // Already UTC
103
+ ]);
104
+
105
+ return new EventResource($event);
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Common Patterns
112
+
113
+ ### Compare Dates
114
+
115
+ ```php
116
+ use Illuminate\Support\Carbon;
117
+
118
+ // Current UTC time
119
+ $now = Carbon::now();
120
+
121
+ // Check if past
122
+ if ($event->scheduled_at->isPast()) {
123
+ // Event has passed
124
+ }
125
+
126
+ // Check if within range
127
+ $start = Carbon::parse($request->start_date);
128
+ $end = Carbon::parse($request->end_date);
129
+
130
+ Event::whereBetween('scheduled_at', [$start, $end])->get();
131
+ ```
132
+
133
+ ### Query by Date Range
134
+
135
+ ```php
136
+ // Frontend sends UTC strings
137
+ // "2024-01-01T00:00:00Z" to "2024-01-31T23:59:59Z"
138
+
139
+ public function index(Request $request)
140
+ {
141
+ $query = Event::query();
142
+
143
+ if ($request->filled('start_date')) {
144
+ $query->where('scheduled_at', '>=', Carbon::parse($request->start_date));
145
+ }
146
+
147
+ if ($request->filled('end_date')) {
148
+ $query->where('scheduled_at', '<=', Carbon::parse($request->end_date));
149
+ }
150
+
151
+ return EventResource::collection($query->paginate());
152
+ }
153
+ ```
154
+
155
+ ### Display for Specific Timezone (if needed)
156
+
157
+ ```php
158
+ // Only when generating reports for specific timezone
159
+ $userTimezone = 'Asia/Tokyo';
160
+
161
+ $localTime = $event->scheduled_at->setTimezone($userTimezone);
162
+ // Keep original as UTC, create copy for display
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Model Setup
168
+
169
+ ### Recommended Model Structure
170
+
171
+ ```php
172
+ <?php
173
+
174
+ namespace App\Models;
175
+
176
+ use Illuminate\Database\Eloquent\Model;
177
+ use Illuminate\Support\Carbon;
178
+
179
+ class Event extends Model
180
+ {
181
+ protected $fillable = [
182
+ 'title',
183
+ 'scheduled_at',
184
+ ];
185
+
186
+ protected $casts = [
187
+ 'scheduled_at' => 'datetime',
188
+ ];
189
+
190
+ // Accessor for formatted date (if needed internally)
191
+ public function getScheduledAtFormattedAttribute(): string
192
+ {
193
+ return $this->scheduled_at?->toISOString() ?? '';
194
+ }
195
+
196
+ // Scope for upcoming events
197
+ public function scopeUpcoming($query)
198
+ {
199
+ return $query->where('scheduled_at', '>', Carbon::now());
200
+ }
201
+
202
+ // Scope for past events
203
+ public function scopePast($query)
204
+ {
205
+ return $query->where('scheduled_at', '<', Carbon::now());
206
+ }
207
+ }
208
+ ```
209
+
210
+ ### Resource Format
211
+
212
+ ```php
213
+ <?php
214
+
215
+ namespace App\Http\Resources;
216
+
217
+ use Illuminate\Http\Resources\Json\JsonResource;
218
+
219
+ class EventResource extends JsonResource
220
+ {
221
+ public function toArray($request): array
222
+ {
223
+ return [
224
+ 'id' => $this->id,
225
+ 'title' => $this->title,
226
+ 'scheduled_at' => $this->scheduled_at?->toISOString(),
227
+ 'is_past' => $this->scheduled_at?->isPast() ?? false,
228
+ 'created_at' => $this->created_at?->toISOString(),
229
+ 'updated_at' => $this->updated_at?->toISOString(),
230
+ ];
231
+ }
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Migration Example
238
+
239
+ ```php
240
+ Schema::create('events', function (Blueprint $table) {
241
+ $table->id();
242
+ $table->string('title');
243
+ $table->timestamp('scheduled_at'); // Use timestamp, not datetime
244
+ $table->timestamps(); // created_at, updated_at
245
+ });
246
+ ```
247
+
248
+ > **Note:** `timestamp` columns in MySQL are stored as UTC internally.
249
+
250
+ ---
251
+
252
+ ## Anti-Patterns ❌
253
+
254
+ ```php
255
+ // ❌ DON'T: Set local timezone in config
256
+ 'timezone' => 'Asia/Tokyo', // Wrong!
257
+
258
+ // ❌ DON'T: Use raw PHP date functions
259
+ $date = date('Y-m-d H:i:s');
260
+ $date = new \DateTime();
261
+
262
+ // ❌ DON'T: Return formatted local time in API
263
+ return ['created_at' => $this->created_at->format('Y/m/d H:i')];
264
+
265
+ // ❌ DON'T: Store timezone offset in database
266
+ $event->scheduled_at = '2024-01-15 19:30:00+09:00';
267
+
268
+ // ❌ DON'T: Convert to local timezone before storing
269
+ $event->scheduled_at = Carbon::parse($input)->setTimezone('Asia/Tokyo');
270
+ ```
271
+
272
+ ## Correct Patterns ✅
273
+
274
+ ```php
275
+ // ✅ DO: Keep UTC timezone
276
+ 'timezone' => 'UTC',
277
+
278
+ // ✅ DO: Use Carbon everywhere
279
+ $now = Carbon::now();
280
+ $date = Carbon::parse($input);
281
+
282
+ // ✅ DO: Return ISO 8601 UTC in API
283
+ return ['created_at' => $this->created_at?->toISOString()];
284
+
285
+ // ✅ DO: Store as UTC
286
+ $event->scheduled_at = Carbon::parse($input); // Input should be UTC from frontend
287
+ ```
288
+
289
+ ---
290
+
291
+ ## API Contract with Frontend
292
+
293
+ | Direction | Format | Example |
294
+ | -------------- | ------------- | ------------------------------- |
295
+ | API → Frontend | ISO 8601 UTC | `"2024-01-15T10:30:00.000000Z"` |
296
+ | Frontend → API | ISO 8601 UTC | `"2024-01-15T10:30:00.000Z"` |
297
+ | Database | UTC Timestamp | `2024-01-15 10:30:00` |
298
+
299
+ ---
300
+
301
+ ## Testing with Dates
302
+
303
+ ```php
304
+ use Illuminate\Support\Carbon;
305
+
306
+ public function test_creates_event_with_correct_date(): void
307
+ {
308
+ Carbon::setTestNow('2024-01-15 10:00:00'); // Freeze time in UTC
309
+
310
+ $response = $this->postJson('/api/events', [
311
+ 'title' => 'Test Event',
312
+ 'scheduled_at' => '2024-01-20T15:00:00.000Z',
313
+ ]);
314
+
315
+ $response->assertCreated();
316
+
317
+ $this->assertDatabaseHas('events', [
318
+ 'title' => 'Test Event',
319
+ 'scheduled_at' => '2024-01-20 15:00:00', // Stored as UTC
320
+ ]);
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Checklist
327
+
328
+ - [ ] `config/app.php` timezone is `UTC`
329
+ - [ ] MySQL timezone is UTC
330
+ - [ ] All models use `datetime` cast for date fields
331
+ - [ ] All Resources return `->toISOString()` for dates
332
+ - [ ] Never use raw `date()` or `DateTime` - always Carbon
333
+ - [ ] Never convert to local timezone before storing
334
+ - [ ] API documentation specifies UTC format
@@ -0,0 +1,369 @@
1
+ # OpenAPI Documentation Guide
2
+
3
+ > **Related:** [README](./README.md) | [Controller Guide](./controller-guide.md) | [Checklist](./checklist.md)
4
+
5
+ ## Overview
6
+
7
+ This project uses [L5-Swagger](https://github.com/DarkaOnLine/L5-Swagger) with PHP 8 Attributes.
8
+
9
+ **Key files:**
10
+ - `app/OpenApi/Schemas.php` - Reusable components (parameters, responses)
11
+ - `app/Http/Controllers/*Controller.php` - Endpoint documentation
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ ./artisan l5-swagger:generate # Generate OpenAPI JSON
17
+ # View at: https://api.{folder}.app/api/documentation
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ app/OpenApi/
26
+ └── Schemas.php ← Define reusable components HERE
27
+
28
+ app/Http/Controllers/
29
+ └── UserController.php ← Use $ref to reference components
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Step 1: Define Reusable Components
35
+
36
+ **File:** `app/OpenApi/Schemas.php`
37
+
38
+ ```php
39
+ <?php
40
+
41
+ namespace App\OpenApi;
42
+
43
+ use OpenApi\Attributes as OA;
44
+
45
+ #[OA\Info(
46
+ version: '1.0.0',
47
+ title: 'My API',
48
+ description: 'API documentation'
49
+ )]
50
+ #[OA\Server(url: '/api', description: 'API Server')]
51
+
52
+ // ============================================================================
53
+ // COMMON PARAMETERS
54
+ // ============================================================================
55
+
56
+ #[OA\Parameter(
57
+ parameter: 'QuerySearch',
58
+ name: 'search',
59
+ in: 'query',
60
+ description: 'Search term',
61
+ schema: new OA\Schema(type: 'string')
62
+ )]
63
+ #[OA\Parameter(
64
+ parameter: 'QueryPage',
65
+ name: 'page',
66
+ in: 'query',
67
+ description: 'Page number',
68
+ schema: new OA\Schema(type: 'integer', default: 1, minimum: 1)
69
+ )]
70
+ #[OA\Parameter(
71
+ parameter: 'QueryPerPage',
72
+ name: 'per_page',
73
+ in: 'query',
74
+ description: 'Items per page',
75
+ schema: new OA\Schema(type: 'integer', default: 10, minimum: 1, maximum: 100)
76
+ )]
77
+ #[OA\Parameter(
78
+ parameter: 'QuerySortBy',
79
+ name: 'sort_by',
80
+ in: 'query',
81
+ description: 'Sort field',
82
+ schema: new OA\Schema(type: 'string', default: 'id')
83
+ )]
84
+ #[OA\Parameter(
85
+ parameter: 'QuerySortOrder',
86
+ name: 'sort_order',
87
+ in: 'query',
88
+ description: 'Sort direction',
89
+ schema: new OA\Schema(type: 'string', enum: ['asc', 'desc'], default: 'desc')
90
+ )]
91
+ #[OA\Parameter(
92
+ parameter: 'PathId',
93
+ name: 'id',
94
+ in: 'path',
95
+ required: true,
96
+ description: 'Resource ID',
97
+ schema: new OA\Schema(type: 'integer', minimum: 1)
98
+ )]
99
+
100
+ // ============================================================================
101
+ // COMMON RESPONSES
102
+ // ============================================================================
103
+
104
+ #[OA\Response(response: 'Success', description: 'Successful operation')]
105
+ #[OA\Response(response: 'Created', description: 'Resource created successfully')]
106
+ #[OA\Response(response: 'NoContent', description: 'Successfully deleted')]
107
+ #[OA\Response(response: 'NotFound', description: 'Resource not found')]
108
+ #[OA\Response(response: 'Unauthorized', description: 'Unauthenticated')]
109
+ #[OA\Response(response: 'Forbidden', description: 'Forbidden')]
110
+ #[OA\Response(
111
+ response: 'ValidationError',
112
+ description: 'Validation failed',
113
+ content: new OA\JsonContent(
114
+ properties: [
115
+ new OA\Property(property: 'message', type: 'string', example: 'The given data was invalid.'),
116
+ new OA\Property(property: 'errors', type: 'object', example: ['email' => ['The email has already been taken.']]),
117
+ ]
118
+ )
119
+ )]
120
+ class Schemas
121
+ {
122
+ // This class exists only to hold OpenAPI attributes
123
+ }
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Step 2: Use $ref in Controllers
129
+
130
+ ### Index (GET list)
131
+
132
+ ```php
133
+ #[OA\Get(
134
+ path: '/api/users',
135
+ summary: 'List users',
136
+ description: 'Paginated list with search and sorting',
137
+ tags: ['Users'],
138
+ parameters: [
139
+ new OA\Parameter(ref: '#/components/parameters/QuerySearch'),
140
+ new OA\Parameter(ref: '#/components/parameters/QueryPage'),
141
+ new OA\Parameter(ref: '#/components/parameters/QueryPerPage'),
142
+ new OA\Parameter(ref: '#/components/parameters/QuerySortBy'),
143
+ new OA\Parameter(ref: '#/components/parameters/QuerySortOrder'),
144
+ ],
145
+ responses: [
146
+ new OA\Response(ref: '#/components/responses/Success', response: 200),
147
+ ]
148
+ )]
149
+ public function index(Request $request): AnonymousResourceCollection
150
+ ```
151
+
152
+ ### Store (POST)
153
+
154
+ ```php
155
+ #[OA\Post(
156
+ path: '/api/users',
157
+ summary: 'Create user',
158
+ description: 'Create a new user account',
159
+ tags: ['Users'],
160
+ requestBody: new OA\RequestBody(
161
+ required: true,
162
+ content: new OA\JsonContent(
163
+ required: ['name_lastname', 'name_firstname', 'email', 'password'],
164
+ properties: [
165
+ new OA\Property(property: 'name_lastname', type: 'string', maxLength: 50, example: '田中'),
166
+ new OA\Property(property: 'name_firstname', type: 'string', maxLength: 50, example: '太郎'),
167
+ new OA\Property(property: 'email', type: 'string', format: 'email', example: 'tanaka@example.com'),
168
+ new OA\Property(property: 'password', type: 'string', format: 'password', minLength: 8),
169
+ ]
170
+ )
171
+ ),
172
+ responses: [
173
+ new OA\Response(ref: '#/components/responses/Created', response: 201),
174
+ new OA\Response(ref: '#/components/responses/ValidationError', response: 422),
175
+ ]
176
+ )]
177
+ public function store(UserStoreRequest $request): UserResource
178
+ ```
179
+
180
+ ### Show (GET single)
181
+
182
+ ```php
183
+ #[OA\Get(
184
+ path: '/api/users/{id}',
185
+ summary: 'Get user',
186
+ description: 'Get user by ID',
187
+ tags: ['Users'],
188
+ parameters: [
189
+ new OA\Parameter(ref: '#/components/parameters/PathId'),
190
+ ],
191
+ responses: [
192
+ new OA\Response(ref: '#/components/responses/Success', response: 200),
193
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
194
+ ]
195
+ )]
196
+ public function show(User $user): UserResource
197
+ ```
198
+
199
+ ### Update (PUT)
200
+
201
+ ```php
202
+ #[OA\Put(
203
+ path: '/api/users/{id}',
204
+ summary: 'Update user',
205
+ description: 'Update user (partial update supported)',
206
+ tags: ['Users'],
207
+ parameters: [
208
+ new OA\Parameter(ref: '#/components/parameters/PathId'),
209
+ ],
210
+ requestBody: new OA\RequestBody(
211
+ content: new OA\JsonContent(
212
+ properties: [
213
+ new OA\Property(property: 'name_lastname', type: 'string', maxLength: 50),
214
+ new OA\Property(property: 'name_firstname', type: 'string', maxLength: 50),
215
+ new OA\Property(property: 'email', type: 'string', format: 'email'),
216
+ new OA\Property(property: 'password', type: 'string', format: 'password', minLength: 8),
217
+ ]
218
+ )
219
+ ),
220
+ responses: [
221
+ new OA\Response(ref: '#/components/responses/Success', response: 200),
222
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
223
+ new OA\Response(ref: '#/components/responses/ValidationError', response: 422),
224
+ ]
225
+ )]
226
+ public function update(UserUpdateRequest $request, User $user): UserResource
227
+ ```
228
+
229
+ ### Destroy (DELETE)
230
+
231
+ ```php
232
+ #[OA\Delete(
233
+ path: '/api/users/{id}',
234
+ summary: 'Delete user',
235
+ description: 'Permanently delete user',
236
+ tags: ['Users'],
237
+ parameters: [
238
+ new OA\Parameter(ref: '#/components/parameters/PathId'),
239
+ ],
240
+ responses: [
241
+ new OA\Response(ref: '#/components/responses/NoContent', response: 204),
242
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
243
+ ]
244
+ )]
245
+ public function destroy(User $user): JsonResponse
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Available Components
251
+
252
+ ### Parameters (use with `ref: '#/components/parameters/...'`)
253
+
254
+ | Name | Description |
255
+ | ---------------- | -------------------------------------- |
256
+ | `QuerySearch` | Search term |
257
+ | `QueryPage` | Page number (default: 1) |
258
+ | `QueryPerPage` | Items per page (default: 10, max: 100) |
259
+ | `QuerySortBy` | Sort field (default: id) |
260
+ | `QuerySortOrder` | Sort direction (asc/desc) |
261
+ | `PathId` | Resource ID in path |
262
+
263
+ ### Responses (use with `ref: '#/components/responses/...'`)
264
+
265
+ | Name | HTTP Code | Description |
266
+ | ----------------- | --------- | -------------------- |
267
+ | `Success` | 200 | Successful operation |
268
+ | `Created` | 201 | Resource created |
269
+ | `NoContent` | 204 | Successfully deleted |
270
+ | `NotFound` | 404 | Resource not found |
271
+ | `ValidationError` | 422 | Validation failed |
272
+ | `Unauthorized` | 401 | Unauthenticated |
273
+ | `Forbidden` | 403 | Forbidden |
274
+
275
+ ---
276
+
277
+ ## ⚠️ Important: Verify Fields Before Writing
278
+
279
+ **DO NOT make up fields!** Check these files first:
280
+
281
+ | What to Document | Check This File |
282
+ | ------------------- | ------------------------------------------------------------------- |
283
+ | Request body fields | `app/Http/Requests/OmnifyBase/*RequestBase.php` → `schemaRules()` |
284
+ | Response fields | `app/Http/Resources/OmnifyBase/*ResourceBase.php` → `schemaArray()` |
285
+
286
+ ---
287
+
288
+ ## Adding New Components
289
+
290
+ ### New Parameter
291
+
292
+ ```php
293
+ // In app/OpenApi/Schemas.php
294
+ #[OA\Parameter(
295
+ parameter: 'QueryStatus', // Unique name
296
+ name: 'status', // Query param name
297
+ in: 'query',
298
+ description: 'Filter by status',
299
+ schema: new OA\Schema(type: 'string', enum: ['active', 'inactive'])
300
+ )]
301
+ ```
302
+
303
+ ### New Response
304
+
305
+ ```php
306
+ // In app/OpenApi/Schemas.php
307
+ #[OA\Response(
308
+ response: 'PaymentRequired',
309
+ description: 'Payment required'
310
+ )]
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Checklist
316
+
317
+ ### Before Writing
318
+
319
+ - [ ] Check `OmnifyBase/*RequestBase.php` for request fields
320
+ - [ ] Check `OmnifyBase/*ResourceBase.php` for response fields
321
+ - [ ] DON'T make up fields that don't exist!
322
+
323
+ ### Writing OpenAPI
324
+
325
+ - [ ] Add `#[OA\Tag]` to controller class
326
+ - [ ] Use `$ref` for common parameters (QuerySearch, QueryPage, etc.)
327
+ - [ ] Use `$ref` for common responses (Success, NotFound, etc.)
328
+ - [ ] Only write `requestBody` properties manually (match FormRequest)
329
+ - [ ] Use Japanese examples for JapaneseName fields
330
+
331
+ ### After Writing
332
+
333
+ - [ ] Run `./artisan l5-swagger:generate`
334
+ - [ ] Verify at `/api/documentation`
335
+
336
+ ---
337
+
338
+ ## Anti-Patterns
339
+
340
+ ```php
341
+ // ❌ BAD: Repeating common parameters
342
+ parameters: [
343
+ new OA\Parameter(name: 'page', in: 'query', schema: new OA\Schema(type: 'integer')),
344
+ new OA\Parameter(name: 'per_page', in: 'query', schema: new OA\Schema(type: 'integer')),
345
+ ]
346
+
347
+ // ✅ GOOD: Use $ref
348
+ parameters: [
349
+ new OA\Parameter(ref: '#/components/parameters/QueryPage'),
350
+ new OA\Parameter(ref: '#/components/parameters/QueryPerPage'),
351
+ ]
352
+
353
+ // ❌ BAD: Repeating response definitions
354
+ responses: [
355
+ new OA\Response(response: 404, description: 'Resource not found'),
356
+ ]
357
+
358
+ // ✅ GOOD: Use $ref
359
+ responses: [
360
+ new OA\Response(ref: '#/components/responses/NotFound', response: 404),
361
+ ]
362
+
363
+ // ❌ BAD: Making up fields
364
+ new OA\Property(property: 'username', ...) // Does this exist?
365
+
366
+ // ✅ GOOD: Check OmnifyBase first, then write
367
+ // Checked: OmnifyBase/UserStoreRequestBase.php has name_lastname, name_firstname...
368
+ new OA\Property(property: 'name_lastname', type: 'string', example: '田中'),
369
+ ```