@famgia/omnify-laravel 0.0.125 → 0.0.127

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/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  laravelPlugin
3
- } from "./chunk-C3AGVZB4.js";
3
+ } from "./chunk-IIA2I36W.js";
4
4
  export {
5
5
  laravelPlugin as default,
6
6
  laravelPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@famgia/omnify-laravel",
3
- "version": "0.0.125",
3
+ "version": "0.0.127",
4
4
  "description": "Laravel migration and TypeScript type generator for omnify-schema",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,9 +25,9 @@
25
25
  "README.md"
26
26
  ],
27
27
  "dependencies": {
28
- "@famgia/omnify-types": "0.0.114",
29
- "@famgia/omnify-core": "0.0.116",
30
- "@famgia/omnify-atlas": "0.0.110"
28
+ "@famgia/omnify-types": "0.0.116",
29
+ "@famgia/omnify-atlas": "0.0.112",
30
+ "@famgia/omnify-core": "0.0.118"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsup",
@@ -41,6 +41,7 @@
41
41
  ├── rules/ # Must-follow rules (WHAT)
42
42
  │ ├── security.md # Security rules
43
43
  │ ├── performance.md # Performance & quality rules
44
+ │ ├── php-standards.md # PHP 8+ standards & best practices
44
45
  │ └── naming.md # Naming conventions
45
46
 
46
47
  ├── guides/ # Implementation guides (REFERENCE)
@@ -67,6 +67,35 @@ propertyName:
67
67
  fillable: false # Exclude from mass assignment
68
68
  ```
69
69
 
70
+ ## ⚠️ Default Value Rules
71
+
72
+ **DB functions NOT allowed in default:**
73
+
74
+ ```yaml
75
+ # ❌ WRONG
76
+ failed_at:
77
+ type: Timestamp
78
+ default: CURRENT_TIMESTAMP # ERROR!
79
+
80
+ # ✅ CORRECT
81
+ failed_at:
82
+ type: Timestamp
83
+ useCurrent: true # = CURRENT_TIMESTAMP
84
+
85
+ updated_at:
86
+ type: Timestamp
87
+ useCurrent: true
88
+ useCurrentOnUpdate: true # ON UPDATE CURRENT_TIMESTAMP
89
+ ```
90
+
91
+ | Type | Valid Default | Invalid |
92
+ |------|---------------|---------|
93
+ | Int | 0, 100, -1 | AUTO_INCREMENT, RAND() |
94
+ | Date | 2024-01-01 | CURRENT_DATE |
95
+ | Time | 14:30:00 | CURRENT_TIME |
96
+ | Timestamp | use `useCurrent` | CURRENT_TIMESTAMP |
97
+ | Uuid | static UUID | UUID() |
98
+
70
99
  ## Associations
71
100
 
72
101
  ```yaml
@@ -0,0 +1,305 @@
1
+ ---
2
+ paths:
3
+ - "{{LARAVEL_ROOT}}app/**/*.php"
4
+ ---
5
+
6
+ # PHP Standards & Best Practices
7
+
8
+ > **PHP 8+ features** and coding standards for Laravel projects.
9
+
10
+ ## 🔵 Strict Types
11
+
12
+ **Always declare strict types at the top of PHP files.**
13
+
14
+ ```php
15
+ <?php
16
+
17
+ declare(strict_types=1);
18
+
19
+ namespace App\Services;
20
+
21
+ // ... class definition
22
+ ```
23
+
24
+ | Rule | Description |
25
+ | ---- | ----------- |
26
+ | `declare(strict_types=1)` | Required in all PHP files |
27
+ | Place at very top | Before namespace declaration |
28
+
29
+ ---
30
+
31
+ ## 🔵 Constructor Property Promotion (PHP 8.0+)
32
+
33
+ **Use constructor promotion for dependency injection.**
34
+
35
+ ```php
36
+ // ❌ OLD STYLE: Verbose
37
+ class UserService
38
+ {
39
+ private UserRepository $repository;
40
+ private CacheManager $cache;
41
+
42
+ public function __construct(UserRepository $repository, CacheManager $cache)
43
+ {
44
+ $this->repository = $repository;
45
+ $this->cache = $cache;
46
+ }
47
+ }
48
+
49
+ // ✅ PHP 8.0+: Constructor promotion
50
+ class UserService
51
+ {
52
+ public function __construct(
53
+ private readonly UserRepository $repository,
54
+ private readonly CacheManager $cache
55
+ ) {}
56
+ }
57
+ ```
58
+
59
+ | Modifier | Usage |
60
+ | -------- | ----- |
61
+ | `private readonly` | Immutable dependencies (preferred) |
62
+ | `private` | Mutable internal state |
63
+ | `protected readonly` | For extension by subclasses |
64
+
65
+ ---
66
+
67
+ ## 🔵 PHP 8+ Features
68
+
69
+ ### Named Arguments
70
+
71
+ ```php
72
+ // ✅ Clear intent
73
+ $this->checkEmailLockout(
74
+ email: $request->email,
75
+ checkOnly: true
76
+ );
77
+
78
+ // Use when function has many optional parameters
79
+ ```
80
+
81
+ ### Nullsafe Operator
82
+
83
+ ```php
84
+ // ❌ OLD STYLE
85
+ $country = $user->address ? ($user->address->country ? $user->address->country->name : null) : null;
86
+
87
+ // ✅ PHP 8.0+: Nullsafe operator
88
+ $country = $user->address?->country?->name;
89
+ ```
90
+
91
+ ### Union Types
92
+
93
+ ```php
94
+ public function process(int|string $id): Response|RedirectResponse
95
+ {
96
+ // ...
97
+ }
98
+ ```
99
+
100
+ ### Match Expression
101
+
102
+ ```php
103
+ // ❌ OLD: switch statement
104
+ switch ($status) {
105
+ case 'active': return 'Active';
106
+ case 'inactive': return 'Inactive';
107
+ default: return 'Unknown';
108
+ }
109
+
110
+ // ✅ PHP 8.0+: match expression
111
+ return match($status) {
112
+ 'active' => 'Active',
113
+ 'inactive' => 'Inactive',
114
+ default => 'Unknown',
115
+ };
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 🔵 Constants Organization
121
+
122
+ **Use dedicated Constants classes instead of magic values.**
123
+
124
+ ```php
125
+ // ❌ BAD: Magic strings scattered in code
126
+ if ($user->role === 'admin') { ... }
127
+ Cache::get('user:' . $id);
128
+ session()->get('2fa:user_id');
129
+
130
+ // ✅ GOOD: Constants in dedicated class
131
+ namespace App\Constants;
132
+
133
+ class TwoFactorConstants
134
+ {
135
+ public const ENABLED = true;
136
+ public const CODE_LENGTH = 6;
137
+ public const EXPIRY_MINUTES = 5;
138
+
139
+ // Session keys
140
+ public const SESSION_USER_ID = '2fa:user_id';
141
+ public const SESSION_EMAIL = '2fa:email';
142
+ }
143
+
144
+ // Usage
145
+ use App\Constants\TwoFactorConstants;
146
+
147
+ if (TwoFactorConstants::ENABLED) {
148
+ $code = Str::random(TwoFactorConstants::CODE_LENGTH);
149
+ session()->put(TwoFactorConstants::SESSION_USER_ID, $user->id);
150
+ }
151
+ ```
152
+
153
+ | Location | Purpose |
154
+ | -------- | ------- |
155
+ | `app/Constants/` | Application-wide constants |
156
+ | Class constants | Domain-specific constants |
157
+ | `config/*.php` | Environment-dependent values |
158
+
159
+ ---
160
+
161
+ ## 🔵 PHPDoc Standards
162
+
163
+ ### Method Documentation
164
+
165
+ ```php
166
+ /**
167
+ * Get user's authorization for a service.
168
+ *
169
+ * @param User $user The user to check
170
+ * @param Service $service The service being accessed
171
+ *
172
+ * @return array{
173
+ * organization_id: int,
174
+ * organization_slug: string,
175
+ * role: string,
176
+ * level: int
177
+ * }|null Returns null if user has no access
178
+ *
179
+ * @throws AuthorizationException When service is inactive
180
+ */
181
+ public function getUserAuthorization(User $user, Service $service): ?array
182
+ {
183
+ // ...
184
+ }
185
+ ```
186
+
187
+ ### Array Shape Documentation
188
+
189
+ ```php
190
+ /**
191
+ * @return array{
192
+ * id: int,
193
+ * name: string,
194
+ * email: string,
195
+ * roles: string[]
196
+ * }
197
+ */
198
+ public function toArray(): array
199
+ ```
200
+
201
+ ### When to Document
202
+
203
+ | Document | Don't Document |
204
+ | -------- | -------------- |
205
+ | Complex return types | Obvious getters/setters |
206
+ | Array shapes | Simple CRUD methods |
207
+ | Exception conditions | Self-explanatory code |
208
+ | Business logic | Framework conventions |
209
+
210
+ ---
211
+
212
+ ## 🔵 Code Complexity Limits
213
+
214
+ ### Method Guidelines
215
+
216
+ | Metric | Limit | Action if exceeded |
217
+ | ------ | ----- | ------------------ |
218
+ | Lines per method | Max 50 | Extract to helper/service |
219
+ | Parameters | Max 4 | Use DTO or config object |
220
+ | Cyclomatic complexity | Max 10 | Split into smaller methods |
221
+ | Nesting depth | Max 3 | Early return pattern |
222
+
223
+ ### Class Guidelines
224
+
225
+ | Metric | Limit | Action if exceeded |
226
+ | ------ | ----- | ------------------ |
227
+ | Public methods | Max 10 | Split class (SRP violation) |
228
+ | Dependencies | Max 5 | Facade or aggregate service |
229
+ | Lines per class | Max 500 | Extract sub-classes |
230
+
231
+ ### Early Return Pattern
232
+
233
+ ```php
234
+ // ❌ BAD: Deep nesting
235
+ public function process($user)
236
+ {
237
+ if ($user) {
238
+ if ($user->isActive()) {
239
+ if ($user->hasPermission('edit')) {
240
+ // do something
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ // ✅ GOOD: Early returns
247
+ public function process($user)
248
+ {
249
+ if (!$user) {
250
+ return;
251
+ }
252
+
253
+ if (!$user->isActive()) {
254
+ return;
255
+ }
256
+
257
+ if (!$user->hasPermission('edit')) {
258
+ return;
259
+ }
260
+
261
+ // do something
262
+ }
263
+ ```
264
+
265
+ ---
266
+
267
+ ## 🔵 Interface & Trait Naming
268
+
269
+ ```php
270
+ // Interfaces: suffix with Interface
271
+ interface PaymentGatewayInterface
272
+ {
273
+ public function charge(int $amount): PaymentResult;
274
+ }
275
+
276
+ // Traits: suffix with Trait (optional but recommended)
277
+ trait HasApiTokens
278
+ {
279
+ // ...
280
+ }
281
+
282
+ // Or descriptive name without suffix
283
+ trait Searchable
284
+ {
285
+ public function scopeSearch(Builder $query, string $term): Builder
286
+ {
287
+ // ...
288
+ }
289
+ }
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Quick Reference
295
+
296
+ | Category | Rule |
297
+ | -------- | ---- |
298
+ | **Strict** | `declare(strict_types=1)` at top |
299
+ | **Constructor** | Use property promotion with `readonly` |
300
+ | **Nullsafe** | `$obj?->prop?->value` instead of null checks |
301
+ | **Match** | Prefer `match` over `switch` |
302
+ | **Constants** | Use `App\Constants\*` classes |
303
+ | **Complexity** | Max 50 lines/method, 4 params, 10 complexity |
304
+ | **Nesting** | Max 3 levels, use early returns |
305
+ | **PHPDoc** | Document complex types and exceptions |
@@ -46,6 +46,20 @@ default: "'cloud'"
46
46
  default: cloud
47
47
  ```
48
48
 
49
+ ## ⚠️ CRITICAL: DB Functions NOT Allowed
50
+
51
+ ```yaml
52
+ # ❌ WRONG - DB functions
53
+ failed_at:
54
+ type: Timestamp
55
+ default: CURRENT_TIMESTAMP # ERROR!
56
+
57
+ # ✅ CORRECT - use useCurrent
58
+ failed_at:
59
+ type: Timestamp
60
+ useCurrent: true # = CURRENT_TIMESTAMP
61
+ ```
62
+
49
63
  ## Property Types
50
64
 
51
65
  | Type | SQL | TypeScript |
@@ -235,6 +235,25 @@ category:
235
235
  relation: belongsTo # belongsTo, hasMany, hasOne, belongsToMany
236
236
  ```
237
237
 
238
+ ### ⚠️ CRITICAL: DB Functions NOT Allowed in Default
239
+
240
+ ```yaml
241
+ # ❌ WRONG - DB functions are NOT allowed
242
+ failed_at:
243
+ type: Timestamp
244
+ default: CURRENT_TIMESTAMP # ERROR!
245
+
246
+ # ✅ CORRECT - Use useCurrent option
247
+ failed_at:
248
+ type: Timestamp
249
+ useCurrent: true # Equivalent to CURRENT_TIMESTAMP
250
+
251
+ updated_at:
252
+ type: Timestamp
253
+ useCurrent: true
254
+ useCurrentOnUpdate: true # ON UPDATE CURRENT_TIMESTAMP
255
+ ```
256
+
238
257
  ---
239
258
 
240
259
  ## Step 5: Run Migration 🗄️
@@ -235,6 +235,35 @@ category:
235
235
  relation: belongsTo # belongsTo, hasMany, hasOne, belongsToMany
236
236
  ```
237
237
 
238
+ ### ⚠️ CRITICAL: DB Functions NOT Allowed in Default
239
+
240
+ ```yaml
241
+ # ❌ WRONG - DB functions are NOT allowed
242
+ failed_at:
243
+ type: Timestamp
244
+ default: CURRENT_TIMESTAMP # ERROR!
245
+
246
+ # ✅ CORRECT - Use useCurrent option
247
+ failed_at:
248
+ type: Timestamp
249
+ useCurrent: true # Equivalent to CURRENT_TIMESTAMP
250
+
251
+ updated_at:
252
+ type: Timestamp
253
+ useCurrent: true
254
+ useCurrentOnUpdate: true # ON UPDATE CURRENT_TIMESTAMP
255
+
256
+ # ❌ WRONG - UUID() function
257
+ external_id:
258
+ type: Uuid
259
+ default: UUID() # ERROR!
260
+
261
+ # ✅ CORRECT - Static value or nullable
262
+ external_id:
263
+ type: Uuid
264
+ nullable: true # Let application generate
265
+ ```
266
+
238
267
  ---
239
268
 
240
269
  ## Step 5: Run Migration 🗄️
@@ -342,3 +371,76 @@ status:
342
371
  status:
343
372
  type: PostStatus # Reference enum schema
344
373
  ```
374
+
375
+ ---
376
+
377
+ ## 🔌 Partial Schema (External Package Connection)
378
+
379
+ When referencing schemas from external packages that aren't loaded:
380
+
381
+ ```yaml
382
+ # schemas/stubs/User.yaml
383
+ # Create stub to connect to external User schema
384
+ name: User
385
+ kind: partial
386
+ target: User # Same name = standalone schema
387
+
388
+ properties:
389
+ email:
390
+ type: Email
391
+ displayName:
392
+ ja: メール
393
+ en: Email
394
+ ```
395
+
396
+ Now you can use `User` in relationships:
397
+
398
+ ```yaml
399
+ # schemas/blog/Post.yaml
400
+ author:
401
+ type: User # ✅ Works! User exists as partial
402
+ relation: belongsTo
403
+ ```
404
+
405
+ ### Extend Package Schema
406
+
407
+ ```yaml
408
+ # schemas/extensions/User.yaml
409
+ name: User
410
+ kind: partial
411
+ target: User # Extend the User schema
412
+
413
+ properties:
414
+ # Add custom properties
415
+ avatar_url:
416
+ type: String
417
+ nullable: true
418
+ ```
419
+
420
+ ---
421
+
422
+ ## 📦 Register Schemas from Laravel Package
423
+
424
+ In your package's ServiceProvider:
425
+
426
+ ```php
427
+ use App\Support\Omnify;
428
+
429
+ public function boot(): void
430
+ {
431
+ if (class_exists(Omnify::class)) {
432
+ Omnify::registerSchemaPath(
433
+ path: __DIR__ . '/../../schemas',
434
+ namespace: 'your-package'
435
+ );
436
+ }
437
+ }
438
+ ```
439
+
440
+ Export paths before generate:
441
+
442
+ ```bash
443
+ php artisan omnify:sync
444
+ # Then
445
+ npx omnify generate
446
+ ```