@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.
- package/dist/{chunk-YVVAJA3T.js → chunk-2QSKZS63.js} +188 -12
- package/dist/chunk-2QSKZS63.js.map +1 -0
- package/dist/index.cjs +190 -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 +186 -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/README.md.stub +95 -0
- 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/react-form-guide.md.stub +259 -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/cursor/omnify.mdc.stub +58 -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,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 `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
|
+
// app/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
|
+
// app/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
|
+
// app/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
|