@omnifyjp/omnify 3.2.3 → 3.2.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnifyjp/omnify",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"zod": "^3.24.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
|
-
"@omnifyjp/omnify-darwin-arm64": "3.2.
|
|
40
|
-
"@omnifyjp/omnify-darwin-x64": "3.2.
|
|
41
|
-
"@omnifyjp/omnify-linux-x64": "3.2.
|
|
42
|
-
"@omnifyjp/omnify-linux-arm64": "3.2.
|
|
43
|
-
"@omnifyjp/omnify-win32-x64": "3.2.
|
|
39
|
+
"@omnifyjp/omnify-darwin-arm64": "3.2.5",
|
|
40
|
+
"@omnifyjp/omnify-darwin-x64": "3.2.5",
|
|
41
|
+
"@omnifyjp/omnify-linux-x64": "3.2.5",
|
|
42
|
+
"@omnifyjp/omnify-linux-arm64": "3.2.5",
|
|
43
|
+
"@omnifyjp/omnify-win32-x64": "3.2.5"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -62,7 +62,7 @@ function generateBaseModel(name, schema, reader, config) {
|
|
|
62
62
|
const prop = properties[p];
|
|
63
63
|
return prop && prop['type'] === 'File';
|
|
64
64
|
});
|
|
65
|
-
const imports = buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait, needsUlidTrait, hasNestedSet, config.nestedset.namespace, hasFiles, modelNamespace, localesNamespace, traitsNamespace, sharedModelsNamespace);
|
|
65
|
+
const imports = buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait, needsUlidTrait, hasNestedSet, config.nestedset.namespace, hasFiles, modelNamespace, localesNamespace, traitsNamespace, sharedModelsNamespace, config);
|
|
66
66
|
const docProperties = buildDocProperties(properties, expandedProperties, propertyOrder);
|
|
67
67
|
const baseClass = isAuthenticatable ? 'Authenticatable' : 'BaseModel';
|
|
68
68
|
const implementsClause = hasTranslatable ? ' implements TranslatableContract' : '';
|
|
@@ -227,7 +227,7 @@ ${traits.join('\n')}
|
|
|
227
227
|
`;
|
|
228
228
|
return userFile(`${config.models.path}/${modelName}.php`, content);
|
|
229
229
|
}
|
|
230
|
-
function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait = false, needsUlidTrait = false, hasNestedSet = false, nestedSetNamespace = 'Aimeos\\Nestedset', hasFiles = false, modelNamespace = '', localesNamespace = '', traitsNamespace = '', sharedModelsNamespace = '') {
|
|
230
|
+
function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait = false, needsUlidTrait = false, hasNestedSet = false, nestedSetNamespace = 'Aimeos\\Nestedset', hasFiles = false, modelNamespace = '', localesNamespace = '', traitsNamespace = '', sharedModelsNamespace = '', config) {
|
|
231
231
|
const lines = [];
|
|
232
232
|
// Import BaseModel from shared namespace when in modular mode
|
|
233
233
|
if (sharedModelsNamespace && sharedModelsNamespace !== baseNamespace && !isAuthenticatable) {
|
|
@@ -252,7 +252,11 @@ function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable
|
|
|
252
252
|
lines.push(`use ${traitsNamespace || baseNamespace + '\\Traits'}\\HasLocalizedDisplayName;`);
|
|
253
253
|
lines.push(`use ${localesNamespace || baseNamespace + '\\Locales'}\\${modelName}Locales;`);
|
|
254
254
|
if (hasFiles) {
|
|
255
|
-
|
|
255
|
+
// HasFiles lives in File module, not Shared
|
|
256
|
+
const fileTraitsNs = config
|
|
257
|
+
? resolveModularBaseNamespace(config, 'File', 'Traits', baseNamespace + '\\Traits')
|
|
258
|
+
: (traitsNamespace || baseNamespace + '\\Traits');
|
|
259
|
+
lines.push(`use ${fileTraitsNs}\\HasFiles;`);
|
|
256
260
|
if (modelNamespace) {
|
|
257
261
|
lines.push(`use ${modelNamespace}\\File;`);
|
|
258
262
|
}
|
|
@@ -134,6 +134,21 @@ function generateBaseRequest(name, schema, reader, config, action) {
|
|
|
134
134
|
needsRuleImport = true;
|
|
135
135
|
rulesLines.push(` '${snakeName}' => ${formatRules(rules)},`);
|
|
136
136
|
attributeKeys.push(snakeName);
|
|
137
|
+
// Translatable: add locale-nested rules (Astrotomic expects {locale}.{field})
|
|
138
|
+
const isTranslatable = prop['translatable'] ?? false;
|
|
139
|
+
if (isTranslatable) {
|
|
140
|
+
const locales = reader.getLocales();
|
|
141
|
+
// Build nullable version of the same rules
|
|
142
|
+
const localeRules = rules.map(r => {
|
|
143
|
+
if (r === 'required')
|
|
144
|
+
return 'nullable';
|
|
145
|
+
return r;
|
|
146
|
+
});
|
|
147
|
+
for (const locale of locales) {
|
|
148
|
+
rulesLines.push(` '${locale}' => ['nullable', 'array'],`);
|
|
149
|
+
rulesLines.push(` '${locale}.${snakeName}' => ${formatRules(localeRules)},`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
137
152
|
}
|
|
138
153
|
const rulesContent = rulesLines.join('\n');
|
|
139
154
|
const localesClass = `${modelLocalesNamespace}\\${modelName}Locales`;
|
|
@@ -67,6 +67,11 @@ function generateBaseResource(name, schema, reader, config) {
|
|
|
67
67
|
const expr = toResourceExpression(snakeName, type);
|
|
68
68
|
fields.push(` '${snakeName}' => ${expr},`);
|
|
69
69
|
}
|
|
70
|
+
// Include translations for translatable models
|
|
71
|
+
const translatableFields = reader.getTranslatableFields(name);
|
|
72
|
+
if (translatableFields.length > 0) {
|
|
73
|
+
fields.push(" 'translations' => $this->getTranslationsArray(),");
|
|
74
|
+
}
|
|
70
75
|
if (hasTimestamps) {
|
|
71
76
|
fields.push(" 'created_at' => $this->created_at?->toISOString(),");
|
|
72
77
|
fields.push(" 'updated_at' => $this->updated_at?->toISOString(),");
|
|
@@ -40,14 +40,15 @@ function generateBaseService(name, schema, reader, config) {
|
|
|
40
40
|
if (!prop)
|
|
41
41
|
continue;
|
|
42
42
|
const colName = toSnakeCase(propName);
|
|
43
|
+
const translatable = prop.translatable ?? false;
|
|
43
44
|
if (prop.searchable) {
|
|
44
|
-
searchableFields.push({ propName, colName });
|
|
45
|
+
searchableFields.push({ propName, colName, translatable });
|
|
45
46
|
}
|
|
46
47
|
if (prop.filterable) {
|
|
47
|
-
filterableFields.push({ propName, colName, type: prop.type, prop });
|
|
48
|
+
filterableFields.push({ propName, colName, type: prop.type, prop, translatable });
|
|
48
49
|
}
|
|
49
50
|
if (prop.sortable) {
|
|
50
|
-
sortableFields.push(colName);
|
|
51
|
+
sortableFields.push({ colName, translatable });
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
// Build search block
|
|
@@ -101,7 +102,12 @@ ${searchBlock}${filterBlock}${sortBlock}
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
if ($search = $filters['search'] ?? null) {
|
|
104
|
-
$query->where(function ($q) use ($search) {${searchableFields.length > 0 ? `\n${searchableFields.map((f, i) =>
|
|
105
|
+
$query->where(function ($q) use ($search) {${searchableFields.length > 0 ? `\n${searchableFields.map((f, i) => {
|
|
106
|
+
if (f.translatable) {
|
|
107
|
+
return ` $q->${i === 0 ? 'whereTranslationLike' : 'orWhereTranslationLike'}('${f.colName}', "%{$search}%");`;
|
|
108
|
+
}
|
|
109
|
+
return ` $q->${i === 0 ? 'where' : 'orWhere'}('${f.colName}', 'like', "%{$search}%");`;
|
|
110
|
+
}).join('\n')}` : `\n // No searchable fields defined`}
|
|
105
111
|
});
|
|
106
112
|
}
|
|
107
113
|
|
|
@@ -196,6 +202,10 @@ function buildSearchBlock(fields) {
|
|
|
196
202
|
return '';
|
|
197
203
|
const clauses = fields.map((f, i) => {
|
|
198
204
|
const method = i === 0 ? 'where' : 'orWhere';
|
|
205
|
+
if (f.translatable) {
|
|
206
|
+
// Astrotomic scope for translatable fields
|
|
207
|
+
return ` $q->${i === 0 ? 'whereTranslationLike' : 'orWhereTranslationLike'}('${f.colName}', "%{$search}%");`;
|
|
208
|
+
}
|
|
199
209
|
return ` $q->${method}('${f.colName}', 'like', "%{$search}%");`;
|
|
200
210
|
});
|
|
201
211
|
return `
|
|
@@ -214,7 +224,14 @@ function buildFilterBlock(fields) {
|
|
|
214
224
|
lines.push('');
|
|
215
225
|
lines.push(' // Filters');
|
|
216
226
|
for (const f of fields) {
|
|
217
|
-
const { colName, type } = f;
|
|
227
|
+
const { colName, type, translatable } = f;
|
|
228
|
+
// Translatable fields use Astrotomic scopes
|
|
229
|
+
if (translatable) {
|
|
230
|
+
lines.push(` if (isset($filters['${colName}'])) {`);
|
|
231
|
+
lines.push(` $query->whereTranslation('${colName}', $filters['${colName}']);`);
|
|
232
|
+
lines.push(` }`);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
218
235
|
switch (type) {
|
|
219
236
|
case 'Decimal':
|
|
220
237
|
case 'Int':
|
|
@@ -264,11 +281,22 @@ function buildSortBlock(sortableFields) {
|
|
|
264
281
|
$query->orderBy('created_at', 'desc');
|
|
265
282
|
`;
|
|
266
283
|
}
|
|
267
|
-
const allowedList = sortableFields.map(f => `'${f}'`).join(', ');
|
|
284
|
+
const allowedList = sortableFields.map(f => `'${f.colName}'`).join(', ');
|
|
285
|
+
const translatableSorts = sortableFields.filter(f => f.translatable).map(f => `'${f.colName}'`);
|
|
286
|
+
const translatableCheck = translatableSorts.length > 0
|
|
287
|
+
? `\n $translatableSorts = [${translatableSorts.join(', ')}];`
|
|
288
|
+
: '';
|
|
289
|
+
const orderByLine = translatableSorts.length > 0
|
|
290
|
+
? ` if (in_array($sortField, $translatableSorts ?? [], true)) {
|
|
291
|
+
$query->orderByTranslation($sortField, $direction);
|
|
292
|
+
} else {
|
|
293
|
+
$query->orderBy($sortField, $direction);
|
|
294
|
+
}`
|
|
295
|
+
: ` $query->orderBy($sortField, $direction);`;
|
|
268
296
|
return `
|
|
269
297
|
// Sort
|
|
270
298
|
$sortParam = $filters['sort'] ?? '-created_at';
|
|
271
|
-
$allowedSorts = [${allowedList}]
|
|
299
|
+
$allowedSorts = [${allowedList}];${translatableCheck}
|
|
272
300
|
|
|
273
301
|
foreach (explode(',', $sortParam) as $sortField) {
|
|
274
302
|
$sortField = trim($sortField);
|
|
@@ -280,7 +308,7 @@ function buildSortBlock(sortableFields) {
|
|
|
280
308
|
}
|
|
281
309
|
|
|
282
310
|
if (in_array($sortField, $allowedSorts, true) || $sortField === 'created_at' || $sortField === 'updated_at') {
|
|
283
|
-
|
|
311
|
+
${orderByLine}
|
|
284
312
|
}
|
|
285
313
|
}
|
|
286
314
|
`;
|