@famgia/omnify-ai-guides 2.0.15
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/README.md +105 -0
- package/dist/chunk-RCTEXK7C.js +549 -0
- package/dist/chunk-RCTEXK7C.js.map +1 -0
- package/dist/config/rules.yaml +524 -0
- package/dist/index.cjs +587 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +55 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/agents/architect.md.stub +150 -0
- package/dist/knowledge/agents/developer.md.stub +190 -0
- package/dist/knowledge/agents/reviewer.md.stub +134 -0
- package/dist/knowledge/agents/tester.md.stub +196 -0
- package/dist/knowledge/checklists/backend.md.stub +112 -0
- package/dist/knowledge/checklists/react.md.stub +108 -0
- package/dist/knowledge/claude-rules/laravel-controllers.md.stub +57 -0
- package/dist/knowledge/claude-rules/laravel-migrations.md.stub +47 -0
- package/dist/knowledge/claude-rules/laravel-tests.md.stub +52 -0
- package/dist/knowledge/claude-rules/naming.md.stub +369 -0
- package/dist/knowledge/claude-rules/performance.md.stub +256 -0
- package/dist/knowledge/claude-rules/php-standards.md.stub +305 -0
- package/dist/knowledge/claude-rules/react-components.md.stub +67 -0
- package/dist/knowledge/claude-rules/schema-yaml.md.stub +83 -0
- package/dist/knowledge/claude-rules/security.md.stub +164 -0
- package/dist/knowledge/cursor-rules/antd-deprecations.mdc.stub +62 -0
- package/dist/knowledge/cursor-rules/basemodel-readonly.mdc.stub +66 -0
- package/dist/knowledge/cursor-rules/baserequest-readonly.mdc.stub +74 -0
- package/dist/knowledge/cursor-rules/baseresource-readonly.mdc.stub +78 -0
- package/dist/knowledge/cursor-rules/laravel-controller.mdc.stub +421 -0
- package/dist/knowledge/cursor-rules/laravel-request.mdc.stub +112 -0
- package/dist/knowledge/cursor-rules/laravel-resource.mdc.stub +73 -0
- package/dist/knowledge/cursor-rules/laravel-review.mdc.stub +69 -0
- package/dist/knowledge/cursor-rules/laravel-testing.mdc.stub +138 -0
- package/dist/knowledge/cursor-rules/laravel.mdc.stub +138 -0
- package/dist/knowledge/cursor-rules/migrations-workflow.mdc.stub +224 -0
- package/dist/knowledge/cursor-rules/model-editable.mdc.stub +120 -0
- package/dist/knowledge/cursor-rules/omnify-migrations.mdc.stub +109 -0
- package/dist/knowledge/cursor-rules/omnify-schema.mdc.stub +358 -0
- package/dist/knowledge/cursor-rules/omnify.mdc.stub +58 -0
- package/dist/knowledge/cursor-rules/react-design.mdc.stub +693 -0
- package/dist/knowledge/cursor-rules/react-form.mdc.stub +292 -0
- package/dist/knowledge/cursor-rules/react-services.mdc.stub +304 -0
- package/dist/knowledge/cursor-rules/react.mdc.stub +336 -0
- package/dist/knowledge/cursor-rules/request-editable.mdc.stub +111 -0
- package/dist/knowledge/cursor-rules/resource-editable.mdc.stub +125 -0
- package/dist/knowledge/cursor-rules/schema-create.mdc.stub +440 -0
- package/dist/knowledge/cursor-rules/validation-rules.mdc.stub +181 -0
- package/dist/knowledge/laravel/README.md.stub +59 -0
- package/dist/knowledge/laravel/architecture.md.stub +424 -0
- package/dist/knowledge/laravel/authentication.md.stub +588 -0
- package/dist/knowledge/laravel/controller.md.stub +484 -0
- package/dist/knowledge/laravel/datetime.md.stub +334 -0
- package/dist/knowledge/laravel/migrations-team.md.stub +376 -0
- package/dist/knowledge/laravel/openapi.md.stub +449 -0
- package/dist/knowledge/laravel/request.md.stub +450 -0
- package/dist/knowledge/laravel/resource.md.stub +516 -0
- package/dist/knowledge/laravel/service.md.stub +503 -0
- package/dist/knowledge/laravel/testing.md.stub +1504 -0
- package/dist/knowledge/omnify/antdesign-guide.md.stub +401 -0
- package/dist/knowledge/omnify/config-guide.md.stub +405 -0
- package/dist/knowledge/omnify/japan-guide.md.stub +186 -0
- package/dist/knowledge/omnify/laravel-guide.md.stub +61 -0
- package/dist/knowledge/omnify/partial-schema-guide.md.stub +353 -0
- package/dist/knowledge/omnify/react-form-guide.md.stub +225 -0
- package/dist/knowledge/omnify/schema-guide.md.stub +144 -0
- package/dist/knowledge/omnify/typescript-guide.md.stub +337 -0
- package/dist/knowledge/react/README.md.stub +221 -0
- package/dist/knowledge/react/antd-guide.md +528 -0
- package/dist/knowledge/react/antd-guide.md.stub +528 -0
- package/dist/knowledge/react/checklist.md.stub +108 -0
- package/dist/knowledge/react/datetime-guide.md.stub +137 -0
- package/dist/knowledge/react/design-philosophy.md.stub +363 -0
- package/dist/knowledge/react/i18n-guide.md.stub +211 -0
- package/dist/knowledge/react/laravel-integration.md.stub +181 -0
- package/dist/knowledge/react/service-pattern.md.stub +180 -0
- package/dist/knowledge/react/tanstack-query.md.stub +339 -0
- package/dist/knowledge/react/types-guide.md +669 -0
- package/dist/knowledge/react/types-guide.md.stub +669 -0
- package/dist/knowledge/workflows/bug-fix.md.stub +201 -0
- package/dist/knowledge/workflows/code-review.md.stub +164 -0
- package/dist/knowledge/workflows/new-feature.md.stub +327 -0
- package/dist/plugin-M95GyBll.d.cts +191 -0
- package/dist/plugin-M95GyBll.d.ts +191 -0
- package/dist/plugin.cjs +573 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +2 -0
- package/dist/plugin.d.ts +2 -0
- package/dist/plugin.js +15 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Bug Fix Workflow
|
|
2
|
+
|
|
3
|
+
> Step-by-step guide for fixing bugs.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
Reproduce --> Locate --> Fix --> Test --> PR
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Step | Action | Output |
|
|
13
|
+
| ---- | --------------------- | ------------------------------ |
|
|
14
|
+
| 1 | Reproduce the bug | Clear reproduction steps |
|
|
15
|
+
| 2 | Locate the cause | File(s) and line(s) identified |
|
|
16
|
+
| 3 | Write failing test | Test that reproduces bug |
|
|
17
|
+
| 4 | Fix the bug | Code changes |
|
|
18
|
+
| 5 | Verify test passes | `./artisan test` |
|
|
19
|
+
| 6 | Check for regressions | All tests pass |
|
|
20
|
+
| 7 | Create PR | Pull request |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 1: Reproduce
|
|
25
|
+
|
|
26
|
+
Before fixing, confirm you can reproduce:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Check logs
|
|
30
|
+
tail -f backend/storage/logs/laravel.log
|
|
31
|
+
|
|
32
|
+
# Test the endpoint
|
|
33
|
+
curl -X GET https://api.boilerplate.app/api/users
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Document:**
|
|
37
|
+
- Steps to reproduce
|
|
38
|
+
- Expected behavior
|
|
39
|
+
- Actual behavior
|
|
40
|
+
- Error message/stack trace
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Step 2: Locate the Cause
|
|
45
|
+
|
|
46
|
+
### Common Locations
|
|
47
|
+
|
|
48
|
+
| Symptom | Check |
|
|
49
|
+
| ------------------- | -------------------------------- |
|
|
50
|
+
| 500 error | `storage/logs/laravel.log` |
|
|
51
|
+
| 422 validation | `*Request.php` rules |
|
|
52
|
+
| Wrong data returned | `*Resource.php` |
|
|
53
|
+
| 404 not found | `routes/api.php` + model binding |
|
|
54
|
+
| N+1 queries | Missing `with()` in controller |
|
|
55
|
+
| Date format wrong | Missing `->toISOString()` |
|
|
56
|
+
|
|
57
|
+
### Debug Commands
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Check route exists
|
|
61
|
+
./artisan route:list | grep users
|
|
62
|
+
|
|
63
|
+
# Check model
|
|
64
|
+
./artisan tinker
|
|
65
|
+
>>> User::find(1)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Step 3: Write Failing Test
|
|
71
|
+
|
|
72
|
+
**ALWAYS write a test that reproduces the bug first.**
|
|
73
|
+
|
|
74
|
+
```php
|
|
75
|
+
// tests/Feature/Api/UserControllerTest.php
|
|
76
|
+
|
|
77
|
+
it('異常: bug #123 - returns 500 when name is null', function () {
|
|
78
|
+
// This should NOT happen but currently does
|
|
79
|
+
$response = $this->postJson('/api/users', [
|
|
80
|
+
'email' => 'test@example.com',
|
|
81
|
+
'password' => 'password123',
|
|
82
|
+
// name is missing - should return 422, not 500
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
$response->assertUnprocessable(); // Currently fails with 500
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Run test to confirm it fails:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
./artisan test --filter="bug #123"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Step 4: Fix the Bug
|
|
98
|
+
|
|
99
|
+
Make the minimal change to fix the issue.
|
|
100
|
+
|
|
101
|
+
**Don't:**
|
|
102
|
+
- Refactor unrelated code
|
|
103
|
+
- Add features
|
|
104
|
+
- Change coding style
|
|
105
|
+
|
|
106
|
+
**Do:**
|
|
107
|
+
- Fix only the bug
|
|
108
|
+
- Keep changes focused
|
|
109
|
+
- Follow existing patterns
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Step 5: Verify Fix
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Run the specific test
|
|
117
|
+
./artisan test --filter="bug #123"
|
|
118
|
+
|
|
119
|
+
# Run all tests for the affected controller
|
|
120
|
+
./artisan test --filter=UserControllerTest
|
|
121
|
+
|
|
122
|
+
# Run all tests
|
|
123
|
+
./artisan test
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Step 6: Check for Regressions
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# All backend tests
|
|
132
|
+
./artisan test
|
|
133
|
+
|
|
134
|
+
# Specific test file
|
|
135
|
+
./artisan test tests/Feature/Api/UserControllerTest.php
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Step 7: Create PR
|
|
141
|
+
|
|
142
|
+
### PR Title Format
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
fix: [#issue] brief description
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Example: `fix: [#123] return 422 instead of 500 when name is missing`
|
|
149
|
+
|
|
150
|
+
### PR Description
|
|
151
|
+
|
|
152
|
+
```markdown
|
|
153
|
+
## Bug
|
|
154
|
+
|
|
155
|
+
[Link to issue or description]
|
|
156
|
+
|
|
157
|
+
## Root Cause
|
|
158
|
+
|
|
159
|
+
[Explanation of what caused the bug]
|
|
160
|
+
|
|
161
|
+
## Fix
|
|
162
|
+
|
|
163
|
+
[Description of the fix]
|
|
164
|
+
|
|
165
|
+
## Test
|
|
166
|
+
|
|
167
|
+
- [ ] Added test that reproduces bug
|
|
168
|
+
- [ ] Test passes after fix
|
|
169
|
+
- [ ] All existing tests pass
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Debugging Tips
|
|
175
|
+
|
|
176
|
+
### Laravel Logs
|
|
177
|
+
|
|
178
|
+
```php
|
|
179
|
+
// Add temporary logging
|
|
180
|
+
Log::info('Debug', ['user' => $user, 'request' => $request->all()]);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Database Queries
|
|
184
|
+
|
|
185
|
+
```php
|
|
186
|
+
// Enable query log
|
|
187
|
+
DB::enableQueryLog();
|
|
188
|
+
|
|
189
|
+
// ... your code ...
|
|
190
|
+
|
|
191
|
+
// Dump queries
|
|
192
|
+
dd(DB::getQueryLog());
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Tinker
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
./artisan tinker
|
|
199
|
+
>>> User::where('email', 'test@example.com')->first()
|
|
200
|
+
>>> app(UserService::class)->someMethod()
|
|
201
|
+
```
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Code Review Workflow
|
|
2
|
+
|
|
3
|
+
> Checklist for reviewing pull requests.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
Read --> Security --> Performance --> Quality --> Test --> Approve
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Understand the Change
|
|
15
|
+
|
|
16
|
+
- [ ] Read PR description
|
|
17
|
+
- [ ] Understand the purpose
|
|
18
|
+
- [ ] Check linked issue/ticket
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Security Review
|
|
23
|
+
|
|
24
|
+
> **Reference:** [/rules/security.md](../rules/security.md)
|
|
25
|
+
|
|
26
|
+
### Must Check
|
|
27
|
+
|
|
28
|
+
| Item | Look For |
|
|
29
|
+
| --------------- | --------------------------------------------------- |
|
|
30
|
+
| Mass Assignment | Using `$request->validated()` not `$request->all()` |
|
|
31
|
+
| SQL Injection | No raw SQL with user input |
|
|
32
|
+
| `$fillable` | Defined in models, no sensitive fields |
|
|
33
|
+
| `$hidden` | Password and tokens hidden |
|
|
34
|
+
| XSS | No `{!! $userInput !!}` in Blade |
|
|
35
|
+
|
|
36
|
+
### Code Review
|
|
37
|
+
|
|
38
|
+
```php
|
|
39
|
+
// ❌ REJECT: Mass assignment vulnerability
|
|
40
|
+
User::create($request->all());
|
|
41
|
+
|
|
42
|
+
// ✅ APPROVE: Using validated data
|
|
43
|
+
User::create($request->validated());
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Performance Review
|
|
49
|
+
|
|
50
|
+
> **Reference:** [/rules/performance.md](../rules/performance.md)
|
|
51
|
+
|
|
52
|
+
### Must Check
|
|
53
|
+
|
|
54
|
+
| Item | Look For |
|
|
55
|
+
| ------------- | -------------------------------- |
|
|
56
|
+
| N+1 Queries | `with()` used for relationships |
|
|
57
|
+
| Pagination | List endpoints use `paginate()` |
|
|
58
|
+
| Resources | `whenLoaded()` for relationships |
|
|
59
|
+
| Eager Loading | No lazy loading in loops |
|
|
60
|
+
|
|
61
|
+
### Code Review
|
|
62
|
+
|
|
63
|
+
```php
|
|
64
|
+
// ❌ REJECT: N+1 problem
|
|
65
|
+
$posts = Post::all();
|
|
66
|
+
foreach ($posts as $post) {
|
|
67
|
+
echo $post->author->name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ✅ APPROVE: Eager loaded
|
|
71
|
+
$posts = Post::with('author')->get();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 4. Code Quality Review
|
|
77
|
+
|
|
78
|
+
### Must Check
|
|
79
|
+
|
|
80
|
+
| Item | Look For |
|
|
81
|
+
| ------------- | ---------------------------------- |
|
|
82
|
+
| Validation | FormRequest not inline validation |
|
|
83
|
+
| Response | Resource not raw model |
|
|
84
|
+
| Route binding | `User $user` not `findOrFail($id)` |
|
|
85
|
+
| Dates | `->toISOString()` in Resources |
|
|
86
|
+
| Types | Return type hints on methods |
|
|
87
|
+
|
|
88
|
+
### Naming
|
|
89
|
+
|
|
90
|
+
> **Reference:** [/rules/naming.md](../rules/naming.md)
|
|
91
|
+
|
|
92
|
+
| Type | Pattern |
|
|
93
|
+
| ---------- | ------------------------ |
|
|
94
|
+
| Controller | `{Model}Controller` |
|
|
95
|
+
| Request | `{Model}{Action}Request` |
|
|
96
|
+
| Resource | `{Model}Resource` |
|
|
97
|
+
| Test | `{Model}ControllerTest` |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 5. Test Review
|
|
102
|
+
|
|
103
|
+
### Must Have
|
|
104
|
+
|
|
105
|
+
| Endpoint | 正常系 | 異常系 |
|
|
106
|
+
| -------- | ------------------ | --------------------- |
|
|
107
|
+
| index | List, filter, sort | Empty, invalid params |
|
|
108
|
+
| store | Creates → 201 | 422 (validation) |
|
|
109
|
+
| show | Returns → 200 | 404 |
|
|
110
|
+
| update | Updates → 200 | 404, 422 |
|
|
111
|
+
| destroy | Deletes → 204 | 404 |
|
|
112
|
+
|
|
113
|
+
### Test Naming
|
|
114
|
+
|
|
115
|
+
```php
|
|
116
|
+
// ✅ Good naming
|
|
117
|
+
it('正常: creates user with valid data')
|
|
118
|
+
it('異常: fails to create user with invalid email')
|
|
119
|
+
it('異常: returns 404 when user not found')
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 6. Final Checks
|
|
125
|
+
|
|
126
|
+
- [ ] All tests pass
|
|
127
|
+
- [ ] No debug code (`dd()`, `dump()`, `Log::debug()`)
|
|
128
|
+
- [ ] No commented-out code
|
|
129
|
+
- [ ] No console.log or debug statements
|
|
130
|
+
- [ ] Follows existing patterns in codebase
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Review Decision
|
|
135
|
+
|
|
136
|
+
### ✅ Approve If
|
|
137
|
+
|
|
138
|
+
- All security checks pass
|
|
139
|
+
- All performance checks pass
|
|
140
|
+
- Tests cover 正常系 + 異常系
|
|
141
|
+
- Code follows conventions
|
|
142
|
+
|
|
143
|
+
### 🔄 Request Changes If
|
|
144
|
+
|
|
145
|
+
- Security vulnerability found
|
|
146
|
+
- Performance issue (N+1, no pagination)
|
|
147
|
+
- Missing tests
|
|
148
|
+
- Naming/pattern violations
|
|
149
|
+
|
|
150
|
+
### Example Comments
|
|
151
|
+
|
|
152
|
+
```markdown
|
|
153
|
+
## Security Issue
|
|
154
|
+
❌ Line 45: Using `$request->all()` - please use `$request->validated()`
|
|
155
|
+
|
|
156
|
+
## Performance Issue
|
|
157
|
+
❌ Line 23: Missing `with('author')` - will cause N+1 queries
|
|
158
|
+
|
|
159
|
+
## Missing Test
|
|
160
|
+
❌ No test for 422 validation error case
|
|
161
|
+
|
|
162
|
+
## Naming
|
|
163
|
+
❌ `UserCreateRequest` should be `UserStoreRequest` (Laravel convention)
|
|
164
|
+
```
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# New Feature Workflow
|
|
2
|
+
|
|
3
|
+
> Step-by-step guide for implementing a complete feature (Backend + Frontend).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
Schema --> Generate --> Migrate --> Backend --> Tests --> OpenAPI --> Frontend
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Step | Command/Action | Output |
|
|
13
|
+
| ---- | --------------------- | --------------------------------------- |
|
|
14
|
+
| 1 | Create schema | `.omnify/schemas/{Module}/{Model}.yaml` |
|
|
15
|
+
| 2 | `npx omnify generate` | Migration + Types + Requests |
|
|
16
|
+
| 3 | `./artisan migrate` | Database tables |
|
|
17
|
+
| 4 | Create Backend | Model, Controller, Resource, Routes |
|
|
18
|
+
| 5 | Write Tests | `tests/Feature/Api/*Test.php` |
|
|
19
|
+
| 6 | `./artisan test` | All tests pass |
|
|
20
|
+
| 7 | Add OpenAPI | Swagger documentation |
|
|
21
|
+
| 8 | Build Frontend | Service, Hooks, Components |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 1: Schema
|
|
26
|
+
|
|
27
|
+
Create `.omnify/schemas/{Module}/{Model}.yaml`:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
# .omnify/schemas/Blog/Post.yaml
|
|
31
|
+
name: Post
|
|
32
|
+
tableName: posts
|
|
33
|
+
properties:
|
|
34
|
+
title:
|
|
35
|
+
type: String
|
|
36
|
+
maxLength: 255
|
|
37
|
+
content:
|
|
38
|
+
type: Text
|
|
39
|
+
user_id:
|
|
40
|
+
type: ForeignId
|
|
41
|
+
references: users
|
|
42
|
+
published_at:
|
|
43
|
+
type: DateTime
|
|
44
|
+
nullable: true
|
|
45
|
+
options:
|
|
46
|
+
timestamps: true
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Guide**: [guides/omnify/schema-guide.md](../guides/omnify/schema-guide.md)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 2-3: Generate & Migrate
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx omnify generate # Generate migration + types
|
|
57
|
+
./artisan migrate # Create database tables
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Verify**:
|
|
61
|
+
- [ ] Check `{{LARAVEL_ROOT}}database/migrations/omnify/` for new migration
|
|
62
|
+
- [ ] Check `frontend/src/types/model/` for TypeScript types
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Step 4: Backend Implementation
|
|
67
|
+
|
|
68
|
+
### 4.1 Model
|
|
69
|
+
|
|
70
|
+
```php
|
|
71
|
+
// {{LARAVEL_BASE}}/Models/Post.php
|
|
72
|
+
class Post extends PostBaseModel
|
|
73
|
+
{
|
|
74
|
+
// $fillable, $casts inherited from base
|
|
75
|
+
|
|
76
|
+
public function author(): BelongsTo
|
|
77
|
+
{
|
|
78
|
+
return $this->belongsTo(User::class, 'user_id');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public function scopePublished($query)
|
|
82
|
+
{
|
|
83
|
+
return $query->whereNotNull('published_at');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 4.2 Controller (Thin!)
|
|
89
|
+
|
|
90
|
+
```php
|
|
91
|
+
// {{LARAVEL_BASE}}/Http/Controllers/PostController.php
|
|
92
|
+
class PostController extends Controller
|
|
93
|
+
{
|
|
94
|
+
public function index(Request $request)
|
|
95
|
+
{
|
|
96
|
+
$posts = Post::with('author')
|
|
97
|
+
->when($request->search, fn($q, $s) => $q->where('title', 'like', "%{$s}%"))
|
|
98
|
+
->paginate($request->input('per_page', 15));
|
|
99
|
+
|
|
100
|
+
return PostResource::collection($posts);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public function store(PostStoreRequest $request): PostResource
|
|
104
|
+
{
|
|
105
|
+
$post = Post::create($request->validated());
|
|
106
|
+
return new PostResource($post);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public function show(Post $post): PostResource
|
|
110
|
+
{
|
|
111
|
+
return new PostResource($post->load('author'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public function update(PostUpdateRequest $request, Post $post): PostResource
|
|
115
|
+
{
|
|
116
|
+
$post->update($request->validated());
|
|
117
|
+
return new PostResource($post);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public function destroy(Post $post): Response
|
|
121
|
+
{
|
|
122
|
+
$post->delete();
|
|
123
|
+
return response()->noContent();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4.3 Resource
|
|
129
|
+
|
|
130
|
+
```php
|
|
131
|
+
// {{LARAVEL_BASE}}/Http/Resources/PostResource.php
|
|
132
|
+
class PostResource extends PostResourceBase
|
|
133
|
+
{
|
|
134
|
+
public function toArray($request): array
|
|
135
|
+
{
|
|
136
|
+
return array_merge($this->schemaArray(), [
|
|
137
|
+
'author' => new UserResource($this->whenLoaded('author')),
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 4.4 Routes
|
|
144
|
+
|
|
145
|
+
```php
|
|
146
|
+
// routes/api.php
|
|
147
|
+
Route::apiResource('posts', PostController::class);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Step 5: Tests
|
|
153
|
+
|
|
154
|
+
Create `tests/Feature/Api/PostControllerTest.php`:
|
|
155
|
+
|
|
156
|
+
```php
|
|
157
|
+
describe('GET /api/posts', function () {
|
|
158
|
+
it('正常: returns paginated posts', function () {
|
|
159
|
+
Post::factory()->count(20)->create();
|
|
160
|
+
|
|
161
|
+
$response = $this->getJson('/api/posts');
|
|
162
|
+
|
|
163
|
+
$response->assertOk()
|
|
164
|
+
->assertJsonCount(15, 'data')
|
|
165
|
+
->assertJsonStructure(['data', 'meta', 'links']);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('POST /api/posts', function () {
|
|
170
|
+
it('正常: creates post with valid data', function () {
|
|
171
|
+
$data = ['title' => 'Test', 'content' => 'Content'];
|
|
172
|
+
|
|
173
|
+
$response = $this->postJson('/api/posts', $data);
|
|
174
|
+
|
|
175
|
+
$response->assertCreated();
|
|
176
|
+
$this->assertDatabaseHas('posts', $data);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('異常: fails with missing title', function () {
|
|
180
|
+
$response = $this->postJson('/api/posts', ['content' => 'Content']);
|
|
181
|
+
|
|
182
|
+
$response->assertUnprocessable()
|
|
183
|
+
->assertJsonValidationErrors(['title']);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('GET /api/posts/{id}', function () {
|
|
188
|
+
it('正常: returns post by id', function () {
|
|
189
|
+
$post = Post::factory()->create();
|
|
190
|
+
|
|
191
|
+
$this->getJson("/api/posts/{$post->id}")
|
|
192
|
+
->assertOk()
|
|
193
|
+
->assertJsonPath('data.id', $post->id);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('異常: returns 404 when not found', function () {
|
|
197
|
+
$this->getJson('/api/posts/99999')
|
|
198
|
+
->assertNotFound();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Run tests:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
./artisan test --filter=PostControllerTest
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
> **Full guide**: [guides/laravel/testing.md](../guides/laravel/testing.md)
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Step 6: OpenAPI Documentation
|
|
214
|
+
|
|
215
|
+
Add to controller:
|
|
216
|
+
|
|
217
|
+
```php
|
|
218
|
+
#[OA\Tag(name: 'Posts', description: 'Post management')]
|
|
219
|
+
class PostController extends Controller
|
|
220
|
+
{
|
|
221
|
+
#[OA\Get(
|
|
222
|
+
path: '/api/posts',
|
|
223
|
+
summary: 'List all posts',
|
|
224
|
+
tags: ['Posts'],
|
|
225
|
+
parameters: [
|
|
226
|
+
new OA\Parameter(ref: '#/components/parameters/QuerySearch'),
|
|
227
|
+
new OA\Parameter(ref: '#/components/parameters/QueryPage'),
|
|
228
|
+
],
|
|
229
|
+
responses: [
|
|
230
|
+
new OA\Response(ref: '#/components/responses/Success', response: 200),
|
|
231
|
+
]
|
|
232
|
+
)]
|
|
233
|
+
public function index() { ... }
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Generate:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
./artisan l5-swagger:generate
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
> **Full guide**: [guides/laravel/openapi.md](../guides/laravel/openapi.md)
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Step 7: Frontend
|
|
248
|
+
|
|
249
|
+
### 7.1 Service
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// services/posts.ts
|
|
253
|
+
export const postService = {
|
|
254
|
+
list: async (params?: PostListParams): Promise<PaginatedResponse<Post>> => {
|
|
255
|
+
const { data } = await api.get('/api/posts', { params });
|
|
256
|
+
return data;
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
create: async (input: PostCreate): Promise<Post> => {
|
|
260
|
+
const { data } = await api.post('/api/posts', input);
|
|
261
|
+
return data.data ?? data;
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
get: async (id: number): Promise<Post> => {
|
|
265
|
+
const { data } = await api.get(`/api/posts/${id}`);
|
|
266
|
+
return data.data ?? data;
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
update: async (id: number, input: PostUpdate): Promise<Post> => {
|
|
270
|
+
const { data } = await api.put(`/api/posts/${id}`, input);
|
|
271
|
+
return data.data ?? data;
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
delete: async (id: number): Promise<void> => {
|
|
275
|
+
await api.delete(`/api/posts/${id}`);
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 7.2 Query Keys
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// lib/queryKeys.ts
|
|
284
|
+
export const queryKeys = {
|
|
285
|
+
posts: {
|
|
286
|
+
all: ['posts'] as const,
|
|
287
|
+
list: (params?: PostListParams) => ['posts', 'list', params] as const,
|
|
288
|
+
detail: (id: number) => ['posts', 'detail', id] as const,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### 7.3 Usage in Component
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// List
|
|
297
|
+
const { data, isLoading } = useQuery({
|
|
298
|
+
queryKey: queryKeys.posts.list(filters),
|
|
299
|
+
queryFn: () => postService.list(filters),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Create
|
|
303
|
+
const createMutation = useMutation({
|
|
304
|
+
mutationFn: postService.create,
|
|
305
|
+
onSuccess: () => {
|
|
306
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.posts.all });
|
|
307
|
+
message.success('Created!');
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
> **Full guide**: [guides/react/README.md](../guides/react/README.md)
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Checklist Summary
|
|
317
|
+
|
|
318
|
+
- [ ] Schema created and generated
|
|
319
|
+
- [ ] Migration verified and run
|
|
320
|
+
- [ ] Model with relationships
|
|
321
|
+
- [ ] Controller (thin, uses FormRequest)
|
|
322
|
+
- [ ] Resource (ISO 8601 dates)
|
|
323
|
+
- [ ] Routes added
|
|
324
|
+
- [ ] Tests written (正常系 + 異常系)
|
|
325
|
+
- [ ] Tests pass
|
|
326
|
+
- [ ] OpenAPI documented
|
|
327
|
+
- [ ] Frontend service + hooks
|