diff --git a/src/orm/base_model/index.ts b/src/orm/base_model/index.ts index f9574551..189d2ebb 100644 --- a/src/orm/base_model/index.ts +++ b/src/orm/base_model/index.ts @@ -64,6 +64,7 @@ import { import { SnakeCaseNamingStrategy } from '../naming_strategies/snake_case.js' import { LazyLoadAggregates } from '../relations/aggregates_loader/lazy_load.js' import * as errors from '../../errors.js' +import { CamelCaseNamingStrategy } from '../naming_strategies/camel_case.js' const MANY_RELATIONS = ['hasMany', 'manyToMany', 'hasManyThrough'] const DATE_TIME_TYPES = { @@ -99,7 +100,7 @@ class BaseModelImpl implements LucidRow { /** * Naming strategy for model properties */ - static namingStrategy = new SnakeCaseNamingStrategy() + static namingStrategy = new CamelCaseNamingStrategy() /** * Primary key is required to build relationships across models diff --git a/src/orm/main.ts b/src/orm/main.ts index 37519609..00931fa7 100644 --- a/src/orm/main.ts +++ b/src/orm/main.ts @@ -13,3 +13,4 @@ export * from './decorators/date_time.js' export { BaseModel, scope } from './base_model/index.js' export { ModelQueryBuilder } from './query_builder/index.js' export { SnakeCaseNamingStrategy } from './naming_strategies/snake_case.js' +export { CamelCaseNamingStrategy } from './naming_strategies/camel_case.js' diff --git a/src/orm/naming_strategies/camel_case.ts b/src/orm/naming_strategies/camel_case.ts new file mode 100644 index 00000000..b0a9abbb --- /dev/null +++ b/src/orm/naming_strategies/camel_case.ts @@ -0,0 +1,110 @@ +/* + * @adonisjs/lucid + * + * (c) Harminder Virk + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import string from '@poppinss/utils/string' +import { ModelRelations } from '../../types/relations.js' +import { NamingStrategyContract, LucidModel } from '../../types/model.js' + +/** + * Camelcase naming strategy for the model to use camelcase keys + * for the serialized output. + */ +export class CamelCaseNamingStrategy implements NamingStrategyContract { + /** + * The default table name for the given model + */ + tableName(model: LucidModel): string { + return string.pluralize(string.snakeCase(model.name)) + } + + /** + * The database column name for a given model attribute + */ + columnName(_: LucidModel, attributeName: string): string { + return string.snakeCase(attributeName) + } + + /** + * The post serialization name for a given model attribute + */ + serializedName(_: LucidModel, attributeName: string): string { + return string.camelCase(attributeName) + } + + /** + * The local key for a given model relationship + */ + relationLocalKey( + relation: ModelRelations['__opaque_type'], + model: LucidModel, + relatedModel: LucidModel + ): string { + if (relation === 'belongsTo') { + return relatedModel.primaryKey + } + + return model.primaryKey + } + + /** + * The foreign key for a given model relationship + */ + relationForeignKey( + relation: ModelRelations['__opaque_type'], + model: LucidModel, + relatedModel: LucidModel + ): string { + if (relation === 'belongsTo') { + return string.camelCase(`${relatedModel.name}_${relatedModel.primaryKey}`) + } + + return string.camelCase(`${model.name}_${model.primaryKey}`) + } + + /** + * Pivot table name for many to many relationship + */ + relationPivotTable(_: 'manyToMany', model: LucidModel, relatedModel: LucidModel): string { + return string.snakeCase([relatedModel.name, model.name].sort().join('_')) + } + + /** + * Pivot foreign key for many to many relationship + */ + relationPivotForeignKey(_: 'manyToMany', model: LucidModel): string { + return string.snakeCase(`${model.name}_${model.primaryKey}`) + } + + /** + * Keys for the pagination meta + */ + paginationMetaKeys(): { + total: string + perPage: string + currentPage: string + lastPage: string + firstPage: string + firstPageUrl: string + lastPageUrl: string + nextPageUrl: string + previousPageUrl: string + } { + return { + total: 'total', + perPage: 'perPage', + currentPage: 'currentPage', + lastPage: 'lastPage', + firstPage: 'firstPage', + firstPageUrl: 'firstPageUrl', + lastPageUrl: 'lastPageUrl', + nextPageUrl: 'nextPageUrl', + previousPageUrl: 'previousPageUrl', + } + } +} diff --git a/test/orm/base_model.spec.ts b/test/orm/base_model.spec.ts index dc07d465..205c4b1b 100644 --- a/test/orm/base_model.spec.ts +++ b/test/orm/base_model.spec.ts @@ -249,7 +249,7 @@ test.group('Base model | boot', (group) => { } User.boot() - assert.deepEqual(User.$keys.attributesToSerialized.get('userName'), 'user_name') + assert.deepEqual(User.$keys.attributesToSerialized.get('userName'), 'userName') }) test('resolve attribute name from column name', async ({ fs, assert }) => { @@ -289,7 +289,7 @@ test.group('Base model | boot', (group) => { } User.boot() - assert.deepEqual(User.$keys.columnsToSerialized.get('user_name'), 'user_name') + assert.deepEqual(User.$keys.columnsToSerialized.get('user_name'), 'userName') }) test('resolve attribute name from serializeAs name', async ({ fs, assert }) => { @@ -309,7 +309,7 @@ test.group('Base model | boot', (group) => { } User.boot() - assert.deepEqual(User.$keys.serializedToAttributes.get('user_name'), 'userName') + assert.deepEqual(User.$keys.serializedToAttributes.get('userName'), 'userName') }) test('resolve column name from serializeAs name', async ({ fs, assert }) => { @@ -329,7 +329,7 @@ test.group('Base model | boot', (group) => { } User.boot() - assert.deepEqual(User.$keys.serializedToColumns.get('user_name'), 'user_name') + assert.deepEqual(User.$keys.serializedToColumns.get('userName'), 'user_name') }) }) @@ -2113,7 +2113,7 @@ test.group('Base Model | serializeRelations', () => { assert.deepEqual(user.serializeRelations(), { profile: { username: 'virk', - user_id: 1, + userId: 1, }, }) }) @@ -2150,7 +2150,7 @@ test.group('Base Model | serializeRelations', () => { assert.deepEqual(user.serializeRelations(), { userProfile: { username: 'virk', - user_id: 1, + userId: 1, }, }) }) @@ -2322,12 +2322,12 @@ test.group('Base Model | serializeRelations', () => { assert.deepEqual( user.serializeRelations({ profile: { - fields: ['user_id'], + fields: ['userId'], }, }), { profile: { - user_id: 1, + userId: 1, }, } ) @@ -2371,7 +2371,7 @@ test.group('Base Model | serializeRelations', () => { }), { profile: { - user_id: 1, + userId: 1, username: 'virk', }, } @@ -2506,7 +2506,7 @@ test.group('Base Model | toJSON', (group) => { const user = new User() user.username = 'virk' - assert.deepEqual(user.toJSON(), { username: 'virk', full_name: 'VIRK' }) + assert.deepEqual(user.toJSON(), { username: 'virk', fullName: 'VIRK' }) }) test('do not add computed property when it returns undefined', async ({ fs, assert }) => { @@ -2585,7 +2585,7 @@ test.group('Base Model | toJSON', (group) => { assert.deepEqual(user.toJSON(), { username: 'virk', - full_name: 'VIRK', + fullName: 'VIRK', meta: { postsCount: 10, }, @@ -2623,7 +2623,7 @@ test.group('Base Model | toJSON', (group) => { assert.deepEqual(user.toJSON(), { username: 'virk', - full_name: 'VIRK', + fullName: 'VIRK', posts: { count: 10, }, @@ -6191,7 +6191,7 @@ test.group('Base Model | date', (group) => { created_at: DateTime.local().toISODate(), }) const user = await User.find(1) - assert.match(user!.toJSON().created_at, /\d{4}-\d{2}-\d{2}/) + assert.match(user!.toJSON().createdAt, /\d{4}-\d{2}-\d{2}/) }) test('do not attempt to serialize, when already a string', async ({ fs, assert }) => { @@ -6223,7 +6223,7 @@ test.group('Base Model | date', (group) => { created_at: DateTime.local().toISODate(), }) const user = await User.find(1) - assert.equal(user!.toJSON().created_at, DateTime.local().minus({ days: 1 }).toISODate()) + assert.equal(user!.toJSON().createdAt, DateTime.local().minus({ days: 1 }).toISODate()) }) }) @@ -6667,7 +6667,7 @@ test.group('Base Model | datetime', (group) => { const user = await User.find(1) assert.match( - user!.toJSON().joined_at, + user!.toJSON().joinedAt, /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}(\+|\-)\d{2}:\d{2}/ ) }) @@ -6705,7 +6705,7 @@ test.group('Base Model | datetime', (group) => { }) const user = await User.find(1) - assert.equal(user!.toJSON().joined_at, DateTime.local().minus({ days: 1 }).toISODate()) + assert.equal(user!.toJSON().joinedAt, DateTime.local().minus({ days: 1 }).toISODate()) }) test('force update when enabledForceUpdate method is called', async ({ fs, assert }) => { @@ -6822,14 +6822,14 @@ test.group('Base Model | paginate', (group) => { assert.isTrue(users.hasTotal) assert.deepEqual(users.getMeta(), { total: 18, - per_page: 5, - current_page: 1, - last_page: 4, - first_page: 1, - first_page_url: '/users?page=1', - last_page_url: '/users?page=4', - next_page_url: '/users?page=2', - previous_page_url: null, + perPage: 5, + currentPage: 1, + lastPage: 4, + firstPage: 1, + firstPageUrl: '/users?page=1', + lastPageUrl: '/users?page=4', + nextPageUrl: '/users?page=2', + previousPageUrl: null, }) }) @@ -6867,14 +6867,14 @@ test.group('Base Model | paginate', (group) => { }) assert.deepEqual(meta, { total: 18, - per_page: 5, - current_page: 1, - last_page: 4, - first_page: 1, - first_page_url: '/users?page=1', - last_page_url: '/users?page=4', - next_page_url: '/users?page=2', - previous_page_url: null, + perPage: 5, + currentPage: 1, + lastPage: 4, + firstPage: 1, + firstPageUrl: '/users?page=1', + lastPageUrl: '/users?page=4', + nextPageUrl: '/users?page=2', + previousPageUrl: null, }) }) @@ -6915,14 +6915,14 @@ test.group('Base Model | paginate', (group) => { assert.isTrue(users.hasTotal) assert.deepEqual(users.getMeta(), { total: 18, - per_page: 5, - current_page: 1, - last_page: 4, - first_page: 1, - first_page_url: '/users?page=1', - last_page_url: '/users?page=4', - next_page_url: '/users?page=2', - previous_page_url: null, + perPage: 5, + currentPage: 1, + lastPage: 4, + firstPage: 1, + firstPageUrl: '/users?page=1', + lastPageUrl: '/users?page=4', + nextPageUrl: '/users?page=2', + previousPageUrl: null, }) }) @@ -7030,14 +7030,14 @@ test.group('Base Model | paginate', (group) => { assert.isTrue(users.hasTotal) assert.deepEqual(users.getMeta(), { total: 1, - per_page: 5, - current_page: 1, - last_page: 1, - first_page: 1, - first_page_url: '/users?page=1', - last_page_url: '/users?page=1', - next_page_url: null, - previous_page_url: null, + perPage: 5, + currentPage: 1, + lastPage: 1, + firstPage: 1, + firstPageUrl: '/users?page=1', + lastPageUrl: '/users?page=1', + nextPageUrl: null, + previousPageUrl: null, }) }) }) @@ -7493,7 +7493,7 @@ test.group('Base model | inheritance', (group) => { meta: undefined, prepare: undefined, serialize: undefined, - serializeAs: 'user_id', + serializeAs: 'userId', }, ], [ @@ -7533,7 +7533,7 @@ test.group('Base model | inheritance', (group) => { meta: undefined, prepare: undefined, serialize: undefined, - serializeAs: 'user_id', + serializeAs: 'userId', }, ], ]) @@ -7612,7 +7612,7 @@ test.group('Base model | inheritance', (group) => { 'fullName', { meta: undefined, - serializeAs: 'full_name', + serializeAs: 'fullName', }, ], [ @@ -7638,7 +7638,7 @@ test.group('Base model | inheritance', (group) => { 'fullName', { meta: undefined, - serializeAs: 'full_name', + serializeAs: 'fullName', }, ], ]) @@ -7733,7 +7733,7 @@ test.group('Base model | inheritance', (group) => { 'fullName', { meta: undefined, - serializeAs: 'full_name', + serializeAs: 'fullName', }, ], ]) diff --git a/test/orm/model_has_many.spec.ts b/test/orm/model_has_many.spec.ts index 39eb8606..2a5bd023 100644 --- a/test/orm/model_has_many.spec.ts +++ b/test/orm/model_has_many.spec.ts @@ -5291,14 +5291,14 @@ test.group('Model | HasMany | paginate', (group) => { assert.isTrue(posts.hasTotal) assert.deepEqual(posts.getMeta(), { total: 18, - per_page: 5, - current_page: 1, - last_page: 4, - first_page: 1, - first_page_url: '/posts?page=1', - last_page_url: '/posts?page=4', - next_page_url: '/posts?page=2', - previous_page_url: null, + perPage: 5, + currentPage: 1, + lastPage: 4, + firstPage: 1, + firstPageUrl: '/posts?page=1', + lastPageUrl: '/posts?page=4', + nextPageUrl: '/posts?page=2', + previousPageUrl: null, }) }) diff --git a/test/orm/model_has_many_through.spec.ts b/test/orm/model_has_many_through.spec.ts index bbf8f979..0f6841a8 100644 --- a/test/orm/model_has_many_through.spec.ts +++ b/test/orm/model_has_many_through.spec.ts @@ -3112,14 +3112,14 @@ test.group('Model | Has Many Through | pagination', (group) => { assert.isTrue(posts.hasTotal) assert.deepEqual(posts.getMeta(), { total: 3, - per_page: 2, - current_page: 1, - last_page: 2, - first_page: 1, - first_page_url: '/posts?page=1', - last_page_url: '/posts?page=2', - next_page_url: '/posts?page=2', - previous_page_url: null, + perPage: 2, + currentPage: 1, + lastPage: 2, + firstPage: 1, + firstPageUrl: '/posts?page=1', + lastPageUrl: '/posts?page=2', + nextPageUrl: '/posts?page=2', + previousPageUrl: null, }) }) diff --git a/test/orm/model_many_to_many.spec.ts b/test/orm/model_many_to_many.spec.ts index 00f27565..bb26c522 100644 --- a/test/orm/model_many_to_many.spec.ts +++ b/test/orm/model_many_to_many.spec.ts @@ -7385,14 +7385,14 @@ test.group('Model | ManyToMany | pagination', (group) => { assert.isTrue(skills.hasTotal) assert.deepEqual(skills.getMeta(), { total: 2, - per_page: 1, - current_page: 1, - last_page: 2, - first_page: 1, - first_page_url: '/skills?page=1', - last_page_url: '/skills?page=2', - next_page_url: '/skills?page=2', - previous_page_url: null, + perPage: 1, + currentPage: 1, + lastPage: 2, + firstPage: 1, + firstPageUrl: '/skills?page=1', + lastPageUrl: '/skills?page=2', + nextPageUrl: '/skills?page=2', + previousPageUrl: null, }) })