Skip to content

Querying Models ​

Every model is a query builder for its table. You chain filters and finish with a method like get() or first(). Column names autocomplete, and results come back as typed model instances.

ts
import Post from '../models/Post'

export async function getPublishedPosts() {
  return await Post
    .where('status', 'published')
    .orderBy('published_at', 'desc')
    .get()
}

Available methods ​

Model-specific methods are described in the method reference below. Shared methods link to the query builder pages, which document them for both models and tables.

Start a query: query(), newQuery(), newModelQuery(), newQueryWithoutScopes(), newQueryWithoutRelationships(), from()

Retrieve models: all(), get(), first(), firstOrFail(), find(), findMany(), findOrFail(), firstWhere(), findOr(), firstOr(), findOrNew(), whereKey(), whereKeyNot(), sole(), hydrate()

Retrieve plain JSON: getJson(), firstJson(), findOrFailJson(), soleJson()

Values and aggregates: value(), valueOrFail(), soleValue(), pluck(), implode(), count(), exists(), doesntExist(), existsOr(), doesntExistOr(), sum(), avg(), average(), min(), max()

Filter rows: where(), orWhere(), whereNot(), orWhereNot(), whereNull(), whereNotNull(), whereIn(), whereNotIn(), whereBetween(), whereNotBetween(), orWhereIn(), orWhereNotIn(), orWhereBetween(), orWhereNotBetween(), whereBetweenColumns(), orWhereBetweenColumns(), whereNotBetweenColumns(), orWhereNotBetweenColumns(), whereValueBetween(), orWhereValueBetween(), whereValueNotBetween(), orWhereValueNotBetween(), whereLike(), orWhereLike(), whereNotLike(), orWhereNotLike(), whereColumn(), orWhereColumn(), whereAny(), orWhereAny(), whereAll(), orWhereAll(), whereNone(), orWhereNone(), whereDate(), orWhereDate(), whereMonth(), orWhereMonth(), whereDay(), orWhereDay(), whereYear(), orWhereYear(), whereTime(), orWhereTime(), wherePast(), orWherePast(), whereNowOrPast(), orWhereNowOrPast(), whereFuture(), orWhereFuture(), whereNowOrFuture(), orWhereNowOrFuture(), whereToday(), orWhereToday(), whereBeforeToday(), orWhereBeforeToday(), whereTodayOrBefore(), orWhereTodayOrBefore(), whereAfterToday(), orWhereAfterToday(), whereTodayOrAfter(), orWhereTodayOrAfter(), whereJson(), whereJsonContains(), whereJsonDoesntContain(), orWhereJsonDoesntContain(), whereJsonContainsKey(), orWhereJsonContainsKey(), whereJsonDoesntContainKey(), orWhereJsonDoesntContainKey(), whereJsonLength(), whereFullText(), whereVectorSimilarTo(), when(), unless(), distinct(), groupBy(), having(), orHaving(), havingBetween(), orHavingBetween(), havingNotBetween(), orHavingNotBetween(), havingNull(), orHavingNull(), havingNotNull(), orHavingNotNull(), unsafeWhere(), orUnsafeWhere(), whereExists(), orWhereExists(), whereNotExists(), orWhereNotExists(), whereInSub(), whereNotInSub(), whereSub(), orWhereSub()

Select columns: select(), addSelect(), selectSub(), addSelectSub(), withCasts()

Joins and unions: join(), leftJoin(), rightJoin(), crossJoin(), joinSub(), leftJoinSub(), rightJoinSub(), joinLateral(), leftJoinLateral(), union(), unionAll()

Order, limit, and lock: orderBy(), orderByDesc(), latest(), oldest(), inRandomOrder(), reorder(), limit(), offset(), skip(), take(), forPage(), lock(), lockForUpdate(), sharedLock()

Eager loading: with(), withCount(), withExists(), withSum(), withAvg(), withMin(), withMax(), withAggregate(), withWhereHas()

Filter by relation: has(), orHas(), whereHas(), orWhereHas(), doesntHave(), orDoesntHave(), whereDoesntHave(), orWhereDoesntHave(), whereRelation(), orWhereRelation(), whereDoesntHaveRelation(), orWhereDoesntHaveRelation(), hasMorph(), orHasMorph(), doesntHaveMorph(), orDoesntHaveMorph(), whereHasMorph(), orWhereHasMorph(), whereDoesntHaveMorph(), orWhereDoesntHaveMorph(), whereMorphRelation(), orWhereMorphRelation(), whereMorphDoesntHaveRelation(), orWhereMorphDoesntHaveRelation(), whereBelongsTo(), orWhereBelongsTo(), whereAttachedTo(), orWhereAttachedTo(), whereMorphedTo(), orWhereMorphedTo(), whereNotMorphedTo(), orWhereNotMorphedTo()

Soft deletes: withTrashed(), onlyTrashed(), withoutTrashed()

Global scopes: withoutGlobalScope(), withoutGlobalScopes()

Batching and pagination: chunk(), chunkById(), chunkByIdDesc(), each(), eachById(), lazy(), lazyById(), lazyByIdDesc(), cursor(), paginate(), paginateJson(), simplePaginate(), simplePaginateJson(), cursorPaginate(), cursorPaginateJson()

Debug and cache: toSQL(), toRawSql(), debug(), dump(), cache()

Writes from a query: update(), updateJson(), increment(), decrement(), incrementEach(), decrementEach(), insertUsing(), insertOrIgnoreUsing(), truncate(), upsert(), delete(), restore(), forceDelete()

Loading behavior: preventLazyLoading(), preventAccessingMissingAttributes(), automaticallyEagerLoadRelationships()

Model metadata: getTableName(), getConnectionName(), getRepository()

Instance checks and refresh: fresh(), refresh(), exists(), is(), isNot(), trashed()

Retrieve all records ​

ts
const posts = await Post.all()

for (const post of posts) {
  console.log(post.title)
}

all() and get() return a collection, an array of model instances with extra helpers.

Build a query ​

Start a query from any filter or ordering method on the model, then chain more:

ts
const posts = await Post
  .where('status', 'published')
  .where('views', '>', 100)
  .latest()
  .get()

When the first method you need isn't available on the model itself, such as limit() or take(), start with query():

ts
const recent = await Post.query().latest().limit(5).get()

The model accepts the same filters as the query builder: whereIn, whereNull, whereBetween, whereLike, date filters, JSON filters, joins, and more.

WARNING

Query builders are immutable. Each method returns a new builder and leaves the original unchanged, so always use the returned value:

ts
let query = Post.query()
query = query.where('status', 'published')

Calling query.where(...) without reassigning it has no effect.

Conditional filters ​

Use when() to add a filter only when a value is present. The callback must return the query:

ts
const posts = await Post
  .when(search, (query, term) => query.whereLike('title', `%${term}%`))
  .when(categoryId, (query, id) => query.where('category_id', id))
  .latest()
  .get()

unless() does the opposite and runs the callback when the value is falsy. Both accept a third callback for the other case.

Grouped conditions ​

Pass a callback to where() to wrap conditions in parentheses:

ts
const posts = await Post
  .where('status', 'published')
  .where(query => query
    .where('title', 'like', '%holo%')
    .orWhere('excerpt', 'like', '%holo%'))
  .get()

Retrieve a single model ​

ts
const post = await Post.find(1)
//    ^? Post | undefined

const first = await Post.where('status', 'draft').first()
const bySlug = await Post.firstWhere('slug', 'hello-world')

find() looks up by primary key. findMany() takes an array of keys and returns a collection:

ts
const posts = await Post.findMany([1, 2, 3])

Find by a different column with query().find():

ts
const post = await Post.query().find('hello-world', 'slug')

Not found errors ​

The OrFail methods throw a ModelNotFoundException instead of returning undefined:

ts
const post = await Post.findOrFail(1)
const draft = await Post.where('status', 'draft').firstOrFail()

The error has statusCode set to 404 and a model property with the model name:

ts
import { ModelNotFoundException } from '@holo-js/db'

try {
  await Post.findOrFail(id)
} catch (error) {
  if (error instanceof ModelNotFoundException) {
    console.log(error.model, error.statusCode)
  }
}

Exactly one result ​

sole() returns the only matching row and throws when there are zero or more than one:

ts
const settings = await Setting.where('key', 'site_name').sole()

Single values and aggregates ​

ts
const title = await Post.where('id', 1).value('title')
//    ^? string | undefined

const titles = await Post.where('status', 'published').pluck('title')
//    ^? string[]

const count = await Post.where('status', 'draft').count()
const totalViews = await Post.sum('views')
const averageViews = await Post.avg('views')
const hasDrafts = await Post.where('status', 'draft').exists()

value() returns undefined when no row matches. valueOrFail() throws instead, and soleValue() throws unless exactly one row matches. min(), max(), doesntExist() are also available. avg(), min(), and max() return null for an empty result.

Select specific columns ​

ts
const posts = await Post.select('id', 'title').get()

Alias a column with as:

ts
const posts = await Post.select('id', 'title as heading').get()

Joined columns ​

A model from a joined query keeps its own table's columns:

ts
const posts = await Post.query()
  .join('users', 'users.id', '=', 'posts.user_id')
  .select('posts.*', 'users.name as author_name')
  .get()

When the joined table has a column with a model column's name, such as id, the model reads its own column: post.id is posts.id, sum('views') and the other unqualified aggregates read posts.views, and orderBy('id') orders by posts.id. find(), findOrFail(), whereKey() and whereKeyNot() filter by the qualified key, posts.id, as Laravel's do. A where('id', …) you write stays unqualified, so on a joined query qualify it yourself. Selected columns that aren't model columns keep the selection order, so when two of them share a name, the later one wins, as in the database's own result.

A selection that would put a joined column into a model column throws a CompilerError before the query runs. That covers select('users.id as id'), and select('posts.title', 'users.id') when the query doesn't also select the model's own id. Alias the joined column to a name that isn't a model column, such as users.id as author_id, or select the model's own column too, such as posts.*.

A joined wildcard such as users.* throws the same way unless the query also selects the model's columns it would replace, such as posts.*. When Holo doesn't know the joined table's columns, select posts.* with it. Each query in a union of joined model queries must select the same tables' columns in the same order.

To cursor paginate a union, a distinct() query or a grouped query by a joined column, select that column under an alias and order by the alias, such as select('posts.*', 'users.created_at as author_joined_at').orderBy('author_joined_at'). A union is ordered by its output column names, and the cursor applies to every query in the union: to the column each query selects at the ordered column's position, or, when a wildcard such as posts.* comes before it, to the column of the same name.

Differs from Laravel

In Laravel, a joined column overwrites the model attribute of the same name, so a model can carry another table's id, and a later save() writes to the wrong row. Holo keeps the model's own columns and rejects selections that would replace them.

Ordering and limiting ​

ts
await Post.orderBy('title').get()
await Post.orderBy('published_at', 'desc').get()
await Post.latest().get()
await Post.oldest('published_at').get()
await Post.inRandomOrder().first()

latest() and oldest() sort by created_at unless you pass a column. reorder() clears earlier ordering.

ts
await Post.query().latest().skip(20).take(10).get()
await Post.query().latest().forPage(3, 10).get()

Pagination ​

ts
const page = await Post.where('status', 'published').latest().paginate(15, 2)

page.data
page.meta.total
page.meta.lastPage

simplePaginate() skips the total count, and cursorPaginate() pages by cursor for large tables. See pagination for the full result shape.

Soft-deleted models ​

On models with soft deletes, queries skip trashed rows automatically. Include them explicitly:

ts
const everything = await Post.withTrashed().get()
const trashed = await Post.onlyTrashed().get()

On a query, withoutTrashed() switches back to the default. Check a single record with post.trashed().

Remove global scopes ​

If the model has global scopes, turn them off per query:

ts
await Post.withoutGlobalScope('published').get()
await Post.withoutGlobalScopes().get()
await Post.withoutGlobalScopes(['published', 'tenant']).get()

Process large tables ​

Don't load thousands of rows with get(). Process them in batches instead.

chunkById() reads batches ordered by primary key and is safe when you update rows inside the callback:

ts
await Post.where('status', 'draft').chunkById(200, async (posts) => {
  for (const post of posts) {
    await post.update({ status: 'archived' })
  }
})

Return false from the callback to stop early. chunk() reads batches in primary key order, chunkById() orders by a column you name, and chunkByIdDesc() walks keys in reverse.

lazy() yields one model at a time while fetching in batches behind the scenes:

ts
for await (const post of Post.lazy(500)) {
  await reindex(post)
}

each() calls a function once per model, and eachById() and lazyById() batch by primary key so you can update models while you walk them:

ts
await Post.where('status', 'draft').eachById(async (post) => {
  await post.update({ status: 'archived' })
})

See chunking large results for more.

Models from rows you already loaded ​

hydrate() turns a row you read with the query builder into a saved model, as find() would have returned it. Use it when the row has to come from somewhere the model can't query, such as a transaction on another connection:

ts
await DB.transaction(async (tx) => {
  const row = await tx.table(Tenant.definition.table).where('id', tenantId).lockForUpdate().first()
  if (!row) {
    return
  }

  const tenant = await Tenant.hydrate(row, tx)
  await actor.policy('tenants').authorize('manageTeam', tenant)
})

The model reads the row the way a query does: it applies casts, fires the retrieved event, and reports exists() as true with no changes. Pass the source connection as the second argument when its driver differs from the model's driver, so values such as JSON are decoded correctly. Without it, decoding uses the model's driver. Relations, policies, and media work as they do on a model from find(), and they run on the model's own connection. fillable and guarded don't apply, because the row comes from the database, not from a request. Pass it only rows you read from the model's table. To build a new, unsaved model, use make().

Plain JSON results ​

Methods ending in Json return plain serialized objects instead of model instances: getJson(), firstJson(), findOrFailJson(), soleJson(), paginateJson(), simplePaginateJson(), and cursorPaginateJson().

ts
const posts = await Post.where('status', 'published').getJson()

They apply your hidden, visible, and appended settings. See serialization.

Cache query results ​

Add cache() with a number of seconds to cache a query's results:

ts
const categories = await Category.orderBy('name').cache(60).get()

See query caching.

Refresh a model ​

fresh() loads a new copy of the record from the database. refresh() reloads the current instance in place and clears its loaded relationships.

ts
const latest = await post.fresh()
await post.refresh()

Compare models ​

is() checks whether two instances are the same row on the same table and connection:

ts
if (post.is(otherPost)) {
  // same record
}

isNot() is the reverse.

Debug a query ​

ts
const { sql, bindings } = Post.where('status', 'draft').toSQL()

debug() adds the connection name and redacts sensitive bindings, which makes it safer to log. dump() prints the debug() output and returns the query so you can keep chaining.

Method reference ​

Model-level methods are described here. Shared query builder methods are documented on the query builder pages.

query() ​

Starts a query for the model. Soft-deleted rows are excluded, global scopes apply, and the relations in the model's with option load automatically.

ts
const posts = await Post.query().where('status', 'published').get()

newQuery() ​

Alias of query(). It builds the same query with the same defaults.

newModelQuery() ​

Alias of query(). It builds the same query with the same defaults.

newQueryWithoutScopes() ​

Starts a query with global scopes and the soft-delete filter turned off. The model's default with relations still load.

ts
const everyPost = await Post.newQueryWithoutScopes().get()

newQueryWithoutRelationships() ​

Starts a query that skips the model's default with relations. Soft-delete filtering and global scopes still apply.

ts
const bareRows = await Post.newQueryWithoutRelationships().get()

all() ​

Returns every visible row as a collection. It's the same as get() with no filters.

ts
const posts = await Post.all()

get() ​

Runs the query and returns a collection of models.

ts
const drafts = await Post.where('status', 'draft').get()

first() ​

Returns the first matching model, or undefined.

ts
const latest = await Post.latest().first()

firstOrFail() ​

Returns the first matching model. Throws ModelNotFoundException when nothing matches.

ts
const post = await Post.where('slug', slug).firstOrFail()

find() ​

Returns one model by primary key, or undefined. Pass a second argument to look up by another column.

ts
const post = await Post.query().find('hello-world', 'slug')

findMany() ​

Returns a collection of models with the given primary keys. An empty array returns an empty collection without querying the database.

ts
const posts = await Post.findMany([1, 2, 3])

findOrFail() ​

Like find(), but throws ModelNotFoundException when no row matches.

ts
const post = await Post.findOrFail(1)

findOr() ​

Returns one model by primary key, or the result of the callback when no row matches.

ts
const post = await Post.findOr(id, () => Post.make({ title: 'Untitled' }))

firstOr() ​

Returns the first matching model, or the result of the callback when nothing matches.

ts
const post = await Post.where('slug', slug).firstOr(() => {
  throw new Error(`No post for ${slug}`)
})

findOrNew() ​

Returns one model by primary key, or a new unsaved model when no row matches.

ts
const settings = await Setting.findOrNew(userId)

whereKey() ​

Filters by primary key. Pass one key or a list.

ts
const posts = await Post.whereKey([1, 2, 3]).get()

whereKeyNot() ​

Filters out one primary key or a list of them.

ts
const others = await Post.whereKeyNot(post.get('id')).get()

firstWhere() ​

Shorthand for where(column, operator, value).first().

ts
const post = await Post.firstWhere('slug', '=', 'hello-world')

sole() ​

Returns the only matching model. Throws when no row matches or when more than one does. See exactly one result.

ts
const settings = await Setting.where('key', 'site_name').sole()

hydrate() ​

Builds a saved model from a row of the model's table that you already loaded, applying casts and firing the retrieved event. Mass assignment rules don't apply. See models from rows you already loaded.

The optional second argument is the source DatabaseContext, such as a transaction's tx. It controls decoding without changing the model's connection.

ts
const tenant = await Tenant.hydrate(row)

getJson() ​

Returns every matching row as a plain object, serialized with the model's hidden, visible, and appended rules.

ts
const posts = await Post.getJson()

firstJson() ​

Returns the first matching row as a plain object, or undefined.

ts
const latest = await Post.latest().firstJson()

findOrFailJson() ​

Returns one row as a plain object by primary key. Throws ModelNotFoundException when no row matches.

ts
return await Post.findOrFailJson(id)

soleJson() ​

Returns the only matching row as a plain object. Throws when zero or several rows match.

ts
const settings = await Setting.where('key', 'site_name').soleJson()

with() ​

Eager loads relations in the same query. Pass a constraint callback to filter one relation's rows. See eager loading.

ts
const posts = await Post
  .with('author', 'tags')
  .with('comments', query => query.where('approved', true))
  .get()

withCount() ​

Adds a {relation}_count attribute to each model, without loading the related rows.

ts
const posts = await Post.withCount('comments').get()
// posts[0].comments_count

withExists() ​

Adds a {relation}_exists boolean to each model. It's true when at least one related row matches.

ts
const authors = await Author.withExists('posts').get()
// authors[0].posts_exists

withSum() ​

Adds a {relation}_sum_{column} attribute with the total of a related column.

ts
const authors = await Author.withSum('books', 'pages').get()
// authors[0].books_sum_pages

withAvg() ​

Adds a {relation}_avg_{column} attribute with the average of a related column.

ts
const authors = await Author.withAvg('books', 'rating').get()

withMin() ​

Adds a {relation}_min_{column} attribute with the smallest value of a related column.

ts
const authors = await Author.withMin('books', 'pages').get()

withMax() ​

Adds a {relation}_max_{column} attribute with the largest value of a related column.

ts
const authors = await Author.withMax('books', 'pages').get()

withAggregate() ​

Adds a {relation}_{function}_{column} attribute using the aggregate function you name: count, exists, sum, avg, min, or max. withSum(), withAvg(), withMin(), withMax(), withCount(), and withExists() are shortcuts for it. With count and exists, the column must be '*', and the attribute is named {relation}_count or {relation}_exists.

ts
const authors = await Author.withAggregate('books', 'pages', 'sum').get()
// author.toJSON().books_sum_pages

withWhereHas() ​

Keeps only models that have a matching related row, and eager loads that same filtered relation.

ts
const authors = await Author
  .withWhereHas('posts', query => query.where('status', 'published'))
  .get()

has() ​

Keeps only models that have at least one related row.

ts
const authorsWithPosts = await Author.has('posts').get()

orHas() ​

Adds an or condition: the model matches if it has at least one related row. Use it after another where clause.

ts
const authors = await Author.where('active', true).orHas('posts').get()

whereHas() ​

Keeps models with a related row that matches the constraint callback.

ts
const authors = await Author
  .whereHas('posts', query => query.where('status', 'published'))
  .get()

orWhereHas() ​

Adds an or version of whereHas().

ts
const authors = await Author
  .where('active', true)
  .orWhereHas('posts', query => query.where('status', 'published'))
  .get()

doesntHave() ​

Keeps only models with no related rows.

ts
const authorsWithoutPosts = await Author.doesntHave('posts').get()

orDoesntHave() ​

Adds an or version of doesntHave().

ts
const authors = await Author.where('vip', true).orDoesntHave('posts').get()

whereDoesntHave() ​

Keeps models with no related row that matches the constraint callback.

ts
const authors = await Author
  .whereDoesntHave('posts', query => query.where('status', 'draft'))
  .get()

orWhereDoesntHave() ​

Adds an or version of whereDoesntHave().

ts
const authors = await Author
  .where('vip', true)
  .orWhereDoesntHave('posts', query => query.where('status', 'draft'))
  .get()

whereRelation() ​

Shorthand for whereHas() with one column condition on the related model.

ts
const authors = await Author.whereRelation('posts', 'status', 'published').get()

orWhereRelation() ​

Adds an or version of whereRelation().

ts
const authors = await Author
  .where('vip', true)
  .orWhereRelation('posts', 'status', 'published')
  .get()

whereDoesntHaveRelation() ​

Shorthand for whereDoesntHave() with one column condition on the related model.

ts
const authors = await Author.whereDoesntHaveRelation('posts', 'status', 'published').get()

orWhereDoesntHaveRelation() ​

Adds an or version of whereDoesntHaveRelation().

ts
const authors = await Author
  .where('vip', true)
  .orWhereDoesntHaveRelation('posts', 'status', 'published')
  .get()

hasMorph() ​

Keeps rows whose morphTo relation points at an existing model of one of the given types.

ts
const comments = await Comment.hasMorph('commentable', [Post, Video]).get()

orHasMorph() ​

Adds an or version of hasMorph().

ts
const comments = await Comment.where('pinned', true).orHasMorph('commentable', Post).get()

doesntHaveMorph() ​

Keeps rows whose morphTo relation does not point at an existing model of the given types.

ts
const comments = await Comment.doesntHaveMorph('commentable', Post).get()

orDoesntHaveMorph() ​

Adds an or version of doesntHaveMorph().

ts
const comments = await Comment.where('pinned', true).orDoesntHaveMorph('commentable', Post).get()

whereHasMorph() ​

Like hasMorph(), with a constraint on the related model. The callback also receives the morph type, so you can constrain each type differently.

ts
const comments = await Comment
  .whereHasMorph('commentable', [Post, Video], (query, type) => type === 'Post'
    ? query.where('status', 'published')
    : query.where('visibility', 'public'))
  .get()

orWhereHasMorph() ​

Adds an or version of whereHasMorph().

ts
const comments = await Comment
  .where('pinned', true)
  .orWhereHasMorph('commentable', Post, query => query.where('status', 'published'))
  .get()

whereDoesntHaveMorph() ​

Like doesntHaveMorph(), with a constraint on the related model.

ts
const comments = await Comment
  .whereDoesntHaveMorph('commentable', Post, query => query.where('status', 'draft'))
  .get()

orWhereDoesntHaveMorph() ​

Adds an or version of whereDoesntHaveMorph().

ts
const comments = await Comment
  .where('pinned', true)
  .orWhereDoesntHaveMorph('commentable', Post, query => query.where('status', 'draft'))
  .get()

whereMorphRelation() ​

Like whereRelation(), for a morphTo relation. Pass the model types the relation may point at.

ts
const comments = await Comment
  .whereMorphRelation('commentable', [Post, Video], 'status', 'published')
  .get()

orWhereMorphRelation() ​

Adds an or version of whereMorphRelation().

ts
const comments = await Comment
  .where('flagged', true)
  .orWhereMorphRelation('commentable', Post, 'status', 'published')
  .get()

whereMorphDoesntHaveRelation() ​

Like whereDoesntHaveRelation(), for a morphTo relation. Pass the model types the relation may point at.

ts
const comments = await Comment
  .whereMorphDoesntHaveRelation('commentable', [Post, Video], 'status', 'draft')
  .get()

orWhereMorphDoesntHaveRelation() ​

Adds an or version of whereMorphDoesntHaveRelation().

ts
const comments = await Comment
  .where('flagged', true)
  .orWhereMorphDoesntHaveRelation('commentable', Post, 'status', 'draft')
  .get()

whereBelongsTo() ​

Keeps rows whose belongsTo relation points at the given model. Pass the relation name when the model has more than one belongsTo relation to that model.

ts
const posts = await Post.whereBelongsTo(author, 'author').get()

orWhereBelongsTo() ​

Adds an or version of whereBelongsTo().

ts
const posts = await Post.where('pinned', true).orWhereBelongsTo(author, 'author').get()

whereAttachedTo() ​

Keeps rows attached to the given model, or to any model in a list, through a many-to-many relation. Pass the relation name when the model has more than one many-to-many relation to that model.

ts
const posts = await Post.whereAttachedTo(tag).get()
const tagged = await Post.whereAttachedTo([news, tips], 'tags').get()

orWhereAttachedTo() ​

Adds an or version of whereAttachedTo().

ts
const posts = await Post.where('pinned', true).orWhereAttachedTo(tag).get()

whereMorphedTo() ​

Keeps rows whose morphTo relation points at the given model, entity, or morph type.

ts
const comments = await Comment.whereMorphedTo('commentable', post).get()

orWhereMorphedTo() ​

Adds an or version of whereMorphedTo().

ts
const comments = await Comment
  .where('flagged', true)
  .orWhereMorphedTo('commentable', post)
  .get()

whereNotMorphedTo() ​

Keeps rows whose morphTo relation does not point at the given model or type.

ts
const comments = await Comment.whereNotMorphedTo('commentable', Video).get()

orWhereNotMorphedTo() ​

Adds an or version of whereNotMorphedTo().

ts
const comments = await Comment
  .where('flagged', true)
  .orWhereNotMorphedTo('commentable', Video)
  .get()

withTrashed() ​

Includes soft-deleted rows in the query.

ts
const everything = await Post.withTrashed().get()

onlyTrashed() ​

Returns only soft-deleted rows.

ts
const trashed = await Post.onlyTrashed().get()

withoutTrashed() ​

Excludes soft-deleted rows. Use it to undo withTrashed() or onlyTrashed() earlier in the chain.

ts
const active = await Post.withoutTrashed().get()

withoutGlobalScope() ​

Runs the query with one named global scope turned off. Call it first in the chain. It starts a new query, so filters added before it are dropped.

ts
const posts = await Post.withoutGlobalScope('published').get()

withoutGlobalScopes() ​

Turns off the named global scopes, or every global scope when you pass no names. Without names, it also removes the soft-delete filter, so trashed rows are included. Call it first in the chain.

ts
await Post.withoutGlobalScopes(['published', 'tenant']).get()

withCasts() ​

Adds casts that apply only to this query's results. Use it for computed columns that the model doesn't cast.

ts
const users = await User.withCasts({ lastPostedAt: 'date' }).get()

preventLazyLoading() ​

Turns on, or with false turns off, the check that throws when a relation is read without being eager loaded. See preventing lazy loading.

ts
Post.preventLazyLoading()

preventAccessingMissingAttributes() ​

Turns on, or with false turns off, the check that throws when a column that wasn't selected is read.

ts
Post.preventAccessingMissingAttributes()

automaticallyEagerLoadRelationships() ​

Turns on, or with false turns off, automatic eager loading. The first access to a relation loads it for the whole result set. See automatic eager loading.

ts
Post.automaticallyEagerLoadRelationships()

getTableName() ​

Returns the table name the model reads from.

ts
Post.getTableName()
// 'posts'

getConnectionName() ​

Returns the connection name from the model's connectionName option, or undefined when it uses the default connection.

ts
Post.getConnectionName()

getRepository() ​

Returns the model's internal repository, which runs its queries. App code rarely needs it.

fresh() ​

Returns a new copy of the record from the database, or undefined if the row no longer exists. The current instance is not changed.

ts
const latest = await post.fresh()

refresh() ​

Reloads the record into the current instance and clears its loaded relations. It throws if the instance was never saved.

ts
await post.refresh()

exists() ​

Returns true when the instance was loaded from the database or saved to it. A model made with make() returns false until you save it.

ts
if (!post.exists()) {
  await post.save()
}

is() ​

Returns true when the other instance is the same row, on the same table and connection.

ts
post.is(otherPost)

isNot() ​

Returns true when the other instance is a different row.

ts
if (post.isNot(otherPost)) {
  // different records
}

trashed() ​

Returns true when the record has a deleted_at value. Models without soft deletes always return false.

ts
if (post.trashed()) {
  // show a restore button
}

Next steps ​

Released under the MIT License.