import {Relation, RelationNotLoadedError} from './Relation' import {TreeModel} from '../TreeModel' import {RelationBuilder} from './RelationBuilder' import {raw} from '../../dialect/SQLDialect' import {AbstractBuilder} from '../../builder/AbstractBuilder' import {ModelBuilder} from '../ModelBuilder' import {Collection, Maybe} from '../../../util' export class HasTreeParent> extends Relation> { protected parentInstance?: T protected loaded = false protected constructor( protected model: T, protected readonly leftTreeField: string, protected readonly parentIdField: string, ) { super(model, model) } protected get parentValue(): any { return this.model.key() } public query(): RelationBuilder { return this.builder() .tap(b => this.model.applyScopes(b)) .select(raw('*')) .orderByAscending(this.leftTreeField) } public applyScope(where: AbstractBuilder): void { const parentId = this.model.parentId() if ( !parentId ) { where.whereMatchNone() return } where.where(this.parentIdField, '=', parentId) } public buildEagerQuery(parentQuery: ModelBuilder, result: Collection): ModelBuilder { const parentIds = result.map(model => model.parentId()).whereDefined() return this.model.query() .without('subtree') .whereIn(this.parentIdField, parentIds) } public matchResults(possiblyRelated: Collection): Collection { return possiblyRelated.filter(related => related.key() === this.model.parentId()) } public setValue(related: Maybe): void { this.loaded = true this.parentInstance = related } public getValue(): Maybe { if ( !this.loaded && this.model.parentId() ) { throw new RelationNotLoadedError() } return this.parentInstance } public isLoaded(): boolean { return this.loaded || !this.model.parentId() } public async get(): Promise> { return this.fetch().first() } }