@omnifyjp/ts 4.10.0 → 5.0.1
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.
|
@@ -6,6 +6,9 @@ export declare class SchemaReader {
|
|
|
6
6
|
private data;
|
|
7
7
|
constructor(data: SchemasJson);
|
|
8
8
|
static fromData(data: SchemasJson): SchemaReader;
|
|
9
|
+
/** Returns the raw root document — needed by generators that read
|
|
10
|
+
* top-level config like locale.enforceLanguageFk. */
|
|
11
|
+
getRoot(): SchemasJson;
|
|
9
12
|
getSchemas(): Record<string, SchemaDefinition>;
|
|
10
13
|
getSchema(name: string): SchemaDefinition | undefined;
|
|
11
14
|
getObjectSchemas(): Record<string, SchemaDefinition>;
|
|
@@ -41,6 +44,7 @@ export declare class SchemaReader {
|
|
|
41
44
|
readonly locales: string[];
|
|
42
45
|
readonly defaultLocale: string;
|
|
43
46
|
readonly fallbackLocale: string;
|
|
47
|
+
readonly enforceLanguageFk?: boolean;
|
|
44
48
|
};
|
|
45
49
|
getLocales(): string[];
|
|
46
50
|
getDefaultLocale(): string;
|
|
@@ -72,4 +76,15 @@ export declare class SchemaReader {
|
|
|
72
76
|
hasServiceSchemas(): boolean;
|
|
73
77
|
/** Get translatable field names (snake_case) for a schema. */
|
|
74
78
|
getTranslatableFields(schemaName: string): string[];
|
|
79
|
+
/**
|
|
80
|
+
* Get translatable fields with type info (snake_case name + the
|
|
81
|
+
* declared property type). Used by translation-model-generator to
|
|
82
|
+
* emit `$casts` for non-string types — without these the model
|
|
83
|
+
* returns raw strings for Json / Boolean / Int columns and Eloquent
|
|
84
|
+
* accessors break at runtime.
|
|
85
|
+
*/
|
|
86
|
+
getTranslatableFieldDetails(schemaName: string): Array<{
|
|
87
|
+
name: string;
|
|
88
|
+
type: string;
|
|
89
|
+
}>;
|
|
75
90
|
}
|
|
@@ -9,6 +9,11 @@ export class SchemaReader {
|
|
|
9
9
|
static fromData(data) {
|
|
10
10
|
return new SchemaReader(data);
|
|
11
11
|
}
|
|
12
|
+
/** Returns the raw root document — needed by generators that read
|
|
13
|
+
* top-level config like locale.enforceLanguageFk. */
|
|
14
|
+
getRoot() {
|
|
15
|
+
return this.data;
|
|
16
|
+
}
|
|
12
17
|
// ---------------------------------------------------------------------------
|
|
13
18
|
// All schemas (project + package)
|
|
14
19
|
// ---------------------------------------------------------------------------
|
|
@@ -225,6 +230,16 @@ export class SchemaReader {
|
|
|
225
230
|
}
|
|
226
231
|
/** Get translatable field names (snake_case) for a schema. */
|
|
227
232
|
getTranslatableFields(schemaName) {
|
|
233
|
+
return this.getTranslatableFieldDetails(schemaName).map((f) => f.name);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get translatable fields with type info (snake_case name + the
|
|
237
|
+
* declared property type). Used by translation-model-generator to
|
|
238
|
+
* emit `$casts` for non-string types — without these the model
|
|
239
|
+
* returns raw strings for Json / Boolean / Int columns and Eloquent
|
|
240
|
+
* accessors break at runtime.
|
|
241
|
+
*/
|
|
242
|
+
getTranslatableFieldDetails(schemaName) {
|
|
228
243
|
const schema = this.getSchema(schemaName);
|
|
229
244
|
if (!schema?.properties)
|
|
230
245
|
return [];
|
|
@@ -233,8 +248,8 @@ export class SchemaReader {
|
|
|
233
248
|
for (const propName of propertyOrder) {
|
|
234
249
|
const prop = schema.properties[propName];
|
|
235
250
|
if (prop?.translatable) {
|
|
236
|
-
|
|
237
|
-
fields.push(
|
|
251
|
+
const snake = propName.replace(/([A-Z])/g, (_, c, i) => (i > 0 ? '_' : '') + c.toLowerCase());
|
|
252
|
+
fields.push({ name: snake, type: prop.type ?? 'String' });
|
|
238
253
|
}
|
|
239
254
|
}
|
|
240
255
|
return fields;
|
|
@@ -12,23 +12,77 @@ import { baseFile, userFile, resolveModularBasePath, resolveModularBaseNamespace
|
|
|
12
12
|
/** Generate translation model files for all project schemas with translatable fields. */
|
|
13
13
|
export function generateTranslationModels(reader, config) {
|
|
14
14
|
const files = [];
|
|
15
|
+
const enforceLanguageFk = reader.getRoot().locale?.enforceLanguageFk === true;
|
|
15
16
|
for (const [name] of Object.entries(reader.getProjectObjectSchemas())) {
|
|
16
|
-
const translatableFields = reader.
|
|
17
|
+
const translatableFields = reader.getTranslatableFieldDetails(name);
|
|
17
18
|
if (translatableFields.length === 0)
|
|
18
19
|
continue;
|
|
19
|
-
files.push(generateTranslationBaseModel(name, translatableFields, config));
|
|
20
|
+
files.push(generateTranslationBaseModel(name, translatableFields, enforceLanguageFk, config));
|
|
20
21
|
files.push(generateTranslationUserModel(name, config));
|
|
21
22
|
}
|
|
22
23
|
return files;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Map an omnify property type to its Eloquent `$casts` value. Returns
|
|
27
|
+
* `undefined` for plain string types (no cast needed). Without these
|
|
28
|
+
* casts, fetching a translation row returns the raw string from the
|
|
29
|
+
* DB for Json / Boolean / Int / DateTime columns and any consumer code
|
|
30
|
+
* that assumed a typed value (e.g. `$translation->metadata['key']`)
|
|
31
|
+
* fails at runtime.
|
|
32
|
+
*/
|
|
33
|
+
function castFor(type) {
|
|
34
|
+
switch (type) {
|
|
35
|
+
case 'Json':
|
|
36
|
+
return 'array';
|
|
37
|
+
case 'Boolean':
|
|
38
|
+
return 'boolean';
|
|
39
|
+
case 'Int':
|
|
40
|
+
case 'TinyInt':
|
|
41
|
+
case 'BigInt':
|
|
42
|
+
return 'integer';
|
|
43
|
+
case 'Float':
|
|
44
|
+
return 'float';
|
|
45
|
+
case 'Decimal':
|
|
46
|
+
return 'decimal:2';
|
|
47
|
+
case 'Date':
|
|
48
|
+
return 'date';
|
|
49
|
+
case 'DateTime':
|
|
50
|
+
case 'Timestamp':
|
|
51
|
+
return 'datetime';
|
|
52
|
+
default:
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function generateTranslationBaseModel(name, translatableFields, enforceLanguageFk, config) {
|
|
25
57
|
const modelName = toPascalCase(name);
|
|
26
58
|
const modelSnake = toSnakeCase(name);
|
|
27
59
|
const baseNamespace = resolveModularBaseNamespace(config, name, 'Models', config.models.baseNamespace);
|
|
28
60
|
const tableName = `${modelSnake}_translations`;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
61
|
+
// Fillable: every translatable column + the optional `language_id`
|
|
62
|
+
// FK column (added by the migration when locale.enforceLanguageFk is
|
|
63
|
+
// on). Without including `language_id` in $fillable, Eloquent mass
|
|
64
|
+
// assignment via Astrotomic silently drops the value and the FK is
|
|
65
|
+
// never populated even though the column exists.
|
|
66
|
+
const fillableNames = translatableFields.map((f) => f.name);
|
|
67
|
+
if (enforceLanguageFk)
|
|
68
|
+
fillableNames.push('language_id');
|
|
69
|
+
const fillableLines = fillableNames.map((f) => ` '${f}',`).join('\n');
|
|
70
|
+
// Casts: only emit for fields whose type has a non-string Eloquent
|
|
71
|
+
// representation. Plain String / Text / Email / EnumRef stay as-is.
|
|
72
|
+
const castEntries = [];
|
|
73
|
+
for (const f of translatableFields) {
|
|
74
|
+
const c = castFor(f.type);
|
|
75
|
+
if (c)
|
|
76
|
+
castEntries.push(` '${f.name}' => '${c}',`);
|
|
77
|
+
}
|
|
78
|
+
const castsBlock = castEntries.length === 0 ? '' : `
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The attributes that should be cast to native types.
|
|
82
|
+
*/
|
|
83
|
+
protected $casts = [
|
|
84
|
+
${castEntries.join('\n')}
|
|
85
|
+
];`;
|
|
32
86
|
const content = `<?php
|
|
33
87
|
|
|
34
88
|
namespace ${baseNamespace};
|
|
@@ -62,7 +116,7 @@ class ${modelName}TranslationBaseModel extends Model
|
|
|
62
116
|
*/
|
|
63
117
|
protected $fillable = [
|
|
64
118
|
${fillableLines}
|
|
65
|
-
]
|
|
119
|
+
];${castsBlock}
|
|
66
120
|
}
|
|
67
121
|
`;
|
|
68
122
|
return baseFile(resolveModularBasePath(config, name, 'Models', `${modelName}TranslationBaseModel.php`, config.models.basePath), content);
|
package/dist/types.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface SchemasJson {
|
|
|
31
31
|
readonly locales: string[];
|
|
32
32
|
readonly defaultLocale: string;
|
|
33
33
|
readonly fallbackLocale: string;
|
|
34
|
+
readonly enforceLanguageFk?: boolean;
|
|
34
35
|
};
|
|
35
36
|
readonly customTypes: {
|
|
36
37
|
readonly compound: Record<string, CompoundTypeDefinition>;
|