Обзор работы с базами данных
Внимание
Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!
Руководство по подключению и работе с различными базами данных в IO7 фреймворке.
Архитектура
IO7 фреймворк предоставляет три уровня абстракции для работы с базами данных:
| Компонент | Назначение |
|---|---|
SqlConnection | Raw PDO + Query Builder для MySQL/PostgreSQL |
ConnectionManager | Фабрика конфигураций подключений |
DB | Фасад — прямой мост к Laravel Capsule |
┌──────────────────────────────────────────────────┐
│ Приложение │
├───────────────┬─────────────────────┬────────────┤
│ SqlConnection │ ConnectionManager │ DB │
├───────────────┴─────────────────────┴────────────┤
│ Illuminate\Database\Capsule │
├──────────────┬───────────────┬───────────────────┤
│ MySQL │ PostgreSQL │ Другие драйверы │
└──────────────┴───────────────┴───────────────────┘
SqlConnection
Единый класс для работы с SQL базами данных. Объединяет raw PDO (для прямых SQL) и Laravel Query Builder.
Пространство имён: IO\Database
Конструктор
public function __construct(string $driver = 'mysql')
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$driver | string | 'mysql' | Нет | Тип БД: 'mysql' или 'pgsql' |
Свойства
| Свойство | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$db_link | ?\PDO | null | Нет | PDO подключение |
$last_query | string | '' | Нет | Последний выполненный запрос (экземпляр) |
$server | string | '' | Да | Адрес сервера БД |
$user | string | '' | Да | Пользователь БД |
$pass | string | '' | Да | Пароль БД |
$db | string | '' | Да | Имя базы данных |
$driver | string | 'mysql' | Нет | Движок БД (mysql или pgsql) |
$lastconnect | int | 1 | Нет | Разрешение на подключение (1 — да, 0 — нет) |
$log_query | int | 0 | Нет | Логирование запросов (0/1) |
$code_page | string | 'utf8' | Нет | Кодировка |
$time_zone | string | '+05:00' | Нет | Часовой пояс |
$_errorstr | string | '' | Нет | Текст последней ошибки |
$_errorno | string | '' | Нет | Код последней ошибки |
$_time_query_max | int | 2000 | Нет | Максимальное время запроса (мс) |
$columns | array | [] | Нет | Структура таблиц information_schema |
Статические свойства
| Свойство | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$last_query_all | string | '' | Нет | Последний выполненный запрос (глобальный) |
$db_connections | array<string, self> | [] | Нет | Кэш подключений |
Методы подключения
tryconnect()
public function tryconnect(): int
Пытается подключиться к БД. Использует свойства $server, $user, $pass, $db.
Возвращает: 1 — успех, 0 — ошибка
$db = new SqlConnection('mysql');
$db->server = 'localhost';
$db->user = 'root';
$db->pass = 'password';
$db->db = 'mydb';
if ($db->tryconnect()) {
echo "Подключено!";
}
connect()
public function connect(string $server, string $user, string $pass, ?string $db = null): int
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$server | string | — | Да | Адрес сервера |
$user | string | — | Да | Пользователь |
$pass | string | — | Да | Пароль |
$db | ?string | null | Нет | Имя базы данных (null для подключения без выбора БД) |
Возвращает: 1 — успех, 0 — ошибка
$db = new SqlConnection('pgsql');
$db->connect('192.168.1.10', 'admin', 'secret', 'production');
create_conn()
public static function create_conn(
string $server,
string $user,
string $pass,
string $db = '',
string $driver = 'mysql'
): self
Создаёт или получает кэшированное подключение.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$server | string | — | Да | Адрес сервера |
$user | string | — | Да | Пользователь |
$pass | string | — | Да | Пароль |
$db | string | '' | Нет | Имя базы данных |
$driver | string | 'mysql' | Нет | Тип БД |
$db = SqlConnection::create_conn('localhost', 'root', 'password', 'mydb');
// Повторный вызов вернёт тот же экземпляр
$db2 = SqlConnection::create_conn('localhost', 'root', 'password', 'mydb');
// $db === $db2
ping()
public function ping(): bool
Проверяет соединение через SELECT 1.
if (!$db->ping()) {
$db->tryconnect();
}
is_valid()
public function is_valid(): bool
Проверяет, установлен ли PDO объект.
connected()
public function connected(): bool
Проверяет активность соединения.
usedb()
public function usedb(string $dbname): void
Переключает на другую базу данных (только MySQL).
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$dbname | string | — | Да | Имя базы данных |
Статические методы (Query Builder)
table()
public static function table(string $table): \Illuminate\Database\Query\Builder
Получить Query Builder для таблицы.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$users = SqlConnection::table('users')
->where('active', 1)
->orderBy('name')
->get();
connection()
public static function connection(string $name = null): \Illuminate\Database\Connection
Получить подключение к БД по имени.
$conn = SqlConnection::connection('mysql');
$schema = $conn->getSchemaBuilder();
__callStatic()
public static function __callStatic(string $method, array $args)
Делегирует вызовы в Capsule. Все методы Laravel Capsule доступны через статический вызов.
SqlConnection::purge();
SqlConnection::beginTransaction();
Raw PDO запросы
query()
public function query(string $query = '', ?array $arr = null, bool $save_log = true)
Выполнить SQL запрос через PDO с подготовленными выражениями.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$query | string | '' | Нет | SQL запрос с плейсхолдерами |
$arr | ?\array | null | Нет | Параметры для подстановки |
$save_log | bool | true | Нет | Сохранять ли в лог |
$save_log | bool | Сохранять ли в лог. По умолчанию true |
Возвращает: \PDOStatement|false
$r = $db->query("SELECT * FROM users WHERE id = ?", [1]);
while ($row = $db->fetch($r)) {
echo $row['name'];
}
$db->free_result($r);
query_params()
public function query_params(string $query = '', ?array $arr = null, bool $save_log = true)
Алиас для query().
fetch()
public function fetch(?\PDOStatement $result): array|false
Получить одну строку из результата (ассоциативный массив).
fetch_assoc()
public function fetch_assoc(?\PDOStatement $result): array|false
Алиас для fetch().
fetchAll()
public function fetchAll(?\PDOStatement $result): array|false
Получить все строки из результата.
free_result()
public function free_result(?\PDOStatement $result): bool
Закрыть курсор результата.
num_rows()
public function num_rows(?\PDOStatement $res): int
Получить количество строк в результате.
last_insert_id()
public function last_insert_id(): string|int
Получить ID последней вставленной записи.
get_var()
public function get_var(string $field, string $q, array $arr = [])
Получить значение одного поля из запроса.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$field | string | — | Да | Имя поля |
$q | string | — | Да | SQL запрос |
$arr | array | [] | Нет | Параметры |
$name = $db->get_var('name', "SELECT name FROM users WHERE id = 1");
get_count()
public function get_count(string $q, string $field = 'count')
Получить значение COUNT из запроса.
Экранирование
escape()
public static function escape(string $msg): string
Экранирование специальных символов для SQL.
$safe = SqlConnection::escape("O'Brien");
// O\'Brien
escape2()
public static function escape2(string $field): string
Экранирование имени таблицы/поля для SQL.
$escaped = SqlConnection::escape2('users.name');
// `users`.`name`
// Префикс # отключает экранирование
$raw = SqlConnection::escape2('#COUNT(*)');
// COUNT(*)
// Префикс * возвращает как есть
$star = SqlConnection::escape2('*');
// *
Высокоуровневые методы
insert()
public function insert(string $table, array $data, ?array $data2 = null): int
Вставить запись в таблицу.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$data | array | — | Да | Ассоциативный массив данных |
$data2 | ?\array | null | Нет | Дополнительные данные (не используется) |
Возвращает: 1 — успех, -1 — ошибка
$db->insert('users', [
'name' => 'John',
'email' => 'john@example.com',
'status' => 1,
]);
update()
public function update(string $table, array $filter, array $data, ?array $data2 = null): int
Обновить записи в таблице.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$filter | array | — | Да | Фильтр (условия WHERE) |
$data | array | — | Да | Данные для обновления |
$db->update('users', ['pkid' => 1], ['name' => 'Jane']);
$db->update('users', ['status' => 0, '>created_at' => '2024-01-01'], ['active' => 0]);
delete()
public function delete(string $table, array $filter, ?int $count = null): int
Удалить записи из таблицы.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$filter | array | — | Да | Фильтр (условия WHERE) |
$count | ?\int | null | Нет | Лимит удаления |
$db->delete('users', ['pkid' => 1]);
$db->delete('logs', ['<created_at' => '2024-01-01'], 100);
insert_or_update()
public function insert_or_update(string $table, array $data, ?array $data2 = null): int
Вставить или обновить запись (MySQL: ON DUPLICATE KEY UPDATE, PostgreSQL: ON CONFLICT DO UPDATE).
insert_or_ignore()
public function insert_or_ignore(string $table, array $data, ?array $data2 = null): int
Вставить или игнорировать (MySQL: INSERT IGNORE, PostgreSQL: ON CONFLICT DO NOTHING).
search()
public function search(string $table, array $params): array
Поиск записей в таблице.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$params | array | — | Да | Параметры поиска |
Параметры $params:
| Ключ | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
filter | array | [] | Нет | Фильтр |
fields | array | [] | Нет | Поля |
join | array|string | [] | Нет | JOIN |
groupby | array | [] | Нет | GROUP BY |
order | array | [] | Нет | ORDER BY [field => 'asc'|'desc'] |
distinct | bool | false | Нет | DISTINCT |
limit | int | 1000 | Нет | Лимит |
start | int | 0 | Нет | Смещение |
Возвращает:
[
'error_str' => '',
'error_code' => EIO_OK,
'res' => [...], // массив записей
'count' => 150, // общее количество
'time' => 5, // время выполнения (мс)
]
$result = $db->search('users', [
'filter' => ['status' => 1, '%name' => 'John'],
'order' => ['name' => 'asc', 'id' => 'desc'],
'limit' => 25,
'start' => 0,
]);
foreach ($result['res'] as $row) {
echo $row['name'];
}
echo "Всего: {$result['count']}";
sync_items()
public function sync_items(string $table, array $params): void
Синхронизация элементов (create/update/delete).
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$table | string | — | Да | Имя таблицы |
$params | array | — | Да | Параметры синхронизации |
Параметры $params:
| Ключ | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
new_items | array | — | Да | Новые элементы |
filter | array | — | Да | Фильтр для поиска старых |
find | callable | — | Да | Функция поиска соответствия function(array $old, array $new): bool |
create | callable|null | null | Нет | Функция создания function(array $new): void |
update | callable|null | null | Нет | Функция обновления function(array $old, array $new): void |
delete | callable|null | null | Нет | Функция удаления function(array $old): void |
$db->sync_items('categories', [
'new_items' => $importedCategories,
'filter' => ['parent_id' => 0],
'find' => function(array $old, array $new): bool {
return $old['code'] === $new['code'];
},
'create' => function(array $new) use ($db): void {
$db->insert('categories', $new);
},
'update' => function(array $old, array $new) use ($db): void {
$db->update('categories', ['pkid' => $old['pkid']], $new);
},
'delete' => function(array $old) use ($db): void {
$db->delete('categories', ['pkid' => $old['pkid']]);
},
]);
Транзакции
public function beginTransaction(): bool
public function commit(): bool
public function rollBack(): bool
$db->beginTransaction();
try {
$db->insert('orders', ['user_id' => 1, 'total' => 100]);
$db->update('users', ['pkid' => 1], ['balance' => '-100']);
$db->commit();
} catch (\Exception $e) {
$db->rollBack();
throw $e;
}
Получение информации
affected_rows()
public function affected_rows(): ?int
Количество затронутых строк (MySQL ROW_COUNT()).
found_rows()
public function found_rows(): ?int
Количество найденных строк (MySQL FOUND_ROWS()).
error()
public function error(): string
Код последней ошибки.
errorstr()
public function errorstr(): string
Текст последней ошибки.
information_schema_load_current_db()
public function information_schema_load_current_db(): void
Загружает структуру таблиц из information_schema (MySQL). Результат доступен в $db->columns.
ConnectionManager
Фабрика конфигураций подключений. Все методы статические.
Пространство имён: IO\Database
getEngine()
public static function getEngine(): string
Определяет движок БД из константы DB_ENGINE или переменной окружения. По умолчанию 'mysql'.
$engine = ConnectionManager::getEngine(); // 'mysql' или 'pgsql'
mysqlConfig()
public static function mysqlConfig(
$host,
$database,
$username,
$password,
$port = 3306
): array
Создаёт конфигурацию для MySQL подключения.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$host | string | — | Да | Адрес сервера |
$database | string | — | Да | Имя базы данных |
$username | string | — | Да | Пользователь |
$password | string | — | Да | Пароль |
$port | int | 3306 | Нет | Порт |
$config = ConnectionManager::mysqlConfig('localhost', 'mydb', 'root', 'secret');
// ['driver' => 'mysql', 'host' => 'localhost', ...]
pgsqlConfig()
public static function pgsqlConfig(
$host,
$database,
$username,
$password,
$port = 5432
): array
Создаёт конфигурацию для PostgreSQL подключения.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$host | string | — | Да | Адрес сервера |
$database | string | — | Да | Имя базы данных |
$username | string | — | Да | Пользователь |
$password | string | — | Да | Пароль |
$port | int | 5432 | Нет | Порт |
readWriteConfig()
public static function readWriteConfig(
$readHosts,
$writeHosts,
$database,
$username,
$password
): array
Конфигурация для read/write разделения.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$readHosts | string|array | — | Да | Адрес(а) реплик для чтения |
$writeHosts | string|array | — | Да | Адрес(а) мастеров для записи |
$database | string | — | Да | Имя базы данных |
$username | string | — | Да | Пользователь |
$password | string | — | Да | Пароль |
$config = ConnectionManager::readWriteConfig(
['replica1.local', 'replica2.local'],
'master.local',
'mydb',
'root',
'secret'
);
fromConstants()
public static function fromConstants(): array
Создаёт подключения из глобальных констант и переменных окружения.
Приоритет:
- Если ProxySQL прокинут — используется его с движком из
DB_ENGINE - Иначе — движок-специфичные переменные (
MYSQL_*илиPGSQL_*)
$connections = ConnectionManager::fromConstants();
// ['mysql' => [...]] или ['pgsql' => [...]]
DB фасад
Прямой мост к Laravel Capsule. Все вызовы проксируются в Capsule, новые методы Laravel доступны автоматически.
Пространство имён: IO\Database
init()
public static function init($config, $name = 'default'): \Illuminate\Database\Capsule\Manager
Инициализация подключения.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$config | array | — | Да | Конфигурация подключения |
$name | string | 'default' | Нет | Имя подключения |
DB::init([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'mydb',
'username' => 'root',
'password' => 'secret',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
]);
initPdo()
public static function initPdo(\PDO $pdo, string $driver = 'mysql'): \Illuminate\Database\Capsule\Manager
Инициализация через существующий PDO (общее подключение).
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$pdo | \PDO | — | Да | Существующее PDO подключение |
$driver | string | 'mysql' | Нет | Тип драйвера |
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'root', 'secret');
DB::initPdo($pdo);
getCapsule()
public static function getCapsule(): \Illuminate\Database\Capsule\Manager
Получить экземпляр Capsule.
connection()
public static function connection($name = null): \Illuminate\Database\Connection
Получить соединение по имени.
$conn = DB::connection('mysql');
__callStatic()
Все неопределённые статические вызовы проксируются в Capsule:
DB::table('users')->where('active', 1)->get();
DB::beginTransaction();
DB::purge();
Примеры
Базовое подключение к MySQL
use IO\Database\SqlConnection;
$db = new SqlConnection('mysql');
$db->server = 'localhost';
$db->user = 'root';
$db->pass = 'password';
$db->db = 'mydb';
if ($db->tryconnect()) {
$result = $db->search('users', [
'filter' => ['status' => 1],
'limit' => 10,
]);
foreach ($result['res'] as $user) {
echo $user['name'] . "\n";
}
}
Подключение через DB фасад
use IO\Database\DB;
use IO\Database\ConnectionManager;
// Из констант окружения
$connections = ConnectionManager::fromConstants();
foreach ($connections as $name => $config) {
DB::init($config, $name);
}
// Использование
$users = DB::table('users')->where('active', 1)->get();
DB::table('users')->insert(['name' => 'New User']);
Raw запросы
$db = new SqlConnection('mysql');
$db->connect('localhost', 'root', 'pass', 'mydb');
// SELECT
$r = $db->query("SELECT * FROM users WHERE status = ?", [1]);
while ($row = $db->fetch($r)) {
echo $row['name'];
}
$db->free_result($r);
// INSERT
$db->query("INSERT INTO logs (msg, created_at) VALUES (?, NOW())", ['test']);
// UPDATE
$db->query("UPDATE users SET active = 0 WHERE last_login < DATE_SUB(NOW(), INTERVAL 1 YEAR)");
Транзакции
$db->beginTransaction();
try {
$db->insert('transfers', [
'from_user' => 1,
'to_user' => 2,
'amount' => 500,
]);
$db->update('users', ['pkid' => 1], ['balance' => '-500']);
$db->update('users', ['pkid' => 2], ['balance' => '+500']);
$db->commit();
} catch (\Exception $e) {
$db->rollBack();
throw $e;
}
Query Builder
// SELECT с фильтрами
$users = SqlConnection::table('users')
->where('active', 1)
->where('role', 'admin')
->orderBy('name', 'asc')
->limit(25)
->get();
// JOIN
$orders = SqlConnection::table('orders')
->join('users', 'users.id', '=', 'orders.user_id')
->select('orders.*', 'users.name')
->where('orders.status', 'pending')
->get();
// COUNT
$count = SqlConnection::table('users')->where('active', 1)->count();
// UPDATE
SqlConnection::table('users')
->where('pkid', 1)
->update(['last_login' => now()]);
IO Фильтры в Query Builder
$filter = [
'status' => 1, // WHERE status = 1
'%name' => 'John', // WHERE name LIKE '%John%'
'>age' => 18, // WHERE age > 18
'<=price' => 100, // WHERE price <= 100
'~deleted_at' => null, // WHERE deleted_at IS NULL
'!~deleted_at' => null, // WHERE deleted_at IS NOT NULL
'role' => ['admin', 'moderator'], // WHERE role IN ('admin', 'moderator')
'$or' => [
['type' => 'premium'],
['status' => 'active'],
],
];
$users = SqlConnection::table('users');
$users = SqlConnection::applyFilterToQuery($users, $filter);
$results = $users->get();
Рекомендации
- Предпочитайте Query Builder raw-запросам для простых операций
- Используйте транзакции для связанных операций записи
- Экранируйте пользовательский ввод через
escape()при raw-запросах - Используйте кэш подключений через
create_conn()для повторных соединений - Проверяйте соединение через
ping()перед критическими операциями - Используйте фильтры IO для единообразной фильтрации данных
- Избегайте
SELECT *— указывайте конкретные поля вsearch()
FAQ
Как переключиться с MySQL на PostgreSQL?
Измените значение DB_ENGINE в .env на pgsql и убедитесь, что переменные PGSQL_* заданы. ConnectionManager::fromConstants() автоматически определит нужный драйвер.
Что лучше: raw SQL или Query Builder?
Query Builder безопаснее (защита от SQL-инъекций) и проще для поддержки. Raw SQL используйте только для сложных запросов, которые нельзя выразить через Query Builder.
Как работать с несколькими подключениями?
$db1 = SqlConnection::create_conn('host1', 'user1', 'pass1', 'db1');
$db2 = SqlConnection::create_conn('host2', 'user2', 'pass2', 'db2');
Как проверить, что соединение активно?
if ($db->ping()) {
// Соединение активно
} else {
$db->tryconnect();
}
Что возвращает search()?
Ассоциативный массив с ключами: error_str, error_code, res (данные), count (общее кол-во), time (время в мс).
Системные SqlClass модели (IO\Classes)
Фреймворковые классы таблиц в modules/io7/php/Classes/:
| Класс | Таблица | Описание |
|---|---|---|
MigrationLog | migrations | Трекинг миграций (batch, applied_at) |
CronJob | cron | Определения cron задач |
CronWork | cron_work | Логи выполнения cron |
CronData | cron_data | Данные cron задач (key-value) |
CacheRow | cache | Кеш в БД (при CACHE_ENGINE=database) |
MigrationLog
use IO\Classes\MigrationLog;
MigrationLog::getApplied(); // list<string> имен миграций
MigrationLog::getMaxBatch(); // int максимальный batch
MigrationLog::log('name', 1); // записать миграцию
MigrationLog::removeLog('name', 1); // удалить запись
CronJob
use IO\Classes\CronJob;
CronJob::getEnabled(); // list<array> активные задачи
CronJob::findByClassname('DeleteCron'); // ?array
CronJob::getRunningCount(1); // int выполняющихся потоков
CronWork
use IO\Classes\CronWork;
CronWork::markRunning(1); // int ID записи
CronWork::markDone($id, $log, $timestamps, $avg, $count);
CronWork::markFailed($id, $error);
CronWork::getStats(); // array{running, completed, failed}
CronWork::cleanupOlderThan('2026-01-01');
CronData
use IO\Classes\CronData;
CronData::load('my_key'); // ?array
CronData::save('my_key', $data);
CacheRow
use IO\Classes\CacheRow;
CacheRow::get('key'); // mixed
CacheRow::set('key', $value, 3600);
CacheRow::forget('key');
CacheRow::flush();
CacheRow::gc(); // удалить истёкшие
Проектные SqlClass модели (App\Classes)
Классы таблиц проекта в app/Classes/:
// app/Classes/User.php
namespace App\Classes;
use IO\SqlClass;
class User extends SqlClass
{
protected string $table = 'users';
protected string $primaryKey = 'pkid';
public bool $timestamps = false;
}