adonis-odm
Version:
A comprehensive MongoDB ODM for AdonisJS with Lucid-style API, type-safe relationships, embedded documents, and transaction support
216 lines (215 loc) • 7.04 kB
JavaScript
/**
* String helper functions for naming conversions
*/
export class StringHelper {
/**
* Convert camelCase to snake_case
*/
static snakeCase(str) {
return str
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.replace(/^_/, '');
}
/**
* Convert snake_case to camelCase
*/
static camelCase(str) {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
/**
* Convert string to singular form (basic implementation)
*/
static singular(str) {
if (str.endsWith('ies')) {
return str.slice(0, -3) + 'y';
}
else if (str.endsWith('es')) {
return str.slice(0, -2);
}
else if (str.endsWith('s')) {
return str.slice(0, -1);
}
return str;
}
/**
* Convert string to plural form (basic implementation)
*/
static plural(str) {
if (str.endsWith('y')) {
return str.slice(0, -1) + 'ies';
}
else if (str.endsWith('s') ||
str.endsWith('sh') ||
str.endsWith('ch') ||
str.endsWith('x') ||
str.endsWith('z')) {
return str + 'es';
}
else {
return str + 's';
}
}
}
/**
* Default naming strategy following AdonisJS Lucid conventions
* This strategy converts camelCase properties to snake_case for serialization
*/
export class CamelCaseNamingStrategy {
/**
* Convert property name to database column name
* By default, converts camelCase to snake_case
*/
columnName(_model, propertyName) {
return StringHelper.snakeCase(propertyName);
}
/**
* Convert property name to serialized name for JSON output
* By default, converts camelCase to snake_case
*/
serializedName(_model, propertyName) {
return StringHelper.snakeCase(propertyName);
}
/**
* Convert model name to collection/table name
* Converts to singular snake_case
*/
tableName(model) {
const modelName = model.name || model.constructor.name;
return StringHelper.singular(StringHelper.snakeCase(modelName));
}
/**
* Generate foreign key name for relationships
*/
relationForeignKey(relation, model, relatedModel) {
if (relation === 'belongsTo') {
const modelName = relatedModel.name || relatedModel.constructor.name;
const primaryKey = relatedModel.primaryKey || '_id';
return StringHelper.camelCase(`${modelName}_${primaryKey}`);
}
const modelName = model.name || model.constructor.name;
const primaryKey = model.primaryKey || '_id';
return StringHelper.camelCase(`${modelName}_${primaryKey}`);
}
/**
* Generate local key name for relationships
*/
relationLocalKey(relation, model, relatedModel) {
if (relation === 'belongsTo') {
return relatedModel.primaryKey || '_id';
}
return model.primaryKey || '_id';
}
/**
* Generate pivot table name for many-to-many relationships
*/
relationPivotTable(_relation, model, relatedModel) {
const modelName = model.name || model.constructor.name;
const relatedModelName = relatedModel.name || relatedModel.constructor.name;
return StringHelper.snakeCase([relatedModelName, modelName].sort().join('_'));
}
/**
* Generate pivot foreign key name for many-to-many relationships
*/
relationPivotForeignKey(_relation, model) {
const modelName = model.name || model.constructor.name;
const primaryKey = model.primaryKey || '_id';
return StringHelper.snakeCase(`${modelName}_${primaryKey}`);
}
/**
* Generate pagination meta keys
*/
paginationMetaKeys() {
return {
total: 'total',
perPage: 'per_page',
currentPage: 'current_page',
lastPage: 'last_page',
firstPage: 'first_page',
firstPageUrl: 'first_page_url',
lastPageUrl: 'last_page_url',
nextPageUrl: 'next_page_url',
previousPageUrl: 'previous_page_url',
};
}
}
/**
* Snake case naming strategy that keeps everything in snake_case
*/
export class SnakeCaseNamingStrategy {
/**
* Convert property name to database column name
* Keeps snake_case as-is
*/
columnName(_model, propertyName) {
return StringHelper.snakeCase(propertyName);
}
/**
* Convert property name to serialized name for JSON output
* Keeps snake_case as-is
*/
serializedName(_model, propertyName) {
return StringHelper.snakeCase(propertyName);
}
/**
* Convert model name to collection/table name
*/
tableName(model) {
const modelName = model.name || model.constructor.name;
return StringHelper.plural(StringHelper.snakeCase(modelName));
}
/**
* Generate foreign key name for relationships
*/
relationForeignKey(relation, model, relatedModel) {
if (relation === 'belongsTo') {
const modelName = relatedModel.name || relatedModel.constructor.name;
const primaryKey = relatedModel.primaryKey || '_id';
return StringHelper.snakeCase(`${modelName}_${primaryKey}`);
}
const modelName = model.name || model.constructor.name;
const primaryKey = model.primaryKey || '_id';
return StringHelper.snakeCase(`${modelName}_${primaryKey}`);
}
/**
* Generate local key name for relationships
*/
relationLocalKey(relation, model, relatedModel) {
if (relation === 'belongsTo') {
return relatedModel.primaryKey || '_id';
}
return model.primaryKey || '_id';
}
/**
* Generate pivot table name for many-to-many relationships
*/
relationPivotTable(_relation, model, relatedModel) {
const modelName = model.name || model.constructor.name;
const relatedModelName = relatedModel.name || relatedModel.constructor.name;
return StringHelper.snakeCase([relatedModelName, modelName].sort().join('_'));
}
/**
* Generate pivot foreign key name for many-to-many relationships
*/
relationPivotForeignKey(_relation, model) {
const modelName = model.name || model.constructor.name;
const primaryKey = model.primaryKey || '_id';
return StringHelper.snakeCase(`${modelName}_${primaryKey}`);
}
/**
* Generate pagination meta keys
*/
paginationMetaKeys() {
return {
total: 'total',
perPage: 'per_page',
currentPage: 'current_page',
lastPage: 'last_page',
firstPage: 'first_page',
firstPageUrl: 'first_page_url',
lastPageUrl: 'last_page_url',
nextPageUrl: 'next_page_url',
previousPageUrl: 'previous_page_url',
};
}
}