Move all database queries in ModelQuery class.

This commit is contained in:
Madeorsk 2024-10-05 18:56:38 +02:00
parent ff793f31f4
commit a29e6f4428
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
2 changed files with 80 additions and 53 deletions

View file

@ -1,16 +1,9 @@
import {IdentifierType, Model, ModelShape, SerializedModel} from "@sharkitek/core";
import {eq, getTableColumns, Table, TableConfig} from "drizzle-orm";
import {IdentifierType, Model, ModelClass, ModelShape} from "@sharkitek/core";
import {Table, TableConfig} from "drizzle-orm";
import {PgDatabase} from "drizzle-orm/pg-core";
import {PgQueryResultHKT} from "drizzle-orm/pg-core/session";
import type {ExtractTablesWithRelations, TablesRelationalConfig} from "drizzle-orm/relations";
import {ModelQuery} from "./Query";
/**
* Serialized values converters for database.
*/
export const serializedToDatabaseTypes: Record<string, (serializedValue: any) => any> = {
"date": (serializedValue: string) => new Date(serializedValue),
};
import {ModelQuery, query} from "./Query";
/**
* Sharkitek model extension for Drizzle.
@ -40,7 +33,8 @@ export class DrizzleModel<
Shape extends ModelShape, Identifier extends keyof Shape = any, TSchema extends TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>
>
{
constructor(protected model: ModelType,
constructor(protected modelClass: ModelClass<ModelType & DrizzleExtension<ModelType, TableType, TC, TQueryResult, TFullSchema, Scopes, Shape, Identifier, TSchema>, Shape, Identifier>,
protected model: ModelType,
protected database: PgDatabase<TQueryResult, TFullSchema, TSchema>,
protected table: TableType,
protected scopes: Scopes & ThisType<ModelQuery<ModelType, TableType, TC, TQueryResult, TFullSchema, Shape, Identifier, TSchema>>)
@ -75,50 +69,17 @@ export class DrizzleModel<
*/
async save(): Promise<boolean>
{
// Get serialized model update.
const serializedModel = this.model.patch();
// Get table columns of the model.
const tableColumns = getTableColumns(this.table);
// Create model data for the database.
const databaseModelData = Object.fromEntries(
Object.entries(serializedModel).map(
// Only keep table column values and convert them into database types, if it is required.
([key, value]) => [key, tableColumns?.[key] ? (serializedToDatabaseTypes?.[tableColumns?.[key].dataType]?.(value) ?? value) : undefined]
)
) as Partial<SerializedModel<Shape>>;
// Initialize query result variable.
let result: any;
if (this.model.isNew())
{ // Insert the new model in database and get the inserted row data.
result = (await (this.database.insert(this.table)).values(databaseModelData as any).returning() as any)?.[0];
}
else
{ // Update model data in database, and get the updated row data.
result = (await (this.database.update(this.table)
.set(databaseModelData)
.where(eq((this.table as any)?.[this.model.getIdentifierName()], this.model.getIdentifier())))
.returning() as any)?.[0];
}
// Update model from inserted or updated row data.
this.model.deserialize(result);
return true;
// New query to save the model.
return query(this.modelClass).save(this.model as any);
}
/**
* Refresh model data from database.
*/
async refresh(): Promise<void>
{
// Update model from up-to-date row data.
this.model.deserialize(
(( // Retrieve model data from database.
await this.database.select().from(this.table)
.where(eq((this.table as any)?.[this.model.getIdentifierName()], this.model.getIdentifier()))
) as any)[0]
);
// New query to refresh the model.
await query(this.modelClass).refresh(this.model as any);
}
}
@ -145,7 +106,10 @@ export function drizzle<
drizzle(): DrizzleModel<ModelType, TableType, TC, TQueryResult, TFullSchema, Scopes, Shape, Identifier, TSchema>
{
// Initialize drizzle model manager instance.
return new DrizzleModel<ModelType, TableType, TC, TQueryResult, TFullSchema, Scopes, Shape, Identifier, TSchema>(this, database, table, scopes);
return new DrizzleModel<ModelType, TableType, TC, TQueryResult, TFullSchema, Scopes, Shape, Identifier, TSchema>(
this.constructor as ModelClass<ModelType & DrizzleExtension<ModelType, TableType, TC, TQueryResult, TFullSchema, Scopes, Shape, Identifier, TSchema>, Shape, Identifier>,
this, database, table, scopes
);
}
};
}

View file

@ -1,5 +1,5 @@
import {AnyModel, IdentifierType, Model, ModelClass, ModelShape, SerializedModel} from "@sharkitek/core";
import {eq, getTableName, Table, TableConfig} from "drizzle-orm";
import {eq, getTableColumns, getTableName, Table, TableConfig} from "drizzle-orm";
import {PgDatabase} from "drizzle-orm/pg-core";
import {PgQueryResultHKT} from "drizzle-orm/pg-core/session";
import type {DBQueryConfig, ExtractTablesWithRelations, TablesRelationalConfig} from "drizzle-orm/relations";
@ -7,6 +7,13 @@ import type {KnownKeysOnly} from "drizzle-orm/utils";
import {DrizzleExtension} from "./Drizzle";
import {inArray} from "drizzle-orm/sql/expressions/conditions";
/**
* Serialized values converters for database.
*/
export const serializedToDatabaseTypes: Record<string, (serializedValue: any) => any> = {
"date": (serializedValue: string) => new Date(serializedValue),
};
/**
* Model query manager.
*/
@ -33,6 +40,62 @@ export class ModelQuery<
return (new this.modelClass()).deserialize(rawModel) as ModelType;
}
/**
* Save a model in database.
* @param model The model to save.
*/
async save(model: ModelType): Promise<boolean>
{
// Get serialized model update.
const serializedModel = model.patch();
// Get table columns of the model.
const tableColumns = getTableColumns(this.table);
// Create model data for the database.
const databaseModelData = Object.fromEntries(
Object.entries(serializedModel).map(
// Only keep table column values and convert them into database types, if it is required.
([key, value]) => [key, tableColumns?.[key] ? (serializedToDatabaseTypes?.[tableColumns?.[key].dataType]?.(value) ?? value) : undefined]
)
) as Partial<SerializedModel<Shape>>;
// Initialize query result variable.
let result: any;
if (model.isNew())
{ // Insert the new model in database and get the inserted row data.
result = (await (this.database.insert(this.table)).values(databaseModelData as any).returning() as any)?.[0];
}
else
{ // Update model data in database, and get the updated row data.
result = (await (this.database.update(this.table)
.set(databaseModelData)
.where(eq((this.table as any)?.[model.getIdentifierName()], model.getIdentifier())))
.returning() as any)?.[0];
}
// Update model from inserted or updated row data.
model.deserialize(result);
return true;
}
/**
* Refresh given model data from database.
* @param model The model to refresh.
*/
async refresh(model: ModelType): Promise<void>
{
// Update model from up-to-date row data.
model.deserialize(
(( // Retrieve model data from database.
await this.database.select().from(this.table)
.where(eq((this.table as any)?.[model.getIdentifierName()], model.getIdentifier()))
) as any)[0]
);
}
/**
* Find models matching the given configuration.
* @param config Request configuration.