Запросы в Lucid
Методы all и find из прошлого урока покрывают простые случаи. Для выборок с условиями у каждой модели есть конструктор запросов (query builder): запрос собирается цепочкой методов и выполняется по await.
Условия и сортировка
// app/controllers/posts_controller.ts
import Post from '#models/post'
const posts = await Post.query()
.where('is_published', true)
.orderBy('created_at', 'desc')
.limit(10)Post.query() начинает запрос к таблице posts. Результат — массив моделей: десять последних опубликованных статей. Обратите внимание: в методах конструктора указывают имена столбцов базы (is_published), а не свойства модели.
Варианты where
| Запись | SQL |
|---|---|
.where('views', '>', 100) | views > 100 |
.whereIn('id', [1, 2, 3]) | id IN (1, 2, 3) |
.whereNull('slug') | slug IS NULL |
.whereLike('title', '%Adonis%') | title LIKE '%Adonis%' |
.orWhere('views', 0) | OR views = 0 |
Условия, записанные подряд, объединяются через AND.
Одна запись
// app/controllers/posts_controller.ts
const post = await Post.query().where('slug', 'hello-adonis').firstOrFail()first вернёт первую подходящую модель или null, а firstOrFail при отсутствии записи выбросит исключение, и посетитель увидит 404.
Поиск по блогу
Соберём метод index, который ищет статьи по строке из адреса /posts?q=...:
// app/controllers/posts_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import Post from '#models/post'
export default class PostsController {
async index({ request, view }: HttpContext) {
const q = request.input('q', '')
const posts = await Post.query()
.where('is_published', true)
.if(q, (query) => query.whereLike('title', `%${q}%`))
.orderBy('created_at', 'desc')
return view.render('pages/posts/index', { posts, q })
}
}Метод if добавляет условие, только когда первый аргумент истинный. Откройте /posts?q=adonis — останутся статьи со словом adonis в заголовке.
Постраничный вывод
// app/controllers/posts_controller.ts
const page = request.input('page', 1)
const posts = await Post.query().orderBy('created_at', 'desc').paginate(page, 10)
posts.baseUrl('/posts')paginate возвращает объект с текущей страницей статей и сведениями о страницах. В шаблоне по нему можно пройти циклом и вывести ссылки:
{{-- resources/views/pages/posts/index.edge --}}
@each(post in posts)
@!post.card({ post })
@end
@each(anchor in posts.getUrlsForRange(1, posts.lastPage))
<a href="{{ anchor.url }}">{{ anchor.page }}</a>
@endПод списком появятся ссылки /posts?page=1, /posts?page=2 и так далее.
Подсчёт и SQL-запросы без модели
// app/controllers/posts_controller.ts
import db from '@adonisjs/lucid/services/db'
const total = await db.from('posts').where('is_published', true).count('* as total')
console.log(total[0].total)Сервис db строит запросы к любой таблице и возвращает обычные объекты, а не модели. Так удобно считать и собирать отчёты.
Частая ошибка
Без await запрос не выполняется: const posts = Post.query().where(...) даст объект конструктора, а не массив статей, и @each в шаблоне ничего не выведет. Каждый запрос к базе завершайте await.