Запросы к базе данных
Запросы в Joomla не пишут строкой SQL. Их собирают построителем — объектом с методами select(), from(), where(). Он подставляет приставку таблиц, экранирует имена и защищает от SQL-инъекций. В моделях прошлого урока построитель уже использовался; разберём его по частям.
Где взять объект базы
| Где находится код | Как получить |
|---|---|
| Модель компонента | $db = $this->getDatabase(); |
Помощник модуля с DatabaseAwareTrait | $db = $this->getDatabase(); |
| Любое другое место | $db = Factory::getContainer()->get(DatabaseInterface::class); |
Для третьего способа нужны строки use Joomla\CMS\Factory; и use Joomla\Database\DatabaseInterface;.
Простой запрос
// components/com_hello/src/Model/PostsModel.php, внутри метода модели
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title']))
->from($db->quoteName('#__hello_posts'));
$posts = $db->setQuery($query)->loadObjectList();getQuery(true) создаёт новый пустой запрос. quoteName() берёт имена столбцов и таблиц в кавычки. setQuery() передаёт запрос базе, а loadObjectList() выполняет его и возвращает массив объектов: $posts[0]->title.
Условия, сортировка, ограничение
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'created']))
->from($db->quoteName('#__hello_posts'))
->where($db->quoteName('published') . ' = 1')
->order($db->quoteName('created') . ' DESC')
->setLimit(10);Несколько вызовов where() соединяются через AND. setLimit(10) оставляет десять строк; второй аргумент — сколько строк пропустить.
Значения от пользователя
Всё, что пришло извне, не вклеивают в запрос, а привязывают параметром. Добавим в модель списка поиск по заголовку:
// components/com_hello/src/Model/PostsModel.php
use Joomla\CMS\Factory;
use Joomla\Database\ParameterType;
protected function getListQuery(): QueryInterface
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'created']))
->from($db->quoteName('#__hello_posts'))
->where($db->quoteName('published') . ' = 1')
->order($db->quoteName('created') . ' DESC');
$search = Factory::getApplication()->getInput()->getString('q', '');
if ($search !== '') {
$search = '%' . $search . '%';
$query->where($db->quoteName('title') . ' LIKE :search')
->bind(':search', $search);
}
return $query;
}В запросе стоит метка :search, а значение передаётся отдельно методом bind(). Откройте index.php?option=com_hello&view=posts&q=первая — в списке осталась одна запись. Для чисел указывают тип: ->bind(':id', $id, ParameterType::INTEGER).
Чтение результата
| Метод | Что возвращает |
|---|---|
loadObjectList() | Все строки — массив объектов |
loadAssocList() | Все строки — массив массивов |
loadObject() | Одну строку объектом или null |
loadResult() | Одно значение — первый столбец первой строки |
loadColumn() | Один столбец массивом |
Например, число опубликованных записей:
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__hello_posts'))
->where($db->quoteName('published') . ' = 1');
$total = (int) $db->setQuery($query)->loadResult();Вставка и изменение
$post = (object) [
'title' => 'Новая запись',
'body' => 'Текст записи',
];
$db->insertObject('#__hello_posts', $post, 'id');
$post->title = 'Исправленный заголовок';
$db->updateObject('#__hello_posts', $post, 'id');Свойства объекта — столбцы таблицы. Третий аргумент — столбец первичного ключа: после вставки в $post->id окажется номер новой строки, а updateObject() по нему находит строку для изменения.
Удаление
$id = 5;
$query = $db->getQuery(true)
->delete($db->quoteName('#__hello_posts'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($query)->execute();Запросы, которые ничего не возвращают, выполняют методом execute(). В bind() передают переменную, а не выражение: метод принимает значение по ссылке.
Частая ошибка
Значение вклеивают в текст запроса: ->where('title = "' . $title . '"'). Кавычка в $title ломает запрос, а подобранная строка позволяет прочитать или стереть чужие данные — это SQL-инъекция. Всегда используйте bind(). И не пишите настоящую приставку вроде abc12_: на другом сайте она другая — только #__.