@spinajs/orm
Advanced tools
Comparing version 2.0.185 to 2.0.186
@@ -286,3 +286,5 @@ import { Container, Constructor, IContainer } from '@spinajs/di'; | ||
protected _container: IContainer; | ||
constructor(container: IContainer, insertQueryBuilder: QueryBuilder, column?: string | string[]); | ||
protected _returning: string[]; | ||
constructor(container: IContainer, insertQueryBuilder: QueryBuilder, column?: string | string[], returning?: string[]); | ||
getReturning(): string[]; | ||
getColumn(): string[]; | ||
@@ -309,3 +311,3 @@ getColumnsToUpdate(): (string | RawQuery)[]; | ||
} | ||
export declare class InsertQueryBuilder extends QueryBuilder<IUpdateResult> { | ||
export declare class InsertQueryBuilder extends QueryBuilder<IUpdateResult | any> { | ||
DuplicateQueryBuilder: OnDuplicateQueryBuilder; | ||
@@ -317,2 +319,3 @@ protected _values: any[][]; | ||
protected _replace: boolean; | ||
protected _returning: string[]; | ||
this: this; | ||
@@ -329,2 +332,3 @@ get Values(): any[][]; | ||
orReplace(): this; | ||
returning(columns: string[]): this; | ||
values(data: {} | Array<{}>): this; | ||
@@ -331,0 +335,0 @@ into(table: string, schema?: string): this; |
@@ -94,3 +94,8 @@ "use strict"; | ||
catch (err) { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
} | ||
@@ -108,7 +113,17 @@ }, onrejected); | ||
catch (err) { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
} | ||
}) | ||
.catch((err) => { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
}); | ||
@@ -897,7 +912,11 @@ } | ||
let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder { | ||
constructor(container, insertQueryBuilder, column) { | ||
constructor(container, insertQueryBuilder, column, returning) { | ||
this._parent = insertQueryBuilder; | ||
this._container = container; | ||
this._column = lodash_1.default.isArray(column) ? column : [column]; | ||
this._returning = returning ?? ["*"]; | ||
} | ||
getReturning() { | ||
return this._returning; | ||
} | ||
getColumn() { | ||
@@ -926,3 +945,3 @@ return this._column; | ||
(0, di_1.NewInstance)(), | ||
__metadata("design:paramtypes", [Object, QueryBuilder, Object]) | ||
__metadata("design:paramtypes", [Object, QueryBuilder, Object, Array]) | ||
], OnDuplicateQueryBuilder); | ||
@@ -977,2 +996,4 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder { | ||
super(container, driver, model); | ||
this._update = false; | ||
this._replace = false; | ||
this._method = enums_js_1.QueryMethod.INSERT; | ||
@@ -991,5 +1012,9 @@ this._columns = []; | ||
orReplace() { | ||
this._update = true; | ||
this._replace = true; | ||
return this; | ||
} | ||
returning(columns) { | ||
this._returning = columns; | ||
return this; | ||
} | ||
values(data) { | ||
@@ -1032,3 +1057,4 @@ const self = this; | ||
this._update = true; | ||
this.DuplicateQueryBuilder = new OnDuplicateQueryBuilder(this._container, this, columnToCheck); | ||
this.DuplicateQueryBuilder = new OnDuplicateQueryBuilder(this._container, this, columnToCheck, this._returning); | ||
this.QueryContext = interfaces_js_1.QueryContext.Upsert; | ||
return this.DuplicateQueryBuilder; | ||
@@ -1035,0 +1061,0 @@ } |
@@ -45,3 +45,4 @@ "use strict"; | ||
toDB(value, model, options) { | ||
switch (model[options.TypeColumn]) { | ||
const type = model[options.TypeColumn]; | ||
switch (type) { | ||
case 'string': | ||
@@ -48,0 +49,0 @@ return value; |
@@ -95,5 +95,3 @@ import { Constructor, IContainer } from '@spinajs/di'; | ||
export declare function ForwardBelongsTo(forwardRef: IForwardReference, foreignKey?: string, primaryKey?: string): any; | ||
export interface IHasManyDecoratorOptions { | ||
foreignKey?: string; | ||
primaryKey?: string; | ||
export interface IRelationDecoratorOptions { | ||
/** | ||
@@ -110,2 +108,24 @@ * Relation factory, sometimes we dont want to create standard relation object. | ||
} | ||
export interface IHasManyToManyDecoratorOptions extends IRelationDecoratorOptions { | ||
/** | ||
* target model primary key name | ||
*/ | ||
targetModelPKey?: string; | ||
/** | ||
* source model primary key name | ||
*/ | ||
sourceModelPKey?: string; | ||
/** | ||
* junction table target primary key name ( foreign key for target model ) | ||
*/ | ||
junctionModelTargetPk?: string; | ||
/** | ||
* junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
junctionModelSourcePk?: string; | ||
} | ||
export interface IHasManyDecoratorOptions extends IRelationDecoratorOptions { | ||
foreignKey?: string; | ||
primaryKey?: string; | ||
} | ||
/** | ||
@@ -126,8 +146,4 @@ * Creates one to many relation with target model. | ||
* @param targetModel - model for related data | ||
* @param targetModelPKey - target model primary key name | ||
* @param sourceModelPKey - source model primary key name | ||
* @param junctionModelTargetPk - junction table target primary key name ( foreign key for target model ) | ||
* @param junctionModelSourcePk - junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
export declare function HasManyToMany(junctionModel: Constructor<ModelBase>, targetModel: Constructor<ModelBase> | string, targetModelPKey?: string, sourceModelPKey?: string, junctionModelTargetPk?: string, junctionModelSourcePk?: string): any; | ||
export declare function HasManyToMany(junctionModel: Constructor<ModelBase>, targetModel: Constructor<ModelBase> | string, options?: IHasManyToManyDecoratorOptions): any; | ||
/** | ||
@@ -151,3 +167,3 @@ * Mark field as datetime type. It will ensure that conversion to & from DB is valid, eg. sqlite DB | ||
*/ | ||
export declare function UniversalConverter(typeColumn: string): any; | ||
export declare function UniversalConverter(typeColumn?: string): any; | ||
/** | ||
@@ -154,0 +170,0 @@ * Mark field as SET type. It will ensure that conversion to & from DB is valid, eg. to emulate field type SET in sqlite |
@@ -321,2 +321,3 @@ "use strict"; | ||
return extractDecoratorDescriptor((model, target, propertyKey) => { | ||
let type = Reflect.getMetadata('design:type', target, propertyKey); | ||
model.Relations.set(propertyKey, { | ||
@@ -331,4 +332,4 @@ Name: propertyKey, | ||
Recursive: false, | ||
Factory: options ? options.factory : null, | ||
RelationClass: options ? options.type : null, | ||
Factory: options?.factory ? options.factory : null, | ||
RelationClass: options?.type ? options.type : () => di_1.DI.resolve("__orm_relation_has_many_factory__", [type]), | ||
}); | ||
@@ -358,10 +359,7 @@ }); | ||
* @param targetModel - model for related data | ||
* @param targetModelPKey - target model primary key name | ||
* @param sourceModelPKey - source model primary key name | ||
* @param junctionModelTargetPk - junction table target primary key name ( foreign key for target model ) | ||
* @param junctionModelSourcePk - junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
function HasManyToMany(junctionModel, targetModel, targetModelPKey, sourceModelPKey, junctionModelTargetPk, junctionModelSourcePk) { | ||
function HasManyToMany(junctionModel, targetModel, options) { | ||
return extractDecoratorDescriptor((model, target, propertyKey) => { | ||
const targetModelDescriptor = (0, model_js_1.extractModelDescriptor)(targetModel); | ||
let type = Reflect.getMetadata('design:type', target, propertyKey); | ||
model.Relations.set(propertyKey, { | ||
@@ -374,7 +372,9 @@ Name: propertyKey, | ||
TargetModel: null, | ||
ForeignKey: targetModelPKey ?? targetModelDescriptor.PrimaryKey, | ||
PrimaryKey: sourceModelPKey ?? model.PrimaryKey, | ||
ForeignKey: options?.targetModelPKey ?? targetModelDescriptor.PrimaryKey, | ||
PrimaryKey: options?.sourceModelPKey ?? model.PrimaryKey, | ||
JunctionModel: junctionModel, | ||
JunctionModelTargetModelFKey_Name: junctionModelTargetPk ?? `${targetModelDescriptor.Name.toLowerCase()}_id`, | ||
JunctionModelSourceModelFKey_Name: junctionModelSourcePk ?? `${model.Name.toLowerCase()}_id`, | ||
JunctionModelTargetModelFKey_Name: options?.junctionModelTargetPk ?? `${targetModelDescriptor.Name.toLowerCase()}_id`, | ||
JunctionModelSourceModelFKey_Name: options?.junctionModelSourcePk ?? `${model.Name.toLowerCase()}_id`, | ||
RelationClass: options?.type ? options.type : () => di_1.DI.resolve("__orm_relation_has_many_to_many_factory__", [type]), | ||
Factory: options ? options.factory : null, | ||
}); | ||
@@ -430,3 +430,3 @@ }); | ||
Options: { | ||
TypeColumn: typeColumn, | ||
TypeColumn: typeColumn ?? "Type", | ||
}, | ||
@@ -433,0 +433,0 @@ }); |
@@ -40,3 +40,9 @@ "use strict"; | ||
if (model[val.Name]) { | ||
obj[val.Name] = [...model[val.Name].map((x) => x.dehydrateWithRelations())]; | ||
const v = [...model[val.Name]]; | ||
if (v.length === 0) { | ||
obj[val.Name] = []; | ||
} | ||
else { | ||
obj[val.Name] = [v.map((x) => x.dehydrateWithRelations())]; | ||
} | ||
} | ||
@@ -43,0 +49,0 @@ } |
@@ -33,3 +33,5 @@ "use strict"; | ||
const column = descriptor.Columns?.find((c) => c.Name === k); | ||
target[k] = column.Converter ? column.Converter.fromDB(values[k], values, descriptor.Converters.get(column.Name).Options) : values[k]; | ||
if (values[k] !== undefined) { | ||
target[k] = column.Converter ? column.Converter.fromDB(values[k], values, descriptor.Converters.get(column.Name)?.Options) : values[k]; | ||
} | ||
}); | ||
@@ -77,3 +79,3 @@ } | ||
}); | ||
const rel = new relation_objects_js_1.OneToManyRelationList(target, val.TargetModel, val, mapRel); | ||
const rel = new relation_objects_js_1.OneToManyRelationList(target, val, mapRel); | ||
entity[key] = rel; | ||
@@ -80,0 +82,0 @@ delete target[val.ForeignKey]; |
@@ -18,2 +18,5 @@ export * from './interfaces.js'; | ||
export * from './exceptions.js'; | ||
export * from "./metadata.js"; | ||
export * from './fp.js'; | ||
export * from './bootstrap.js'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -34,2 +34,5 @@ "use strict"; | ||
__exportStar(require("./exceptions.js"), exports); | ||
__exportStar(require("./metadata.js"), exports); | ||
__exportStar(require("./fp.js"), exports); | ||
__exportStar(require("./bootstrap.js"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -18,3 +18,4 @@ import { Op } from './enums.js'; | ||
Schema = 4, | ||
Transaction = 5 | ||
Transaction = 5, | ||
Upsert = 6 | ||
} | ||
@@ -45,6 +46,6 @@ export declare enum ColumnAlterationType { | ||
/** | ||
* Removes from relation & deletes from db | ||
* | ||
* @param obj - data to remove | ||
*/ | ||
* Removes from relation & deletes from db | ||
* | ||
* @param obj - data to remove | ||
*/ | ||
remove(obj: R | R[] | ((a: R, b: R) => boolean)): R[]; | ||
@@ -394,3 +395,3 @@ /** | ||
*/ | ||
Factory?: (model: ModelBase<unknown>, relation: IRelationDescriptor, container: IContainer) => Relation<ModelBase<unknown>, ModelBase<unknown>>; | ||
Factory?: (model: ModelBase<unknown>, relation: IRelationDescriptor, container: IContainer, data: any[]) => Relation<ModelBase<unknown>, ModelBase<unknown>>; | ||
/** | ||
@@ -400,3 +401,3 @@ * sometimes we dont want to create standard relation object, so we create type | ||
*/ | ||
RelationClass?: Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>>; | ||
RelationClass?: Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>> | (() => Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>>); | ||
} | ||
@@ -465,11 +466,11 @@ export interface IModelStatic extends Constructor<ModelBase<unknown>> { | ||
*/ | ||
destroy(): Promise<void>; | ||
destroy(): Promise<IUpdateResult>; | ||
/** | ||
* If model can be in achived state - sets archived at date and saves it to db | ||
*/ | ||
archive(): Promise<void>; | ||
archive(): Promise<IUpdateResult>; | ||
/** | ||
* Updates model to db | ||
*/ | ||
update(): Promise<void>; | ||
update(): Promise<IUpdateResult>; | ||
/** | ||
@@ -484,6 +485,4 @@ * Save all changes to db. It creates new entry id db or updates existing one if | ||
* its value in db if primary key is set | ||
* | ||
* @param insertBehaviour - insert mode | ||
*/ | ||
insertOrUpdate(insertBehaviour?: InsertBehaviour): Promise<void>; | ||
insertOrUpdate(): Promise<IUpdateResult>; | ||
/** | ||
@@ -632,2 +631,8 @@ * Gets model data from database and returns as fresh instance. | ||
abstract down(connection: OrmDriver): Promise<void>; | ||
/** | ||
* Migrate data - execute AFTER orm module has been initialized | ||
* | ||
* It means that all model & relations are avaible | ||
*/ | ||
data(): Promise<void>; | ||
} | ||
@@ -973,3 +978,3 @@ /** | ||
*/ | ||
toDB(_value: any, _model: ModelBase<any>, _options: any): any; | ||
toDB(_value: any, _model: ModelBase<any>, _options?: any): any; | ||
/** | ||
@@ -980,3 +985,3 @@ * Converts value from database type eg. mysql timestamp to DateTime | ||
*/ | ||
fromDB(_value: any, _rawData: any, _options: any): any; | ||
fromDB(_value: any, _rawData?: any, _options?: any): any; | ||
} | ||
@@ -989,2 +994,7 @@ /** | ||
/** | ||
* Convert 0/1 to boolean ( mysql, sqlite etc. use 0/1 for boolean fields and tinyint/bit types ) | ||
*/ | ||
export declare class BooleanValueConverter extends ValueConverter { | ||
} | ||
/** | ||
* Converter for set field (eg. mysql SET) | ||
@@ -991,0 +1001,0 @@ */ |
@@ -9,3 +9,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ObjectToSqlConverter = exports.ModelToSqlConverter = exports.QueryScope = exports.TableAliasCompiler = exports.SetValueConverter = exports.DatetimeValueConverter = exports.ValueConverter = exports.ModelMiddleware = exports.QueryMiddleware = exports.GroupByQueryCompiler = exports.OrderByQueryCompiler = exports.AlterColumnQueryCompiler = exports.ColumnQueryCompiler = exports.DropTableCompiler = exports.TableExistsCompiler = exports.AlterTableQueryCompiler = exports.DropEventQueryCompiler = exports.EventQueryCompiler = exports.TableCloneQueryCompiler = exports.TruncateTableQueryCompiler = exports.TableHistoryQueryCompiler = exports.TableQueryCompiler = exports.OnDuplicateQueryCompiler = exports.InsertQueryCompiler = exports.UpdateQueryCompiler = exports.DeleteQueryCompiler = exports.ForeignKeyQueryCompiler = exports.LimitQueryCompiler = exports.IndexQueryCompiler = exports.JoinQueryCompiler = exports.SelectQueryCompiler = exports.RecursiveQueryCompiler = exports.OrmMigration = exports.RelationType = exports.MigrationTransactionMode = exports.ReferentialAction = exports.InsertBehaviour = exports.DefaultValueBuilder = exports.ColumnAlterationType = exports.QueryContext = void 0; | ||
exports.ObjectToSqlConverter = exports.ModelToSqlConverter = exports.QueryScope = exports.TableAliasCompiler = exports.SetValueConverter = exports.BooleanValueConverter = exports.DatetimeValueConverter = exports.ValueConverter = exports.ModelMiddleware = exports.QueryMiddleware = exports.GroupByQueryCompiler = exports.OrderByQueryCompiler = exports.AlterColumnQueryCompiler = exports.ColumnQueryCompiler = exports.DropTableCompiler = exports.TableExistsCompiler = exports.AlterTableQueryCompiler = exports.DropEventQueryCompiler = exports.EventQueryCompiler = exports.TableCloneQueryCompiler = exports.TruncateTableQueryCompiler = exports.TableHistoryQueryCompiler = exports.TableQueryCompiler = exports.OnDuplicateQueryCompiler = exports.InsertQueryCompiler = exports.UpdateQueryCompiler = exports.DeleteQueryCompiler = exports.ForeignKeyQueryCompiler = exports.LimitQueryCompiler = exports.IndexQueryCompiler = exports.JoinQueryCompiler = exports.SelectQueryCompiler = exports.RecursiveQueryCompiler = exports.OrmMigration = exports.RelationType = exports.MigrationTransactionMode = exports.ReferentialAction = exports.InsertBehaviour = exports.DefaultValueBuilder = exports.ColumnAlterationType = exports.QueryContext = void 0; | ||
const di_1 = require("@spinajs/di"); | ||
@@ -21,2 +21,4 @@ const exceptions_1 = require("@spinajs/exceptions"); | ||
QueryContext[QueryContext["Transaction"] = 5] = "Transaction"; | ||
// Insert or UPDATE | ||
QueryContext[QueryContext["Upsert"] = 6] = "Upsert"; | ||
})(QueryContext || (exports.QueryContext = QueryContext = {})); | ||
@@ -80,2 +82,8 @@ var ColumnAlterationType; | ||
let OrmMigration = class OrmMigration { | ||
/** | ||
* Migrate data - execute AFTER orm module has been initialized | ||
* | ||
* It means that all model & relations are avaible | ||
*/ | ||
async data() { } | ||
}; | ||
@@ -260,2 +268,9 @@ exports.OrmMigration = OrmMigration; | ||
/** | ||
* Convert 0/1 to boolean ( mysql, sqlite etc. use 0/1 for boolean fields and tinyint/bit types ) | ||
*/ | ||
class BooleanValueConverter extends ValueConverter { | ||
} | ||
exports.BooleanValueConverter = BooleanValueConverter; | ||
; | ||
/** | ||
* Converter for set field (eg. mysql SET) | ||
@@ -262,0 +277,0 @@ */ |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -6,3 +9,6 @@ exports.DiscriminationMapMiddleware = exports.BelongsToRelationResultTransformMiddleware = exports.BelongsToPopulateDataMiddleware = exports.HasManyToManyRelationMiddleware = exports.BelongsToRelationRecursiveMiddleware = exports.HasManyRelationMiddleware = void 0; | ||
const interfaces_js_1 = require("./interfaces.js"); | ||
const lodash_1 = __importDefault(require("lodash")); | ||
const relation_objects_js_1 = require("./relation-objects.js"); | ||
const di_1 = require("@spinajs/di"); | ||
const exceptions_js_1 = require("./exceptions.js"); | ||
class HasManyRelationMiddleware { | ||
@@ -38,3 +44,16 @@ constructor(_relationQuery, _description, _path) { | ||
}); | ||
d[self._description.Name] = new relation_objects_js_1.OneToManyRelationList(d, self._description.TargetModel, self._description, relData); | ||
if (self._description.Factory) { | ||
d[self._description.Name] = self._description.Factory(d, self._description, d.Container, relData); | ||
} | ||
else { | ||
if (!self._description.RelationClass) { | ||
throw new exceptions_js_1.OrmException(`Relation class not defined for ${self._description.Name} in ${self._description.SourceModel.name}`); | ||
} | ||
if (lodash_1.default.isFunction(self._description.RelationClass)) { | ||
d[self._description.Name] = di_1.DI.resolve(self._description.RelationClass(), [d, self._description, relData]); | ||
} | ||
else { | ||
d[self._description.Name] = di_1.DI.resolve(self._description.RelationClass, [d, self._description, relData]); | ||
} | ||
} | ||
}); | ||
@@ -46,3 +65,3 @@ }, | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -89,3 +108,3 @@ return []; | ||
// manipulation of the recursive data | ||
d[name] = new relation_objects_js_1.OneToManyRelationList(d, d.Model, { | ||
d[name] = new relation_objects_js_1.OneToManyRelationList(d, { | ||
Name: name, | ||
@@ -115,3 +134,3 @@ Type: interfaces_js_1.RelationType.Many, | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -146,3 +165,3 @@ } | ||
const relData = relationData.filter((rd) => rd.JunctionModel[self._description.ForeignKey] === d[self._description.PrimaryKey]); | ||
d[self._description.Name] = new relation_objects_js_1.ManyToManyRelationList(d, self._description.TargetModel, self._description, relData); | ||
d[self._description.Name] = new relation_objects_js_1.ManyToManyRelationList(d, self._description, relData); | ||
}); | ||
@@ -157,3 +176,3 @@ relationData.forEach((d) => delete d.JunctionModel); | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -160,0 +179,0 @@ return []; |
@@ -6,6 +6,6 @@ import { ModelData, ModelDataWithRelationData, PartialArray, PickRelations } from './types.js'; | ||
import { Op } from './enums.js'; | ||
import { Wrap } from './statements.js'; | ||
import { OrmDriver } from './driver.js'; | ||
import { Class, IContainer, Constructor } from '@spinajs/di'; | ||
import { Wrap } from './statements.js'; | ||
import { DateTime } from 'luxon'; | ||
import { OrmDriver } from './driver.js'; | ||
export declare function extractModelDescriptor(targetOrForward: any): IModelDescriptor; | ||
@@ -186,8 +186,11 @@ export declare class ModelBase<M = unknown> implements IModelBase { | ||
*/ | ||
destroy(): Promise<void>; | ||
destroy(): Promise<any>; | ||
/** | ||
* If model can be in achived state - sets archived at date and saves it to db | ||
*/ | ||
archive(): Promise<void>; | ||
update(): Promise<void>; | ||
archive(): Promise<IUpdateResult>; | ||
update(data?: Partial<this>): Promise<{ | ||
RowsAffected: number; | ||
LastInsertId: number; | ||
}>; | ||
/** | ||
@@ -197,3 +200,3 @@ * Save all changes to db. It creates new entry id db or updates existing one if | ||
*/ | ||
insert(insertBehaviour?: InsertBehaviour): Promise<IUpdateResult>; | ||
insert(insertBehaviour?: InsertBehaviour): Promise<any>; | ||
/** | ||
@@ -206,3 +209,3 @@ * | ||
*/ | ||
insertOrUpdate(insertBehaviour?: InsertBehaviour): Promise<void>; | ||
insertOrUpdate(): Promise<any>; | ||
/** | ||
@@ -279,3 +282,3 @@ * Gets model data from database and returns as fresh instance. | ||
*/ | ||
insert<T_1 extends typeof ModelBase>(this: T_1, data: InstanceType<T_1> | Partial<InstanceType<T_1>> | InstanceType<T_1>[] | Partial<InstanceType<T_1>>[], insertBehaviour?: InsertBehaviour): Promise<IUpdateResult>; | ||
insert<T_1 extends typeof ModelBase>(this: T_1, data: InstanceType<T_1> | Partial<InstanceType<T_1>> | InstanceType<T_1>[] | Partial<InstanceType<T_1>>[], insertBehaviour?: InsertBehaviour): Promise<any>; | ||
find<T_2 extends typeof ModelBase>(this: T_2, pks: any[]): Promise<InstanceType<T_2>[]>; | ||
@@ -282,0 +285,0 @@ findOrFail<T_3 extends typeof ModelBase>(this: T_3, pks: any[]): Promise<InstanceType<T_3>[]>; |
@@ -7,3 +7,2 @@ "use strict"; | ||
exports._modelProxyFactory = exports.MODEL_STATIC_MIXINS = exports.createQuery = exports.HistoricalModel = exports.ModelBase = exports.extractModelDescriptor = void 0; | ||
/* eslint-disable prettier/prettier */ | ||
const enums_js_1 = require("./enums.js"); | ||
@@ -13,12 +12,12 @@ const decorators_js_1 = require("./decorators.js"); | ||
const builders_js_1 = require("./builders.js"); | ||
const di_1 = require("@spinajs/di"); | ||
const orm_js_1 = require("./orm.js"); | ||
const hydrators_js_1 = require("./hydrators.js"); | ||
const lodash_1 = __importDefault(require("lodash")); | ||
const uuid_1 = require("uuid"); | ||
const exceptions_js_1 = require("./exceptions.js"); | ||
const dehydrators_js_1 = require("./dehydrators.js"); | ||
const luxon_1 = require("luxon"); | ||
const relation_objects_js_1 = require("./relation-objects.js"); | ||
const middlewares_js_1 = require("./middlewares.js"); | ||
const di_1 = require("@spinajs/di"); | ||
const luxon_1 = require("luxon"); | ||
const lodash_1 = __importDefault(require("lodash")); | ||
const uuid_1 = require("uuid"); | ||
const MODEL_PROXY_HANDLER = { | ||
@@ -28,3 +27,3 @@ set: (target, p, value) => { | ||
target[p] = value; | ||
if (p !== 'IsDirty') { | ||
if (p !== 'IsDirty' && target.ModelDescriptor.Columns.find((x) => x.Name === p)) { | ||
target.IsDirty = true; | ||
@@ -55,3 +54,3 @@ } | ||
if (Array.isArray(a)) { | ||
return a.concat(b); | ||
return lodash_1.default.uniq(a.concat(b)); | ||
} | ||
@@ -79,4 +78,3 @@ return a; | ||
if (!this._container) { | ||
const orm = di_1.DI.get(orm_js_1.Orm); | ||
const driver = orm.Connections.get(this.ModelDescriptor.Connection); | ||
const driver = di_1.DI.resolve('OrmConnection', [this.ModelDescriptor.Connection]); | ||
if (!driver) { | ||
@@ -311,3 +309,3 @@ throw new Error(`model ${this.constructor.name} have invalid connection ${this.ModelDescriptor.Connection}, please check your db config file or model connection name`); | ||
// instead we must use belongsTo relation on data model to update | ||
data[v.ForeignKey] = this.PrimaryKeyValue; | ||
//(data as any)[v.ForeignKey] = this.PrimaryKeyValue; | ||
switch (v.Type) { | ||
@@ -318,2 +316,7 @@ case interfaces_js_1.RelationType.One: | ||
case interfaces_js_1.RelationType.Many: | ||
// attach to related model too | ||
const rel = [...data.ModelDescriptor.Relations.entries()].find((e) => e[1].ForeignKey === v.ForeignKey); | ||
if (rel) { | ||
data[rel[0]].Value = this; | ||
} | ||
case interfaces_js_1.RelationType.ManyToMany: | ||
@@ -325,2 +328,3 @@ this[v.Name].push(data); | ||
} | ||
this.IsDirty = true; | ||
} | ||
@@ -352,4 +356,5 @@ /** | ||
} | ||
await this.constructor.destroy(this.PrimaryKeyValue); | ||
const result = await this.constructor.destroy(this.PrimaryKeyValue); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -367,11 +372,23 @@ /** | ||
const { query } = this.createUpdateQuery(); | ||
await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
return await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
} | ||
async update() { | ||
async update(data) { | ||
const { query } = this.createUpdateQuery(); | ||
let result = { | ||
RowsAffected: 0, | ||
LastInsertId: 0, | ||
}; | ||
if (data) { | ||
this.hydrate(data); | ||
} | ||
// if no changes, return without update | ||
if (this.IsDirty === false) { | ||
return result; | ||
} | ||
if (this.ModelDescriptor.Timestamps.UpdatedAt) { | ||
this[this.ModelDescriptor.Timestamps.UpdatedAt] = luxon_1.DateTime.now(); | ||
} | ||
await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
result = await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -395,15 +412,22 @@ /** | ||
} | ||
const iMidleware = { | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId; | ||
return data; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}; | ||
query.middleware(iMidleware); | ||
return query.values(this.toSql()).then((res) => { | ||
this.IsDirty = false; | ||
return res; | ||
}); | ||
query.QueryContext === interfaces_js_1.QueryContext.Upsert | ||
// when upsert, we take affecter row ID from primary key returned ( returning statement) | ||
? query.middleware({ | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data[0][this.PrimaryKeyName]; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}) | ||
: query.middleware({ | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId; | ||
return data; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}); | ||
const result = query.values(this.toSql()); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -417,9 +441,7 @@ /** | ||
*/ | ||
async insertOrUpdate(insertBehaviour = interfaces_js_1.InsertBehaviour.None) { | ||
async insertOrUpdate() { | ||
if (this.PrimaryKeyValue) { | ||
await this.update(); | ||
return await this.update(); | ||
} | ||
else { | ||
await this.insert(insertBehaviour); | ||
} | ||
return await this.insert(); | ||
} | ||
@@ -473,13 +495,12 @@ /** | ||
if (rel.Factory) { | ||
this[rel.Name] = rel.Factory(this, rel, this.Container); | ||
this[rel.Name] = rel.Factory(this, rel, this.Container, []); | ||
} | ||
else if (rel.RelationClass) { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel.TargetModel, rel, []]); | ||
if (lodash_1.default.isFunction(rel.RelationClass)) { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass(), [this, rel, []]); | ||
} | ||
else { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel, []]); | ||
} | ||
} | ||
else if (rel.Type === interfaces_js_1.RelationType.Many) { | ||
this[rel.Name] = new relation_objects_js_1.OneToManyRelationList(this, rel.TargetModel, rel, []); | ||
} | ||
else if (rel.Type === interfaces_js_1.RelationType.ManyToMany) { | ||
this[rel.Name] = new relation_objects_js_1.ManyToManyRelationList(this, rel.TargetModel, rel, []); | ||
} | ||
else { | ||
@@ -554,4 +575,3 @@ this[rel.Name] = new relation_objects_js_1.SingleRelation(this, rel.TargetModel, rel, null); | ||
} | ||
const orm = di_1.DI.get(orm_js_1.Orm); | ||
const driver = orm.Connections.get(dsc.Connection); | ||
const driver = di_1.DI.resolve('OrmConnection', [dsc.Connection]); | ||
if (!driver) { | ||
@@ -561,3 +581,4 @@ throw new Error(`model ${model.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`); | ||
const cnt = driver.Container; | ||
const qr = cnt.resolve(query, [driver, injectModel ? orm.Models.find((x) => x.name === model.name).type : null]); | ||
const models = di_1.DI.getRegisteredTypes('__models__'); | ||
const qr = cnt.resolve(query, [driver, injectModel ? models.find((x) => x.name === model.name) : null]); | ||
if (qr instanceof builders_js_1.SelectQueryBuilder) { | ||
@@ -633,2 +654,5 @@ const scope = model._queryScopes; | ||
update(data) { | ||
if (data instanceof ModelBase) { | ||
throw new exceptions_js_1.OrmException(`use model::update() function to update model`); | ||
} | ||
const { query } = createQuery(this, builders_js_1.UpdateQueryBuilder); | ||
@@ -635,0 +659,0 @@ return query.update(data); |
@@ -20,3 +20,3 @@ import { Configuration } from '@spinajs/configuration-common'; | ||
*/ | ||
migrateUp(name?: string, force?: boolean): Promise<void>; | ||
migrateUp(name?: string, force?: boolean): Promise<OrmMigration[]>; | ||
/** | ||
@@ -23,0 +23,0 @@ * |
@@ -48,2 +48,3 @@ "use strict"; | ||
this.Log.info('DB migration UP started ...'); | ||
const executedMigrations = []; | ||
await this.executeAvaibleMigrations(name, async (migration, driver) => { | ||
@@ -59,2 +60,3 @@ const trFunction = async (driver) => { | ||
}); | ||
executedMigrations.push(migration); | ||
this.Log.info(`Migration ${migration.constructor.name}:up() success !`); | ||
@@ -70,2 +72,3 @@ }; | ||
this.Log.info('DB migration ended ...'); | ||
return executedMigrations; | ||
} | ||
@@ -109,2 +112,7 @@ /** | ||
const connection = this.Connections.get(descriptor.Connection); | ||
if (!connection) { | ||
this.Log.warn(`Cannot find connection ${descriptor.Connection} in connection list (model ${descriptor.Name})`); | ||
continue; | ||
} | ||
const converters = connection.Container.get('__orm_db_value_converters__'); | ||
if (connection) { | ||
@@ -118,8 +126,23 @@ m.type[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL].Driver = connection; | ||
// m.type[MODEL_DESCTRIPTION_SYMBOL].Schema = buildJsonSchema(columns); | ||
} | ||
for (const [key, val] of descriptor.Converters) { | ||
const column = m.type[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL].Columns.find((c) => c.Name === key); | ||
if (column) { | ||
column.Converter = connection.Container.hasRegistered(val.Class) ? connection.Container.resolve(val.Class) : null; | ||
/** | ||
* Add coverters to columns set by decorators | ||
* eg. @CreatedAt decorator etc. | ||
*/ | ||
for (const [key, val] of descriptor.Converters) { | ||
const column = m.type[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL].Columns.find((c) => c.Name === key); | ||
if (column) { | ||
column.Converter = connection.Container.hasRegistered(val.Class) ? connection.Container.resolve(val.Class) : null; | ||
} | ||
} | ||
/** | ||
* Add any other converted that is not set by decorators, but is set in container | ||
* for given column type eg. default boolean converter | ||
*/ | ||
columns.forEach((c) => { | ||
if (!c.Converter) { | ||
if (converters && converters.has(c.NativeType.toLocaleLowerCase())) { | ||
c.Converter = connection.Container.resolve(converters.get(c.NativeType.toLocaleLowerCase())); | ||
} | ||
} | ||
}); | ||
} | ||
@@ -145,7 +168,11 @@ } | ||
} | ||
await this.migrateUp(undefined, false); | ||
const executedMigrations = await this.migrateUp(undefined, false); | ||
this.registerDefaultConverters(); | ||
await this.reloadTableInfo(); | ||
this.wireRelations(); | ||
this.applyModelMixins(); | ||
this.registerDefaultConverters(); | ||
for (const m of executedMigrations) { | ||
this.Log.trace(`Migrating data function for migration ${m.constructor.name} ...`); | ||
await m.data(); | ||
} | ||
} | ||
@@ -155,2 +182,4 @@ registerDefaultConverters() { | ||
this.Container.register(interfaces_js_1.DatetimeValueConverter).asMapValue('__orm_db_value_converters__', luxon_1.DateTime.name); | ||
this.Container.register(interfaces_js_1.BooleanValueConverter).asMapValue('__orm_db_value_converters__', Boolean.name.toLowerCase()); | ||
this.Container.register(interfaces_js_1.BooleanValueConverter).asMapValue('__orm_db_value_converters__', 'bool'); | ||
} | ||
@@ -240,3 +269,3 @@ wireRelations() { | ||
} | ||
// register in continaer factory func for retrieving db connections | ||
// register in container factory func for retrieving db connections | ||
// it will allow for easy access to it in modules | ||
@@ -248,3 +277,3 @@ di_1.DI.register((_container, connectionName) => { | ||
return null; | ||
}).as("OrmConnection"); | ||
}).as('OrmConnection'); | ||
} | ||
@@ -251,0 +280,0 @@ applyModelMixins() { |
@@ -32,11 +32,10 @@ import { IRelationDescriptor, IModelDescriptor, InsertBehaviour, ForwardRefFunction, IRelation, ISelectQueryBuilder } from './interfaces.js'; | ||
export declare abstract class Relation<R extends ModelBase<R>, O extends ModelBase<O>> extends Array<R> implements IRelation<R, O> { | ||
protected owner: O; | ||
protected Model: Constructor<R> | ForwardRefFunction; | ||
protected Owner: O; | ||
protected Relation: IRelationDescriptor; | ||
TargetModelDescriptor: IModelDescriptor; | ||
protected Orm: Orm; | ||
Populated: boolean; | ||
protected Driver: OrmDriver; | ||
protected IsModelAForwardRef: boolean; | ||
constructor(owner: O, Model: Constructor<R> | ForwardRefFunction, Relation: IRelationDescriptor, objects?: R[]); | ||
protected Model: Constructor<R> | ForwardRefFunction; | ||
constructor(Owner: O, Relation: IRelationDescriptor, objects?: R[]); | ||
/** | ||
@@ -77,2 +76,8 @@ * Removes all objects from relation by comparison functions | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
abstract update(): Promise<void>; | ||
/** | ||
* | ||
@@ -135,2 +140,3 @@ * Calculates intersection between data in this relation and provided dataset | ||
sync(): Promise<void>; | ||
update(): Promise<void>; | ||
populate(): Promise<void>; | ||
@@ -159,2 +165,8 @@ } | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
update(): Promise<void>; | ||
/** | ||
* Calculates difference between this relation and dataset ( items from this relation that are not in dataset and items from dataset that are not in this relation) | ||
@@ -172,3 +184,3 @@ * | ||
*/ | ||
set(obj: T[] | ((data: T[], pKey: string) => T[])): void; | ||
set(obj: T[] | ((data: T[], pKeyName: string) => T[])): void; | ||
/** | ||
@@ -189,2 +201,7 @@ * Calculates intersection between data in this relation and provided dataset | ||
/** | ||
* Returns the elements of an array that meet the condition specified in a callback function. | ||
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. | ||
*/ | ||
filter(predicate: (value: T, index?: number, array?: T[]) => boolean): T[]; | ||
/** | ||
* Removes from relation & deletes from db | ||
@@ -196,8 +213,17 @@ * | ||
/** | ||
* Removes all objects that met condition | ||
* @param obj - predicate | ||
*/ | ||
remove(obj: (a: T) => boolean): T[]; | ||
/** | ||
* Removes all objects by primary key | ||
* | ||
* @param obj - data to remove | ||
* @param obj data array to remove | ||
*/ | ||
remove(obj: T | T[]): T[]; | ||
remove(obj: T[]): T[]; | ||
/** | ||
* Removes object by primary key | ||
* @param obj data to remove | ||
* */ | ||
remove(obj: T): T[]; | ||
} | ||
//# sourceMappingURL=relation-objects.d.ts.map |
@@ -7,2 +7,4 @@ "use strict"; | ||
exports.OneToManyRelationList = exports.ManyToManyRelationList = exports.SingleRelation = exports.Relation = exports.Dataset = void 0; | ||
/* eslint-disable prettier/prettier */ | ||
const interfaces_js_1 = require("./interfaces.js"); | ||
const di_1 = require("@spinajs/di"); | ||
@@ -55,6 +57,5 @@ const builders_js_1 = require("./builders.js"); | ||
class Relation extends Array { | ||
constructor(owner, Model, Relation, objects) { | ||
constructor(Owner, Relation, objects) { | ||
super(); | ||
this.owner = owner; | ||
this.Model = Model; | ||
this.Owner = Owner; | ||
this.Relation = Relation; | ||
@@ -65,6 +66,6 @@ this.Populated = false; | ||
} | ||
this.TargetModelDescriptor = (0, model_js_1.extractModelDescriptor)(Model); | ||
this.Orm = di_1.DI.get(orm_js_1.Orm); | ||
this.Model = this.Relation.TargetModel; // TODO: fix typings | ||
this.TargetModelDescriptor = (0, model_js_1.extractModelDescriptor)(this.Model); | ||
if (this.TargetModelDescriptor) { | ||
this.Driver = this.Orm.Connections.get(this.TargetModelDescriptor.Connection); | ||
this.Driver = di_1.DI.resolve('OrmConnection', [this.TargetModelDescriptor.Connection]); | ||
} | ||
@@ -152,2 +153,5 @@ this.IsModelAForwardRef = !(0, di_1.isConstructor)(this.Model); | ||
} | ||
async update() { | ||
throw new Error('Method not implemented.'); | ||
} | ||
async populate() { | ||
@@ -166,4 +170,3 @@ throw new Error('Method not implemented.'); | ||
async _dbDiff(data) { | ||
const query = this.Driver.del().from(this.TargetModelDescriptor.TableName).where(this.Relation.ForeignKey, this.owner.PrimaryKeyValue); | ||
const self = this; | ||
const query = this.Driver.del().from(this.TargetModelDescriptor.TableName).where(this.Relation.ForeignKey, this.Owner.PrimaryKeyValue); | ||
if (this.Driver.Options.Database) { | ||
@@ -175,3 +178,3 @@ query.database(this.Driver.Options.Database); | ||
if (toDelete.length !== 0) { | ||
query.whereNotIn(self.Relation.PrimaryKey, toDelete); | ||
query.whereNotIn(this.TargetModelDescriptor.PrimaryKey, toDelete); | ||
} | ||
@@ -184,3 +187,3 @@ await query; | ||
async populate(callback) { | ||
const query = this.Relation.TargetModel.where(this.Relation.ForeignKey, this.owner.PrimaryKeyValue); | ||
const query = this.Relation.TargetModel.where(this.Relation.ForeignKey, this.Owner.PrimaryKeyValue); | ||
if (callback) { | ||
@@ -192,2 +195,5 @@ callback.apply(query); | ||
this.length = 0; | ||
result.forEach((r) => { | ||
this.Owner.attach(r); | ||
}); | ||
this.push(...result); | ||
@@ -205,10 +211,18 @@ } | ||
async sync() { | ||
await this.update(); | ||
await this._dbDiff(this); | ||
} | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
async update() { | ||
const dirty = this.filter((x) => x.IsDirty || x.PrimaryKeyValue === null); | ||
this.forEach((d) => { | ||
d[this.Relation.ForeignKey] = this.owner.PrimaryKeyValue; | ||
d[this.Relation.ForeignKey] = this.Owner.PrimaryKeyValue; | ||
}); | ||
for (const f of dirty) { | ||
await f.insertOrUpdate(); | ||
await f.insert(interfaces_js_1.InsertBehaviour.InsertOrUpdate); | ||
} | ||
await this._dbDiff(this); | ||
} | ||
@@ -253,8 +267,15 @@ /** | ||
} | ||
/** | ||
* Returns the elements of an array that meet the condition specified in a callback function. | ||
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. | ||
*/ | ||
filter(predicate) { | ||
return [...this].filter(predicate); | ||
} | ||
remove(obj) { | ||
if (lodash_1.default.isFunction(obj)) { | ||
return lodash_1.default.remove(this, obj); | ||
} | ||
const data = (Array.isArray(obj) ? obj : [obj]).map((d) => d.PrimaryKeyValue); | ||
return lodash_1.default.remove(this, (o) => data.indexOf(o.PrimaryKeyValue) !== -1); | ||
const toRemove = lodash_1.default.isFunction(obj) ? this.filter(obj) : Array.isArray(obj) ? obj : [obj]; | ||
this.set((data) => { | ||
return data.filter((d) => !toRemove.includes(d)); | ||
}); | ||
return toRemove; | ||
} | ||
@@ -261,0 +282,0 @@ } |
@@ -286,3 +286,5 @@ import { Container, Constructor, IContainer } from '@spinajs/di'; | ||
protected _container: IContainer; | ||
constructor(container: IContainer, insertQueryBuilder: QueryBuilder, column?: string | string[]); | ||
protected _returning: string[]; | ||
constructor(container: IContainer, insertQueryBuilder: QueryBuilder, column?: string | string[], returning?: string[]); | ||
getReturning(): string[]; | ||
getColumn(): string[]; | ||
@@ -309,3 +311,3 @@ getColumnsToUpdate(): (string | RawQuery)[]; | ||
} | ||
export declare class InsertQueryBuilder extends QueryBuilder<IUpdateResult> { | ||
export declare class InsertQueryBuilder extends QueryBuilder<IUpdateResult | any> { | ||
DuplicateQueryBuilder: OnDuplicateQueryBuilder; | ||
@@ -317,2 +319,3 @@ protected _values: any[][]; | ||
protected _replace: boolean; | ||
protected _returning: string[]; | ||
this: this; | ||
@@ -329,2 +332,3 @@ get Values(): any[][]; | ||
orReplace(): this; | ||
returning(columns: string[]): this; | ||
values(data: {} | Array<{}>): this; | ||
@@ -331,0 +335,0 @@ into(table: string, schema?: string): this; |
@@ -88,3 +88,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
catch (err) { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
} | ||
@@ -102,7 +107,17 @@ }, onrejected); | ||
catch (err) { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
} | ||
}) | ||
.catch((err) => { | ||
onrejected(err); | ||
if (onrejected) { | ||
onrejected(err); | ||
} | ||
else { | ||
throw err; | ||
} | ||
}); | ||
@@ -890,7 +905,11 @@ } | ||
let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder { | ||
constructor(container, insertQueryBuilder, column) { | ||
constructor(container, insertQueryBuilder, column, returning) { | ||
this._parent = insertQueryBuilder; | ||
this._container = container; | ||
this._column = _.isArray(column) ? column : [column]; | ||
this._returning = returning ?? ["*"]; | ||
} | ||
getReturning() { | ||
return this._returning; | ||
} | ||
getColumn() { | ||
@@ -918,3 +937,3 @@ return this._column; | ||
NewInstance(), | ||
__metadata("design:paramtypes", [Object, QueryBuilder, Object]) | ||
__metadata("design:paramtypes", [Object, QueryBuilder, Object, Array]) | ||
], OnDuplicateQueryBuilder); | ||
@@ -970,2 +989,4 @@ export { OnDuplicateQueryBuilder }; | ||
super(container, driver, model); | ||
this._update = false; | ||
this._replace = false; | ||
this._method = QueryMethod.INSERT; | ||
@@ -984,5 +1005,9 @@ this._columns = []; | ||
orReplace() { | ||
this._update = true; | ||
this._replace = true; | ||
return this; | ||
} | ||
returning(columns) { | ||
this._returning = columns; | ||
return this; | ||
} | ||
values(data) { | ||
@@ -1025,3 +1050,4 @@ const self = this; | ||
this._update = true; | ||
this.DuplicateQueryBuilder = new OnDuplicateQueryBuilder(this._container, this, columnToCheck); | ||
this.DuplicateQueryBuilder = new OnDuplicateQueryBuilder(this._container, this, columnToCheck, this._returning); | ||
this.QueryContext = QueryContext.Upsert; | ||
return this.DuplicateQueryBuilder; | ||
@@ -1028,0 +1054,0 @@ } |
@@ -40,3 +40,4 @@ import { DateTime } from 'luxon'; | ||
toDB(value, model, options) { | ||
switch (model[options.TypeColumn]) { | ||
const type = model[options.TypeColumn]; | ||
switch (type) { | ||
case 'string': | ||
@@ -43,0 +44,0 @@ return value; |
@@ -95,5 +95,3 @@ import { Constructor, IContainer } from '@spinajs/di'; | ||
export declare function ForwardBelongsTo(forwardRef: IForwardReference, foreignKey?: string, primaryKey?: string): any; | ||
export interface IHasManyDecoratorOptions { | ||
foreignKey?: string; | ||
primaryKey?: string; | ||
export interface IRelationDecoratorOptions { | ||
/** | ||
@@ -110,2 +108,24 @@ * Relation factory, sometimes we dont want to create standard relation object. | ||
} | ||
export interface IHasManyToManyDecoratorOptions extends IRelationDecoratorOptions { | ||
/** | ||
* target model primary key name | ||
*/ | ||
targetModelPKey?: string; | ||
/** | ||
* source model primary key name | ||
*/ | ||
sourceModelPKey?: string; | ||
/** | ||
* junction table target primary key name ( foreign key for target model ) | ||
*/ | ||
junctionModelTargetPk?: string; | ||
/** | ||
* junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
junctionModelSourcePk?: string; | ||
} | ||
export interface IHasManyDecoratorOptions extends IRelationDecoratorOptions { | ||
foreignKey?: string; | ||
primaryKey?: string; | ||
} | ||
/** | ||
@@ -126,8 +146,4 @@ * Creates one to many relation with target model. | ||
* @param targetModel - model for related data | ||
* @param targetModelPKey - target model primary key name | ||
* @param sourceModelPKey - source model primary key name | ||
* @param junctionModelTargetPk - junction table target primary key name ( foreign key for target model ) | ||
* @param junctionModelSourcePk - junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
export declare function HasManyToMany(junctionModel: Constructor<ModelBase>, targetModel: Constructor<ModelBase> | string, targetModelPKey?: string, sourceModelPKey?: string, junctionModelTargetPk?: string, junctionModelSourcePk?: string): any; | ||
export declare function HasManyToMany(junctionModel: Constructor<ModelBase>, targetModel: Constructor<ModelBase> | string, options?: IHasManyToManyDecoratorOptions): any; | ||
/** | ||
@@ -151,3 +167,3 @@ * Mark field as datetime type. It will ensure that conversion to & from DB is valid, eg. sqlite DB | ||
*/ | ||
export declare function UniversalConverter(typeColumn: string): any; | ||
export declare function UniversalConverter(typeColumn?: string): any; | ||
/** | ||
@@ -154,0 +170,0 @@ * Mark field as SET type. It will ensure that conversion to & from DB is valid, eg. to emulate field type SET in sqlite |
@@ -301,2 +301,3 @@ /* eslint-disable prettier/prettier */ | ||
return extractDecoratorDescriptor((model, target, propertyKey) => { | ||
let type = Reflect.getMetadata('design:type', target, propertyKey); | ||
model.Relations.set(propertyKey, { | ||
@@ -311,4 +312,4 @@ Name: propertyKey, | ||
Recursive: false, | ||
Factory: options ? options.factory : null, | ||
RelationClass: options ? options.type : null, | ||
Factory: options?.factory ? options.factory : null, | ||
RelationClass: options?.type ? options.type : () => DI.resolve("__orm_relation_has_many_factory__", [type]), | ||
}); | ||
@@ -336,10 +337,7 @@ }); | ||
* @param targetModel - model for related data | ||
* @param targetModelPKey - target model primary key name | ||
* @param sourceModelPKey - source model primary key name | ||
* @param junctionModelTargetPk - junction table target primary key name ( foreign key for target model ) | ||
* @param junctionModelSourcePk - junction table source primary key name ( foreign key for source model ) | ||
*/ | ||
export function HasManyToMany(junctionModel, targetModel, targetModelPKey, sourceModelPKey, junctionModelTargetPk, junctionModelSourcePk) { | ||
export function HasManyToMany(junctionModel, targetModel, options) { | ||
return extractDecoratorDescriptor((model, target, propertyKey) => { | ||
const targetModelDescriptor = extractModelDescriptor(targetModel); | ||
let type = Reflect.getMetadata('design:type', target, propertyKey); | ||
model.Relations.set(propertyKey, { | ||
@@ -352,7 +350,9 @@ Name: propertyKey, | ||
TargetModel: null, | ||
ForeignKey: targetModelPKey ?? targetModelDescriptor.PrimaryKey, | ||
PrimaryKey: sourceModelPKey ?? model.PrimaryKey, | ||
ForeignKey: options?.targetModelPKey ?? targetModelDescriptor.PrimaryKey, | ||
PrimaryKey: options?.sourceModelPKey ?? model.PrimaryKey, | ||
JunctionModel: junctionModel, | ||
JunctionModelTargetModelFKey_Name: junctionModelTargetPk ?? `${targetModelDescriptor.Name.toLowerCase()}_id`, | ||
JunctionModelSourceModelFKey_Name: junctionModelSourcePk ?? `${model.Name.toLowerCase()}_id`, | ||
JunctionModelTargetModelFKey_Name: options?.junctionModelTargetPk ?? `${targetModelDescriptor.Name.toLowerCase()}_id`, | ||
JunctionModelSourceModelFKey_Name: options?.junctionModelSourcePk ?? `${model.Name.toLowerCase()}_id`, | ||
RelationClass: options?.type ? options.type : () => DI.resolve("__orm_relation_has_many_to_many_factory__", [type]), | ||
Factory: options ? options.factory : null, | ||
}); | ||
@@ -405,3 +405,3 @@ }); | ||
Options: { | ||
TypeColumn: typeColumn, | ||
TypeColumn: typeColumn ?? "Type", | ||
}, | ||
@@ -408,0 +408,0 @@ }); |
@@ -35,3 +35,9 @@ import { OrmException } from './exceptions.js'; | ||
if (model[val.Name]) { | ||
obj[val.Name] = [...model[val.Name].map((x) => x.dehydrateWithRelations())]; | ||
const v = [...model[val.Name]]; | ||
if (v.length === 0) { | ||
obj[val.Name] = []; | ||
} | ||
else { | ||
obj[val.Name] = [v.map((x) => x.dehydrateWithRelations())]; | ||
} | ||
} | ||
@@ -38,0 +44,0 @@ } |
@@ -29,3 +29,5 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
const column = descriptor.Columns?.find((c) => c.Name === k); | ||
target[k] = column.Converter ? column.Converter.fromDB(values[k], values, descriptor.Converters.get(column.Name).Options) : values[k]; | ||
if (values[k] !== undefined) { | ||
target[k] = column.Converter ? column.Converter.fromDB(values[k], values, descriptor.Converters.get(column.Name)?.Options) : values[k]; | ||
} | ||
}); | ||
@@ -73,3 +75,3 @@ } | ||
}); | ||
const rel = new OneToManyRelationList(target, val.TargetModel, val, mapRel); | ||
const rel = new OneToManyRelationList(target, val, mapRel); | ||
entity[key] = rel; | ||
@@ -76,0 +78,0 @@ delete target[val.ForeignKey]; |
@@ -18,2 +18,5 @@ export * from './interfaces.js'; | ||
export * from './exceptions.js'; | ||
export * from "./metadata.js"; | ||
export * from './fp.js'; | ||
export * from './bootstrap.js'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -18,2 +18,5 @@ export * from './interfaces.js'; | ||
export * from './exceptions.js'; | ||
export * from "./metadata.js"; | ||
export * from './fp.js'; | ||
export * from './bootstrap.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -18,3 +18,4 @@ import { Op } from './enums.js'; | ||
Schema = 4, | ||
Transaction = 5 | ||
Transaction = 5, | ||
Upsert = 6 | ||
} | ||
@@ -45,6 +46,6 @@ export declare enum ColumnAlterationType { | ||
/** | ||
* Removes from relation & deletes from db | ||
* | ||
* @param obj - data to remove | ||
*/ | ||
* Removes from relation & deletes from db | ||
* | ||
* @param obj - data to remove | ||
*/ | ||
remove(obj: R | R[] | ((a: R, b: R) => boolean)): R[]; | ||
@@ -394,3 +395,3 @@ /** | ||
*/ | ||
Factory?: (model: ModelBase<unknown>, relation: IRelationDescriptor, container: IContainer) => Relation<ModelBase<unknown>, ModelBase<unknown>>; | ||
Factory?: (model: ModelBase<unknown>, relation: IRelationDescriptor, container: IContainer, data: any[]) => Relation<ModelBase<unknown>, ModelBase<unknown>>; | ||
/** | ||
@@ -400,3 +401,3 @@ * sometimes we dont want to create standard relation object, so we create type | ||
*/ | ||
RelationClass?: Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>>; | ||
RelationClass?: Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>> | (() => Constructor<Relation<ModelBase<unknown>, ModelBase<unknown>>>); | ||
} | ||
@@ -465,11 +466,11 @@ export interface IModelStatic extends Constructor<ModelBase<unknown>> { | ||
*/ | ||
destroy(): Promise<void>; | ||
destroy(): Promise<IUpdateResult>; | ||
/** | ||
* If model can be in achived state - sets archived at date and saves it to db | ||
*/ | ||
archive(): Promise<void>; | ||
archive(): Promise<IUpdateResult>; | ||
/** | ||
* Updates model to db | ||
*/ | ||
update(): Promise<void>; | ||
update(): Promise<IUpdateResult>; | ||
/** | ||
@@ -484,6 +485,4 @@ * Save all changes to db. It creates new entry id db or updates existing one if | ||
* its value in db if primary key is set | ||
* | ||
* @param insertBehaviour - insert mode | ||
*/ | ||
insertOrUpdate(insertBehaviour?: InsertBehaviour): Promise<void>; | ||
insertOrUpdate(): Promise<IUpdateResult>; | ||
/** | ||
@@ -632,2 +631,8 @@ * Gets model data from database and returns as fresh instance. | ||
abstract down(connection: OrmDriver): Promise<void>; | ||
/** | ||
* Migrate data - execute AFTER orm module has been initialized | ||
* | ||
* It means that all model & relations are avaible | ||
*/ | ||
data(): Promise<void>; | ||
} | ||
@@ -973,3 +978,3 @@ /** | ||
*/ | ||
toDB(_value: any, _model: ModelBase<any>, _options: any): any; | ||
toDB(_value: any, _model: ModelBase<any>, _options?: any): any; | ||
/** | ||
@@ -980,3 +985,3 @@ * Converts value from database type eg. mysql timestamp to DateTime | ||
*/ | ||
fromDB(_value: any, _rawData: any, _options: any): any; | ||
fromDB(_value: any, _rawData?: any, _options?: any): any; | ||
} | ||
@@ -989,2 +994,7 @@ /** | ||
/** | ||
* Convert 0/1 to boolean ( mysql, sqlite etc. use 0/1 for boolean fields and tinyint/bit types ) | ||
*/ | ||
export declare class BooleanValueConverter extends ValueConverter { | ||
} | ||
/** | ||
* Converter for set field (eg. mysql SET) | ||
@@ -991,0 +1001,0 @@ */ |
@@ -17,2 +17,4 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
QueryContext[QueryContext["Transaction"] = 5] = "Transaction"; | ||
// Insert or UPDATE | ||
QueryContext[QueryContext["Upsert"] = 6] = "Upsert"; | ||
})(QueryContext || (QueryContext = {})); | ||
@@ -75,2 +77,8 @@ export var ColumnAlterationType; | ||
let OrmMigration = class OrmMigration { | ||
/** | ||
* Migrate data - execute AFTER orm module has been initialized | ||
* | ||
* It means that all model & relations are avaible | ||
*/ | ||
async data() { } | ||
}; | ||
@@ -251,2 +259,8 @@ OrmMigration = __decorate([ | ||
/** | ||
* Convert 0/1 to boolean ( mysql, sqlite etc. use 0/1 for boolean fields and tinyint/bit types ) | ||
*/ | ||
export class BooleanValueConverter extends ValueConverter { | ||
} | ||
; | ||
/** | ||
* Converter for set field (eg. mysql SET) | ||
@@ -253,0 +267,0 @@ */ |
/* eslint-disable prettier/prettier */ | ||
import { RelationType } from './interfaces.js'; | ||
import _ from 'lodash'; | ||
import { ManyToManyRelationList, OneToManyRelationList } from './relation-objects.js'; | ||
import { DI } from '@spinajs/di'; | ||
import { OrmException } from './exceptions.js'; | ||
export class HasManyRelationMiddleware { | ||
@@ -34,3 +37,16 @@ constructor(_relationQuery, _description, _path) { | ||
}); | ||
d[self._description.Name] = new OneToManyRelationList(d, self._description.TargetModel, self._description, relData); | ||
if (self._description.Factory) { | ||
d[self._description.Name] = self._description.Factory(d, self._description, d.Container, relData); | ||
} | ||
else { | ||
if (!self._description.RelationClass) { | ||
throw new OrmException(`Relation class not defined for ${self._description.Name} in ${self._description.SourceModel.name}`); | ||
} | ||
if (_.isFunction(self._description.RelationClass)) { | ||
d[self._description.Name] = DI.resolve(self._description.RelationClass(), [d, self._description, relData]); | ||
} | ||
else { | ||
d[self._description.Name] = DI.resolve(self._description.RelationClass, [d, self._description, relData]); | ||
} | ||
} | ||
}); | ||
@@ -42,3 +58,3 @@ }, | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -84,3 +100,3 @@ return []; | ||
// manipulation of the recursive data | ||
d[name] = new OneToManyRelationList(d, d.Model, { | ||
d[name] = new OneToManyRelationList(d, { | ||
Name: name, | ||
@@ -110,3 +126,3 @@ Type: RelationType.Many, | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -140,3 +156,3 @@ } | ||
const relData = relationData.filter((rd) => rd.JunctionModel[self._description.ForeignKey] === d[self._description.PrimaryKey]); | ||
d[self._description.Name] = new ManyToManyRelationList(d, self._description.TargetModel, self._description, relData); | ||
d[self._description.Name] = new ManyToManyRelationList(d, self._description, relData); | ||
}); | ||
@@ -151,3 +167,3 @@ relationData.forEach((d) => delete d.JunctionModel); | ||
this._relationQuery.middleware(hydrateMiddleware); | ||
return await this._relationQuery; | ||
return (await this._relationQuery); | ||
} | ||
@@ -154,0 +170,0 @@ return []; |
@@ -6,6 +6,6 @@ import { ModelData, ModelDataWithRelationData, PartialArray, PickRelations } from './types.js'; | ||
import { Op } from './enums.js'; | ||
import { Wrap } from './statements.js'; | ||
import { OrmDriver } from './driver.js'; | ||
import { Class, IContainer, Constructor } from '@spinajs/di'; | ||
import { Wrap } from './statements.js'; | ||
import { DateTime } from 'luxon'; | ||
import { OrmDriver } from './driver.js'; | ||
export declare function extractModelDescriptor(targetOrForward: any): IModelDescriptor; | ||
@@ -186,8 +186,11 @@ export declare class ModelBase<M = unknown> implements IModelBase { | ||
*/ | ||
destroy(): Promise<void>; | ||
destroy(): Promise<any>; | ||
/** | ||
* If model can be in achived state - sets archived at date and saves it to db | ||
*/ | ||
archive(): Promise<void>; | ||
update(): Promise<void>; | ||
archive(): Promise<IUpdateResult>; | ||
update(data?: Partial<this>): Promise<{ | ||
RowsAffected: number; | ||
LastInsertId: number; | ||
}>; | ||
/** | ||
@@ -197,3 +200,3 @@ * Save all changes to db. It creates new entry id db or updates existing one if | ||
*/ | ||
insert(insertBehaviour?: InsertBehaviour): Promise<IUpdateResult>; | ||
insert(insertBehaviour?: InsertBehaviour): Promise<any>; | ||
/** | ||
@@ -206,3 +209,3 @@ * | ||
*/ | ||
insertOrUpdate(insertBehaviour?: InsertBehaviour): Promise<void>; | ||
insertOrUpdate(): Promise<any>; | ||
/** | ||
@@ -279,3 +282,3 @@ * Gets model data from database and returns as fresh instance. | ||
*/ | ||
insert<T_1 extends typeof ModelBase>(this: T_1, data: InstanceType<T_1> | Partial<InstanceType<T_1>> | InstanceType<T_1>[] | Partial<InstanceType<T_1>>[], insertBehaviour?: InsertBehaviour): Promise<IUpdateResult>; | ||
insert<T_1 extends typeof ModelBase>(this: T_1, data: InstanceType<T_1> | Partial<InstanceType<T_1>> | InstanceType<T_1>[] | Partial<InstanceType<T_1>>[], insertBehaviour?: InsertBehaviour): Promise<any>; | ||
find<T_2 extends typeof ModelBase>(this: T_2, pks: any[]): Promise<InstanceType<T_2>[]>; | ||
@@ -282,0 +285,0 @@ findOrFail<T_3 extends typeof ModelBase>(this: T_3, pks: any[]): Promise<InstanceType<T_3>[]>; |
@@ -1,16 +0,15 @@ | ||
/* eslint-disable prettier/prettier */ | ||
import { SortOrder } from './enums.js'; | ||
import { MODEL_DESCTRIPTION_SYMBOL } from './decorators.js'; | ||
import { RelationType, InsertBehaviour, ModelToSqlConverter, ObjectToSqlConverter } from './interfaces.js'; | ||
import { RelationType, InsertBehaviour, ModelToSqlConverter, ObjectToSqlConverter, QueryContext } from './interfaces.js'; | ||
import { UpdateQueryBuilder, TruncateTableQueryBuilder, SelectQueryBuilder, DeleteQueryBuilder, InsertQueryBuilder } from './builders.js'; | ||
import { DI, isConstructor } from '@spinajs/di'; | ||
import { Orm } from './orm.js'; | ||
import { ModelHydrator } from './hydrators.js'; | ||
import _ from 'lodash'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import { OrmException } from './exceptions.js'; | ||
import { StandardModelDehydrator, StandardModelWithRelationsDehydrator } from './dehydrators.js'; | ||
import { SingleRelation } from './relation-objects.js'; | ||
import { DiscriminationMapMiddleware } from './middlewares.js'; | ||
import { DI, isConstructor } from '@spinajs/di'; | ||
import { DateTime } from 'luxon'; | ||
import { ManyToManyRelationList, OneToManyRelationList, SingleRelation } from './relation-objects.js'; | ||
import { DiscriminationMapMiddleware } from './middlewares.js'; | ||
import _ from 'lodash'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
const MODEL_PROXY_HANDLER = { | ||
@@ -20,3 +19,3 @@ set: (target, p, value) => { | ||
target[p] = value; | ||
if (p !== 'IsDirty') { | ||
if (p !== 'IsDirty' && target.ModelDescriptor.Columns.find((x) => x.Name === p)) { | ||
target.IsDirty = true; | ||
@@ -47,3 +46,3 @@ } | ||
if (Array.isArray(a)) { | ||
return a.concat(b); | ||
return _.uniq(a.concat(b)); | ||
} | ||
@@ -70,4 +69,3 @@ return a; | ||
if (!this._container) { | ||
const orm = DI.get(Orm); | ||
const driver = orm.Connections.get(this.ModelDescriptor.Connection); | ||
const driver = DI.resolve('OrmConnection', [this.ModelDescriptor.Connection]); | ||
if (!driver) { | ||
@@ -302,3 +300,3 @@ throw new Error(`model ${this.constructor.name} have invalid connection ${this.ModelDescriptor.Connection}, please check your db config file or model connection name`); | ||
// instead we must use belongsTo relation on data model to update | ||
data[v.ForeignKey] = this.PrimaryKeyValue; | ||
//(data as any)[v.ForeignKey] = this.PrimaryKeyValue; | ||
switch (v.Type) { | ||
@@ -309,2 +307,7 @@ case RelationType.One: | ||
case RelationType.Many: | ||
// attach to related model too | ||
const rel = [...data.ModelDescriptor.Relations.entries()].find((e) => e[1].ForeignKey === v.ForeignKey); | ||
if (rel) { | ||
data[rel[0]].Value = this; | ||
} | ||
case RelationType.ManyToMany: | ||
@@ -316,2 +319,3 @@ this[v.Name].push(data); | ||
} | ||
this.IsDirty = true; | ||
} | ||
@@ -343,4 +347,5 @@ /** | ||
} | ||
await this.constructor.destroy(this.PrimaryKeyValue); | ||
const result = await this.constructor.destroy(this.PrimaryKeyValue); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -358,11 +363,23 @@ /** | ||
const { query } = this.createUpdateQuery(); | ||
await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
return await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
} | ||
async update() { | ||
async update(data) { | ||
const { query } = this.createUpdateQuery(); | ||
let result = { | ||
RowsAffected: 0, | ||
LastInsertId: 0, | ||
}; | ||
if (data) { | ||
this.hydrate(data); | ||
} | ||
// if no changes, return without update | ||
if (this.IsDirty === false) { | ||
return result; | ||
} | ||
if (this.ModelDescriptor.Timestamps.UpdatedAt) { | ||
this[this.ModelDescriptor.Timestamps.UpdatedAt] = DateTime.now(); | ||
} | ||
await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
result = await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -386,15 +403,22 @@ /** | ||
} | ||
const iMidleware = { | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId; | ||
return data; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}; | ||
query.middleware(iMidleware); | ||
return query.values(this.toSql()).then((res) => { | ||
this.IsDirty = false; | ||
return res; | ||
}); | ||
query.QueryContext === QueryContext.Upsert | ||
// when upsert, we take affecter row ID from primary key returned ( returning statement) | ||
? query.middleware({ | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data[0][this.PrimaryKeyName]; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}) | ||
: query.middleware({ | ||
afterQuery: (data) => { | ||
this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId; | ||
return data; | ||
}, | ||
modelCreation: () => null, | ||
afterHydration: () => null, | ||
}); | ||
const result = query.values(this.toSql()); | ||
this.IsDirty = false; | ||
return result; | ||
} | ||
@@ -408,9 +432,7 @@ /** | ||
*/ | ||
async insertOrUpdate(insertBehaviour = InsertBehaviour.None) { | ||
async insertOrUpdate() { | ||
if (this.PrimaryKeyValue) { | ||
await this.update(); | ||
return await this.update(); | ||
} | ||
else { | ||
await this.insert(insertBehaviour); | ||
} | ||
return await this.insert(); | ||
} | ||
@@ -464,13 +486,12 @@ /** | ||
if (rel.Factory) { | ||
this[rel.Name] = rel.Factory(this, rel, this.Container); | ||
this[rel.Name] = rel.Factory(this, rel, this.Container, []); | ||
} | ||
else if (rel.RelationClass) { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel.TargetModel, rel, []]); | ||
if (_.isFunction(rel.RelationClass)) { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass(), [this, rel, []]); | ||
} | ||
else { | ||
this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel, []]); | ||
} | ||
} | ||
else if (rel.Type === RelationType.Many) { | ||
this[rel.Name] = new OneToManyRelationList(this, rel.TargetModel, rel, []); | ||
} | ||
else if (rel.Type === RelationType.ManyToMany) { | ||
this[rel.Name] = new ManyToManyRelationList(this, rel.TargetModel, rel, []); | ||
} | ||
else { | ||
@@ -543,4 +564,3 @@ this[rel.Name] = new SingleRelation(this, rel.TargetModel, rel, null); | ||
} | ||
const orm = DI.get(Orm); | ||
const driver = orm.Connections.get(dsc.Connection); | ||
const driver = DI.resolve('OrmConnection', [dsc.Connection]); | ||
if (!driver) { | ||
@@ -550,3 +570,4 @@ throw new Error(`model ${model.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`); | ||
const cnt = driver.Container; | ||
const qr = cnt.resolve(query, [driver, injectModel ? orm.Models.find((x) => x.name === model.name).type : null]); | ||
const models = DI.getRegisteredTypes('__models__'); | ||
const qr = cnt.resolve(query, [driver, injectModel ? models.find((x) => x.name === model.name) : null]); | ||
if (qr instanceof SelectQueryBuilder) { | ||
@@ -621,2 +642,5 @@ const scope = model._queryScopes; | ||
update(data) { | ||
if (data instanceof ModelBase) { | ||
throw new OrmException(`use model::update() function to update model`); | ||
} | ||
const { query } = createQuery(this, UpdateQueryBuilder); | ||
@@ -623,0 +647,0 @@ return query.update(data); |
@@ -20,3 +20,3 @@ import { Configuration } from '@spinajs/configuration-common'; | ||
*/ | ||
migrateUp(name?: string, force?: boolean): Promise<void>; | ||
migrateUp(name?: string, force?: boolean): Promise<OrmMigration[]>; | ||
/** | ||
@@ -23,0 +23,0 @@ * |
@@ -10,3 +10,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
}; | ||
import { DatetimeValueConverter } from './interfaces.js'; | ||
import { BooleanValueConverter, DatetimeValueConverter } from './interfaces.js'; | ||
import { Configuration } from '@spinajs/configuration-common'; | ||
@@ -43,2 +43,3 @@ import { AsyncService, Autoinject, Container, DI } from '@spinajs/di'; | ||
this.Log.info('DB migration UP started ...'); | ||
const executedMigrations = []; | ||
await this.executeAvaibleMigrations(name, async (migration, driver) => { | ||
@@ -54,2 +55,3 @@ const trFunction = async (driver) => { | ||
}); | ||
executedMigrations.push(migration); | ||
this.Log.info(`Migration ${migration.constructor.name}:up() success !`); | ||
@@ -65,2 +67,3 @@ }; | ||
this.Log.info('DB migration ended ...'); | ||
return executedMigrations; | ||
} | ||
@@ -104,2 +107,7 @@ /** | ||
const connection = this.Connections.get(descriptor.Connection); | ||
if (!connection) { | ||
this.Log.warn(`Cannot find connection ${descriptor.Connection} in connection list (model ${descriptor.Name})`); | ||
continue; | ||
} | ||
const converters = connection.Container.get('__orm_db_value_converters__'); | ||
if (connection) { | ||
@@ -113,8 +121,23 @@ m.type[MODEL_DESCTRIPTION_SYMBOL].Driver = connection; | ||
// m.type[MODEL_DESCTRIPTION_SYMBOL].Schema = buildJsonSchema(columns); | ||
} | ||
for (const [key, val] of descriptor.Converters) { | ||
const column = m.type[MODEL_DESCTRIPTION_SYMBOL].Columns.find((c) => c.Name === key); | ||
if (column) { | ||
column.Converter = connection.Container.hasRegistered(val.Class) ? connection.Container.resolve(val.Class) : null; | ||
/** | ||
* Add coverters to columns set by decorators | ||
* eg. @CreatedAt decorator etc. | ||
*/ | ||
for (const [key, val] of descriptor.Converters) { | ||
const column = m.type[MODEL_DESCTRIPTION_SYMBOL].Columns.find((c) => c.Name === key); | ||
if (column) { | ||
column.Converter = connection.Container.hasRegistered(val.Class) ? connection.Container.resolve(val.Class) : null; | ||
} | ||
} | ||
/** | ||
* Add any other converted that is not set by decorators, but is set in container | ||
* for given column type eg. default boolean converter | ||
*/ | ||
columns.forEach((c) => { | ||
if (!c.Converter) { | ||
if (converters && converters.has(c.NativeType.toLocaleLowerCase())) { | ||
c.Converter = connection.Container.resolve(converters.get(c.NativeType.toLocaleLowerCase())); | ||
} | ||
} | ||
}); | ||
} | ||
@@ -140,7 +163,11 @@ } | ||
} | ||
await this.migrateUp(undefined, false); | ||
const executedMigrations = await this.migrateUp(undefined, false); | ||
this.registerDefaultConverters(); | ||
await this.reloadTableInfo(); | ||
this.wireRelations(); | ||
this.applyModelMixins(); | ||
this.registerDefaultConverters(); | ||
for (const m of executedMigrations) { | ||
this.Log.trace(`Migrating data function for migration ${m.constructor.name} ...`); | ||
await m.data(); | ||
} | ||
} | ||
@@ -150,2 +177,4 @@ registerDefaultConverters() { | ||
this.Container.register(DatetimeValueConverter).asMapValue('__orm_db_value_converters__', DateTime.name); | ||
this.Container.register(BooleanValueConverter).asMapValue('__orm_db_value_converters__', Boolean.name.toLowerCase()); | ||
this.Container.register(BooleanValueConverter).asMapValue('__orm_db_value_converters__', 'bool'); | ||
} | ||
@@ -235,3 +264,3 @@ wireRelations() { | ||
} | ||
// register in continaer factory func for retrieving db connections | ||
// register in container factory func for retrieving db connections | ||
// it will allow for easy access to it in modules | ||
@@ -243,3 +272,3 @@ DI.register((_container, connectionName) => { | ||
return null; | ||
}).as("OrmConnection"); | ||
}).as('OrmConnection'); | ||
} | ||
@@ -246,0 +275,0 @@ applyModelMixins() { |
@@ -32,11 +32,10 @@ import { IRelationDescriptor, IModelDescriptor, InsertBehaviour, ForwardRefFunction, IRelation, ISelectQueryBuilder } from './interfaces.js'; | ||
export declare abstract class Relation<R extends ModelBase<R>, O extends ModelBase<O>> extends Array<R> implements IRelation<R, O> { | ||
protected owner: O; | ||
protected Model: Constructor<R> | ForwardRefFunction; | ||
protected Owner: O; | ||
protected Relation: IRelationDescriptor; | ||
TargetModelDescriptor: IModelDescriptor; | ||
protected Orm: Orm; | ||
Populated: boolean; | ||
protected Driver: OrmDriver; | ||
protected IsModelAForwardRef: boolean; | ||
constructor(owner: O, Model: Constructor<R> | ForwardRefFunction, Relation: IRelationDescriptor, objects?: R[]); | ||
protected Model: Constructor<R> | ForwardRefFunction; | ||
constructor(Owner: O, Relation: IRelationDescriptor, objects?: R[]); | ||
/** | ||
@@ -77,2 +76,8 @@ * Removes all objects from relation by comparison functions | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
abstract update(): Promise<void>; | ||
/** | ||
* | ||
@@ -135,2 +140,3 @@ * Calculates intersection between data in this relation and provided dataset | ||
sync(): Promise<void>; | ||
update(): Promise<void>; | ||
populate(): Promise<void>; | ||
@@ -159,2 +165,8 @@ } | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
update(): Promise<void>; | ||
/** | ||
* Calculates difference between this relation and dataset ( items from this relation that are not in dataset and items from dataset that are not in this relation) | ||
@@ -172,3 +184,3 @@ * | ||
*/ | ||
set(obj: T[] | ((data: T[], pKey: string) => T[])): void; | ||
set(obj: T[] | ((data: T[], pKeyName: string) => T[])): void; | ||
/** | ||
@@ -189,2 +201,7 @@ * Calculates intersection between data in this relation and provided dataset | ||
/** | ||
* Returns the elements of an array that meet the condition specified in a callback function. | ||
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. | ||
*/ | ||
filter(predicate: (value: T, index?: number, array?: T[]) => boolean): T[]; | ||
/** | ||
* Removes from relation & deletes from db | ||
@@ -196,8 +213,17 @@ * | ||
/** | ||
* Removes all objects that met condition | ||
* @param obj - predicate | ||
*/ | ||
remove(obj: (a: T) => boolean): T[]; | ||
/** | ||
* Removes all objects by primary key | ||
* | ||
* @param obj - data to remove | ||
* @param obj data array to remove | ||
*/ | ||
remove(obj: T | T[]): T[]; | ||
remove(obj: T[]): T[]; | ||
/** | ||
* Removes object by primary key | ||
* @param obj data to remove | ||
* */ | ||
remove(obj: T): T[]; | ||
} | ||
//# sourceMappingURL=relation-objects.d.ts.map |
@@ -0,1 +1,3 @@ | ||
/* eslint-disable prettier/prettier */ | ||
import { InsertBehaviour } from './interfaces.js'; | ||
import { DI, isConstructor } from '@spinajs/di'; | ||
@@ -47,6 +49,5 @@ import { SelectQueryBuilder } from './builders.js'; | ||
export class Relation extends Array { | ||
constructor(owner, Model, Relation, objects) { | ||
constructor(Owner, Relation, objects) { | ||
super(); | ||
this.owner = owner; | ||
this.Model = Model; | ||
this.Owner = Owner; | ||
this.Relation = Relation; | ||
@@ -57,6 +58,6 @@ this.Populated = false; | ||
} | ||
this.TargetModelDescriptor = extractModelDescriptor(Model); | ||
this.Orm = DI.get(Orm); | ||
this.Model = this.Relation.TargetModel; // TODO: fix typings | ||
this.TargetModelDescriptor = extractModelDescriptor(this.Model); | ||
if (this.TargetModelDescriptor) { | ||
this.Driver = this.Orm.Connections.get(this.TargetModelDescriptor.Connection); | ||
this.Driver = DI.resolve('OrmConnection', [this.TargetModelDescriptor.Connection]); | ||
} | ||
@@ -142,2 +143,5 @@ this.IsModelAForwardRef = !isConstructor(this.Model); | ||
} | ||
async update() { | ||
throw new Error('Method not implemented.'); | ||
} | ||
async populate() { | ||
@@ -155,4 +159,3 @@ throw new Error('Method not implemented.'); | ||
async _dbDiff(data) { | ||
const query = this.Driver.del().from(this.TargetModelDescriptor.TableName).where(this.Relation.ForeignKey, this.owner.PrimaryKeyValue); | ||
const self = this; | ||
const query = this.Driver.del().from(this.TargetModelDescriptor.TableName).where(this.Relation.ForeignKey, this.Owner.PrimaryKeyValue); | ||
if (this.Driver.Options.Database) { | ||
@@ -164,3 +167,3 @@ query.database(this.Driver.Options.Database); | ||
if (toDelete.length !== 0) { | ||
query.whereNotIn(self.Relation.PrimaryKey, toDelete); | ||
query.whereNotIn(this.TargetModelDescriptor.PrimaryKey, toDelete); | ||
} | ||
@@ -173,3 +176,3 @@ await query; | ||
async populate(callback) { | ||
const query = this.Relation.TargetModel.where(this.Relation.ForeignKey, this.owner.PrimaryKeyValue); | ||
const query = this.Relation.TargetModel.where(this.Relation.ForeignKey, this.Owner.PrimaryKeyValue); | ||
if (callback) { | ||
@@ -181,2 +184,5 @@ callback.apply(query); | ||
this.length = 0; | ||
result.forEach((r) => { | ||
this.Owner.attach(r); | ||
}); | ||
this.push(...result); | ||
@@ -194,10 +200,18 @@ } | ||
async sync() { | ||
await this.update(); | ||
await this._dbDiff(this); | ||
} | ||
/** | ||
* Updates or ads data to relation | ||
* It will not delete data from db that are not in relation. It will only update or insert new data. | ||
* Only dirty models are updated. | ||
*/ | ||
async update() { | ||
const dirty = this.filter((x) => x.IsDirty || x.PrimaryKeyValue === null); | ||
this.forEach((d) => { | ||
d[this.Relation.ForeignKey] = this.owner.PrimaryKeyValue; | ||
d[this.Relation.ForeignKey] = this.Owner.PrimaryKeyValue; | ||
}); | ||
for (const f of dirty) { | ||
await f.insertOrUpdate(); | ||
await f.insert(InsertBehaviour.InsertOrUpdate); | ||
} | ||
await this._dbDiff(this); | ||
} | ||
@@ -242,10 +256,17 @@ /** | ||
} | ||
/** | ||
* Returns the elements of an array that meet the condition specified in a callback function. | ||
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. | ||
*/ | ||
filter(predicate) { | ||
return [...this].filter(predicate); | ||
} | ||
remove(obj) { | ||
if (_.isFunction(obj)) { | ||
return _.remove(this, obj); | ||
} | ||
const data = (Array.isArray(obj) ? obj : [obj]).map((d) => d.PrimaryKeyValue); | ||
return _.remove(this, (o) => data.indexOf(o.PrimaryKeyValue) !== -1); | ||
const toRemove = _.isFunction(obj) ? this.filter(obj) : Array.isArray(obj) ? obj : [obj]; | ||
this.set((data) => { | ||
return data.filter((d) => !toRemove.includes(d)); | ||
}); | ||
return toRemove; | ||
} | ||
} | ||
//# sourceMappingURL=relation-objects.js.map |
{ | ||
"name": "@spinajs/orm", | ||
"version": "2.0.185", | ||
"version": "2.0.186", | ||
"description": "framework orm module", | ||
@@ -8,6 +8,6 @@ "main": "lib/cjs/index.js", | ||
"exports": { | ||
".": { | ||
"import": "./lib/mjs/index.js", | ||
"require": "./lib/cjs/index.js" | ||
} | ||
".": { | ||
"import": "./lib/mjs/index.js", | ||
"require": "./lib/cjs/index.js" | ||
} | ||
}, | ||
@@ -32,3 +32,2 @@ "type": "module", | ||
"version": "npm run format && git add -A src" | ||
}, | ||
@@ -38,3 +37,2 @@ "files": [ | ||
], | ||
"repository": { | ||
@@ -63,8 +61,8 @@ "type": "git", | ||
"dependencies": { | ||
"@spinajs/configuration-common": "^2.0.185", | ||
"@spinajs/di": "^2.0.185", | ||
"@spinajs/exceptions": "^2.0.185", | ||
"@spinajs/log-common": "^2.0.185", | ||
"@spinajs/configuration-common": "^2.0.186", | ||
"@spinajs/di": "^2.0.186", | ||
"@spinajs/exceptions": "^2.0.186", | ||
"@spinajs/log-common": "^2.0.186", | ||
"glob": "^8.1.0", | ||
"glob-to-regexp": "^0.4.1", | ||
"lodash": "^4.17.21", | ||
@@ -71,0 +69,0 @@ "luxon": "^3.2.1", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1254128
174
16868
9
+ Addedglob-to-regexp@^0.4.1
Updated@spinajs/di@^2.0.186
Updated@spinajs/exceptions@^2.0.186
Updated@spinajs/log-common@^2.0.186