Skip to main content

Model Scopes

Scopes package reusable query constraints on a model. Ormed supports:

Ormed supports:

  • Generated scopes (declared on the model with @OrmScope)
  • Inline scopes (registered at runtime on a ScopeRegistry)

:::note Snippet context

  • The examples focus on the scope-related lines and omit full bootstrapping.
  • Where setup is required, the snippet calls bootstrapOrm() to wire generated registrations. :::

Define scopes as static methods that accept a Query<$Model> as the first parameter.

Global scopes apply to every query for the model:

(global: true)
static Query<$ScopedUser> activeOnly(Query<$ScopedUser> query) =>
query.whereEquals('active', true);

Local scopes are opt-in and can accept positional or named arguments:

()
static Query<$ScopedUser> withDomain(
Query<$ScopedUser> query,
String domain,
) => query.whereLike('email', '%@$domain');
()
static Query<$ScopedUser> roleIs(
Query<$ScopedUser> query, {
required String role,
}) => query.whereEquals('role', role);

Register generated scopes once during bootstrap:

// bootstrapOrm() wires up generated scopes (and other generated helpers).
final registry = bootstrapOrm();

Use generated query helpers:

// Local scopes compose via generated extensions
final admins = await dataSource.context
.query<$ScopedUser>()
.withDomain('example.com')
.roleIs(role: 'admin')
.get();

Global scopes apply automatically:

// Global scope filters inactive rows automatically
final active = await dataSource.context.query<$ScopedUser>().get();

Disable a specific global scope when needed:

// Opt out of globals when needed
final allRows = await dataSource.context
.query<$ScopedUser>()
.withoutGlobalScope('activeOnly')
.get();

Rules & notes​

  • Static only: scopes must be static so both the user model type and generated $Model query APIs can reference them consistently.
  • Global scopes must be argument-free (beyond the initial Query<T>).
  • Identifiers default to the method name; override with @OrmScope(identifier: 'custom') when needed.

Practical Guidance​

  • Use global scopes only for always-on constraints (for example soft-delete filters).
  • Use local scopes for business filters reused across endpoints.
  • Prefer explicit scope names that describe intent (active, recent, forTenant).

Verify Scope Behavior​

  1. Test that global scopes apply automatically.
  2. Test withoutGlobalScope(...) for override paths.
  3. Test local scope arguments (positional + named) in queries.

Read This Next​