UNPKG

@adonisjs/lucid

Version:

SQL ORM built on top of Active Record pattern

160 lines (159 loc) 6.29 kB
/* * @adonisjs/lucid * * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { BaseTransformer } from '@adonisjs/core/transformers'; import { Database } from "../src/database/main.js"; import { Adapter } from "../src/orm/adapter/index.js"; import { BaseModel } from "../src/orm/base_model/index.js"; import { QueryClient } from "../src/query_client/index.js"; import { DatabaseTestUtils } from '../src/test_utils/database.js'; import { defineTransformerBindings } from "../src/bindings/transformer.js"; /** * AdonisJS service provider that wires up the entire Lucid ORM layer. * * Responsibilities: * - Registers the {@link Database} singleton and the default * {@link QueryClient} in the IoC container. * - Attaches the ORM {@link Adapter} to {@link BaseModel}. * - Registers the `db` macro on {@link TestUtils} for test helpers. * - Binds REPL helpers when running in the `repl` environment. * - Registers the `unique` / `exists` VineJS validation rules when * VineJS is present. * - Optionally enables pretty-printed debug query logging. * * @example * // providers/database_provider.ts is auto-discovered by AdonisJS. * // No manual instantiation is required. */ export default class DatabaseServiceProvider { app; /** * @param {ApplicationService} app - The AdonisJS application instance * used to access the IoC container, configuration, and environment * information. */ constructor(app) { this.app = app; } /** * Registers REPL bindings so that interactive database helpers * (e.g. `db`, `User`) are available when the application is * started with `node ace repl`. * * This is a no-op in any environment other than `repl`. */ async registerReplBindings() { if (this.app.getEnvironment() === 'repl') { const { defineReplBindings } = await import('../src/bindings/repl.js'); defineReplBindings(this.app, await this.app.container.make('repl')); } } /** * Registers the database-backed `unique` and `exists` validation * rules on VineJS when the application is using VineJS as its * validation engine. * * This is a no-op when VineJS is not installed or configured. * * @param {Database} db - The resolved {@link Database} singleton * that will be passed into the validation rule implementations. */ async registerVineJSRules(db) { if (this.app.usingVineJS) { const { defineValidationRules } = await import('../src/bindings/vinejs.js'); defineValidationRules(db); } defineTransformerBindings(BaseTransformer); } /** * Registers a `db` macro on the core {@link TestUtils} class. * The macro is lazily set up via `container.resolving('testUtils')` * so it is only wired when tests actually request the `testUtils` * binding. * * @example * // Inside a test file * const { db } = await TestUtils.create(app) * await db.migrate() */ async registerTestUtils() { this.app.container.resolving('testUtils', async () => { const { TestUtils } = await import('@adonisjs/core/test_utils'); TestUtils.macro('db', (connectionName) => { return new DatabaseTestUtils(this.app, connectionName); }); }); } /** * Attaches a listener to the `db:query` event that pretty-prints * every executed query to the console. Only active when the * `prettyPrintDebugQueries` flag is enabled in the `database.ts` * configuration file. * * @param {Database} db - The resolved {@link Database} singleton * whose config is checked and whose `prettyPrint` handler is used. */ async prettyPrintDebugQueries(db) { if (db.config.prettyPrintDebugQueries) { const emitter = await this.app.container.make('emitter'); emitter.on('db:query', db.prettyPrint); } } /** * Lifecycle hook invoked by AdonisJS during the **register** phase. * * Registers the following singletons in the IoC container: * - `Database` (also aliased as `lucid.db`) — the central connection * manager, built from the `database` config key. * - `QueryClient` — a query client bound to the **default** * connection, convenient for simple queries without explicitly * choosing a connection. */ register() { this.app.container.singleton(Database, async (resolver) => { const config = this.app.config.get('database'); const emitter = await resolver.make('emitter'); const logger = await resolver.make('logger'); const db = new Database(config, logger, emitter); return db; }); this.app.container.singleton(QueryClient, async (resolver) => { const db = await resolver.make('lucid.db'); return db.connection(); }); this.app.container.alias('lucid.db', Database); } /** * Lifecycle hook invoked by AdonisJS during the **boot** phase, * after all providers have registered their bindings. * * - Sets the ORM {@link Adapter} on {@link BaseModel} so that all * model classes share a single, application-scoped adapter. * - Calls the remaining setup helpers: debug query logging, test * utilities, REPL bindings, and VineJS rules. */ async boot() { const db = await this.app.container.make('lucid.db'); BaseModel.$adapter = new Adapter(db); await this.prettyPrintDebugQueries(db); await this.registerTestUtils(); await this.registerReplBindings(); await this.registerVineJSRules(db); } /** * Lifecycle hook invoked by AdonisJS during application shutdown. * * Gracefully closes **all** open database connections managed by the * {@link Database} connection manager so that no sockets or pools * are left dangling. */ async shutdown() { const db = await this.app.container.make('lucid.db'); await db.manager.closeAll(); } }