Encodera

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

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

Оглавление

Построитель запросов Sql

Простого select(['id' => 5]) хватает не всегда: нужны сортировка, поиск по части слова, данные из двух таблиц. Для этого в laminas-db есть построитель запросов — объекты, из которых запрос собирают вызовами методов, а готовый SQL пишет сам пакет. Отдельных файлов создавать не нужно: построитель используют внутри класса таблицы.

Сортировка и ограничение

Шлюз таблицы отдаёт заготовку запроса для своей таблицы. Добавьте метод в module/Blog/src/Model/PostTable.php:

// module/Blog/src/Model/PostTable.php
public function fetchLatest(int $limit = 10): array
{
    $select = $this->gateway->getSql()->select();
    $select->order('id DESC')->limit($limit);
 
    return iterator_to_array($this->gateway->selectWith($select));
}

getSql()->select() создаёт объект Select для таблицы posts, а selectWith() выполняет его. Вызов $this->posts->fetchLatest(5) в контроллере вернёт пять последних статей в виде объектов Post.

Условия

Простые условия передают массивом, более сложные — методами объекта where:

public function search(string $text): array
{
    $select = $this->gateway->getSql()->select();
    $select->where->like('title', '%' . $text . '%');
    $select->order('title ASC');
 
    return iterator_to_array($this->gateway->selectWith($select));
}

Метод найдёт статьи, в заголовке которых встречается текст. Значение уходит в базу параметром, а не вклеивается в запрос.

Метод whereУсловие SQL
equalTo('status', 1)status = 1
notEqualTo('status', 0)status != 0
greaterThan('views', 100)views > 100
like('title', '%php%')title LIKE '%php%'
in('id', [1, 2, 3])id IN (1, 2, 3)
isNull('deleted_at')deleted_at IS NULL

Несколько вызовов подряд соединяются через AND.

Соединение таблиц

Для запроса по нескольким таблицам удобнее объект Sql, созданный из адаптера. Пусть есть таблица users, а в posts — столбец user_id:

use Laminas\Db\Sql\Sql;
 
$sql = new Sql($adapter);
 
$select = $sql->select('posts')
    ->columns(['id', 'title'])
    ->join('users', 'users.id = posts.user_id', ['author' => 'name'])
    ->where(['users.id' => 1]);
 
$rows = $sql->prepareStatementForSqlObject($select)->execute();
 
foreach ($rows as $row) {
    echo $row['title'] . ' — ' . $row['author'];
}

Третий аргумент join() — столбцы второй таблицы; запись ['author' => 'name'] означает «столбец name под именем author». Каждая строка результата — обычный массив.

Подсчёт

Выражения SQL вроде COUNT(*) оборачивают в Expression:

use Laminas\Db\Sql\Expression;
 
$select = $sql->select('posts')->columns(['total' => new Expression('COUNT(*)')]);
 
$row = $sql->prepareStatementForSqlObject($select)->execute()->current();
 
$total = (int) $row['total'];

В $total окажется число статей.

Транзакции

Транзакция объединяет несколько запросов: либо выполняются все, либо ни один.

$connection = $adapter->getDriver()->getConnection();
$connection->beginTransaction();
 
try {
    $this->posts->save($post);
    $this->tags->saveFor($post, $tags);
 
    $connection->commit();
} catch (\Throwable $e) {
    $connection->rollback();
 
    throw $e;
}

Если сохранение меток вызовет ошибку, статья тоже не останется в базе.

Частая ошибка

Строковое условие с вклеенным значением — $select->where("title = '$title'") — открывает путь SQL-инъекции: посетитель может дописать к запросу свой код. Передавайте условия массивом ['title' => $title] или методами where->equalTo(), where->like() — тогда значение уходит в базу отдельно от запроса.

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

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