@omnifyjp/ts 6.7.13 → 6.7.14
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/php/enum-generator.js +6 -3
- package/dist/php/model-generator.js +2 -1
- package/dist/php/relation-builder.js +5 -0
- package/dist/php/request-generator.js +55 -21
- package/dist/php/resource-generator.js +1 -0
- package/dist/php/service-generator.js +14 -12
- package/dist/php/trait-generator.js +3 -3
- package/dist/php/type-mapper.js +1 -1
- package/package.json +1 -1
|
@@ -32,6 +32,10 @@ function generateEnumClass(name, schema, config) {
|
|
|
32
32
|
const values = (schema.values ?? []);
|
|
33
33
|
const cases = values.map(v => buildCase(v));
|
|
34
34
|
const labelsConst = buildLabelsConstant(values);
|
|
35
|
+
const allValuesHaveLabels = values.every((value) => typeof value.label === 'string' || Object.keys(value.label ?? {}).length > 0);
|
|
36
|
+
const labelThirdFallback = allValuesHaveLabels
|
|
37
|
+
? '$labels[array_key_first($labels)]'
|
|
38
|
+
: '(array_values($labels)[0] ?? $this->value)';
|
|
35
39
|
const content = `<?php
|
|
36
40
|
|
|
37
41
|
namespace ${namespace};
|
|
@@ -59,12 +63,11 @@ ${labelsConst}
|
|
|
59
63
|
public function label(?string $locale = null): string
|
|
60
64
|
{
|
|
61
65
|
$locale = $locale ?? app()->getLocale();
|
|
62
|
-
$labels = self::LABELS[$this->value]
|
|
66
|
+
$labels = self::LABELS[$this->value];
|
|
63
67
|
|
|
64
68
|
return $labels[$locale]
|
|
65
69
|
?? $labels[config('app.fallback_locale', 'en')]
|
|
66
|
-
?? $
|
|
67
|
-
?? $this->value;
|
|
70
|
+
?? ${labelThirdFallback};
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -451,7 +451,7 @@ function generateUserModel(name, schema, reader, config, hasNestedSet = false) {
|
|
|
451
451
|
if (hasNestedSet) {
|
|
452
452
|
imports.push(`use ${nestedSetNamespace}\\NodeTrait;`);
|
|
453
453
|
}
|
|
454
|
-
const traits = [
|
|
454
|
+
const traits = [` /** @use HasFactory<${modelName}Factory> */\n use HasFactory;`];
|
|
455
455
|
if (hasNestedSet) {
|
|
456
456
|
traits.push(' use NodeTrait;');
|
|
457
457
|
}
|
|
@@ -1054,6 +1054,7 @@ function buildInverseRelations(parentName, modelNamespace, reader, declaredMetho
|
|
|
1054
1054
|
methods.push(`
|
|
1055
1055
|
/**
|
|
1056
1056
|
* Auto-generated inverse of ${childName}.${childPropName} (${relation}).
|
|
1057
|
+
* @return ${returnType}<${fqcn}, $this>
|
|
1057
1058
|
*/
|
|
1058
1059
|
public function ${methodName}(): ${returnType}
|
|
1059
1060
|
{
|
|
@@ -39,6 +39,11 @@ export function buildRelation(propName, property, modelNamespace, context) {
|
|
|
39
39
|
}
|
|
40
40
|
if (method === null)
|
|
41
41
|
return null;
|
|
42
|
+
const type = getReturnType(relation);
|
|
43
|
+
const related = relation === 'MorphTo'
|
|
44
|
+
? '\\Illuminate\\Database\\Eloquent\\Model'
|
|
45
|
+
: '\\' + modelNamespace + '\\' + target;
|
|
46
|
+
method = method.replace(' */', ` * @return ${type}<${related}, $this>\n */`);
|
|
42
47
|
return { method, imports };
|
|
43
48
|
}
|
|
44
49
|
/** Generate the table name Laravel's joiningTable() infers from model basenames. */
|
|
@@ -180,6 +180,58 @@ function generateBaseRequest(name, schema, reader, config, action) {
|
|
|
180
180
|
const localesClass = `${modelLocalesNamespace}\\${modelName}Locales`;
|
|
181
181
|
const attributeKeysList = attributeKeys.map(k => ` '${k}',`).join('\n');
|
|
182
182
|
const needsRuleImport = rulesContent.includes('Rule::');
|
|
183
|
+
// WHICH KEYS THE LOCALES CLASS WILL ACTUALLY HOLD, which is not the same
|
|
184
|
+
// question as which keys exist. `locales-generator` skips every Association
|
|
185
|
+
// outright, while this generator pushes each ManyToOne's FK column into
|
|
186
|
+
// `attributeKeys` — so a schema whose properties are ALL associations gets a
|
|
187
|
+
// non-empty key list and an EMPTY PROPERTY_DISPLAY_NAMES, and the loop below
|
|
188
|
+
// then reads offsets off `array{}`: ten unfixable PHPStan errors per such schema.
|
|
189
|
+
//
|
|
190
|
+
// Measured twice. `PlanItem` hit it first and was dropped for an unrelated
|
|
191
|
+
// reason, which hid it; `PlanMembership` hit it again and could not be
|
|
192
|
+
// dropped, because a self-referential many-to-many is REFUSED as a pivot —
|
|
193
|
+
// omnify itself says "declare a regular `kind: object` schema with two
|
|
194
|
+
// ManyToOne associations". So it is a shape the generator prescribes and
|
|
195
|
+
// then cannot generate for.
|
|
196
|
+
//
|
|
197
|
+
// Fixing the LOCALES side instead — emitting association display names
|
|
198
|
+
// under their FK column — was tried and reverted: it is better semantically
|
|
199
|
+
// (a validation error would say 計画課題 rather than `plan_issue_id`) but it
|
|
200
|
+
// populates the constant for every schema in the repo, which makes PHPStan
|
|
201
|
+
// prove the `??` fallback chain dead and turns 0 errors into 317. Worth
|
|
202
|
+
// doing upstream, where the generated body can change with it. Not here.
|
|
203
|
+
const hasNamedAttributes = Object.values(properties).some((candidate) => (candidate?.type ?? 'String') !== 'Association');
|
|
204
|
+
const attributesBody = attributeKeys.length === 0 || !hasNamedAttributes
|
|
205
|
+
? [
|
|
206
|
+
' protected function schemaAttributes(): array',
|
|
207
|
+
' {',
|
|
208
|
+
' return [];',
|
|
209
|
+
' }',
|
|
210
|
+
].join('\n')
|
|
211
|
+
: [
|
|
212
|
+
' protected function schemaAttributes(): array',
|
|
213
|
+
' {',
|
|
214
|
+
" $locale = app()->getLocale();",
|
|
215
|
+
" $fallback = config('app.fallback_locale', 'en');",
|
|
216
|
+
` $propertyNames = ${modelName}Locales::PROPERTY_DISPLAY_NAMES;`,
|
|
217
|
+
' $attributes = [];',
|
|
218
|
+
'',
|
|
219
|
+
' $keys = [',
|
|
220
|
+
attributeKeysList,
|
|
221
|
+
' ];',
|
|
222
|
+
'',
|
|
223
|
+
' foreach ($keys as $key) {',
|
|
224
|
+
' $names = $propertyNames[$key] ?? [];',
|
|
225
|
+
" $attributes[$key] = $names[$locale] ?? $names[$fallback] ?? $names[array_key_first($names) ?? 'en'] ?? $key;",
|
|
226
|
+
' }',
|
|
227
|
+
'',
|
|
228
|
+
' return $attributes;',
|
|
229
|
+
' }',
|
|
230
|
+
].join('\n');
|
|
231
|
+
// Only the non-empty body references the Locales class.
|
|
232
|
+
const localesImport = attributeKeys.length === 0 || !hasNamedAttributes
|
|
233
|
+
? ''
|
|
234
|
+
: `\nuse ${localesClass};`;
|
|
183
235
|
const ruleImport = needsRuleImport ? '\nuse Illuminate\\Validation\\Rule;' : '';
|
|
184
236
|
const content = `<?php
|
|
185
237
|
|
|
@@ -192,8 +244,7 @@ namespace ${baseNamespace};
|
|
|
192
244
|
* @generated by omnify
|
|
193
245
|
*/
|
|
194
246
|
|
|
195
|
-
use Illuminate\\Foundation\\Http\\FormRequest;${ruleImport}
|
|
196
|
-
use ${localesClass};
|
|
247
|
+
use Illuminate\\Foundation\\Http\\FormRequest;${ruleImport}${localesImport}
|
|
197
248
|
|
|
198
249
|
class ${modelName}${action}RequestBase extends FormRequest
|
|
199
250
|
{
|
|
@@ -229,7 +280,7 @@ class ${modelName}${action}RequestBase extends FormRequest
|
|
|
229
280
|
/**
|
|
230
281
|
* Get the schema-defined validation rules.
|
|
231
282
|
*
|
|
232
|
-
* @return array<string, array
|
|
283
|
+
* @return array<string, array<mixed>>
|
|
233
284
|
*/
|
|
234
285
|
protected function schemaRules(): array
|
|
235
286
|
{
|
|
@@ -244,24 +295,7 @@ ${rulesContent}
|
|
|
244
295
|
*
|
|
245
296
|
* @return array<string, string>
|
|
246
297
|
*/
|
|
247
|
-
|
|
248
|
-
{
|
|
249
|
-
$locale = app()->getLocale();
|
|
250
|
-
$fallback = config('app.fallback_locale', 'en');
|
|
251
|
-
$propertyNames = ${modelName}Locales::PROPERTY_DISPLAY_NAMES;
|
|
252
|
-
$attributes = [];
|
|
253
|
-
|
|
254
|
-
$keys = [
|
|
255
|
-
${attributeKeysList}
|
|
256
|
-
];
|
|
257
|
-
|
|
258
|
-
foreach ($keys as $key) {
|
|
259
|
-
$names = $propertyNames[$key] ?? [];
|
|
260
|
-
$attributes[$key] = $names[$locale] ?? $names[$fallback] ?? $names[array_key_first($names) ?? 'en'] ?? $key;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return $attributes;
|
|
264
|
-
}
|
|
298
|
+
${attributesBody}
|
|
265
299
|
}
|
|
266
300
|
`;
|
|
267
301
|
// Issue #96 + #98 v5.8+: legacy honors flatBase + group-by-folder.
|
|
@@ -554,7 +554,7 @@ function generateBaseService(name, schema, reader, config) {
|
|
|
554
554
|
// ── Build class body ──────────────────────────────────────────────────────
|
|
555
555
|
const sections = [];
|
|
556
556
|
// Model property
|
|
557
|
-
sections.push(` protected string $model = ${modelName}::class;`);
|
|
557
|
+
sections.push(` /** @var class-string<${modelName}> */\n protected string $model = ${modelName}::class;`);
|
|
558
558
|
// ── Query section ─────────────────────────────────────────────────────────
|
|
559
559
|
sections.push('');
|
|
560
560
|
sections.push(buildSectionComment('Query'));
|
|
@@ -595,7 +595,7 @@ function generateBaseService(name, schema, reader, config) {
|
|
|
595
595
|
// ── Eager Load Hooks (#81) ────────────────────────────────────────────────
|
|
596
596
|
sections.push('');
|
|
597
597
|
sections.push(buildSectionComment('Eager Load Hooks (#81)'));
|
|
598
|
-
sections.push(buildEagerLoadHooks());
|
|
598
|
+
sections.push(buildEagerLoadHooks(modelName));
|
|
599
599
|
// ── Translatable helpers ──────────────────────────────────────────────────
|
|
600
600
|
if (hasTranslatable) {
|
|
601
601
|
sections.push('');
|
|
@@ -675,14 +675,14 @@ function buildSectionComment(title) {
|
|
|
675
675
|
* config will be removed in a future major release per the #81
|
|
676
676
|
* deprecation path.
|
|
677
677
|
*/
|
|
678
|
-
function buildEagerLoadHooks() {
|
|
678
|
+
function buildEagerLoadHooks(modelName) {
|
|
679
679
|
return `
|
|
680
680
|
/**
|
|
681
681
|
* Override in the editable sibling service to eager-load relations
|
|
682
682
|
* on the list() query. Default: no-op. Legacy schema-level eagerLoad
|
|
683
683
|
* configuration still applies before this hook runs.
|
|
684
684
|
*
|
|
685
|
-
* @param \\Illuminate\\Database\\Eloquent\\Builder $query
|
|
685
|
+
* @param \\Illuminate\\Database\\Eloquent\\Builder<${modelName}> $query
|
|
686
686
|
*/
|
|
687
687
|
protected function applyListEagerLoads($query): void
|
|
688
688
|
{
|
|
@@ -694,7 +694,7 @@ function buildEagerLoadHooks() {
|
|
|
694
694
|
* Override in the editable sibling service to eager-load relations on
|
|
695
695
|
* the findById() query. Default: no-op.
|
|
696
696
|
*
|
|
697
|
-
* @param \\Illuminate\\Database\\Eloquent\\Builder $query
|
|
697
|
+
* @param \\Illuminate\\Database\\Eloquent\\Builder<${modelName}> $query
|
|
698
698
|
*/
|
|
699
699
|
protected function applyFindByIdEagerLoads($query): void
|
|
700
700
|
{
|
|
@@ -711,7 +711,7 @@ function buildEagerLoadHooks() {
|
|
|
711
711
|
* the lookup() query. Default: no-op. Most lookup endpoints should
|
|
712
712
|
* stay lean — eager loading defeats the point of a dropdown projection.
|
|
713
713
|
*
|
|
714
|
-
* @param \\Illuminate\\Database\\Eloquent\\Builder $query
|
|
714
|
+
* @param \\Illuminate\\Database\\Eloquent\\Builder<${modelName}> $query
|
|
715
715
|
*/
|
|
716
716
|
protected function applyLookupEagerLoads($query): void
|
|
717
717
|
{
|
|
@@ -875,10 +875,7 @@ function buildListFilterShape(filterableFields, allowedSortColumns, hasSoftDelet
|
|
|
875
875
|
lines.push(` * ${f.colName}?: ${phpType},`);
|
|
876
876
|
}
|
|
877
877
|
lines.push(` * search?: string,`);
|
|
878
|
-
|
|
879
|
-
? allowedSortColumns.flatMap(c => [`'${c}'`, `'-${c}'`]).join('|')
|
|
880
|
-
: 'string';
|
|
881
|
-
lines.push(` * sort?: ${sortUnion},`);
|
|
878
|
+
lines.push(` * sort?: string,`);
|
|
882
879
|
lines.push(` * per_page?: int,`);
|
|
883
880
|
if (hasSoftDelete) {
|
|
884
881
|
lines.push(` * with_trashed?: bool,`);
|
|
@@ -1106,6 +1103,7 @@ function buildListMethod(modelName, searchableFields, filterableFields, sortable
|
|
|
1106
1103
|
* @param array{
|
|
1107
1104
|
${filterShape}
|
|
1108
1105
|
* } $filters
|
|
1106
|
+
* @return LengthAwarePaginator<int, ${modelName}>
|
|
1109
1107
|
*
|
|
1110
1108
|
${listExample}
|
|
1111
1109
|
*/
|
|
@@ -1188,11 +1186,15 @@ function resolvePhpFilterType(type) {
|
|
|
1188
1186
|
}
|
|
1189
1187
|
function buildFilterLine(f) {
|
|
1190
1188
|
if (f.translatable) {
|
|
1191
|
-
return `
|
|
1189
|
+
return ` if (isset($filters['${f.colName}'])) {
|
|
1190
|
+
$query->whereTranslation('${f.colName}', $filters['${f.colName}']);
|
|
1191
|
+
}`;
|
|
1192
1192
|
}
|
|
1193
1193
|
switch (f.type) {
|
|
1194
1194
|
case 'Boolean':
|
|
1195
|
-
return `
|
|
1195
|
+
return ` if (isset($filters['${f.colName}'])) {
|
|
1196
|
+
$query->where('${f.colName}', $filters['${f.colName}']);
|
|
1197
|
+
}`;
|
|
1196
1198
|
default:
|
|
1197
1199
|
return ` $query->when($filters['${f.colName}'] ?? null, fn ($q, $v) => $q->where('${f.colName}', $v));`;
|
|
1198
1200
|
}
|
|
@@ -31,7 +31,7 @@ trait HasLocalizedDisplayName
|
|
|
31
31
|
public static function displayName(?string $locale = null): string
|
|
32
32
|
{
|
|
33
33
|
$locale = $locale ?? app()->getLocale();
|
|
34
|
-
$displayNames = static::$localizedDisplayNames
|
|
34
|
+
$displayNames = static::$localizedDisplayNames;
|
|
35
35
|
|
|
36
36
|
return $displayNames[$locale]
|
|
37
37
|
?? $displayNames[config('app.fallback_locale', 'en')]
|
|
@@ -46,7 +46,7 @@ trait HasLocalizedDisplayName
|
|
|
46
46
|
*/
|
|
47
47
|
public static function allDisplayNames(): array
|
|
48
48
|
{
|
|
49
|
-
return static::$localizedDisplayNames
|
|
49
|
+
return static::$localizedDisplayNames;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -88,7 +88,7 @@ trait HasLocalizedDisplayName
|
|
|
88
88
|
$locale = $locale ?? app()->getLocale();
|
|
89
89
|
$result = [];
|
|
90
90
|
|
|
91
|
-
foreach (static::$localizedPropertyDisplayNames
|
|
91
|
+
foreach (static::$localizedPropertyDisplayNames as $property => $displayNames) {
|
|
92
92
|
$result[$property] = $displayNames[$locale]
|
|
93
93
|
?? $displayNames[config('app.fallback_locale', 'en')]
|
|
94
94
|
?? $displayNames[array_key_first($displayNames) ?? 'en']
|
package/dist/php/type-mapper.js
CHANGED