@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,450 @@
1
+ # Form Request Guide
2
+
3
+ > **Related:** [README](./README.md) | [Controller Guide](./controller-guide.md)
4
+
5
+ ## Why FormRequest?
6
+
7
+ | Without FormRequest | With FormRequest |
8
+ | ------------------------ | -------------------- |
9
+ | Validation in controller | Separated validation |
10
+ | Fat controllers | Thin controllers |
11
+ | Duplicate validation | Reusable rules |
12
+ | Hard to test | Easy to test |
13
+
14
+ ---
15
+
16
+ ## Request Templates
17
+
18
+ ### Store Request
19
+
20
+ ```php
21
+ <?php
22
+
23
+ namespace App\Http\Requests;
24
+
25
+ use Illuminate\Foundation\Http\FormRequest;
26
+
27
+ class UserStoreRequest extends FormRequest
28
+ {
29
+ /**
30
+ * Determine if the user is authorized to make this request.
31
+ */
32
+ public function authorize(): bool
33
+ {
34
+ return true; // Or: return $this->user()->can('create', User::class);
35
+ }
36
+
37
+ /**
38
+ * Get the validation rules that apply to the request.
39
+ *
40
+ * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
41
+ */
42
+ public function rules(): array
43
+ {
44
+ return [
45
+ 'name' => ['required', 'string', 'max:255'],
46
+ 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
47
+ 'password' => ['required', 'string', 'min:8'],
48
+ ];
49
+ }
50
+
51
+ /**
52
+ * Get custom attributes for validator errors.
53
+ */
54
+ public function attributes(): array
55
+ {
56
+ return [
57
+ 'name' => '名前',
58
+ 'email' => 'メールアドレス',
59
+ 'password' => 'パスワード',
60
+ ];
61
+ }
62
+
63
+ /**
64
+ * Get the error messages for the defined validation rules.
65
+ */
66
+ public function messages(): array
67
+ {
68
+ return [
69
+ 'email.unique' => 'このメールアドレスは既に使用されています。',
70
+ ];
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### Update Request
76
+
77
+ ```php
78
+ <?php
79
+
80
+ namespace App\Http\Requests;
81
+
82
+ use Illuminate\Foundation\Http\FormRequest;
83
+ use Illuminate\Validation\Rule;
84
+
85
+ class UserUpdateRequest extends FormRequest
86
+ {
87
+ public function authorize(): bool
88
+ {
89
+ return true;
90
+ }
91
+
92
+ public function rules(): array
93
+ {
94
+ return [
95
+ 'name' => ['sometimes', 'string', 'max:255'],
96
+ 'email' => [
97
+ 'sometimes',
98
+ 'string',
99
+ 'email',
100
+ 'max:255',
101
+ Rule::unique('users')->ignore($this->user), // Ignore current user
102
+ ],
103
+ 'password' => ['sometimes', 'string', 'min:8'],
104
+ ];
105
+ }
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Common Validation Rules
112
+
113
+ ### String Fields
114
+
115
+ ```php
116
+ 'name' => ['required', 'string', 'max:255'],
117
+ 'title' => ['required', 'string', 'min:3', 'max:100'],
118
+ 'slug' => ['required', 'string', 'alpha_dash', 'max:100'],
119
+ 'description' => ['nullable', 'string', 'max:1000'],
120
+ ```
121
+
122
+ ### Email
123
+
124
+ ```php
125
+ 'email' => ['required', 'email', 'max:255', 'unique:users'],
126
+ 'email' => ['required', 'email', Rule::unique('users')->ignore($this->user)],
127
+ ```
128
+
129
+ ### Password
130
+
131
+ ```php
132
+ use Illuminate\Validation\Rules\Password;
133
+
134
+ 'password' => ['required', 'confirmed', Password::defaults()],
135
+ 'password' => ['required', Password::min(8)->mixedCase()->numbers()->symbols()],
136
+ ```
137
+
138
+ ### Numbers
139
+
140
+ ```php
141
+ 'age' => ['required', 'integer', 'min:0', 'max:150'],
142
+ 'price' => ['required', 'numeric', 'min:0', 'max:999999.99'],
143
+ 'quantity' => ['required', 'integer', 'min:1'],
144
+ ```
145
+
146
+ ### Dates
147
+
148
+ ```php
149
+ 'birth_date' => ['required', 'date', 'before:today'],
150
+ 'scheduled_at' => ['required', 'date', 'after:now'],
151
+ 'start_date' => ['required', 'date'],
152
+ 'end_date' => ['required', 'date', 'after:start_date'],
153
+ ```
154
+
155
+ ### Enums
156
+
157
+ ```php
158
+ 'status' => ['required', 'in:draft,published,archived'],
159
+ 'status' => ['required', Rule::enum(PostStatus::class)],
160
+ 'role' => ['required', Rule::in(['admin', 'user', 'guest'])],
161
+ ```
162
+
163
+ ### Files
164
+
165
+ ```php
166
+ 'avatar' => ['nullable', 'image', 'max:2048'], // 2MB max
167
+ 'document' => ['required', 'file', 'mimes:pdf,doc,docx', 'max:10240'],
168
+ 'images' => ['nullable', 'array', 'max:5'],
169
+ 'images.*' => ['image', 'max:2048'],
170
+ ```
171
+
172
+ ### Arrays
173
+
174
+ ```php
175
+ 'tags' => ['nullable', 'array', 'max:10'],
176
+ 'tags.*' => ['string', 'max:50'],
177
+ 'items' => ['required', 'array', 'min:1'],
178
+ 'items.*.product_id' => ['required', 'exists:products,id'],
179
+ 'items.*.quantity' => ['required', 'integer', 'min:1'],
180
+ ```
181
+
182
+ ### Relationships
183
+
184
+ ```php
185
+ 'user_id' => ['required', 'exists:users,id'],
186
+ 'category_id' => ['required', 'exists:categories,id'],
187
+ 'tag_ids' => ['nullable', 'array'],
188
+ 'tag_ids.*' => ['exists:tags,id'],
189
+ ```
190
+
191
+ ### Conditional Rules
192
+
193
+ ```php
194
+ public function rules(): array
195
+ {
196
+ return [
197
+ 'type' => ['required', 'in:individual,company'],
198
+ 'company_name' => ['required_if:type,company', 'string', 'max:255'],
199
+ 'tax_id' => ['required_if:type,company', 'string', 'max:20'],
200
+ ];
201
+ }
202
+
203
+ // Or using Rule::when()
204
+ 'company_name' => [
205
+ Rule::when($this->type === 'company', ['required', 'string', 'max:255']),
206
+ ],
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Authorization
212
+
213
+ ### Simple Authorization
214
+
215
+ ```php
216
+ public function authorize(): bool
217
+ {
218
+ return $this->user() !== null; // Must be logged in
219
+ }
220
+ ```
221
+
222
+ ### Policy-Based Authorization
223
+
224
+ ```php
225
+ public function authorize(): bool
226
+ {
227
+ $post = $this->route('post'); // Get route parameter
228
+ return $this->user()->can('update', $post);
229
+ }
230
+ ```
231
+
232
+ ### Role-Based Authorization
233
+
234
+ ```php
235
+ public function authorize(): bool
236
+ {
237
+ return $this->user()->hasRole('admin');
238
+ }
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Data Preparation
244
+
245
+ ### Modify Input Before Validation
246
+
247
+ ```php
248
+ protected function prepareForValidation(): void
249
+ {
250
+ $this->merge([
251
+ 'slug' => Str::slug($this->title),
252
+ 'email' => Str::lower($this->email),
253
+ ]);
254
+ }
255
+ ```
256
+
257
+ ### Modify Validated Data
258
+
259
+ ```php
260
+ public function validated($key = null, $default = null): array
261
+ {
262
+ $validated = parent::validated($key, $default);
263
+
264
+ // Hash password if present
265
+ if (isset($validated['password'])) {
266
+ $validated['password'] = bcrypt($validated['password']);
267
+ }
268
+
269
+ return $validated;
270
+ }
271
+ ```
272
+
273
+ ### Add Data After Validation
274
+
275
+ ```php
276
+ // In controller
277
+ public function store(PostStoreRequest $request): PostResource
278
+ {
279
+ $data = array_merge($request->validated(), [
280
+ 'user_id' => $request->user()->id,
281
+ 'published_at' => now(),
282
+ ]);
283
+
284
+ $post = Post::create($data);
285
+ return new PostResource($post);
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Custom Validation Rules
292
+
293
+ ### Inline Rule
294
+
295
+ ```php
296
+ use Illuminate\Validation\Validator;
297
+
298
+ public function withValidator(Validator $validator): void
299
+ {
300
+ $validator->after(function (Validator $validator) {
301
+ if ($this->hasInvalidCoupon()) {
302
+ $validator->errors()->add('coupon', 'Invalid or expired coupon.');
303
+ }
304
+ });
305
+ }
306
+
307
+ private function hasInvalidCoupon(): bool
308
+ {
309
+ if (!$this->coupon_code) return false;
310
+
311
+ return !Coupon::where('code', $this->coupon_code)
312
+ ->where('expires_at', '>', now())
313
+ ->exists();
314
+ }
315
+ ```
316
+
317
+ ### Custom Rule Class
318
+
319
+ ```php
320
+ // app/Rules/ValidCoupon.php
321
+ <?php
322
+
323
+ namespace App\Rules;
324
+
325
+ use App\Models\Coupon;
326
+ use Closure;
327
+ use Illuminate\Contracts\Validation\ValidationRule;
328
+
329
+ class ValidCoupon implements ValidationRule
330
+ {
331
+ public function validate(string $attribute, mixed $value, Closure $fail): void
332
+ {
333
+ $coupon = Coupon::where('code', $value)
334
+ ->where('expires_at', '>', now())
335
+ ->first();
336
+
337
+ if (!$coupon) {
338
+ $fail('The coupon is invalid or expired.');
339
+ }
340
+ }
341
+ }
342
+
343
+ // Usage in FormRequest
344
+ 'coupon_code' => ['nullable', 'string', new ValidCoupon],
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Error Response Format
350
+
351
+ Laravel automatically returns validation errors in this format:
352
+
353
+ ```json
354
+ {
355
+ "message": "The email field must be a valid email address.",
356
+ "errors": {
357
+ "email": [
358
+ "The email field must be a valid email address."
359
+ ],
360
+ "password": [
361
+ "The password field must be at least 8 characters."
362
+ ]
363
+ }
364
+ }
365
+ ```
366
+
367
+ HTTP Status: `422 Unprocessable Entity`
368
+
369
+ ---
370
+
371
+ ## Testing Requests
372
+
373
+ ### Test Validation Rules
374
+
375
+ ```php
376
+ public function test_name_is_required(): void
377
+ {
378
+ $response = $this->postJson('/api/users', [
379
+ 'email' => 'test@example.com',
380
+ 'password' => 'password123',
381
+ ]);
382
+
383
+ $response->assertStatus(422)
384
+ ->assertJsonValidationErrors(['name']);
385
+ }
386
+
387
+ public function test_email_must_be_unique(): void
388
+ {
389
+ User::factory()->create(['email' => 'existing@example.com']);
390
+
391
+ $response = $this->postJson('/api/users', [
392
+ 'name' => 'John',
393
+ 'email' => 'existing@example.com',
394
+ 'password' => 'password123',
395
+ ]);
396
+
397
+ $response->assertStatus(422)
398
+ ->assertJsonValidationErrors(['email']);
399
+ }
400
+
401
+ public function test_valid_data_creates_user(): void
402
+ {
403
+ $response = $this->postJson('/api/users', [
404
+ 'name' => 'John Doe',
405
+ 'email' => 'john@example.com',
406
+ 'password' => 'password123',
407
+ ]);
408
+
409
+ $response->assertStatus(201)
410
+ ->assertJsonStructure(['data' => ['id', 'name', 'email']]);
411
+ }
412
+ ```
413
+
414
+ ---
415
+
416
+ ## Best Practices
417
+
418
+ ### ✅ DO
419
+
420
+ ```php
421
+ // Separate Store and Update requests
422
+ class UserStoreRequest extends FormRequest { ... }
423
+ class UserUpdateRequest extends FormRequest { ... }
424
+
425
+ // Use 'sometimes' for optional updates
426
+ 'name' => ['sometimes', 'string', 'max:255'],
427
+
428
+ // Use Rule class for complex rules
429
+ Rule::unique('users')->ignore($this->user)
430
+ Rule::enum(PostStatus::class)
431
+
432
+ // Prepare data before validation
433
+ protected function prepareForValidation(): void { ... }
434
+ ```
435
+
436
+ ### ❌ DON'T
437
+
438
+ ```php
439
+ // Don't validate in controller
440
+ public function store(Request $request) {
441
+ $request->validate([...]); // Use FormRequest instead
442
+ }
443
+
444
+ // Don't use same request for store and update
445
+ class UserRequest extends FormRequest { ... } // Split into Store/Update
446
+
447
+ // Don't hardcode messages (use lang files for i18n)
448
+ 'name.required' => 'Name is required', // OK for simple apps
449
+ // Use resources/lang/{locale}/validation.php for i18n
450
+ ```