scyllinx
Version:
A modern TypeScript ORM for ScyllaDB and SQL databases with Laravel-inspired syntax
290 lines (289 loc) • 9.59 kB
TypeScript
import type { Model } from "@/model/Model";
import { QueryBuilder } from "@/query/QueryBuilder";
import { Relationship } from "./Relationship";
/**
* Represents a many-to-many relationship between two models.
* Uses a pivot table to store the relationship data between the two models.
* Supports additional pivot columns and constraints.
*
* @template Parent - The parent model type
* @template Related - The related model type
*
* @example
*
* // In User model
* rolesRelation(): BelongsToMany<User, Role> {
* return new BelongsToMany(
* this,
* Role,
* 'user_roles', // pivot table
* 'user_id', // foreign key for parent
* 'role_id', // foreign key for related
* 'id', // parent key
* 'id' // related key
* );
* }
*
* // Usage
* const user = await User.find(1);
* const roles = await user.rolesRelation().getResults();
*
* // Attach roles
* await user.rolesRelation().attach([1, 2, 3]);
*
* // With pivot data
* await user.rolesRelation().attach(1, { assigned_at: new Date() });
*
*/
export declare class BelongsToMany<Parent extends Model<any>, Related extends Model<any>> extends Relationship<Parent, Related> {
protected pivotTable: string;
protected relatedPivotKey: string;
protected parentPivotKey: string;
protected relatedKey: string;
protected parentKey: string;
protected pivotColumns: string[];
protected pivotWheres: Array<{
type?: string;
column: string;
operator?: any;
value?: any;
values?: any[];
}>;
/**
* Creates a new BelongsToMany relationship instance.
*
* @param parent - Parent model instance
* @param related - Related model constructor
* @param pivotTable - Name of the pivot table
* @param foreignKey - Foreign key for parent in pivot table
* @param relatedKey - Foreign key for related model in pivot table
* @param parentKey - Local key on parent model
* @param relatedPivotKey - Local key on related model
*
* @example
*
* // User belongs to many Roles through user_roles table
* new BelongsToMany(
* this, // User instance
* Role, // Role constructor
* 'user_roles', // pivot table
* 'user_id', // parent foreign key
* 'role_id', // related foreign key
* 'id', // parent local key
* 'id' // related local key
* );
*
*/
constructor(parent: Parent, related: new () => Related, pivotTable: string, foreignKey: string, relatedKey: string, parentKey: string, relatedPivotKey: string);
/**
* Adds constraints to the relationship query.
* For SQL databases, uses JOIN to connect through pivot table.
* For ScyllaDB, uses separate queries due to limited JOIN support.
*
* @param query - Query builder to add constraints to
* @returns Modified query builder with relationship constraints
*/
addConstraints(query: QueryBuilder<Related, any>): QueryBuilder<Related, any>;
/**
* Gets the relationship results.
* Handles both SQL and NoSQL database approaches for many-to-many relationships.
*
* @returns Promise resolving to array of related models with pivot data
*
* @example
*
* const user = await User.find(1);
* const roles = await user.rolesRelation().getResults();
*
* roles.forEach(role => {
* console.log(`Role: ${role.name}`);
* console.log(`Assigned at: ${role.pivot.assigned_at}`);
* });
*
*/
getResults(): Promise<Related[]>;
/**
* Attaches related models to the parent through the pivot table.
*
* @param ids - ID or array of IDs to attach
* @param attributes - Additional pivot table attributes
* @returns Promise that resolves when attachment is complete
*
* @example
*
* const user = await User.find(1);
*
* // Attach single role
* await user.rolesRelation().attach(1);
*
* // Attach multiple roles
* await user.rolesRelation().attach([1, 2, 3]);
*
* // Attach with pivot data
* await user.rolesRelation().attach(1, {
* assigned_at: new Date(),
* assigned_by: 'admin'
* });
*
*/
attach(ids: any | any[], attributes?: Record<string, any>): Promise<void>;
/**
* Detaches related models from the parent by removing pivot table records.
*
* @param ids - Optional ID or array of IDs to detach. If not provided, detaches all
* @returns Promise resolving to number of detached records
*
* @example
*
* const user = await User.find(1);
*
* // Detach specific roles
* await user.rolesRelation().detach([1, 2]);
*
* // Detach all roles
* await user.rolesRelation().detach();
*
*/
detach(ids?: any | any[]): Promise<number>;
/**
* Synchronizes the relationship to match the given array of IDs.
* Attaches missing relationships and optionally detaches extra ones.
*
* @param ids - Array of IDs that should be attached
* @param detaching - Whether to detach IDs not in the array (default: true)
* @returns Promise resolving to sync results with attached/detached arrays
*
* @example
*
* const user = await User.find(1);
*
* // Sync to specific roles (detaches others)
* const result = await user.rolesRelation().sync([1, 2, 3]);
* console.log(`Attached: ${result.attached.length}`);
* console.log(`Detached: ${result.detached.length}`);
*
* // Sync without detaching
* await user.rolesRelation().sync([4, 5], false);
*
*/
sync(ids: any[], detaching?: boolean): Promise<{
attached: any[];
detached: any[];
updated: any[];
}>;
/**
* Toggles the attachment of related models.
* Attaches if not currently attached, detaches if currently attached.
*
* @param ids - ID or array of IDs to toggle
* @returns Promise resolving to toggle results with attached/detached arrays
*
* @example
*
* const user = await User.find(1);
*
* // Toggle roles - attach if not attached, detach if attached
* const result = await user.rolesRelation().toggle([1, 2, 3]);
* console.log(`Attached: ${result.attached}`);
* console.log(`Detached: ${result.detached}`);
*
*/
toggle(ids: any | any[]): Promise<{
attached: any[];
detached: any[];
}>;
/**
* Updates existing pivot table records for a specific related model.
*
* @param id - ID of the related model
* @param attributes - Attributes to update in the pivot table
* @returns Promise resolving to number of updated records
*
* @example
*
* const user = await User.find(1);
*
* // Update pivot data for a specific role
* await user.rolesRelation().updateExistingPivot(1, {
* updated_at: new Date(),
* notes: 'Role permissions updated'
* });
*
*/
updateExistingPivot(id: any, attributes: Record<string, any>): Promise<number>;
/**
* Specifies additional pivot table columns to include in query results.
*
* @param columns - Pivot column names to include
* @returns This relationship instance for method chaining
*
* @example
*
* const user = await User.find(1);
* const roles = await user.rolesRelation()
* .withPivot('assigned_at', 'assigned_by')
* .getResults();
*
* roles.forEach(role => {
* console.log(`Assigned at: ${role.pivot.assigned_at}`);
* console.log(`Assigned by: ${role.pivot.assigned_by}`);
* });
*
*/
withPivot(...columns: string[]): this;
/**
* Adds a WHERE constraint on pivot table columns.
*
* @param column - Pivot column name
* @param operator - Comparison operator or value if using 2-param form
* @param value - Value to compare against
* @returns This relationship instance for method chaining
*
* @example
*
* const user = await User.find(1);
* const activeRoles = await user.rolesRelation()
* .wherePivot('status', 'active')
* .wherePivot('assigned_at', '>', lastWeek)
* .getResults();
*
*/
wherePivot(column: string, operator: any, value?: any): this;
/**
* Adds a WHERE IN constraint on pivot table columns.
*
* @param column - Pivot column name
* @param values - Array of values to match against
* @returns This relationship instance for method chaining
*
* @example
*
* const user = await User.find(1);
* const roles = await user.rolesRelation()
* .wherePivotIn('status', ['active', 'pending'])
* .getResults();
*
*/
wherePivotIn(column: string, values: any[]): this;
/**
* Gets the pivot table columns to select with proper aliasing.
*
* @protected
* @returns Array of pivot column select statements
*/
protected getPivotColumns(): string[];
/**
* Creates a new query builder for the pivot table.
*
* @protected
* @returns QueryBuilder instance for pivot table operations
*/
protected newPivotQuery(): QueryBuilder<any, any>;
/**
* Gets the related model's table name.
*
* @protected
* @returns Related model table name
*/
protected getRelatedTable(): string;
}