Lucid models
A model is a class that matches a database table, and each object of that class is one row of the table. Instead of SQL you call model methods: Post.find(1), post.save(). It is the counterpart of Eloquent models in Laravel, except that columns are listed in the class explicitly.
Creating a model
node ace make:model PostThe file app/models/post.ts appears. The Post model works with the posts table by default. With the -m flag the command also creates a migration: node ace make:model Comment -m.
Describing columns
Each table column is declared as a property with the @column decorator:
// app/models/post.ts
import { DateTime } from 'luxon'
import { BaseModel, column } from '@adonisjs/lucid/orm'
export default class Post extends BaseModel {
@column({ isPrimary: true })
declare id: number
@column()
declare title: string
@column()
declare body: string
@column()
declare isPublished: boolean
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime
}Properties are written in camelCase, and Lucid maps them to snake_case columns itself: isPublished is is_published. Dates arrive as DateTime objects from the Luxon library. autoCreate and autoUpdate fill in the creation and update times.
Creating a record
// app/controllers/posts_controller.ts
import Post from '#models/post'
const post = await Post.create({
title: 'First post',
body: 'Post text',
})
console.log(post.id)create inserts a row into the table and returns the model with id filled in. The same result comes from new Post(), assigning properties and await post.save().
Reading
// app/controllers/posts_controller.ts
const posts = await Post.all()
const post = await Post.find(1)
const bySlug = await Post.findBy('slug', 'hello')all returns an array of models, find the model with the given id or null, findBy the first record with the given column value.
Updating and deleting
// app/controllers/posts_controller.ts
const post = await Post.findOrFail(1)
post.merge({ title: 'New title' })
await post.save()
await post.delete()merge assigns several properties at once, save writes the changes to the database, delete removes the row.
A model in a controller and a template
// app/controllers/posts_controller.ts
async show({ params, view }: HttpContext) {
const post = await Post.findOrFail(params.id)
return view.render('pages/posts/show', { post })
}{{-- resources/views/pages/posts/show.edge --}}
@layout({ title: post.title })
<h1>{{ post.title }}</h1>
<p>{{ post.createdAt.toFormat('dd.MM.yyyy') }}</p>
<div>{{ post.body }}</div>
@endOpen /posts/1 — the post appears with its date. If the record does not exist, findOrFail throws an exception and the visitor sees a 404 page.
Common mistake
If you forget @column on a property, Lucid does not know about the column: when reading, the property is undefined, and with create the value does not reach the database. Declare a decorated property for every table column you need in code.