import Dream from '../Dream.js'; import { PassthroughOnClause } from '../types/associations/shared.js'; import { DbConnectionType } from '../types/db.js'; import { AllDefaultScopeNames, DreamSerializerKey, PassthroughColumnNames } from '../types/dream.js'; import { LoadForModifierFn } from '../types/query.js'; import { VariadicLoadArgs } from '../types/variadic.js'; import DreamTransaction from './DreamTransaction.js'; export default class LoadBuilder { private dreamTransaction?; private dream; private query; /** * An intermediate builder for loading associations onto a Dream model. * Accessed via the `#load` method on a Dream instance. * * **Note:** The LoadBuilder works on a clone of the original model. * Call `.execute()` to get the clone with associations loaded. * * ```ts * const user = await User.firstOrFail() * const loaded = await user.load('settings').execute() * loaded.settings // association is loaded on the returned clone * ``` */ constructor(dream: Dream, dreamTransaction?: (DreamTransaction | null) | undefined); passthrough, PassthroughColumns extends PassthroughColumnNames>(this: I, passthroughWhereStatement: PassthroughOnClause): I; /** * Prevents a specific default scope from applying when * loading associations * * ```ts * const user = await User.firstOrFail() * const loaded = await user * .load('posts') * .removeDefaultScope('dream:SoftDelete') * .execute() * ``` * * @param scopeName - The default scope to bypass * @returns The LoadBuilder instance for chaining */ removeDefaultScope>(this: I, scopeName: AllDefaultScopeNames): I; /** * @deprecated Use {@link removeDefaultScope} instead. This method will become private in v3. * * Prevents user-removable default scopes from applying when loading * associations. An associated STI child's reserved type discriminator remains * enforced; loading through its STI base still spans registered children. * * ```ts * const user = await User.firstOrFail() * const loaded = await user * .load('posts') * .removeDefaultScope('dream:SoftDelete') * .execute() * ``` * * @returns The LoadBuilder instance for chaining */ removeAllDefaultScopes>(this: I): I; /** * Sets the database connection to use when loading associations * * ```ts * const user = await User.firstOrFail() * const loaded = await user * .load('posts') * .connection('replica') * .execute() * ``` * * @param connection - The connection type ('primary' or 'replica') * @returns The LoadBuilder instance for chaining */ connection>(this: I, connection: DbConnectionType): I; /** * Attaches a load statement to the load builder * * ```ts * const user = await User.firstOrFail() * await user * .load('settings') * .load('posts', 'comments', 'replies', ['image', 'localizedText']) * .execute() * ``` * * @param args - A chain of association names and and/andNot/andAny clauses to load * @returns A chainable LoadBuilder instance */ load, DB extends DreamInstance['DB'], TableName extends DreamInstance['table'], Schema extends DreamInstance['schema'], const Arr extends readonly unknown[]>(this: I, ...args: [...Arr, VariadicLoadArgs]): I; /** * Recursively loads all Dream associations referenced by `rendersOne` and `rendersMany` * in a DreamSerializer. This traverses the entire content tree of serializers to automatically * load all necessary associations, eliminating N+1 query problems and removing the need to * manually remember which associations to preload for serialization. * * This method decouples data loading code from data rendering code by having the serializer * (rendering code) inform the query (loading code) about which associations are needed. * As serializers evolve over time - adding new `rendersOne` and `rendersMany` calls or * modifying existing ones - the loading code automatically adapts without requiring * corresponding modifications to preload statements. * * This method analyzes the serializer (specified by `serializerKey` or 'default') and * automatically preloads all associations that will be needed during serialization. * * ```ts * // Instead of manually specifying all associations: * await User.preload('posts', 'comments', 'replies').all() * * // Automatically preload everything needed for serialization: * await user.loadFor('summary').execute() * * // Add where conditions to specific associations during preloading: * await user.loadFor('detailed', (associationName, dreamClass) => { * if (dreamClass.typeof(Post) && associationName === 'comments') { * return { and: { published: true } } * } * }) * .execute() * * // Skip preloading specific associations to handle them manually: * await user * .loadFor('summary', (associationName, dreamClass) => { * if (dreamClass.typeof(User) && associationName === 'posts') { * return 'omit' // Handle posts preloading separately with custom logic * } * }) * .load('posts', { and: { featured: true } }) // Custom preloading * .execute() * ``` * * @param serializerKey - The serializer key to use for determining which associations to preload. * @param modifierFn - Optional callback function to modify or omit specific associations during preloading. Called for each association with the Dream class and association name. Return an object with `and`, `andAny`, or `andNot` properties to add where conditions, return 'omit' to skip preloading that association (useful when you want to handle it manually), or return undefined to use default preloading * @returns A Query with all serialization associations preloaded */ loadFor, SerializerKey extends DreamSerializerKey>(this: I, serializerKey: SerializerKey, modifierFn?: LoadForModifierFn): I; /** * Executes the load query, binding all associations to a **clone** of the * original model instance. The original instance is not modified. * * You must use the returned value to access the loaded associations: * * ```ts * const user = await User.firstOrFail() * const loaded = await user * .load('settings') * .load('posts', 'comments', 'replies', ['image', 'localizedText']) * .execute() * * loaded.settings // works — associations are on the returned clone * ``` * * @returns A clone of the Dream instance with all requested associations loaded */ execute(): Promise; }