parent
b5bde7d077
commit
2fd3c6c22b
@ -0,0 +1,19 @@
|
||||
import ModelResultOperator from "./ModelResultOperator.ts";
|
||||
import {Model} from "./Model.ts";
|
||||
import Instantiable from "../../../di/src/type/Instantiable.ts";
|
||||
import {HasOne} from "./relation/HasOne.ts";
|
||||
import {QueryRow} from "../db/types.ts";
|
||||
|
||||
export default class RelationResultOperator<T extends Model<T>> extends ModelResultOperator<T> {
|
||||
constructor(
|
||||
protected ModelClass: Instantiable<T>,
|
||||
protected relation: HasOne<any, T>,
|
||||
) {
|
||||
super(ModelClass)
|
||||
}
|
||||
|
||||
inflate_row(row: QueryRow): T {
|
||||
const instance = super.inflate_row(row)
|
||||
return instance
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import {Model} from '../Model.ts'
|
||||
import {HasOneOrMany} from './HasOneOrMany.ts'
|
||||
import {Collection} from '../../../../lib/src/collection/Collection.ts'
|
||||
|
||||
export class HasMany<T extends Model<T>, T2 extends Model<T2>> extends HasOneOrMany<T, T2> {
|
||||
public async get(): Promise<Collection<T2>> {
|
||||
return this.fetch().collect()
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import {Model} from '../Model.ts'
|
||||
import {HasOneOrMany} from './HasOneOrMany.ts'
|
||||
|
||||
export class HasOne<T extends Model<T>, T2 extends Model<T2>> extends HasOneOrMany<T, T2> {
|
||||
public async get(): Promise<T2 | undefined> {
|
||||
return this.fetch().first()
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import {Model} from "../Model.ts";
|
||||
import {Relation} from "./Relation.ts";
|
||||
import ConnectionExecutable from "../../builder/type/ConnectionExecutable.ts";
|
||||
import {WhereBuilder} from "../../builder/type/WhereBuilder.ts";
|
||||
|
||||
export abstract class HasOneOrMany<T extends Model<T>, T2 extends Model<T2>> extends Relation<T, T2> {
|
||||
constructor(
|
||||
protected parent: T,
|
||||
public readonly related: T2,
|
||||
protected foreign_key_spec?: string,
|
||||
protected local_key_spec?: string,
|
||||
) { super(parent, related) }
|
||||
|
||||
public get foreign_key() {
|
||||
return this.foreign_key_spec || this.parent.key_name()
|
||||
}
|
||||
|
||||
public get local_key() {
|
||||
return this.local_key_spec || this.foreign_key
|
||||
}
|
||||
|
||||
public get qualified_foreign_key() {
|
||||
return this.related.qualify_column(this.foreign_key)
|
||||
}
|
||||
|
||||
public get qualified_local_key() {
|
||||
return this.related.qualify_column(this.local_key)
|
||||
}
|
||||
|
||||
protected get parent_value() {
|
||||
// @ts-ignore
|
||||
return this.parent[this.local_key]
|
||||
}
|
||||
|
||||
public query(): ConnectionExecutable<T2> {
|
||||
return this.builder().select('*')
|
||||
}
|
||||
|
||||
public scope_query(where: WhereBuilder) {
|
||||
where.where(where => {
|
||||
where.where(this.qualified_foreign_key, '=', this.parent_value)
|
||||
.whereRaw(this.qualified_foreign_key, 'IS NOT', 'NULL')
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
import {Model} from '../Model.ts'
|
||||
import AppClass from '../../../../lib/src/lifecycle/AppClass.ts'
|
||||
import RelationResultOperator from '../RelationResultOperator.ts'
|
||||
import ConnectionExecutable from '../../builder/type/ConnectionExecutable.ts'
|
||||
import {AsyncCollection} from '../../../../lib/src/collection/AsyncCollection.ts'
|
||||
import {Collection} from '../../../../lib/src/collection/Collection.ts'
|
||||
import {WhereBuilder} from '../../builder/type/WhereBuilder.ts'
|
||||
import {RelationBuilder} from './RelationBuilder.ts'
|
||||
import {Select} from "../../builder/type/Select.ts";
|
||||
import {FieldSet} from "../../builder/types.ts";
|
||||
import {Update} from "../../builder/type/Update.ts";
|
||||
import {Delete} from "../../builder/type/Delete.ts";
|
||||
|
||||
export type RelationResult<T> = T | Collection<T> | undefined
|
||||
|
||||
export abstract class Relation<T extends Model<T>, T2 extends Model<T2>> extends AppClass {
|
||||
constructor(
|
||||
protected parent: T,
|
||||
public readonly related: T2,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
protected abstract get parent_value(): any
|
||||
|
||||
public get_operator() {
|
||||
const related_class = this.related.constructor as typeof Model
|
||||
return this.make(RelationResultOperator, related_class, this)
|
||||
}
|
||||
|
||||
public abstract query(): ConnectionExecutable<T2>
|
||||
|
||||
public abstract scope_query(where: WhereBuilder): void
|
||||
|
||||
public fetch(): AsyncCollection<T2> {
|
||||
return this.query().results()
|
||||
}
|
||||
|
||||
public abstract get(): Promise<RelationResult<T2>>
|
||||
|
||||
public then(callback: (result: RelationResult<T2>) => any) {
|
||||
this.get().then(callback)
|
||||
}
|
||||
|
||||
public get related_query_source() {
|
||||
const related_class = this.related.constructor as typeof Model
|
||||
return related_class.query_source()
|
||||
}
|
||||
|
||||
public builder(): RelationBuilder<T2> {
|
||||
return new RelationBuilder(this)
|
||||
}
|
||||
|
||||
public select(...fields: FieldSet[]): Select<T2> {
|
||||
if ( fields.length < 1 ) fields.push(this.related.qualify_column('*'))
|
||||
return this.builder().select(...fields)
|
||||
}
|
||||
|
||||
public update(): Update<T2> {
|
||||
return this.builder().update()
|
||||
}
|
||||
|
||||
public delete(): Delete<T2> {
|
||||
return this.builder().delete()
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import {Builder} from '../../builder/Builder.ts'
|
||||
import {Select} from '../../builder/type/Select.ts'
|
||||
import {Relation} from './Relation.ts'
|
||||
import {Update} from '../../builder/type/Update.ts'
|
||||
import {FieldSet, QuerySource} from '../../builder/types.ts'
|
||||
import {Delete} from '../../builder/type/Delete.ts'
|
||||
import {Model} from '../Model.ts'
|
||||
|
||||
export class RelationBuilder<T extends Model<T>> extends Builder<T> {
|
||||
constructor(
|
||||
protected relation: Relation<any, T>
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public select(...fields: FieldSet[]): Select<T> {
|
||||
const select = this.relation.related.select(...fields)
|
||||
.from(this.relation.related_query_source)
|
||||
|
||||
select.target_operator(this.relation.get_operator())
|
||||
this.relation.scope_query(select)
|
||||
return select
|
||||
}
|
||||
|
||||
public update(target?: QuerySource, alias?: string): Update<T> {
|
||||
const update = this.relation.related.update(this.relation.related_query_source)
|
||||
|
||||
update.target_operator(this.relation.get_operator())
|
||||
this.relation.scope_query(update)
|
||||
return update
|
||||
}
|
||||
|
||||
public delete(target?: QuerySource, alias?: string): Delete<T> {
|
||||
const delete_query = this.relation.related.delete(this.relation.related_query_source)
|
||||
|
||||
delete_query.target_operator(this.relation.get_operator())
|
||||
this.relation.scope_query(delete_query)
|
||||
return delete_query
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import {Model} from '../Model.ts'
|
||||
|
||||
export function Relation(): MethodDecorator {
|
||||
return (target: any, propertyKey, descriptor) => {
|
||||
console.log('relation decorator', target, propertyKey, descriptor)
|
||||
// @ts-ignore
|
||||
const original = descriptor.value
|
||||
|
||||
// @ts-ignore
|
||||
descriptor.value = function(...args) {
|
||||
const model = this as Model<any>
|
||||
const cache = model.relation_cache
|
||||
|
||||
const existing_relation = cache.firstWhere('accessor_name', '=', propertyKey)
|
||||
if ( existing_relation ) return existing_relation.relation
|
||||
|
||||
// @ts-ignore
|
||||
const value = original.apply(this, args)
|
||||
|
||||
cache.push({
|
||||
accessor_name: propertyKey,
|
||||
relation: value,
|
||||
})
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue