Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Запросы в 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.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.