@omnifyjp/ts 5.8.39 → 5.9.0
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/generator.js
CHANGED
|
@@ -333,15 +333,43 @@ function generateI18nFile(options) {
|
|
|
333
333
|
/** Generate index.ts re-exports. */
|
|
334
334
|
function generateIndexFile(schemas, schemaEnums, pluginEnums, typeAliases, hasFiles = false) {
|
|
335
335
|
const parts = [generateBaseHeader()];
|
|
336
|
-
// Common types
|
|
336
|
+
// Common types.
|
|
337
|
+
// A generator-owned common type can collide with a user model schema of the
|
|
338
|
+
// same name (e.g. a `Locale` entity vs the `Locale` locale-code union). The
|
|
339
|
+
// model is the user's explicit schema and stays canonical (keeps the bare
|
|
340
|
+
// name in the model section below); the common type is re-exported under an
|
|
341
|
+
// alias here so the barrel never emits a duplicate identifier (TS2300).
|
|
337
342
|
parts.push(`// Common Types\n`);
|
|
343
|
+
const modelNames = new Set(Object.values(schemas)
|
|
344
|
+
.filter((s) => s.kind !== 'enum' && s.options?.hidden !== true)
|
|
345
|
+
.map((s) => s.name));
|
|
346
|
+
// Preferred, human-readable aliases for the known common types. Any other
|
|
347
|
+
// collision falls back to a `Common<Name>` alias.
|
|
348
|
+
const COMMON_TYPE_ALIASES = {
|
|
349
|
+
Locale: 'LocaleCode',
|
|
350
|
+
LocaleMap: 'LocaleStringMap',
|
|
351
|
+
DateTimeString: 'CommonDateTimeString',
|
|
352
|
+
DateString: 'CommonDateString',
|
|
353
|
+
OmnifyFile: 'OmnifyFileType',
|
|
354
|
+
};
|
|
355
|
+
const commonTypeNames = hasFiles
|
|
356
|
+
? ['LocaleMap', 'Locale', 'DateTimeString', 'DateString', 'OmnifyFile']
|
|
357
|
+
: ['LocaleMap', 'Locale', 'DateTimeString', 'DateString'];
|
|
358
|
+
const commonTypeExports = commonTypeNames.map((name) => {
|
|
359
|
+
if (!modelNames.has(name)) {
|
|
360
|
+
return name;
|
|
361
|
+
}
|
|
362
|
+
const alias = COMMON_TYPE_ALIASES[name] ?? `Common${name}`;
|
|
363
|
+
// eslint-disable-next-line no-console
|
|
364
|
+
console.warn(`[omnify-ts] Common type "${name}" collides with model schema "${name}"; ` +
|
|
365
|
+
`re-exporting the common type from index.ts as "${alias}". ` +
|
|
366
|
+
`The "${name}" model keeps the canonical name.`);
|
|
367
|
+
return `${name} as ${alias}`;
|
|
368
|
+
});
|
|
369
|
+
parts.push(`export type { ${commonTypeExports.join(', ')} } from './common';\n`);
|
|
338
370
|
if (hasFiles) {
|
|
339
|
-
parts.push(`export type { LocaleMap, Locale, DateTimeString, DateString, OmnifyFile } from './common';\n`);
|
|
340
371
|
parts.push(`export { OmnifyFileSchema } from './common';\n`);
|
|
341
372
|
}
|
|
342
|
-
else {
|
|
343
|
-
parts.push(`export type { LocaleMap, Locale, DateTimeString, DateString } from './common';\n`);
|
|
344
|
-
}
|
|
345
373
|
// I18n
|
|
346
374
|
parts.push(`// i18n (Internationalization)\n`);
|
|
347
375
|
parts.push(`export {\n`);
|
|
@@ -77,6 +77,19 @@ function generateBaseModel(name, schema, reader, config) {
|
|
|
77
77
|
const expandedProperties = reader.getExpandedProperties(name);
|
|
78
78
|
const propertyOrder = reader.getPropertyOrder(name);
|
|
79
79
|
const tableName = reader.getTableName(name);
|
|
80
|
+
// Multi-database routing: a schema may declare a top-level `connection:`
|
|
81
|
+
// (matching an omnify.yaml connections key + a Laravel connection). Emit the
|
|
82
|
+
// matching `$connection` so the model resolves to the right database. The
|
|
83
|
+
// implicit `default` connection is left unset so Laravel uses its default.
|
|
84
|
+
const connectionName = schema['connection'];
|
|
85
|
+
const connectionSection = connectionName && connectionName !== 'default'
|
|
86
|
+
? `
|
|
87
|
+
/**
|
|
88
|
+
* The database connection for the model.
|
|
89
|
+
*/
|
|
90
|
+
protected $connection = '${connectionName}';
|
|
91
|
+
`
|
|
92
|
+
: '';
|
|
80
93
|
const isAuthenticatable = options['authenticatable'] ?? false;
|
|
81
94
|
const hasSoftDelete = options.softDelete ?? false;
|
|
82
95
|
const hasTimestamps = options.timestamps ?? false;
|
|
@@ -116,6 +129,31 @@ function generateBaseModel(name, schema, reader, config) {
|
|
|
116
129
|
const fileAccessors = hasFiles ? buildFileAccessors(properties, propertyOrder, modelNamespace) : '';
|
|
117
130
|
const auditSection = buildAuditSection(options, reader, modelNamespace);
|
|
118
131
|
const nestedSetMethod = buildNestedSetParentIdMethod(nestedSetParentColumn);
|
|
132
|
+
// Authenticatable base models extend Illuminate's Authenticatable, not
|
|
133
|
+
// BaseModel, so they cannot inherit its getMorphClass() override. Give them
|
|
134
|
+
// their own copy so application subclasses (e.g.
|
|
135
|
+
// `App\Models\User extends App\Models\Omnify\User`) still resolve under an
|
|
136
|
+
// enforced morph map instead of throwing ClassMorphViolationException. #106
|
|
137
|
+
const morphClassMethod = isAuthenticatable ? `
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the polymorphic alias from the morph map, walking up the class
|
|
140
|
+
* hierarchy so application subclasses of this generated model still resolve.
|
|
141
|
+
*/
|
|
142
|
+
public function getMorphClass(): string
|
|
143
|
+
{
|
|
144
|
+
$morphMap = \\Illuminate\\Database\\Eloquent\\Relations\\Relation::morphMap();
|
|
145
|
+
|
|
146
|
+
for ($class = static::class; $class !== false; $class = get_parent_class($class)) {
|
|
147
|
+
$alias = array_search($class, $morphMap, true);
|
|
148
|
+
|
|
149
|
+
if ($alias !== false) {
|
|
150
|
+
return $alias;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return parent::getMorphClass();
|
|
155
|
+
}
|
|
156
|
+
` : '';
|
|
119
157
|
let keyTypeSection = '';
|
|
120
158
|
if (idType === 'Uuid' || idType === 'Ulid' || idType === 'String') {
|
|
121
159
|
keyTypeSection = `
|
|
@@ -147,7 +185,7 @@ ${imports}
|
|
|
147
185
|
${docProperties} */
|
|
148
186
|
class ${modelName}BaseModel extends ${baseClass}${implementsClause}
|
|
149
187
|
{
|
|
150
|
-
${traits} /**
|
|
188
|
+
${traits}${connectionSection} /**
|
|
151
189
|
* The table associated with the model.
|
|
152
190
|
*/
|
|
153
191
|
protected $table = '${tableName}';
|
|
@@ -212,7 +250,7 @@ ${appends} ];
|
|
|
212
250
|
return [
|
|
213
251
|
${casts} ];
|
|
214
252
|
}
|
|
215
|
-
${auditLogProperties}${relations}${accessors}${fileAccessors}${auditSection}${nestedSetMethod}
|
|
253
|
+
${auditLogProperties}${relations}${accessors}${fileAccessors}${auditSection}${nestedSetMethod}${morphClassMethod}
|
|
216
254
|
}
|
|
217
255
|
`;
|
|
218
256
|
// Replace placeholder class name so the file matches resolved className
|
|
@@ -89,8 +89,15 @@ class OmnifyServiceProvider extends ServiceProvider
|
|
|
89
89
|
*/
|
|
90
90
|
public function boot(): void
|
|
91
91
|
{
|
|
92
|
-
// Load Omnify migrations
|
|
93
|
-
|
|
92
|
+
// Load Omnify migrations: the default-connection directory plus every
|
|
93
|
+
// per-connection subdirectory (multi-database setups route each domain's
|
|
94
|
+
// migrations into its own subfolder; those migrations target their
|
|
95
|
+
// connection via Schema::connection(), so a single migrate run suffices).
|
|
96
|
+
$omnifyMigrations = database_path('migrations/omnify');
|
|
97
|
+
$this->loadMigrationsFrom($omnifyMigrations);
|
|
98
|
+
foreach (glob($omnifyMigrations.'/*', GLOB_ONLYDIR) as $connectionDir) {
|
|
99
|
+
$this->loadMigrationsFrom($connectionDir);
|
|
100
|
+
}
|
|
94
101
|
${packageMigrationsBlock}
|
|
95
102
|
// Register morph map for polymorphic relationships
|
|
96
103
|
Relation::enforceMorphMap([
|
|
@@ -17,7 +17,9 @@ export function generateTranslationModels(reader, config) {
|
|
|
17
17
|
const translatableFields = reader.getTranslatableFieldDetails(name);
|
|
18
18
|
if (translatableFields.length === 0)
|
|
19
19
|
continue;
|
|
20
|
-
|
|
20
|
+
// A translation table co-locates with its parent — same connection/database.
|
|
21
|
+
const connectionName = reader.getSchema(name)?.['connection'];
|
|
22
|
+
files.push(generateTranslationBaseModel(name, translatableFields, enforceLanguageFk, config, connectionName));
|
|
21
23
|
files.push(generateTranslationUserModel(name, config));
|
|
22
24
|
}
|
|
23
25
|
return files;
|
|
@@ -53,9 +55,19 @@ function castFor(type) {
|
|
|
53
55
|
return undefined;
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
|
-
function generateTranslationBaseModel(name, translatableFields, enforceLanguageFk, config) {
|
|
58
|
+
function generateTranslationBaseModel(name, translatableFields, enforceLanguageFk, config, connectionName) {
|
|
57
59
|
const modelName = toPascalCase(name);
|
|
58
60
|
const modelSnake = toSnakeCase(name);
|
|
61
|
+
// Multi-database routing: follow the parent schema's connection so the
|
|
62
|
+
// `*_translations` table resolves to the same database as its parent.
|
|
63
|
+
const connectionSection = connectionName && connectionName !== 'default'
|
|
64
|
+
? `
|
|
65
|
+
/**
|
|
66
|
+
* The database connection for the model.
|
|
67
|
+
*/
|
|
68
|
+
protected $connection = '${connectionName}';
|
|
69
|
+
`
|
|
70
|
+
: '';
|
|
59
71
|
// Issue #98 v5.8.2: consolidate every Translation model under one
|
|
60
72
|
// dedicated `Translation/` subfolder + sub-namespace. Reporter's
|
|
61
73
|
// rationale: 39 translation models flat at the top of `Models/`
|
|
@@ -123,7 +135,7 @@ use Illuminate\\Database\\Eloquent\\Model;
|
|
|
123
135
|
*/
|
|
124
136
|
class ${baseClassName} extends Model
|
|
125
137
|
{
|
|
126
|
-
/**
|
|
138
|
+
${connectionSection} /**
|
|
127
139
|
* The table associated with the model.
|
|
128
140
|
*/
|
|
129
141
|
protected $table = '${tableName}';
|