UNPKG

tspace-mysql

Version:

Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.

1,359 lines (1,358 loc) 81.4 kB
import "reflect-metadata"; import { DB } from "./DB"; import { AbstractModel } from "./Abstracts/AbstractModel"; import { Blueprint } from "./Blueprint"; import { JoinModel } from "./JoinModel"; import { CONSTANTS } from "../constants"; import type { T } from "./UtilityTypes"; import type { TCache, TRelationOptions, TValidateSchema, TGlobalSetting, TPattern, TRelationQueryOptions, TModelOrObject, TRelationKeys, TLifecycle } from "../types"; import { Join } from "./Join"; /** * * The 'Model' class is a representation of a database table * @generic {Type} TS * @generic {Type} TR * @example * import { Model, Blueprint, type T } from 'tspace-mysql' * * const schema = { * id : new Blueprint().int().primary().autoIncrement(), * uuid : new Blueprint().varchar(50).null(), * email : new Blueprint().varchar(50).null(), * name : new Blueprint().varchar(255).null(), * } * * type TS = T.Schema<typeof Schema> * * class User extends Model<TS> { * boot() { * this.useSchema(schema) * } * } * * const users = await new User().findMany() * console.log(users) */ declare class Model<TS extends Record<string, any> = any, TR = unknown> extends AbstractModel { constructor(); /** * The 'global' method is used setting global variables in models. * @static * @param {GlobalSetting} settings * @example * Model.global({ * softDelete : true, * uuid : true, * timestamp : true, * debug : true * logger : { * selected : true, * inserted : true, * updated : true, * deleted : true * }, * }) * @returns {void} void */ static global(settings: TGlobalSetting): void; /** * The 'table' method is used get table name. * @static * @returns {string} name of the table */ static get table(): string | null; /** * The 'formatPattern' method is used to change the format of the pattern. * @param {object} data { data , pattern } * @property {Record | string} data * @property {string} parttern * @returns {Record | string} T */ static formatPattern<T extends Record<string, any> | string>({ data, pattern, }: { data: T; pattern: TPattern; }): T; /** * The 'instance' method is used get instance. * @override * @static * @returns {Model} instance of the Model */ static get instance(): Model; /** * The 'cache' method is used get the functions from the Cache * @returns {TCache} cache */ static get cache(): TCache; /** * The 'boot' method is a special method that you can define within a model. * @example * class User extends Model { * boot() { * this.useUUID() * this.usePrimaryKey('id') * this.useTimestamp() * this.useSoftDelete() * } * } * @returns {void} void */ protected boot(): void; /** * The 'globalScope' method is a feature that allows you to apply query constraints to all queries for a given model. * * Suported only methods -> select , except , where , orderBy, GroupBy , limit and offset * @example * class User extends Model { * boot() { * super() * this.globalScope(query => { * return query.where('id' , '>' , 10) * }) * } * } * @returns {void} void */ protected globalScope<M extends Model>(callback: (query: M) => M): this; /** * The 'useGlobalScope' method is a feature that allows you to apply query constraints to all queries for a given model. * * Suported only methods -> select , except , where , orderBy, GroupBy , limit and offset * * @example * class User extends Model { * boot() { * super() * this.useGlobalScope(query => { * return query.where('id' , '>' , 10) * }) * } * } * @returns {void} void */ protected useGlobalScope<M extends Model>(callback: (query: M) => M): this; /** * The "useObserve" method is used to pattern refers to a way of handling model events using observer classes. * Model events are triggered when certain actions occur on models, * such as creating, updating, deleting, or saving a record. * * Observers are used to encapsulate the event-handling logic for these events, * keeping the logic separate from the model itself and promoting cleaner, more maintainable code. * @param {Function} observer * @returns this * @example * * class UserObserve { * public selected(results : unknown) { * console.log({ results , selected : true }) * } * * public created(results : unknown) { * console.log({ results , created : true }) * } * * public updated(results : unknown) { * console.log({ results ,updated : true }) * } * * public deleted(results : unknown) { * console.log({ results ,deleted : true }) * } * } * * class User extends Model { * boot() { * super() * this.useObserver(UserObserve) * } * } */ protected useObserver(observer: new () => { selected: Function; created: Function; updated: Function; deleted: Function; }): this; /** * The "useLogger" method is used to keeping query data and changed in models. * * @type {object} options * @property {boolean} options.selected - default is false * @property {boolean} options.inserted - default is true * @property {boolean} options.updated - default is true * @property {boolean} options.deleted - default is true * @example * class User extends Model { * boot() { * this.useLogger({ * selected : true, * inserted : true, * updated : true, * deleted : true, * }) * } * } * @returns {this} this */ protected useLogger({ selected, inserted, updated, deleted, }?: { selected?: boolean | undefined; inserted?: boolean | undefined; updated?: boolean | undefined; deleted?: boolean | undefined; }): this; /** * The "useSchema" method is used to define the schema. * * It's automatically create, called when not exists table or columns. * @param {object} schema using Blueprint for schema * @example * import { Blueprint } from 'tspace-mysql'; * class User extends Model { * boot() { * this.useSchema ({ * id : new Blueprint().int().notNull().primary().autoIncrement(), * uuid : new Blueprint().varchar(50).null(), * email : new Blueprint().varchar(50).null(), * name : new Blueprint().varchar(255).null(), * created_at : new Blueprint().timestamp().null(), * updated_at : new Blueprint().timestamp().null() * }) * } * } * @returns {this} this */ protected useSchema(schema: Record<string, Blueprint>): this; /** * The "useTransform " method is used to define value transformers for model columns.. * * Each transformer defines how a value is converted: * - `to` : before persisting to the database * - `from` : after retrieving from the database * * Transformers can be synchronous or asynchronous. * * @param {object} transforms * @example * import { Blueprint } from 'tspace-mysql'; * class User extends Model { * boot() { * this.useTransform({ * name : { * to : async (v) => `${v}-> transform@before`, * from : async (v) => `${v}-> transform@after`, * }, * }) * } * } * @returns {this} this */ protected useTransform(transforms: Record<string, { to: (value: unknown) => any | Promise<any>; from: (value: unknown) => any | Promise<any>; }>): this; /** * The "usePrimaryKey" method is add primary keys for database tables. * * @param {string} primary * @returns {this} this * @example * class User extends Model { * boot() { * this.usePrimaryKey() * } * } */ protected usePrimaryKey(primary: string): this; /** * The "useUUID" method is a concept of using UUIDs (Universally Unique Identifiers) as column 'uuid' in table. * * It's automatically genarate when created a result. * @param {string?} column [column=uuid] make new name column for custom column replace uuid with this * @returns {this} this * @example * class User extends Model { * boot() { * this.useUUID() * } * } */ protected useUUID(column?: string): this; /** * The "useDebug" method is viewer raw-sql logs when excute the results. * @returns {this} this */ protected useDebug(): this; /** * The "usePattern" method is used to assign pattern [snake_case , camelCase]. * @param {string} pattern * @returns {this} this * @example * class User extends Model { * boot() { * this.usePattern('camelCase') * } * } */ protected usePattern(pattern: TPattern): this; /** * The "useCamelCase" method is used to assign pattern camelCase. * @returns {this} this * @example * class User extends Model { * boot() { * this.useCamelCase() * } * } */ protected useCamelCase(): this; /** * The "SnakeCase" method is used to assign pattern snake_case. * @returns {this} this * @example * class User extends Model { * boot() { * this.SnakeCase() * } * } */ protected useSnakeCase(): this; /** * The "useSoftDelete" refer to a feature that allows you to "soft delete" records from a database table instead of permanently deleting them. * * Soft deleting means that the records are not physically removed from the database but are instead marked as deleted by setting a timestamp in a dedicated column. * * This feature is particularly useful when you want to retain a record of deleted data and potentially recover it later, * or when you want to maintain referential integrity in your database * @param {string?} column default deleted_at * @returns {this} this * @example * class User extends Model { * boot() { * this.useSoftDelete('deletedAt') * } * } */ protected useSoftDelete(column?: string): this; /** * The "useTimestamp" method is used to assign a timestamp when creating a new record, * or updating a record. * @param {object} timestampFormat * @property {string} timestampFormat.createdAt - change new name column replace by default [created at] * @property {string} timestampFormat.updatedAt - change new name column replace by default updated at * @returns {this} this * @example * class User extends Model { * boot() { * this.useTimestamp({ * createdAt : 'createdAt', * updatedAt : 'updatedAt' * }) * } * } */ protected useTimestamp(timestampFormat?: { createdAt: string; updatedAt: string; }): this; /** * This "useTable" method is used to assign the name of the table. * @param {string} table table name in database * @returns {this} this * @example * class User extends Model { * boot() { * this.useTable('setTableNameIsUser') // => 'setTableNameIsUser' * } * } */ protected useTable(table: string): this; /** * This "useTableSingular" method is used to assign the name of the table with signgular pattern. * @returns {this} this * @example * class User extends Model { * boot() { * this.useTableSingular() // => 'user' * } * } */ protected useTableSingular(): this; /** * This "useTablePlural " method is used to assign the name of the table with pluarl pattern * @returns {this} this * @example * class User extends Model { * boot() { * this.useTablePlural() // => 'users' * } * } */ protected useTablePlural(): this; /** * This 'useValidationSchema' method is used to validate the schema when have some action create or update. * @param {Object<ValidateSchema>} schema types (String Number and Date) * @returns {this} this * @example * class User extends Model { * boot() { * this.useValidationSchema({ * id : Number, * uuid : Number, * name : { * type : String, * require : true * // json : true, * // enum : ["1","2","3"] * }, * email : { * type : String, * require : true, * length : 199, * match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, * unique : true, * fn : async (email : string) => /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email) * }, * createdAt : Date, * updatedAt : Date, * deletedAt : Date * }) * } * } */ protected useValidationSchema(schema?: TValidateSchema): this; /** * This 'useValidateSchema' method is used to validate the schema when have some action create or update. * @param {Object<ValidateSchema>} schema types (String Number and Date) * @returns {this} this * @example * class User extends Model { * boot() { * this.useValidationSchema({ * id : Number, * uuid : string, * name : { * type : String, * require : true * }, * email : { * type : String, * require : true, * length : 199, * // json : true, * // enum : ["1","2","3"] * match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, * unique : true, * fn : async (email : string) => /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email) * }, * createdAt : Date, * updatedAt : Date, * deletedAt : Date * }) * } * } */ protected useValidateSchema(schema?: TValidateSchema): this; /** * The "useHooks" method is used to assign hook function when execute returned results to callback function. * @param {Function[]} funs functions for callback result * @returns {this} this * @example * class User extends Model { * boot() { * this.useHook([(results) => console.log(results)]) * } * } */ protected useHooks(funs: Function[]): this; /** * The "useMiddleware" method is used to register functions that run before the main handler executes. * * @param {Function} func functions for execute * @returns {this} this * @example * class User extends Model { * boot() { * this.useMiddleware(() => function ...) * } * } */ protected useMiddleware(func: Function): this; /** * * The 'useLifecycle' method is used to register lifecycle hooks for model events. * * Supported lifecycle types: * - "beforeInsert" * - "afterInsert" * - "beforeUpdate" * - "afterUpdate" * - "beforeRemove" * - "afterRemove" * * Each hook is executed in the order they are registered. * * @param {LifecycleType} type * The lifecycle event to register. Determines which hook * collection the function(s) will be stored in. * * @param {Function | Function[]} funcs * A function or array of functions to be executed when the * lifecycle event is triggered. All functions must be valid * JavaScript functions, otherwise an assertion error is thrown. * * @throws {Error} * Throws if any provided item in `funcs` is not a function. * * @returns {this} * Returns the current instance to enable method chaining. * * @example * // Register a single hook * this.useLifecycle("beforeInsert", () => { * console.log("Before insert hook"); * }); * * @example * // Register multiple hooks * this.useLifecycle("afterUpdate", [ * () => console.log("Update #1"), * () => console.log("Update #2"), * ]); * * @example * // Method chaining * this * .useLifecycle("beforeInsert", fnA) * .useLifecycle("afterInsert", fnB); */ protected useLifecycle(type: TLifecycle, funcs: Function | Function[]): this; /** * The "whenCreatingTable" method is used exection function when creating the table. * @param {Function} fn functions for executing when creating the table * @returns {this} this * @example * class User extends Model { * boot() { * this.whenCreatingTable(async () => { * await new User() * .create({ * ...columns * }) * .save() * }) * } * } */ protected whenCreatingTable(fn: () => Promise<any> | any): this; /** * The "onCreatingTable" method is used exection function when creating the table. * @param {Function} fn functions for executing when creating the table * @returns {this} this * @example * class User extends Model { * boot() { * this.onCreatingTable(async () => { * await new User() * .create({ * ...columns * }) * .save() * }) * } * } */ protected onCreatedTable(fn: () => Promise<any> | any): this; /** * The "onSyncTable" method is used exection function when sync the table. * @param {Function} fn functions for executing when sync table * @returns {this} this * @example * class User extends Model { * boot() { * this.onSyncTable(async () => { * console.log('onSyncTable!!') * }) * } * } */ onSyncTable(fn: () => Promise<any> | any): this; /** * exceptColumns for method except * @override * @returns {promise<string>} string */ protected exceptColumns(): Promise<string[]>; /** * Build method for relation in model * @param {string} name name relation registry in your model * @param {Function} callback query callback * @returns {this} this */ protected buildMethodRelation<K extends T.RelationKeys<this>>(name: K, callback?: Function): this; /** * The 'audit' method is used to sets the audit information for the tracking. * * @param {number} userId - The ID of the user performing the audit. * @param {Record<string, any>} [metadata] - Optional metadata to store with the audit. * @returns {this} this */ audit(userId: number, metadata?: Record<string, any>): this; meta(meta: "MAIN" | "SUBORDINATE"): this; /** * The 'typeOfSchema' method is used get type of schema. * @returns {TS} type of schema */ typeOfSchema(): TS; /** * The 'typeOfRelation' method is used get type of relation. * @returns {TR} type of Relation */ typeOfRelation(): TR; /** * The 'cache' method is used get data from cache. * @param {Object} object * @property {string} key key of cache * @property {number} expires ms * @returns {this} this */ cache({ key, expires }: { key: string; expires: number; }): this; /** * * @override * @param {string[]} ...columns * @returns {this} this */ select<K extends T.ColumnKeys<this> | "*">(...columns: K[]): this; addSelect<K extends T.ColumnKeys<this>>(...columns: K[]): this; /** * * @override * @param {...string} columns * @returns {this} this */ except<K extends T.ColumnKeys<this>>(...columns: K[]): this; /** * * @override * @returns {this} this */ exceptTimestamp(): this; /** * * @override * @param {string} column * @param {string?} order by default order = 'asc' but you can used 'asc' or 'desc' * @returns {this} */ orderBy<K extends T.ColumnKeys<this>>(column: K, order?: "ASC" | "asc" | "DESC" | "desc"): this; /** * * @override * @param {string?} columns [column=id] * @returns {this} */ latest<K extends T.ColumnKeys<this>>(...columns: K[]): this; /** * * @override * @param {string?} columns [column=id] * @returns {this} */ oldest<K extends T.ColumnKeys<this>>(...columns: K[]): this; /** * * @override * @param {string?} columns [column=id] * @returns {this} */ groupBy<K extends T.ColumnKeys<this>>(...columns: K[]): this; /** * @override * @param {string} column * @returns {string} return table.column */ bindColumn(column: string, pattern?: boolean): string; /** * * @override * The 'makeSelectStatement' method is used to make select statement. * @returns {Promise<string>} string */ makeSelectStatement(): Promise<string>; /** * * @override * The 'makeInsertStatement' method is used to make insert table statement. * @returns {Promise<string>} string */ makeInsertStatement(): Promise<string>; /** * * @override * The 'makeUpdateStatement' method is used to make update table statement. * @returns {Promise<string>} string */ makeUpdateStatement(): Promise<string>; /** * * @override * The 'makeDeleteStatement' method is used to make delete statement. * @returns {Promise<string>} string */ makeDeleteStatement(): Promise<string>; /** * * @override * The 'makeCreateTableStatement' method is used to make create table statement. * @returns {Promise<string>} string */ makeCreateTableStatement(): Promise<string>; /** * * Clone instance of model * @param {Model} instance instance of model * @returns {this} this */ clone(instance: Model): this; /** * * Copy an instance of model * @param {Model} instance instance of model * @param {Object} options keep data * @returns {Model} Model */ copyModel(instance: Model, options?: { update?: boolean; insert?: boolean; delete?: boolean; where?: boolean; limit?: boolean; orderBy?: boolean; join?: boolean; offset?: boolean; groupBy?: boolean; select?: boolean; having?: boolean; relations?: boolean; }): this; /** * * execute the query using raw sql syntax * @override * @param {string} sql * @returns {this} this */ protected _queryStatement(sql: string, { retry }?: { retry?: boolean | undefined; }): Promise<any[]>; /** * * execute the query using raw sql syntax actions for insert update and delete * @override * @param {string} sql * @returns {this} this */ protected _actionStatement(sql: string, { retry }?: { retry?: boolean | undefined; }): Promise<any>; /** * The 'CTEs' method is used to create common table expressions(CTEs). * * @override * @returns {string} return sql query */ CTEs<M extends Model>(as: string, callback: (query: M) => M, bindModel?: new () => M): this; /** * The 'disableSoftDelete' method is used to disable the soft delete. * * @param {boolean} condition * @returns {this} this */ disableSoftDelete(condition?: boolean): this; /** * The 'disableSoftDelete' method is used to disable the soft delete. * * @param {boolean} condition * @returns {this} this */ disableTransform(condition?: boolean): this; disabledValidateSchema(condition?: boolean): this; /** * The 'ignoreSoftDelete' method is used to disable the soft delete. * @param {boolean} condition * @returns {this} this */ ignoreSoftDelete(condition?: boolean): this; /** * The 'disableVoid' method is used to disable void. * * @returns {this} this */ disableVoid(): this; /** * The 'ignoreVoid' method is used to ignore void. * * @returns {this} this */ ignoreVoid(): this; /** * The 'disabledGlobalScope' method is used to disable globalScope. * * @returns {this} this */ disabledGlobalScope(condition?: boolean): this; /** * The 'ignoreGlobalScope' method is used to disable globalScope. * * @returns {this} this */ ignoreGlobalScope(condition?: boolean): this; /** * The 'with' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient. * * @param {...string} nameRelations ...name registry in models using (hasOne , hasMany , belongsTo , belongsToMany) * @returns {this} this * @example * import { Model , TR } from 'tspace-mysql' * * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use 'with' for results of relationship * await new User().with('posts').findMany() * */ with<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relations' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient. * * @param {...string} nameRelations ...name registry in models using (hasOne , hasMany , belongsTo , belongsToMany) * @returns {this} this * @example * import { Model , TR } from 'tspace-mysql' * * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use 'with' for results of relationship * await new User().relations('posts').findMany() * */ relations<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'withAll' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient * It's method ignore soft delete * @param {...string} nameRelations if data exists return empty * @returns {this} this */ withAll<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relationsAll' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient. * * It's method ignore soft delete * @param {...string} nameRelations if data exists return empty * @returns {this} this */ relationsAll<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'withCount' method is used to eager load related (relations) data and count data in the relation. * * @param {...string} nameRelations if data exists return 0 * @returns {this} this */ withCount<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relationsCount' method is used to eager load related (relations) data and count data in the relation. * * @param {...string} nameRelations if data exists return 0 * @returns {this} this */ relationsCount<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'withTrashed' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient. * * It's method return results only in trash (soft deleted) * @param {...string} nameRelations if data exists return blank * @returns {this} this */ withTrashed<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relationsTrashed' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient. * * It's method return results only in trash (soft deleted) * @param {...string} nameRelations if data exists return blank * @returns {this} this */ relationsTrashed<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'withExists' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient * It's method return only exists result of relation query * @param {...string} nameRelations * @returns {this} this * @example * import { Model } from 'tspace-mysql' * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use with for results of relationship if relations is exists * await new User().withExists('posts').findMany() */ withExists<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relationsExists' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient * It's method return only exists result of relation query * @param {...string} nameRelations * @returns {this} this * @example * import { Model } from 'tspace-mysql' * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use with for results of relationship if relations is exists * await new User().relationsExists('posts').findMany() */ relationsExists<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'has' method is used to eager load related (relations) data when retrieving records from a database. * * Eager loading allows you to retrieve a primary model and its related models in a more efficient * It's method return only exists result of relation query * @param {...string} nameRelations * @returns {this} this * @example * import { Model } from 'tspace-mysql' * import { TRelationOptions } from '../types'; * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use with for results of relationship if relations is exists * await new User().has('posts').findMany() */ has<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'withNotExists' method is used to eager load related (relations) data when not exists relation from a database. * * It's method return only not exists result of relation query * @param {...string} nameRelations * @returns {this} this * @example * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use with for results of relationship if relations is exists * await new User().withNotExists('posts').findMany() */ withNotExists<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * The 'relationsNotExists' method is used to eager load related (relations) data when not exists relation from a database. * * It's method return only not exists result of relation query * @param {...string} nameRelations * @returns {this} this * @example * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * // use with for results of relationship if relations is exists * await new User().relationsNotExists('posts').findMany() */ relationsNotExists<K extends T.RelationKeys<this>>(...nameRelations: K[]): this; /** * * The 'withQuery' method is particularly useful when you want to filter or add conditions records based on related data. * * Use relation '${name}' registry models then return callback queries * @param {string} nameRelation name relation in registry in your model * @param {function} callback query callback * @param {object} options pivot the query * @example * import { Model } from 'tspace-mysql' * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * * class Comment extends Model { * constructor(){ * super() * this.hasMany({ name : 'users' , model : User }) * this.belongsTo({ name : 'post' , model : Post }) * } * } * * await new User().relations('posts') * .withQuery('posts', (query : Post) => { * return query.relations('comments','user') * .withQuery('comments', (query : Comment) => { * return query.relations('user','post') * }) * .withQuery('user', (query : User) => { * return query.relations('posts').withQuery('posts',(query : Post)=> { * return query.relations('comments','user') * // relation n, n, ...n * }) * }) * }) * .findMany() * @returns {this} this */ withQuery<K extends T.RelationKeys<this>, R extends T.Relations<this>>(nameRelation: K, callback: (query: `$${K & string}` extends keyof R ? R[`$${K & string}`] extends (infer X)[] ? X : R[`$${K & string}`] extends Model ? R[`$${K & string}`] : Model : K extends keyof R ? R[K] extends (infer X)[] ? X : R[K] extends Model ? R[K] : Model : Model) => any, options?: { pivot: boolean; }): this; withQueryExists<K extends T.RelationKeys<this>, R extends T.Relations<this>>(nameRelation: K, callback: (query: `$${K & string}` extends keyof R ? R[`$${K & string}`] extends (infer X)[] ? X : R[`$${K & string}`] extends Model ? R[`$${K & string}`] : Model : K extends keyof R ? R[K] extends (infer X)[] ? X : R[K] extends Model ? R[K] : Model : Model) => any, options?: { pivot: boolean; }): this; /** * * The 'relationQuery' method is particularly useful when you want to filter or add conditions records based on related data. * * Use relation '${name}' registry models then return callback queries * @param {string} nameRelation name relation in registry in your model * @param {function} callback query callback * @param {object} options pivot the query * @example * import { Model } from 'tspace-mysql' * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * * class Comment extends Model { * constructor(){ * super() * this.hasMany({ name : 'users' , model : User }) * this.belongsTo({ name : 'post' , model : Post }) * } * } * * await new User().relations('posts') * .relationQuery('posts', (query : Post) => { * return query.relations('comments','user') * .relationQuery('comments', (query : Comment) => { * return query.relations('user','post') * }) * .relationQuery('user', (query : User) => { * return query.relations('posts').relationsQuery('posts',(query : Post)=> { * return query.relations('comments','user') * // relation n, n, ...n * }) * }) * }) * .findMany() * @returns {this} this */ relationQuery<K extends T.RelationKeys<this>, R extends T.Relations<this>>(nameRelation: K, callback: (query: `$${K & string}` extends keyof R ? R[`$${K & string}`] extends (infer X)[] ? X : R[`$${K & string}`] extends Model ? R[`$${K & string}`] : Model : K extends keyof R ? R[K] extends (infer X)[] ? X : R[K] extends Model ? R[K] : Model : Model) => any, options?: { pivot: boolean; }): this; /** * * The 'relationQueryExists' method is particularly useful when you want to filter or add conditions records based on related data. * * Use relation '${name}' registry models then return callback queries * @param {string} nameRelation name relation in registry in your model * @param {function} callback query callback * @param {object} options pivot the query * @example * import { Model } from 'tspace-mysql' * class User extends Model { * constructor(){ * super() * this.hasMany({ name : 'posts' , model : Post }) * } * } * * class Post extends Model { * constructor(){ * super() * this.hasMany({ name : 'comments' , model : Comment }) * this.belongsTo({ name : 'user' , model : User }) * } * } * * class Comment extends Model { * constructor(){ * super() * this.hasMany({ name : 'users' , model : User }) * this.belongsTo({ name : 'post' , model : Post }) * } * } * * await new User().relations('posts') * .relationQuery('posts', (query : Post) => { * return query.relations('comments','user') * .relationQuery('comments', (query : Comment) => { * return query.relations('user','post') * }) * .relationQuery('user', (query : User) => { * return query.relations('posts').relationsQuery('posts',(query : Post)=> { * return query.relations('comments','user') * // relation n, n, ...n * }) * }) * }) * .findMany() * @returns {this} this */ relationQueryExists<K extends T.RelationKeys<this>, R extends T.Relations<this>>(nameRelation: K, callback: (query: `$${K & string}` extends keyof R ? R[`$${K & string}`] extends (infer X)[] ? X : R[`$${K & string}`] extends Model ? R[`$${K & string}`] : Model : K extends keyof R ? R[K] extends (infer X)[] ? X : R[K] extends Model ? R[K] : Model : Model) => any, options?: { pivot: boolean; }): this; /** * * The 'findWithQuery' method is used to find instance call back from relation. * * @param {string} name name relation in registry in your model * @returns {Model} model instance */ findWithQuery<K extends T.RelationKeys<this>>(name: K): Model | null; /** * The 'hasOne' relationship defines a one-to-one relationship between two database tables. * * It indicates that a particular record in the primary table is associated with one and only one record in the related table. * * This is typically used when you have a foreign key in the related table that references the primary table. * * @param {object} relations registry relation in your model * @property {string} relation.name * @property {string} relation.as * @property {class} relation.model * @property {string} relation.localKey * @property {string} relation.foreignKey * @property {string} relation.freezeTable * @returns {this} this */ protected hasOne<K extends TR extends object ? TRelationKeys<TR> : string>({ name, as, model, localKey, foreignKey, freezeTable, }: TRelationOptions<K>): this; /** * The 'hasMany' relationship defines a one-to-many relationship between two database tables. * * It indicates that a record in the primary table can be associated with multiple records in the related table. * * This is typically used when you have a foreign key in the related table that references the primary table. * * @param {object} relations registry relation in your model * @property {string} relation.name * @property {string} relation.as * @property {class} relation.model * @property {string} relation.localKey * @property {string} relation.foreignKey * @property {string} relation.freezeTable * @returns {this} this */ protected hasMany<K extends TR extends object ? TRelationKeys<TR> : string>({ name, as, model, localKey, foreignKey, freezeTable, }: TRelationOptions<K>): this; /** * The 'belongsTo' relationship defines a one-to-one or many-to-one relationship between two database tables. * * It indicates that a record in the related table belongs to a single record in the primary table. * * This is typically used when you have a foreign key in the primary table that references the related table. * * @param {object} relations registry relation in your model * @property {string} relation.name * @property {string} relation.as * @property {class} relation.model * @property {string} relation.localKey * @property {string} relation.foreignKey * @property {string} relation.freezeTable * @returns {this} this */ protected belongsTo<K extends TR extends object ? TRelationKeys<TR> : string>({ name, as, model, localKey, foreignKey, freezeTable, }: TRelationOptions<K>): this; /** * The 'belongsToMany' relationship defines a many-to-many relationship between two database tables. * * It indicates that records in both the primary table and the related table can be associated * with multiple records in each other's table through an intermediate table. * * This is commonly used when you have a many-to-many relationship between entities, such as users and roles or products and categories. * @param {object} relations registry relation in your model * @property {string} relation.name * @property {string} relation.as * @property {class} relation.model * @property {string} relation.localKey * @property {string} relation.foreignKey * @property {string} relation.freezeTable freeae table name * @property {string} relation.pivot table name of pivot * @property {string} relation.oldVersion return value of old version * @property {class?} relation.modelPivot model for pivot * @returns {this} this */ protected belongsToMany<K extends TR extends object ? TRelationKeys<TR> : string>({ name, as, model, localKey, foreignKey, freezeTable, pivot, oldVersion, modelPivot, }: TRelationOptions<K>): this; /** * The 'belongsToManySingle' relationship defines a many-to-many relationship between two database tables. * But return object * * It indicates that records in both the primary table and the related table can be associated * with multiple records in each other's table through an intermediate table. * * This is commonly used when you have a many-to-many relationship between entities, such as users and roles or products and categories. * @param {object} relations registry relation in your model * @property {string} relation.name * @property {string} relation.as * @property {class} relation.model * @property {string} relation.localKey * @property {string} relation.foreignKey * @property {string} relation.freezeTable freeae table name * @property {string} relation.pivot table name of pivot * @property {string} relation.oldVersion return value of old version * @property {class?} relation.modelPivot model for pivot * @returns {this} this */ protected belongsToManySingle<K extends TR extends object ? TRelationKeys<TR> : string>({ name, as, model, localKey, foreignKey, freezeTable, pivot, oldVersion, modelPivot, }: TRelationOptions<K>): this; /** * The 'hasOneBuilder' method is useful for creating 'hasOne' relationship to function * * @param {object} relation registry relation in your model * @type {object} relation * @property {class} model * @property {string?} name * @property {string?} as * @property {string?} localKey * @property {string?} foreignKey * @property {string?} freezeTable * @param {Function?} callback callback of query * @returns {this} this */ protected hasOneBuilder({ name, as, model, localKey, foreignKey, freezeTable, }: TRelationQueryOptions, callback?: Function): this; /** * The 'hasManyBuilder' method is useful for creating 'hasMany' relationship to function * * @param {object} relation registry relation in your model * @type {object} relation * @property {class} model * @property {string?} name * @property {string?} as * @property {string?} localKey * @property {string?} foreignKey * @property {string?} freezeTable * @param {function?} callback callback of query * @returns {this} this */ protected hasManyBuilder({ name, as, model, localKey, foreignKey, freezeTable, }: TRelationQueryOptions, callback?: Function): this; /** * The 'belongsToBuilder' method is useful for creating 'belongsTo' relationship to function * @param {object} relation registry relation in your model * @type {object} relation * @property {class} model * @property {string?} name * @property {string?} as * @property {string?} localKey * @property {string?} foreignKey * @property {string?} freezeTable * @param {function?} callback callback of query * @returns {this} this */ protected belongsToBuilder({ name, as, mo