@famgia/omnify-laravel 0.0.17 → 0.0.19

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/README.md CHANGED
@@ -90,11 +90,13 @@ const types = generateTypeScript(schemas, {
90
90
 
91
91
  - Laravel migration generation
92
92
  - TypeScript interface generation
93
+ - Eloquent model generation with localization support
93
94
  - Support for all Laravel column types
94
95
  - Relationship handling (belongsTo, hasMany, etc.)
95
96
  - Index and constraint generation
96
97
  - Enum type support
97
98
  - Plugin system with DAG-based generator ordering
99
+ - Multi-language displayName support (i18n)
98
100
 
99
101
  ## Generated Output
100
102
 
@@ -135,6 +137,134 @@ export interface User {
135
137
  }
136
138
  ```
137
139
 
140
+ ### Eloquent Models with Localized Display Names
141
+
142
+ The generator creates a clean model structure with separated locale data:
143
+
144
+ ```
145
+ app/Models/OmnifyBase/
146
+ ├── Traits/
147
+ │ └── HasLocalizedDisplayName.php # Reusable trait
148
+ ├── Locales/
149
+ │ ├── UserLocales.php # User locale data
150
+ │ ├── PostLocales.php # Post locale data
151
+ │ └── ...
152
+ ├── UserBaseModel.php # Clean base model
153
+ ├── PostBaseModel.php
154
+ └── ...
155
+ ```
156
+
157
+ #### Schema Definition with i18n
158
+
159
+ Define multi-language displayNames in your YAML schema:
160
+
161
+ ```yaml
162
+ # schemas/auth/User.yaml
163
+ name: User
164
+ displayName:
165
+ ja: ユーザー
166
+ en: User
167
+ vi: Người dùng
168
+
169
+ properties:
170
+ name:
171
+ type: String
172
+ displayName:
173
+ ja: 氏名
174
+ en: Full Name
175
+ vi: Họ tên
176
+
177
+ email:
178
+ type: String
179
+ unique: true
180
+ displayName:
181
+ ja: メールアドレス
182
+ en: Email Address
183
+ vi: Địa chỉ email
184
+ ```
185
+
186
+ #### Generated Locales Class
187
+
188
+ ```php
189
+ // app/Models/OmnifyBase/Locales/UserLocales.php
190
+ class UserLocales
191
+ {
192
+ public const DISPLAY_NAMES = [
193
+ 'ja' => 'ユーザー',
194
+ 'en' => 'User',
195
+ 'vi' => 'Người dùng',
196
+ ];
197
+
198
+ public const PROPERTY_DISPLAY_NAMES = [
199
+ 'name' => [
200
+ 'ja' => '氏名',
201
+ 'en' => 'Full Name',
202
+ 'vi' => 'Họ tên',
203
+ ],
204
+ // ...
205
+ ];
206
+ }
207
+ ```
208
+
209
+ #### Generated Base Model
210
+
211
+ ```php
212
+ // app/Models/OmnifyBase/UserBaseModel.php
213
+ use App\Models\OmnifyBase\Traits\HasLocalizedDisplayName;
214
+ use App\Models\OmnifyBase\Locales\UserLocales;
215
+
216
+ class UserBaseModel extends Authenticatable
217
+ {
218
+ use HasLocalizedDisplayName;
219
+
220
+ protected static array $localizedDisplayNames = UserLocales::DISPLAY_NAMES;
221
+ protected static array $localizedPropertyDisplayNames = UserLocales::PROPERTY_DISPLAY_NAMES;
222
+
223
+ // ... rest of model
224
+ }
225
+ ```
226
+
227
+ #### Using Localized Display Names in PHP
228
+
229
+ ```php
230
+ // Get model display name (uses Laravel's app()->getLocale())
231
+ User::displayName(); // "ユーザー" (if locale is 'ja')
232
+ User::displayName('en'); // "User"
233
+ User::displayName('vi'); // "Người dùng"
234
+
235
+ // Get property display name
236
+ User::propertyDisplayName('name'); // "氏名" (if locale is 'ja')
237
+ User::propertyDisplayName('name', 'en'); // "Full Name"
238
+
239
+ // Get all display names
240
+ User::allDisplayNames(); // ['ja' => 'ユーザー', 'en' => 'User', 'vi' => 'Người dùng']
241
+
242
+ // Get all property names for current locale
243
+ User::allPropertyDisplayNamesForLocale(); // ['name' => '氏名', 'email' => 'メールアドレス', ...]
244
+ ```
245
+
246
+ #### Locale Configuration
247
+
248
+ Configure locales in `omnify.config.ts`:
249
+
250
+ ```typescript
251
+ import { defineConfig } from '@famgia/omnify';
252
+ import laravel from '@famgia/omnify-laravel/plugin';
253
+
254
+ export default defineConfig({
255
+ schemasDir: './schemas',
256
+
257
+ // Locale configuration
258
+ locale: {
259
+ locales: ['ja', 'en', 'vi'],
260
+ defaultLocale: 'ja',
261
+ fallbackLocale: 'en',
262
+ },
263
+
264
+ plugins: [laravel()],
265
+ });
266
+ ```
267
+
138
268
  ## Creating Custom Plugins
139
269
 
140
270
  You can create your own generator plugins following this pattern:
@@ -1,5 +1,5 @@
1
1
  // src/plugin.ts
2
- import { readFileSync, existsSync } from "fs";
2
+ import { readFileSync, existsSync, readdirSync } from "fs";
3
3
  import { join } from "path";
4
4
 
5
5
  // src/migration/schema-builder.ts
@@ -14,6 +14,7 @@ var TYPE_METHOD_MAP = {
14
14
  LongText: "longText",
15
15
  Date: "date",
16
16
  Time: "time",
17
+ DateTime: "dateTime",
17
18
  Timestamp: "timestamp",
18
19
  Json: "json",
19
20
  Email: "string",
@@ -86,6 +87,10 @@ function propertyToColumnMethod(propertyName, property) {
86
87
  if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
87
88
  modifiers.push({ method: "unsigned" });
88
89
  }
90
+ const displayName = property.displayName;
91
+ if (displayName) {
92
+ modifiers.push({ method: "comment", args: [displayName] });
93
+ }
89
94
  return {
90
95
  name: columnName,
91
96
  method,
@@ -220,6 +225,9 @@ function generateForeignKey(propertyName, property, allSchemas) {
220
225
  if (assocProp.default !== void 0 && assocProp.default !== null) {
221
226
  modifiers.push({ method: "default", args: [assocProp.default] });
222
227
  }
228
+ if (assocProp.displayName) {
229
+ modifiers.push({ method: "comment", args: [assocProp.displayName] });
230
+ }
223
231
  const column = {
224
232
  name: columnName,
225
233
  method,
@@ -239,7 +247,65 @@ function generateForeignKey(propertyName, property, allSchemas) {
239
247
  };
240
248
  return { column, foreignKey, index };
241
249
  }
242
- function schemaToBlueprint(schema, allSchemas) {
250
+ function expandCompoundType(propName, property, customTypes) {
251
+ const typeDef = customTypes.get(property.type);
252
+ if (!typeDef || !typeDef.compound || !typeDef.expand) {
253
+ return null;
254
+ }
255
+ const expanded = [];
256
+ const baseProp = property;
257
+ for (const field of typeDef.expand) {
258
+ const suffixSnake = toColumnName(field.suffix);
259
+ const columnName = `${propName}_${suffixSnake}`;
260
+ const expandedProp = {
261
+ type: "String"
262
+ // Default type, will be overridden by sql definition
263
+ };
264
+ if (field.sql) {
265
+ const sqlType = field.sql.sqlType.toUpperCase();
266
+ if (sqlType === "VARCHAR" || sqlType === "CHAR" || sqlType === "STRING") {
267
+ expandedProp.type = "String";
268
+ if (field.sql.length) {
269
+ expandedProp.length = field.sql.length;
270
+ }
271
+ } else if (sqlType === "INT" || sqlType === "INTEGER") {
272
+ expandedProp.type = "Int";
273
+ } else if (sqlType === "BIGINT") {
274
+ expandedProp.type = "BigInt";
275
+ } else if (sqlType === "TEXT") {
276
+ expandedProp.type = "Text";
277
+ } else if (sqlType === "BOOLEAN" || sqlType === "BOOL") {
278
+ expandedProp.type = "Boolean";
279
+ } else if (sqlType === "DECIMAL") {
280
+ expandedProp.type = "Decimal";
281
+ if (field.sql.precision) expandedProp.precision = field.sql.precision;
282
+ if (field.sql.scale) expandedProp.scale = field.sql.scale;
283
+ } else if (sqlType === "DATE") {
284
+ expandedProp.type = "Date";
285
+ } else if (sqlType === "TIMESTAMP" || sqlType === "DATETIME") {
286
+ expandedProp.type = "Timestamp";
287
+ }
288
+ if (field.sql.nullable !== void 0) {
289
+ expandedProp.nullable = field.sql.nullable;
290
+ } else if (baseProp.nullable !== void 0) {
291
+ expandedProp.nullable = baseProp.nullable;
292
+ }
293
+ if (field.sql.default !== void 0) {
294
+ expandedProp.default = field.sql.default;
295
+ }
296
+ }
297
+ if (baseProp.displayName) {
298
+ expandedProp.displayName = `${baseProp.displayName} (${field.suffix})`;
299
+ }
300
+ expanded.push({
301
+ name: columnName,
302
+ property: expandedProp
303
+ });
304
+ }
305
+ return expanded;
306
+ }
307
+ function schemaToBlueprint(schema, allSchemas, options = {}) {
308
+ const { customTypes = /* @__PURE__ */ new Map() } = options;
243
309
  const tableName = toTableName(schema.name);
244
310
  const columns = [];
245
311
  const foreignKeys = [];
@@ -250,6 +316,16 @@ function schemaToBlueprint(schema, allSchemas) {
250
316
  }
251
317
  if (schema.properties) {
252
318
  for (const [propName, property] of Object.entries(schema.properties)) {
319
+ const expandedProps = expandCompoundType(propName, property, customTypes);
320
+ if (expandedProps) {
321
+ for (const { name: expandedName, property: expandedProp } of expandedProps) {
322
+ const columnMethod2 = propertyToColumnMethod(expandedName, expandedProp);
323
+ if (columnMethod2) {
324
+ columns.push(columnMethod2);
325
+ }
326
+ }
327
+ continue;
328
+ }
253
329
  const columnMethod = propertyToColumnMethod(propName, property);
254
330
  if (columnMethod) {
255
331
  columns.push(columnMethod);
@@ -696,7 +772,9 @@ function generateMigrations(schemas, options = {}) {
696
772
  const timestamp = options.timestamp ?? generateTimestamp();
697
773
  const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
698
774
  timestampOffset++;
699
- const blueprint = schemaToBlueprint(schema, schemas);
775
+ const blueprint = schemaToBlueprint(schema, schemas, {
776
+ customTypes: options.customTypes
777
+ });
700
778
  const migration = generateCreateMigration(blueprint, {
701
779
  ...options,
702
780
  timestamp: offsetTimestamp
@@ -774,6 +852,7 @@ var TYPE_METHOD_MAP2 = {
774
852
  LongText: "longText",
775
853
  Date: "date",
776
854
  Time: "time",
855
+ DateTime: "dateTime",
777
856
  Timestamp: "timestamp",
778
857
  Json: "json",
779
858
  Email: "string",
@@ -1043,6 +1122,9 @@ function generateMigrationsFromChanges(changes, options = {}) {
1043
1122
  return migrations;
1044
1123
  }
1045
1124
 
1125
+ // src/model/generator.ts
1126
+ import { isLocaleMap } from "@famgia/omnify-types";
1127
+
1046
1128
  // src/utils.ts
1047
1129
  function toSnakeCase(str) {
1048
1130
  return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
@@ -1072,6 +1154,45 @@ var DEFAULT_OPTIONS = {
1072
1154
  baseModelPath: "app/Models/OmnifyBase",
1073
1155
  modelPath: "app/Models"
1074
1156
  };
1157
+ function generateLocalizedDisplayNames(displayName, indent = " ") {
1158
+ if (displayName === void 0) {
1159
+ return "";
1160
+ }
1161
+ if (typeof displayName === "string") {
1162
+ return `${indent}'en' => '${escapePhpString(displayName)}',`;
1163
+ }
1164
+ if (isLocaleMap(displayName)) {
1165
+ const entries = Object.entries(displayName).map(([locale, value]) => `${indent}'${locale}' => '${escapePhpString(value)}',`).join("\n");
1166
+ return entries;
1167
+ }
1168
+ return "";
1169
+ }
1170
+ function generatePropertyLocalizedDisplayNames(schema, indent = " ") {
1171
+ const properties = schema.properties ?? {};
1172
+ const entries = [];
1173
+ for (const [propName, propDef] of Object.entries(properties)) {
1174
+ const snakeName = toSnakeCase(propName);
1175
+ const displayName = propDef.displayName;
1176
+ if (displayName === void 0) {
1177
+ continue;
1178
+ }
1179
+ const innerIndent = indent + " ";
1180
+ if (typeof displayName === "string") {
1181
+ entries.push(`${indent}'${snakeName}' => [
1182
+ ${innerIndent}'en' => '${escapePhpString(displayName)}',
1183
+ ${indent}],`);
1184
+ } else if (isLocaleMap(displayName)) {
1185
+ const localeEntries = Object.entries(displayName).map(([locale, value]) => `${innerIndent}'${locale}' => '${escapePhpString(value)}',`).join("\n");
1186
+ entries.push(`${indent}'${snakeName}' => [
1187
+ ${localeEntries}
1188
+ ${indent}],`);
1189
+ }
1190
+ }
1191
+ return entries.join("\n");
1192
+ }
1193
+ function escapePhpString(str) {
1194
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1195
+ }
1075
1196
  function resolveOptions(options) {
1076
1197
  return {
1077
1198
  baseModelNamespace: options?.baseModelNamespace ?? DEFAULT_OPTIONS.baseModelNamespace,
@@ -1454,11 +1575,14 @@ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
1454
1575
  use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1455
1576
  use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1456
1577
  use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1578
+ use {{BASE_MODEL_NAMESPACE}}\\Traits\\HasLocalizedDisplayName;
1579
+ use {{BASE_MODEL_NAMESPACE}}\\Locales\\{{CLASS_NAME}}Locales;
1457
1580
  {{IMPORTS}}
1458
1581
 
1459
1582
  {{DOC_COMMENT}}
1460
1583
  class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
1461
1584
  {
1585
+ use HasLocalizedDisplayName;
1462
1586
  {{TRAITS}}
1463
1587
  /**
1464
1588
  * The table associated with the model.
@@ -1477,6 +1601,20 @@ class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
1477
1601
  */
1478
1602
  public $timestamps = {{TIMESTAMPS}};
1479
1603
 
1604
+ /**
1605
+ * Localized display names for this model.
1606
+ *
1607
+ * @var array<string, string>
1608
+ */
1609
+ protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;
1610
+
1611
+ /**
1612
+ * Localized display names for properties.
1613
+ *
1614
+ * @var array<string, array<string, string>>
1615
+ */
1616
+ protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;
1617
+
1480
1618
  /**
1481
1619
  * The attributes that are mass assignable.
1482
1620
  */
@@ -1533,12 +1671,15 @@ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1533
1671
  use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1534
1672
  use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1535
1673
  use Illuminate\\Notifications\\Notifiable;
1674
+ use {{BASE_MODEL_NAMESPACE}}\\Traits\\HasLocalizedDisplayName;
1675
+ use {{BASE_MODEL_NAMESPACE}}\\Locales\\{{CLASS_NAME}}Locales;
1536
1676
  {{IMPORTS}}
1537
1677
 
1538
1678
  {{DOC_COMMENT}}
1539
1679
  class {{CLASS_NAME}}BaseModel extends Authenticatable
1540
1680
  {
1541
1681
  use Notifiable;
1682
+ use HasLocalizedDisplayName;
1542
1683
  {{TRAITS}}
1543
1684
  /**
1544
1685
  * The table associated with the model.
@@ -1557,6 +1698,20 @@ class {{CLASS_NAME}}BaseModel extends Authenticatable
1557
1698
  */
1558
1699
  public $timestamps = {{TIMESTAMPS}};
1559
1700
 
1701
+ /**
1702
+ * Localized display names for this model.
1703
+ *
1704
+ * @var array<string, string>
1705
+ */
1706
+ protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;
1707
+
1708
+ /**
1709
+ * Localized display names for properties.
1710
+ *
1711
+ * @var array<string, array<string, string>>
1712
+ */
1713
+ protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;
1714
+
1560
1715
  /**
1561
1716
  * The attributes that are mass assignable.
1562
1717
  */
@@ -1661,6 +1816,132 @@ class OmnifyServiceProvider extends ServiceProvider
1661
1816
  ]);
1662
1817
  }
1663
1818
  }
1819
+ `,
1820
+ "has-localized-display-name": `<?php
1821
+
1822
+ namespace {{BASE_MODEL_NAMESPACE}}\\Traits;
1823
+
1824
+ /**
1825
+ * Trait for localized display names.
1826
+ * Uses Laravel's app()->getLocale() for locale resolution.
1827
+ *
1828
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1829
+ * Any changes will be overwritten on next generation.
1830
+ *
1831
+ * @generated by @famgia/omnify-laravel
1832
+ */
1833
+ trait HasLocalizedDisplayName
1834
+ {
1835
+ /**
1836
+ * Get the localized display name for this model.
1837
+ *
1838
+ * @param string|null $locale Locale code (defaults to app locale)
1839
+ * @return string
1840
+ */
1841
+ public static function displayName(?string $locale = null): string
1842
+ {
1843
+ $locale = $locale ?? app()->getLocale();
1844
+ $displayNames = static::$localizedDisplayNames ?? [];
1845
+
1846
+ return $displayNames[$locale]
1847
+ ?? $displayNames[config('app.fallback_locale', 'en')]
1848
+ ?? $displayNames[array_key_first($displayNames) ?? 'en']
1849
+ ?? class_basename(static::class);
1850
+ }
1851
+
1852
+ /**
1853
+ * Get all localized display names for this model.
1854
+ *
1855
+ * @return array<string, string>
1856
+ */
1857
+ public static function allDisplayNames(): array
1858
+ {
1859
+ return static::$localizedDisplayNames ?? [];
1860
+ }
1861
+
1862
+ /**
1863
+ * Get the localized display name for a property.
1864
+ *
1865
+ * @param string $property Property name
1866
+ * @param string|null $locale Locale code (defaults to app locale)
1867
+ * @return string
1868
+ */
1869
+ public static function propertyDisplayName(string $property, ?string $locale = null): string
1870
+ {
1871
+ $locale = $locale ?? app()->getLocale();
1872
+ $displayNames = static::$localizedPropertyDisplayNames[$property] ?? [];
1873
+
1874
+ return $displayNames[$locale]
1875
+ ?? $displayNames[config('app.fallback_locale', 'en')]
1876
+ ?? $displayNames[array_key_first($displayNames) ?? 'en']
1877
+ ?? $property;
1878
+ }
1879
+
1880
+ /**
1881
+ * Get all localized display names for a property.
1882
+ *
1883
+ * @param string $property Property name
1884
+ * @return array<string, string>
1885
+ */
1886
+ public static function allPropertyDisplayNames(string $property): array
1887
+ {
1888
+ return static::$localizedPropertyDisplayNames[$property] ?? [];
1889
+ }
1890
+
1891
+ /**
1892
+ * Get all property display names for a given locale.
1893
+ *
1894
+ * @param string|null $locale Locale code (defaults to app locale)
1895
+ * @return array<string, string>
1896
+ */
1897
+ public static function allPropertyDisplayNamesForLocale(?string $locale = null): array
1898
+ {
1899
+ $locale = $locale ?? app()->getLocale();
1900
+ $result = [];
1901
+
1902
+ foreach (static::$localizedPropertyDisplayNames ?? [] as $property => $displayNames) {
1903
+ $result[$property] = $displayNames[$locale]
1904
+ ?? $displayNames[config('app.fallback_locale', 'en')]
1905
+ ?? $displayNames[array_key_first($displayNames) ?? 'en']
1906
+ ?? $property;
1907
+ }
1908
+
1909
+ return $result;
1910
+ }
1911
+ }
1912
+ `,
1913
+ "locales": `<?php
1914
+
1915
+ namespace {{BASE_MODEL_NAMESPACE}}\\Locales;
1916
+
1917
+ /**
1918
+ * Localized display names for {{CLASS_NAME}}.
1919
+ *
1920
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1921
+ * Any changes will be overwritten on next generation.
1922
+ *
1923
+ * @generated by @famgia/omnify-laravel
1924
+ */
1925
+ class {{CLASS_NAME}}Locales
1926
+ {
1927
+ /**
1928
+ * Localized display names for the model.
1929
+ *
1930
+ * @var array<string, string>
1931
+ */
1932
+ public const DISPLAY_NAMES = [
1933
+ {{LOCALIZED_DISPLAY_NAMES}}
1934
+ ];
1935
+
1936
+ /**
1937
+ * Localized display names for properties.
1938
+ *
1939
+ * @var array<string, array<string, string>>
1940
+ */
1941
+ public const PROPERTY_DISPLAY_NAMES = [
1942
+ {{LOCALIZED_PROPERTY_DISPLAY_NAMES}}
1943
+ ];
1944
+ }
1664
1945
  `
1665
1946
  };
1666
1947
  return stubs[stubName] ?? "";
@@ -1680,15 +1961,42 @@ function generateServiceProvider(schemas, options, stubContent) {
1680
1961
  schemaName: "__service_provider__"
1681
1962
  };
1682
1963
  }
1964
+ function generateLocalizedDisplayNameTrait(options, stubContent) {
1965
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace);
1966
+ return {
1967
+ path: `${options.baseModelPath}/Traits/HasLocalizedDisplayName.php`,
1968
+ content,
1969
+ type: "trait",
1970
+ overwrite: true,
1971
+ // Always overwrite trait
1972
+ schemaName: "__trait__"
1973
+ };
1974
+ }
1975
+ function generateLocalesClass(schema, options, stubContent) {
1976
+ const className = toPascalCase(schema.name);
1977
+ const localizedDisplayNames = generateLocalizedDisplayNames(schema.displayName);
1978
+ const localizedPropertyDisplayNames = generatePropertyLocalizedDisplayNames(schema);
1979
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{CLASS_NAME\}\}/g, className).replace(/\{\{LOCALIZED_DISPLAY_NAMES\}\}/g, localizedDisplayNames).replace(/\{\{LOCALIZED_PROPERTY_DISPLAY_NAMES\}\}/g, localizedPropertyDisplayNames);
1980
+ return {
1981
+ path: `${options.baseModelPath}/Locales/${className}Locales.php`,
1982
+ content,
1983
+ type: "locales",
1984
+ overwrite: true,
1985
+ // Always overwrite locales
1986
+ schemaName: schema.name
1987
+ };
1988
+ }
1683
1989
  function generateModels(schemas, options) {
1684
1990
  const resolved = resolveOptions(options);
1685
1991
  const models = [];
1686
1992
  models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
1993
+ models.push(generateLocalizedDisplayNameTrait(resolved, getStubContent("has-localized-display-name")));
1687
1994
  models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
1688
1995
  for (const schema of Object.values(schemas)) {
1689
1996
  if (schema.kind === "enum") {
1690
1997
  continue;
1691
1998
  }
1999
+ models.push(generateLocalesClass(schema, resolved, getStubContent("locales")));
1692
2000
  models.push(generateEntityBaseModel(
1693
2001
  schema,
1694
2002
  schemas,
@@ -2054,6 +2362,24 @@ function getFactoryPath(factory) {
2054
2362
  }
2055
2363
 
2056
2364
  // src/plugin.ts
2365
+ function getExistingMigrationTables(migrationsDir) {
2366
+ const existingTables = /* @__PURE__ */ new Set();
2367
+ if (!existsSync(migrationsDir)) {
2368
+ return existingTables;
2369
+ }
2370
+ try {
2371
+ const files = readdirSync(migrationsDir);
2372
+ const createMigrationPattern = /^\d{4}_\d{2}_\d{2}_\d{6}_create_(.+)_table\.php$/;
2373
+ for (const file of files) {
2374
+ const match = file.match(createMigrationPattern);
2375
+ if (match) {
2376
+ existingTables.add(match[1]);
2377
+ }
2378
+ }
2379
+ } catch {
2380
+ }
2381
+ return existingTables;
2382
+ }
2057
2383
  var LARAVEL_CONFIG_SCHEMA = {
2058
2384
  fields: [
2059
2385
  {
@@ -2137,18 +2463,81 @@ function laravelPlugin(options) {
2137
2463
  generate: async (ctx) => {
2138
2464
  const migrationOptions = {
2139
2465
  connection: resolved.connection,
2140
- timestamp: resolved.timestamp
2466
+ timestamp: resolved.timestamp,
2467
+ customTypes: ctx.customTypes
2141
2468
  };
2142
- const migrations = generateMigrations(ctx.schemas, migrationOptions);
2143
- return migrations.map((migration) => ({
2144
- path: getMigrationPath(migration, resolved.migrationsPath),
2145
- content: migration.content,
2146
- type: "migration",
2147
- metadata: {
2148
- tableName: migration.tables[0],
2149
- migrationType: migration.type
2469
+ const outputs = [];
2470
+ const migrationsDir = join(ctx.cwd, resolved.migrationsPath);
2471
+ const existingTables = getExistingMigrationTables(migrationsDir);
2472
+ if (ctx.changes !== void 0) {
2473
+ if (ctx.changes.length === 0) {
2474
+ return outputs;
2150
2475
  }
2151
- }));
2476
+ const addedSchemaNames = new Set(
2477
+ ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
2478
+ );
2479
+ if (addedSchemaNames.size > 0) {
2480
+ const addedSchemas = Object.fromEntries(
2481
+ Object.entries(ctx.schemas).filter(([name]) => addedSchemaNames.has(name))
2482
+ );
2483
+ const createMigrations = generateMigrations(addedSchemas, migrationOptions);
2484
+ for (const migration of createMigrations) {
2485
+ const tableName = migration.tables[0];
2486
+ if (existingTables.has(tableName)) {
2487
+ ctx.logger.debug(`Skipping CREATE for ${tableName} (already exists)`);
2488
+ continue;
2489
+ }
2490
+ outputs.push({
2491
+ path: getMigrationPath(migration, resolved.migrationsPath),
2492
+ content: migration.content,
2493
+ type: "migration",
2494
+ metadata: {
2495
+ tableName,
2496
+ migrationType: "create"
2497
+ }
2498
+ });
2499
+ }
2500
+ }
2501
+ const alterChanges = ctx.changes.filter(
2502
+ (c) => c.changeType === "modified" || c.changeType === "removed"
2503
+ );
2504
+ if (alterChanges.length > 0) {
2505
+ const alterMigrations = generateMigrationsFromChanges(
2506
+ alterChanges,
2507
+ migrationOptions
2508
+ );
2509
+ for (const migration of alterMigrations) {
2510
+ outputs.push({
2511
+ path: getMigrationPath(migration, resolved.migrationsPath),
2512
+ content: migration.content,
2513
+ type: "migration",
2514
+ metadata: {
2515
+ tableName: migration.tables[0],
2516
+ migrationType: migration.type
2517
+ }
2518
+ });
2519
+ }
2520
+ }
2521
+ } else {
2522
+ const migrations = generateMigrations(ctx.schemas, migrationOptions);
2523
+ for (const migration of migrations) {
2524
+ const tableName = migration.tables[0];
2525
+ if (migration.type === "create" && existingTables.has(tableName)) {
2526
+ ctx.logger.debug(`Skipping migration for ${tableName} (already exists)`);
2527
+ continue;
2528
+ }
2529
+ outputs.push({
2530
+ path: getMigrationPath(migration, resolved.migrationsPath),
2531
+ content: migration.content,
2532
+ type: "migration",
2533
+ metadata: {
2534
+ tableName,
2535
+ migrationType: migration.type
2536
+ }
2537
+ });
2538
+ }
2539
+ }
2540
+ return outputs;
2152
2541
  }
2153
2542
  };
2154
2543
  const modelGenerator = {
@@ -2274,4 +2663,4 @@ export {
2274
2663
  generateMigrationsFromChanges,
2275
2664
  laravelPlugin
2276
2665
  };
2277
- //# sourceMappingURL=chunk-52JAFQBQ.js.map
2666
+ //# sourceMappingURL=chunk-ILSNN7UV.js.map