WSDB
WSDB предоставляет статический доступ к основному подключению базы данных CMS.Поддерживает
- статический доступ к основному подключению
- регистрацию дополнительных подключений
- выборку, добавление, обновление и удаление данных
- атомарное увеличение числовых значений
- выбор одной приоритетной строки из каждой группы
- подсчёт уникальных значений столбца
- условия, сортировку и ограничение выборки
- работу с JSON-полями
- транзакции с автоматическим откатом
- блокировку параллельного выполнения операций
- проверку существования таблиц, столбцов и индексов
- автоматическое использование префикса таблиц
Класс автоматически доступен после запуска CMS. Создавать его экземпляр не нужно.
Быстрый старт
Получение одной строки:
$page = WSDB::fetchOne(
'page',
['id' => 15],
['id', 'title', 'url']
);Получение списка:
$pages = WSDB::fetchAll(
table: 'page',
where: ['status' => 'publish'],
columns: ['id', 'title', 'url'],
order: ['id' => 'DESC'],
limit: 10
);Добавление данных:
$id = WSDB::insert(
'page',
[
'title' => 'Новая страница',
'status' => 'draft',
]
);Подключения
Основное подключение регистрируется CMS автоматически. Для обычной работы дополнительно настраивать его не нужно.
on(string $name = 'default'): Database
Возвращает объект Database для выполнения запросов:
$database = WSDB::on();
$page = $database->fetchOne('page', ['id' => 15]);Для основного подключения методы Database можно вызывать напрямую через WSDB:
$page = WSDB::fetchOne('page', ['id' => 15]);connection(string $name = 'default'): DatabaseConnection
Возвращает параметры зарегистрированного подключения:
$connection = WSDB::connection();
$pdo = $connection->pdo();
$prefix = $connection->prefix();Обычно получать DatabaseConnection вручную не требуется.
isRegistered(string $name = 'default'): bool
Проверяет, зарегистрировано ли подключение с указанным именем:
if (WSDB::isRegistered('analytics')) {
$database = WSDB::on('analytics');
}register(DatabaseConnection $connection, string $name = 'default'): void
Регистрирует дополнительное подключение:
$connection = DatabaseConnection::connect('localhost', 'analytics', 'user', 'password', 'ANALYTICS_');
WSDB::register($connection, 'analytics');После регистрации запросы выполняются через выбранное подключение:
$events = WSDB::on('analytics')->fetchAllFrom('events', ['id', 'name']);Имя подключения приводится к нижнему регистру. Оно должно начинаться с латинской буквы или цифры и может содержать цифры, _ и -.
Получение данных
| Метод | Возвращает | Описание |
|---|---|---|
fetchOne() |
array|null |
Возвращает первую найденную строку. |
fetchAll() |
array |
Возвращает строки по условиям. |
fetchGroupedRepresentatives() |
array |
Возвращает одну представительную строку из каждой группы. |
fetchAllFrom() |
array |
Возвращает все строки таблицы. |
count() |
int |
Возвращает количество строк. |
countDistinct() |
int |
Возвращает количество уникальных значений столбца. |
fetchOne(string $table, array $where, array $columns = ['*'], ?array $order = null, array $jsonFields = [], bool $forUpdate = false): ?array
$user = WSDB::fetchOne(
'user',
['id' => 7],
['id', 'login', 'email']
);Если строка не найдена, возвращается null.
Чтение с блокировкой
Параметр $forUpdate по умолчанию равен false. При передаче true выполняется блокирующее чтение, для которого обязательна активная транзакция.
Пример для собственной таблицы products с полями id и stock: уменьшение остатка на единицу, если товар существует и остаток положительный.
$changed = WSDB::transaction(function (Database $database): bool {
$product = $database->fetchOne('products', ['id' => 15], ['id', 'stock'], forUpdate: true);
if ($product === null || (int) $product['stock'] < 1) {
return false;
}
return $database->update('products', ['stock' => (int) $product['stock'] - 1], ['id' => $product['id']]) === 1;
});Чтение и зависящие от него изменения выполняйте внутри одного callback, используя переданное подключение $database.
Блокировки освобождаются при фиксации или откате транзакции. После завершения WSDB::transaction() полученная строка больше не остаётся заблокированной этой транзакцией.
fetchAll(string $table, array $where, array $columns = ['*'], ?array $order = null, ?int $limit = null, ?int $offset = null, array $jsonFields = []): array
$posts = WSDB::fetchAll(
table: 'post_news',
where: ['status' => 'publish'],
columns: ['id', 'title'],
order: ['publish_date' => 'DESC'],
limit: 20
);Параметр $where обязателен, но допускает пустой массив. При передаче [] отбор по условиям не выполняется:
$posts = WSDB::fetchAll(table: 'post_news', where: [], columns: ['id', 'title'], order: ['id' => 'DESC'], limit: 10);Сортировка и ограничение продолжают действовать. В примере возвращается не более десяти строк с наибольшими ID.
Параметр $offset можно использовать только вместе с $limit.
fetchGroupedRepresentatives(string $table, string $groupColumn, string $preferredColumn, mixed $preferredValue, array $where = [], array $columns = ['*'], ?array $order = null, ?int $limit = null, ?int $offset = null, array $jsonFields = []): array
Возвращает одну строку из каждой группы, отдавая предпочтение заданному значению:
$posts = WSDB::fetchGroupedRepresentatives(
table: 'post_news',
groupColumn: 'translation_key',
preferredColumn: 'language',
preferredValue: 'ru',
where: ['status' => 'publish'],
columns: ['id', 'title', 'language', 'translation_key'],
order: ['id' => 'DESC'],
limit: 20
);Сначала применяются условия where, затем строки объединяются по groupColumn. Если приоритетному значению соответствуют несколько строк, выбирается строка с наименьшим id среди них. Если совпадений нет, выбирается строка с наименьшим id во всей группе.
Таблица должна содержать столбец id. Сортировка, ограничение и смещение применяются к уже выбранным строкам. offset можно использовать только вместе с limit.
fetchAllFrom(string $table, array $columns = ['*'], ?array $order = null, array $jsonFields = []): array
Используйте этот метод для выборки без условий:
$languages = WSDB::fetchAllFrom('site_language', ['id', 'code', 'label'], ['sort_order' => 'ASC', 'id' => 'ASC']);count(string $table, array $where = []): int
$total = WSDB::count(
'page',
['status' => 'publish']
);Без условий подсчитываются все строки таблицы:
$total = WSDB::count('page');countDistinct(string $table, string $column, array $where = []): int
Возвращает количество уникальных значений столбца:
$total = WSDB::countDistinct(
'post_news',
'translation_key',
['status' => 'publish']
);Без условий проверяются все строки таблицы. Значения null при подсчёте не учитываются.
Условия
Простое значение проверяет равенство:
$where = ['status' => 'publish'];Поддерживаемые операторы:
| Оператор | Описание |
|---|---|
= |
Равно. |
in |
Значение входит в список. |
like |
Сравнение через LIKE. |
> |
Больше. |
< |
Меньше. |
>= |
Больше или равно. |
<= |
Меньше или равно. |
!= |
Не равно. |
is-null |
Значение равно NULL. |
is-not-null |
Значение не равно NULL. |
json-search |
Поиск значения внутри JSON-массива. |
any-row |
Хотя бы одна строка JSON-массива соответствует условиям. Только для чтения и подсчёта. |
$pages = WSDB::fetchAll(
'page',
[
'status' => [
'in' => ['publish', 'draft'],
],
]
);Для объединения групп доступны $or и $and:
$users = WSDB::fetchAll(
'user',
[
'$or' => [
['role' => 'admin'],
['role' => 'editor'],
],
]
);Числовое сравнение
Чтобы сравнивать значение поля как число, добавьте numeric: true в правило:
$where = [
'meta.price' => ['>=' => 100, 'numeric' => true],
];Пример отбирает строки, у которых значение price в JSON-поле meta не меньше 100.
Поддерживаются операторы =, !=, >, <, >=, <= и in. Правило содержит один оператор и необязательный флаг numeric. Для диапазона объединяйте отдельные условия через $and.
Для сравнения передавайте конечное число или числовую строку. Оператор in принимает непустой массив таких значений. Флаг numeric должен быть логическим значением.
Пустые, отсутствующие и нечисловые значения поля не соответствуют числовому условию.
Проверка строк JSON-массива
Оператор any-row проверяет, что хотя бы одна строка JSON-массива соответствует заданным условиям. Каждая строка представлена массивом ячеек, нумерация которых начинается с 0.
Например, JSON-поле meta содержит варианты товара:
{
"offers": [
["red", "XL", 150],
["blue", "M", 90]
]
}Для поиска красного варианта размера XL с ценой не меньше 100 используйте:
$where = [
'meta.offers' => [
'any-row' => [
0 => 'red',
1 => 'XL',
2 => ['>=' => 100, 'numeric' => true],
],
],
];Здесь ячейка 0 содержит цвет, 1 содержит размер, а 2 содержит цену. Все условия должны выполняться в одной строке. Совпадения из разных строк не объединяются.
Внутри any-row поддерживаются группы $and, $or и числовые условия для отдельных ячеек. Вложенный any-row и оператор json-search не поддерживаются.
Правило any-row должно содержать непустой массив условий. На одном уровне с ним нельзя указывать другие операторы или numeric: true.
Оператор доступен только в запросах чтения и подсчёта. В условиях изменения и удаления данных он не поддерживается.
Отсутствующий или пустой массив не даёт совпадений. Элементы, которые не являются массивами, пропускаются.
Сортировка
Сортировка передаётся массивом поле => направление:
$order = [
'publish_date' => 'DESC',
'id' => 'ASC',
];Поддерживаются только ASC и DESC.
Числовая сортировка
Для сортировки числовых значений, в том числе внутри JSON, передайте настройки поля:
$order = [
'meta.price' => ['direction' => 'ASC', 'numeric' => true],
'id' => 'ASC',
];В этом примере цена сортируется по возрастанию как число: 2 будет перед 10. При одинаковой цене строки упорядочиваются по id.
В настройках поля direction по умолчанию равен ASC. Пустые, отсутствующие и нечисловые значения располагаются в конце как при ASC, так и при DESC.
Числовая сортировка не включает числовое сравнение в условиях $where.
Выбор столбцов
По умолчанию возвращаются все столбцы. Для ограничения результата передайте список:
$page = WSDB::fetchOne(
'page',
['id' => 15],
['id', 'title', 'url']
);Для псевдонима используйте исходное поле => новое имя:
$page = WSDB::fetchOne(
'page',
['id' => 15],
[
'title' => 'name',
]
);Изменение данных
| Метод | Возвращает | Описание |
|---|---|---|
insert() |
int |
Добавляет строку и возвращает её идентификатор. |
update() |
int |
Обновляет строки и возвращает их количество. |
increment() |
int |
Увеличивает числовое значение и возвращает количество изменённых строк. |
upsert() |
int |
Добавляет или обновляет строку. |
delete() |
int |
Удаляет строки и возвращает их количество. |
insert(string $table, array $data): int
$id = WSDB::insert(
'user',
[
'login' => 'new-user',
'email' => 'user@example.com',
'role' => 'user',
]
);update(string $table, array $data, array $where): int
$updated = WSDB::update(
'page',
['status' => 'publish'],
['id' => 15]
);increment(string $table, string $column, array $where, int $amount = 1): int
Увеличивает значение числового столбца и возвращает количество изменённых строк:
$updated = WSDB::increment(
'post_news',
'views',
['id' => 15]
);По умолчанию значение увеличивается на 1. Другую величину можно передать через amount:
$updated = WSDB::increment(
'post_news',
'views',
['id' => 15],
5
);Изменение выполняется одним запросом без предварительного получения текущего значения. Если столбец содержит null, исходным значением считается 0.
Условия where обязательны. Значение amount должно быть больше 0; уменьшение значений метод не поддерживает.
upsert(string $table, array $data, array $update = []): int
$id = WSDB::upsert(
'options',
[
'type' => 'global',
'name' => 'sitename',
'value' => 'Мой сайт',
],
[
'value' => 'Мой сайт',
]
);Метод обновляет строку при конфликте уникального ключа.
delete(string $table, array $where): int
$deleted = WSDB::delete(
'page',
['id' => 15]
);Удаление без условий запрещено.
JSON-поля
Для преобразования JSON-полей в массивы передайте их через jsonFields:
$page = WSDB::fetchOne(
table: 'page',
where: ['id' => 15],
columns: ['id', 'meta'],
jsonFields: ['meta']
);Пустое значение преобразуется в пустой массив.
updateJson(string $table, string $jsonColumn, array $set, array $where): int
Обновляет отдельные значения JSON-поля:
$updated = WSDB::updateJson(
'user',
'meta',
[
'profile.name' => 'Иван',
'profile.phone' => '+375000000000',
],
['id' => 7]
);upsertJson(string $table, array $keyData, string $jsonColumn, array $jsonSet): int
Создаёт строку или обновляет отдельные значения JSON-поля:
$id = WSDB::upsertJson(
'options',
[
'type' => 'themes',
'name' => 'example',
],
'value',
[
'color' => '#1251b7',
]
);Массивы, переданные в обычные методы записи, автоматически сохраняются как JSON.
Транзакции
transaction(callable $callback): mixed
Выполняет несколько операций как единое целое и возвращает результат callback.
В примере используются собственные таблицы orders и order_items с полями, указанными в коде. Таблица orders должна автоматически создавать идентификатор id.
$orderId = WSDB::transaction(function (Database $database): int {
$orderId = $database->insert('orders', ['status' => 'new']);
$database->insert('order_items', ['order_id' => $orderId, 'product_id' => 15, 'quantity' => 2]);
return $orderId;
});Если callback завершился успешно, изменения фиксируются. Если возникло исключение, изменения отменяются, а исключение передаётся вызывающему коду.
Все связанные операции выполняйте через подключение $database, переданное в callback.
Вложенные транзакции на одном подключении не поддерживаются.
Блокировка операций
withAdvisoryLock(string $name, int $timeout, callable $callback): mixed
Не позволяет нескольким процессам одновременно выполнять одну операцию:
$result = WSDB::withAdvisoryLock('product-import', 10, function (Database $database) {
return $database->fetchAllFrom('products');
});$timeout задаёт время ожидания блокировки в секундах. Блокировка освобождается после завершения callback, в том числе при возникновении исключения.
Имя блокировки должно содержать от 1 до 64 латинских букв, цифр или символов _, ., :, -.
Информация о базе данных
| Метод | Возвращает | Описание |
|---|---|---|
getPrefix() |
string |
Возвращает префикс таблиц подключения. |
getServerVersion() |
string |
Возвращает версию сервера базы данных. |
listTables() |
array |
Возвращает список таблиц. |
describeTable(string $table) |
array |
Возвращает описание столбцов таблицы. |
showIndexes(string $table) |
array |
Возвращает индексы таблицы. |
tableExists(string $table) |
bool |
Проверяет существование таблицы. |
columnExists(string $table, string $column) |
bool |
Проверяет существование столбца. |
indexExists(string $table, string $index) |
bool |
Проверяет существование индекса. |
Имена таблиц передаются без системного префикса:
if (WSDB::tableExists('page')) {
$columns = WSDB::describeTable('page');
}Префикс таблиц
Передавайте имя таблицы без системного префикса:
$page = WSDB::fetchOne('page', ['id' => 15]);Префикс текущего подключения добавляется автоматически.
Исключения
| Исключение | Когда возникает |
|---|---|
InvalidArgumentException |
Передано недопустимое имя, пустые данные или неверный аргумент запроса. |
LogicException |
Подключение отсутствует или уже зарегистрировано; выполняется вложенная транзакция либо повторно запрошена блокировка с тем же именем на том же подключении. |
LogicException |
forUpdate: true передан без активной транзакции на том же подключении. |
RuntimeException |
Не удалось подключиться, завершить транзакцию или получить блокировку. |
BadMethodCallException |
Через WSDB вызван неизвестный метод Database. |
PDOException |
Сервер базы данных отклонил запрос. |
JsonException |
Переданное значение невозможно преобразовать в JSON. |
Важные замечания
- не создавайте экземпляр
WSDB - основное подключение регистрируется CMS автоматически
- статические методы используют основное подключение
- для дополнительного подключения используйте
WSDB::on($name) fetchOne(),update(),increment()иdelete()требуют непустые условия- для выборки без условий передайте
[]вfetchAll()или используйтеfetchAllFrom() count()без условий подсчитывает все строки таблицы- значения запросов передаются через подготовленные выражения
- имена таблиц и столбцов должны задаваться разработчиком