@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.
Files changed (91) hide show
  1. package/README.md +105 -0
  2. package/dist/chunk-RCTEXK7C.js +549 -0
  3. package/dist/chunk-RCTEXK7C.js.map +1 -0
  4. package/dist/config/rules.yaml +524 -0
  5. package/dist/index.cjs +587 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.cts +55 -0
  8. package/dist/index.d.ts +55 -0
  9. package/dist/index.js +26 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/knowledge/agents/architect.md.stub +150 -0
  12. package/dist/knowledge/agents/developer.md.stub +190 -0
  13. package/dist/knowledge/agents/reviewer.md.stub +134 -0
  14. package/dist/knowledge/agents/tester.md.stub +196 -0
  15. package/dist/knowledge/checklists/backend.md.stub +112 -0
  16. package/dist/knowledge/checklists/react.md.stub +108 -0
  17. package/dist/knowledge/claude-rules/laravel-controllers.md.stub +57 -0
  18. package/dist/knowledge/claude-rules/laravel-migrations.md.stub +47 -0
  19. package/dist/knowledge/claude-rules/laravel-tests.md.stub +52 -0
  20. package/dist/knowledge/claude-rules/naming.md.stub +369 -0
  21. package/dist/knowledge/claude-rules/performance.md.stub +256 -0
  22. package/dist/knowledge/claude-rules/php-standards.md.stub +305 -0
  23. package/dist/knowledge/claude-rules/react-components.md.stub +67 -0
  24. package/dist/knowledge/claude-rules/schema-yaml.md.stub +83 -0
  25. package/dist/knowledge/claude-rules/security.md.stub +164 -0
  26. package/dist/knowledge/cursor-rules/antd-deprecations.mdc.stub +62 -0
  27. package/dist/knowledge/cursor-rules/basemodel-readonly.mdc.stub +66 -0
  28. package/dist/knowledge/cursor-rules/baserequest-readonly.mdc.stub +74 -0
  29. package/dist/knowledge/cursor-rules/baseresource-readonly.mdc.stub +78 -0
  30. package/dist/knowledge/cursor-rules/laravel-controller.mdc.stub +421 -0
  31. package/dist/knowledge/cursor-rules/laravel-request.mdc.stub +112 -0
  32. package/dist/knowledge/cursor-rules/laravel-resource.mdc.stub +73 -0
  33. package/dist/knowledge/cursor-rules/laravel-review.mdc.stub +69 -0
  34. package/dist/knowledge/cursor-rules/laravel-testing.mdc.stub +138 -0
  35. package/dist/knowledge/cursor-rules/laravel.mdc.stub +138 -0
  36. package/dist/knowledge/cursor-rules/migrations-workflow.mdc.stub +224 -0
  37. package/dist/knowledge/cursor-rules/model-editable.mdc.stub +120 -0
  38. package/dist/knowledge/cursor-rules/omnify-migrations.mdc.stub +109 -0
  39. package/dist/knowledge/cursor-rules/omnify-schema.mdc.stub +358 -0
  40. package/dist/knowledge/cursor-rules/omnify.mdc.stub +58 -0
  41. package/dist/knowledge/cursor-rules/react-design.mdc.stub +693 -0
  42. package/dist/knowledge/cursor-rules/react-form.mdc.stub +292 -0
  43. package/dist/knowledge/cursor-rules/react-services.mdc.stub +304 -0
  44. package/dist/knowledge/cursor-rules/react.mdc.stub +336 -0
  45. package/dist/knowledge/cursor-rules/request-editable.mdc.stub +111 -0
  46. package/dist/knowledge/cursor-rules/resource-editable.mdc.stub +125 -0
  47. package/dist/knowledge/cursor-rules/schema-create.mdc.stub +440 -0
  48. package/dist/knowledge/cursor-rules/validation-rules.mdc.stub +181 -0
  49. package/dist/knowledge/laravel/README.md.stub +59 -0
  50. package/dist/knowledge/laravel/architecture.md.stub +424 -0
  51. package/dist/knowledge/laravel/authentication.md.stub +588 -0
  52. package/dist/knowledge/laravel/controller.md.stub +484 -0
  53. package/dist/knowledge/laravel/datetime.md.stub +334 -0
  54. package/dist/knowledge/laravel/migrations-team.md.stub +376 -0
  55. package/dist/knowledge/laravel/openapi.md.stub +449 -0
  56. package/dist/knowledge/laravel/request.md.stub +450 -0
  57. package/dist/knowledge/laravel/resource.md.stub +516 -0
  58. package/dist/knowledge/laravel/service.md.stub +503 -0
  59. package/dist/knowledge/laravel/testing.md.stub +1504 -0
  60. package/dist/knowledge/omnify/antdesign-guide.md.stub +401 -0
  61. package/dist/knowledge/omnify/config-guide.md.stub +405 -0
  62. package/dist/knowledge/omnify/japan-guide.md.stub +186 -0
  63. package/dist/knowledge/omnify/laravel-guide.md.stub +61 -0
  64. package/dist/knowledge/omnify/partial-schema-guide.md.stub +353 -0
  65. package/dist/knowledge/omnify/react-form-guide.md.stub +225 -0
  66. package/dist/knowledge/omnify/schema-guide.md.stub +144 -0
  67. package/dist/knowledge/omnify/typescript-guide.md.stub +337 -0
  68. package/dist/knowledge/react/README.md.stub +221 -0
  69. package/dist/knowledge/react/antd-guide.md +528 -0
  70. package/dist/knowledge/react/antd-guide.md.stub +528 -0
  71. package/dist/knowledge/react/checklist.md.stub +108 -0
  72. package/dist/knowledge/react/datetime-guide.md.stub +137 -0
  73. package/dist/knowledge/react/design-philosophy.md.stub +363 -0
  74. package/dist/knowledge/react/i18n-guide.md.stub +211 -0
  75. package/dist/knowledge/react/laravel-integration.md.stub +181 -0
  76. package/dist/knowledge/react/service-pattern.md.stub +180 -0
  77. package/dist/knowledge/react/tanstack-query.md.stub +339 -0
  78. package/dist/knowledge/react/types-guide.md +669 -0
  79. package/dist/knowledge/react/types-guide.md.stub +669 -0
  80. package/dist/knowledge/workflows/bug-fix.md.stub +201 -0
  81. package/dist/knowledge/workflows/code-review.md.stub +164 -0
  82. package/dist/knowledge/workflows/new-feature.md.stub +327 -0
  83. package/dist/plugin-M95GyBll.d.cts +191 -0
  84. package/dist/plugin-M95GyBll.d.ts +191 -0
  85. package/dist/plugin.cjs +573 -0
  86. package/dist/plugin.cjs.map +1 -0
  87. package/dist/plugin.d.cts +2 -0
  88. package/dist/plugin.d.ts +2 -0
  89. package/dist/plugin.js +15 -0
  90. package/dist/plugin.js.map +1 -0
  91. package/package.json +53 -0
@@ -0,0 +1,336 @@
1
+ ---
2
+ description: "React + Ant Design + TanStack Query rules for Omnify projects: component patterns, form validation with Zod, i18n, service layer, and generated types. Apply when creating React components, forms, or API integrations."
3
+ globs: ["{{TYPESCRIPT_BASE}}/**/*.{ts,tsx}"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # React + Ant Design Rules
8
+
9
+ > **Related Rules:**
10
+ > - **Form Development:** `.cursor/rules/omnify/react-form.mdc`
11
+ > - **Design System:** `.cursor/rules/omnify/react-design.mdc`
12
+ > - **Services:** `.cursor/rules/omnify/react-services.mdc`
13
+ > - **Schema Creation:** `.cursor/rules/omnify/schema-create.mdc` (mention `@schema-create` in chat)
14
+
15
+ ## Guide Documentation
16
+
17
+ - Read `.claude/omnify/guides/react/` for patterns
18
+
19
+ ## Critical Rules
20
+
21
+ 1. **Use Ant Design** - Don't recreate existing components
22
+ 2. **Use Omnify types** - Import from `@omnify/schemas`, don't duplicate
23
+ 3. **Use i18n** - Use `useTranslations()` for UI text
24
+ 4. **Service Layer** - API calls in `services/`, not components
25
+ 5. **TanStack Query** - For all server state, no `useState` + `useEffect`
26
+
27
+ ## Quick Patterns
28
+
29
+ ### Service
30
+
31
+ ```typescript
32
+ export const userService = {
33
+ list: (params?: UserListParams) => api.get("/api/users", { params }).then(r => r.data),
34
+ create: (input: UserCreate) => api.post("/api/users", input).then(r => r.data.data),
35
+ };
36
+ ```
37
+
38
+ ### Query
39
+
40
+ ```typescript
41
+ const { data, isLoading } = useQuery({
42
+ queryKey: queryKeys.users.list(filters),
43
+ queryFn: () => userService.list(filters),
44
+ });
45
+ ```
46
+
47
+ ### Mutation
48
+
49
+ ```typescript
50
+ const mutation = useMutation({
51
+ mutationFn: userService.create,
52
+ onSuccess: () => {
53
+ queryClient.invalidateQueries({ queryKey: queryKeys.users.all });
54
+ message.success(t("messages.created"));
55
+ },
56
+ onError: (error) => form.setFields(getFormErrors(error)),
57
+ });
58
+ ```
59
+
60
+ ## Types Rule
61
+
62
+ ```typescript
63
+ // ✅ Use Omnify-generated types
64
+ import type { User, UserCreate } from "@omnify/schemas";
65
+
66
+ // ✅ Only define query params locally
67
+ export interface UserListParams { ... }
68
+
69
+ // ❌ Don't redefine Omnify types
70
+ export interface UserCreateInput { ... } // WRONG
71
+ ```
72
+
73
+ ## ⚠️ Enum Usage Rules (CRITICAL)
74
+
75
+ **ALWAYS use generated Enums** - NEVER inline union types or type extractions!
76
+
77
+ ### ❌ BAD Patterns (FORBIDDEN)
78
+
79
+ ```typescript
80
+ // ❌ Complex type extraction - FORBIDDEN!
81
+ newFilter.approval_status = status as NonNullable<ListParams["filter"]>["approval_status"];
82
+
83
+ // ❌ Hardcoded union type - NOT DRY!
84
+ newFilter.approval_status = status as "pending" | "approved" | "rejected";
85
+
86
+ // ❌ Using string type - NO TYPE SAFETY!
87
+ const [status, setStatus] = useState<string>("");
88
+
89
+ // ❌ Hardcoded comparisons
90
+ if (status === "pending") { ... }
91
+ ```
92
+
93
+ ### ✅ GOOD Patterns (REQUIRED)
94
+
95
+ ```typescript
96
+ // ✅ Import Enum from generated file
97
+ import {
98
+ PostStatus,
99
+ PostStatusValues,
100
+ isPostStatus,
101
+ getPostStatusLabel
102
+ } from "@omnify/enum/PostStatus";
103
+
104
+ // ✅ Type assertions with Enum
105
+ newFilter.approval_status = status as PostStatus;
106
+
107
+ // ✅ State with Enum type
108
+ const [status, setStatus] = useState<PostStatus | "">("");
109
+ // OR with default value:
110
+ const [status, setStatus] = useState<PostStatus>(PostStatus.Pending);
111
+
112
+ // ✅ Enum comparisons
113
+ if (status === PostStatus.Pending) { ... }
114
+
115
+ // ✅ Iterate with Values array
116
+ const options = PostStatusValues.map(value => ({
117
+ value,
118
+ label: getPostStatusLabel(value, locale)
119
+ }));
120
+
121
+ // ✅ Type guard for validation
122
+ if (isPostStatus(unknownValue)) {
123
+ // unknownValue is PostStatus here
124
+ }
125
+ ```
126
+
127
+ ### Generated Enum Files Structure
128
+
129
+ ```
130
+ @omnify/enum/
131
+ ├── PostStatus.ts
132
+ │ ├── PostStatus (enum)
133
+ │ ├── PostStatusValues (array of all values)
134
+ │ ├── isPostStatus() (type guard)
135
+ │ └── getPostStatusLabel() (i18n label)
136
+ ├── OrderStatus.ts
137
+ └── ...
138
+ ```
139
+
140
+ ### Select/Filter Options Pattern
141
+
142
+ ```typescript
143
+ import { PostStatus, PostStatusValues, getPostStatusLabel } from "@omnify/enum/PostStatus";
144
+
145
+ // ✅ Build options from Enum
146
+ const statusOptions = PostStatusValues.map(value => ({
147
+ value,
148
+ label: getPostStatusLabel(value, locale)
149
+ }));
150
+
151
+ <Select options={statusOptions} />
152
+ ```
153
+
154
+ ## Form Pattern (Omnify)
155
+
156
+ > **Detailed Guide:** See `react-form.mdc` for complete form development guide.
157
+
158
+ **Quick Reference:**
159
+
160
+ ```typescript
161
+ // 1. Imports
162
+ import { zodRule, setZodLocale } from '@/omnify/lib';
163
+ import { OmnifyForm } from '@/omnify/components';
164
+ import { customerSchemas, customerI18n, getCustomerFieldLabel } from '@/omnify/schemas';
165
+
166
+ // 2. Helper functions (MUST define in every form)
167
+ const LOCALE = 'ja';
168
+ const label = (key: string) => getCustomerFieldLabel(key, LOCALE);
169
+ const rule = (key: keyof typeof customerSchemas) => zodRule(customerSchemas[key], label(key));
170
+
171
+ // 3. Form.Item pattern
172
+ <Form.Item name="email" label={label('email')} rules={[rule('email')]}>
173
+ <Input />
174
+ </Form.Item>
175
+
176
+ // 4. Japanese compound fields
177
+ <OmnifyForm.JapaneseName schemas={customerSchemas} i18n={customerI18n} prefix="name" />
178
+ <OmnifyForm.JapaneseAddress form={form} schemas={customerSchemas} i18n={customerI18n} prefix="address" />
179
+ ```
180
+
181
+ ## File Location
182
+
183
+ | What | Where |
184
+ | ----------------------- | ------------------------------------------- |
185
+ | Component (1 feature) | `features/{feature}/` |
186
+ | Component (2+ features) | `components/common/` |
187
+ | Service (API) | `services/` (ALWAYS) |
188
+ | Hook (1 feature) | `features/{feature}/` |
189
+ | Hook (2+ features) | `hooks/` |
190
+ | Zod Schema | `schemas/{model}.ts` |
191
+ | Validation utils | `lib/form-validation.ts`, `lib/zod-i18n.ts` |
192
+
193
+ ## Ant Design v6 Deprecated Props
194
+
195
+ | Deprecated | Use Instead |
196
+ | -------------------------- | ----------------------- |
197
+ | `visible` | `open` |
198
+ | `direction` (Space) | `orientation` |
199
+ | `dropdownMatchSelectWidth` | `popupMatchSelectWidth` |
200
+
201
+ ## Ant Design Static Method Warning
202
+
203
+ ⚠️ **Warning:** `Static function can not consume context like dynamic theme`
204
+
205
+ ```typescript
206
+ // ❌ Wrong - Static import has no context
207
+ import { message } from "antd";
208
+ message.success("Done"); // Warning!
209
+
210
+ // ✅ Correct - Use App.useApp() hook
211
+ import { App } from "antd";
212
+
213
+ function MyComponent() {
214
+ const { message, notification, modal } = App.useApp();
215
+ message.success("Done"); // ✅ No warning
216
+ }
217
+ ```
218
+
219
+ **Note:**
220
+ - `App` component is already wrapped in `AntdThemeProvider`
221
+ - Just use `App.useApp()` in your component/hook
222
+ - Applies to: `message`, `notification`, `modal`
223
+
224
+ ## Common Mistakes
225
+
226
+ ```typescript
227
+ // ❌ Wrong
228
+ useEffect(() => { fetchData() }, []); // Use useQuery
229
+ const [users, setUsers] = useState([]); // Use TanStack for server state
230
+ <Button>Save</Button> // Use i18n
231
+
232
+ // ✅ Correct
233
+ const { data } = useQuery({ queryKey, queryFn });
234
+ <Button>{t("common.save")}</Button>
235
+ ```
236
+
237
+ ### Form Validation Mistakes
238
+
239
+ ```typescript
240
+ // ❌ Wrong - Hardcoded message in schema
241
+ z.string().min(1, "Name is required")
242
+
243
+ // ❌ Wrong - Define schema in component
244
+ const schema = z.object({ name: z.string() });
245
+
246
+ // ❌ Wrong - No field label comment
247
+ <Form.Item name="name" label={label("name")}>
248
+
249
+ // ✅ Correct - Schema in schemas/, messages in i18n/
250
+ import { userSchemas } from "@/schemas/user";
251
+ {/* Name */}
252
+ <Form.Item name="name" label={label("name")} rules={[zodRule(userSchemas.name, label("name"))]}>
253
+ ```
254
+
255
+ ### Form.useForm() Warning
256
+
257
+ ⚠️ **Warning:** `Instance created by useForm is not connected to any Form element`
258
+
259
+ ```typescript
260
+ // ❌ Wrong - Export hook creates unused form instance
261
+ export function useUserForm() {
262
+ return Form.useForm(); // Instance not connected to any Form!
263
+ }
264
+
265
+ // ❌ Wrong - Create form instance but don't pass to Form
266
+ const [form] = Form.useForm();
267
+ return <Form>...</Form> // Missing form={form}
268
+
269
+ // ✅ Correct - Form instance must connect to Form
270
+ const [form] = Form.useForm();
271
+ return <Form form={form}>...</Form>
272
+ ```
273
+
274
+ **Rule:** Each `Form.useForm()` must have exactly one corresponding `<Form form={form}>`.
275
+
276
+ ### Backend Validation Errors (422)
277
+
278
+ ⚠️ **IMPORTANT:** Form must display errors from backend!
279
+
280
+ **Form component MUST receive `form` prop from parent:**
281
+
282
+ ```typescript
283
+ // Form component
284
+ interface UserFormProps {
285
+ form: FormInstance; // ← REQUIRED
286
+ onSubmit: (values: UserCreate) => void;
287
+ // ...
288
+ }
289
+
290
+ export function UserForm({ form, onSubmit, ... }: UserFormProps) {
291
+ return (
292
+ <Form form={form} onFinish={onSubmit}>
293
+ ...
294
+ </Form>
295
+ );
296
+ }
297
+ ```
298
+
299
+ **Page creates form and handles backend errors:**
300
+
301
+ ```typescript
302
+ // Page component
303
+ export default function NewUserPage() {
304
+ const [form] = Form.useForm(); // ← Create in parent
305
+
306
+ const mutation = useMutation({
307
+ mutationFn: userService.create,
308
+ onSuccess: () => { ... },
309
+ onError: (error) => {
310
+ form.setFields(getFormErrors(error)); // ← Set errors from backend
311
+ },
312
+ });
313
+
314
+ return (
315
+ <UserForm
316
+ form={form} // ← Pass to form component
317
+ onSubmit={(values) => mutation.mutate(values)}
318
+ />
319
+ );
320
+ }
321
+ ```
322
+
323
+ **Helper `getFormErrors`** (available in `lib/api.ts`):
324
+
325
+ ```typescript
326
+ import { getFormErrors } from "@/lib/api";
327
+
328
+ // Converts Laravel 422 response to Ant Design format:
329
+ // { errors: { email: ["Email already exists"] } }
330
+ // → [{ name: "email", errors: ["Email already exists"] }]
331
+ ```
332
+
333
+ **Rules:**
334
+ 1. Form component does NOT create `Form.useForm()` - receives from parent
335
+ 2. Page creates form instance and passes down
336
+ 3. `onError` calls `form.setFields(getFormErrors(error))`
@@ -0,0 +1,111 @@
1
+ ---
2
+ description: "User-editable form requests. MUST read corresponding BaseRequest before editing to avoid duplicating auto-generated validation rules."
3
+ globs: ["{{LARAVEL_BASE}}/Http/Requests/*.php"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Request Editing Rules
8
+
9
+ These are **user-editable** request files that extend auto-generated BaseRequests.
10
+
11
+ ## ⚠️ BEFORE EDITING - MANDATORY STEPS
12
+
13
+ ### 1. Read the BaseRequest First
14
+
15
+ Before editing `PostStoreRequest.php`, you **MUST** read `OmnifyBase/PostStoreRequestBase.php` to understand:
16
+ - What validation rules already exist
17
+ - What messages are defined
18
+ - What attributes are set
19
+
20
+ ### 2. Check for Existing Rules
21
+
22
+ If a rule exists in BaseRequest, **DO NOT** duplicate it here.
23
+
24
+ ## ❌ NEVER DUPLICATE
25
+
26
+ ```php
27
+ // These are ALREADY in BaseRequest - NEVER recreate!
28
+ 'title' => ['required', 'string', 'max:255'], // ❌ From schema
29
+ 'email' => ['required', 'email'], // ❌ From schema
30
+ 'status' => ['required', 'in:draft,published'], // ❌ From enum
31
+ ```
32
+
33
+ ## ✅ ONLY ADD Custom Validation
34
+
35
+ ```php
36
+ class PostStoreRequest extends PostStoreRequestBase
37
+ {
38
+ /**
39
+ * Merge custom rules with base rules
40
+ */
41
+ public function rules(): array
42
+ {
43
+ return array_merge(parent::rules(), [
44
+ // ✅ Unique constraint (database-specific)
45
+ 'slug' => ['unique:posts,slug'],
46
+
47
+ // ✅ Complex business rules
48
+ 'published_at' => ['required_if:status,published'],
49
+
50
+ // ✅ Custom format validation
51
+ 'featured_image' => ['url', 'regex:/\.(jpg|png|webp)$/i'],
52
+ ]);
53
+ }
54
+
55
+ /**
56
+ * Add custom validation logic
57
+ */
58
+ public function withValidator($validator): void
59
+ {
60
+ $validator->after(function ($validator) {
61
+ // ✅ Cross-field validation
62
+ if ($this->status === 'published' && empty($this->content)) {
63
+ $validator->errors()->add('content', 'Content is required for published posts.');
64
+ }
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Custom authorization logic
70
+ */
71
+ public function authorize(): bool
72
+ {
73
+ // ✅ Add authorization check
74
+ return $this->user()->can('create', Post::class);
75
+ }
76
+
77
+ /**
78
+ * Prepare data before validation
79
+ */
80
+ protected function prepareForValidation(): void
81
+ {
82
+ // ✅ Data transformation
83
+ $this->merge([
84
+ 'slug' => Str::slug($this->title),
85
+ ]);
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## For Update Requests
91
+
92
+ ```php
93
+ class PostUpdateRequest extends PostUpdateRequestBase
94
+ {
95
+ public function rules(): array
96
+ {
97
+ return array_merge(parent::rules(), [
98
+ // ✅ Ignore current record for unique check
99
+ 'slug' => ['unique:posts,slug,' . $this->route('post')],
100
+ ]);
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Quick Checklist Before Saving
106
+
107
+ - [ ] Read `OmnifyBase/{Request}Base.php` first
108
+ - [ ] Not duplicating validation rules from BaseRequest
109
+ - [ ] Using `array_merge(parent::rules(), [...])` pattern
110
+ - [ ] Only adding custom/complex validation
111
+ - [ ] Using `parent::authorize()` if BaseRequest has authorization
@@ -0,0 +1,125 @@
1
+ ---
2
+ description: "User-editable API resources. MUST read corresponding BaseResource before editing to avoid duplicating auto-generated field mappings."
3
+ globs: ["{{LARAVEL_BASE}}/Http/Resources/*.php"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Resource Editing Rules
8
+
9
+ These are **user-editable** resource files that extend auto-generated BaseResources.
10
+
11
+ ## ⚠️ BEFORE EDITING - MANDATORY STEPS
12
+
13
+ ### 1. Read the BaseResource First
14
+
15
+ Before editing `PostResource.php`, you **MUST** read `OmnifyBase/PostResourceBase.php` to understand:
16
+ - What fields are already mapped
17
+ - What relations are included
18
+ - How timestamps are formatted
19
+
20
+ ### 2. Check for Existing Fields
21
+
22
+ If a field exists in BaseResource, **DO NOT** duplicate it here.
23
+
24
+ ## ❌ NEVER DUPLICATE
25
+
26
+ ```php
27
+ // These are ALREADY in BaseResource - NEVER recreate!
28
+ 'id' => $this->id, // ❌ From schema
29
+ 'title' => $this->title, // ❌ From schema
30
+ 'author' => UserResource::make(...), // ❌ From association
31
+ 'created_at' => $this->created_at, // ❌ Auto-included
32
+ ```
33
+
34
+ ## ✅ ONLY ADD Custom Fields
35
+
36
+ ```php
37
+ class PostResource extends PostResourceBase
38
+ {
39
+ public function toArray($request): array
40
+ {
41
+ return array_merge(parent::toArray($request), [
42
+ // ✅ Computed fields
43
+ 'read_time' => $this->getReadTimeInMinutes(),
44
+ 'word_count' => str_word_count($this->content ?? ''),
45
+
46
+ // ✅ Derived boolean flags
47
+ 'is_published' => $this->status === 'published',
48
+ 'is_featured' => !empty($this->featured_image),
49
+
50
+ // ✅ Formatted data
51
+ 'excerpt_html' => Str::markdown($this->excerpt ?? ''),
52
+
53
+ // ✅ Conditional fields
54
+ 'edit_url' => $this->when(
55
+ $request->user()?->can('update', $this->resource),
56
+ route('posts.edit', $this->id)
57
+ ),
58
+
59
+ // ✅ Aggregated data
60
+ 'comments_count' => $this->whenCounted('comments'),
61
+
62
+ // ✅ Custom relations not in schema
63
+ 'related_posts' => PostResource::collection(
64
+ $this->whenLoaded('relatedPosts')
65
+ ),
66
+ ]);
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## Common Patterns
72
+
73
+ ### Conditional Fields Based on User
74
+
75
+ ```php
76
+ return array_merge(parent::toArray($request), [
77
+ // Only show to admins
78
+ 'internal_notes' => $this->when(
79
+ $request->user()?->isAdmin(),
80
+ $this->internal_notes
81
+ ),
82
+ ]);
83
+ ```
84
+
85
+ ### Nested Resource Customization
86
+
87
+ ```php
88
+ return array_merge(parent::toArray($request), [
89
+ // Override relation resource
90
+ 'author' => $this->whenLoaded('author', function () {
91
+ return [
92
+ 'id' => $this->author->id,
93
+ 'name' => $this->author->name,
94
+ // Custom: only include essential fields
95
+ ];
96
+ }),
97
+ ]);
98
+ ```
99
+
100
+ ### Collection Resources
101
+
102
+ ```php
103
+ class PostCollection extends ResourceCollection
104
+ {
105
+ public function toArray($request): array
106
+ {
107
+ return [
108
+ 'data' => $this->collection,
109
+ // ✅ Add metadata
110
+ 'meta' => [
111
+ 'total_published' => $this->collection->where('status', 'published')->count(),
112
+ ],
113
+ ];
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Quick Checklist Before Saving
119
+
120
+ - [ ] Read `OmnifyBase/{Resource}Base.php` first
121
+ - [ ] Not duplicating fields from BaseResource
122
+ - [ ] Using `array_merge(parent::toArray($request), [...])` pattern
123
+ - [ ] Only adding computed/custom fields
124
+ - [ ] Using `$this->when()` for conditional fields
125
+ - [ ] Using `$this->whenLoaded()` for optional relations