Wiki

Clone wiki

SimpleDatabaseAdapter / Документация по SDBA на русском языке

Simple Database Adapter (SDBA)

Для русскоговорящих

По сути это ещё один велосипед с реализацией упрощённого обращения к базе данных под управлением СУБД MySQL, берущий часть повторяющейся рутины на себя. Под капотом активно использует MySQLi и только его (не «мост» всё-таки).
Представляет собой набор статических методов. Да, создавать объект не нужно! Откуда берутся параметры? В корне сайта ожидается файл env.cfg (про него чуть позже), но можете изменить это поведение, модифицировав метод Start().

Сам файл env.cfg представляет собой простой исполняемый PHP-файл, возвращающий массив. Важно запретить веб-серверу прямые обращения к этому файлу извне! SDBA считывает ключ db у этого массива. Его значение опять же должен быть массив, состоящий из следующих полей: host, login, password, database и timezone. Думаю, названия параметров говорят сами за себя. Рядом с этим файлом помощи также должен находиться env.cfg.example, который Вы можете взять за основу.

Прежде чем перейти к рассмотрению конкретных функций, сразу хотелось бы оговорить, что в случае возникающих ошибок все они бросают стандартное исключение mysqli_sql_exception.

Теперь перейдём к рассмотрению основных функций класса.

Выборка данных

Здесь рассмотрим один, но весьма обширный метод: Select($columns, $table, array $filter=null, $sorting=null, $limit=null, $unique=false).
Принимает он целых 6 параметров, 2 из которых обязательны! Давайте рассмотрим их все:

  1. $columns — определяет, какие поля мы будем получать из базы данных. Может быть как строкой из названия одного поля, так и массивом строк.
  2. $table — определяет название таблицы, откуда будем брать данные. Просто строка.
  3. $filter — здесь нужно описывать условия фильтрации результатов выборки. Может быть либо массивом, либо null (по умолчанию). Структура массива не так проста, так что рассмотрим её чуть позже.
  4. $sorting — задаёт сортировку результата. Может быть строкой (тогда будет отсортировано по указанному полю в порядке возрастания) либо массивом строк (ну или null, как по умолчанию). При этом каждая строка может быть ключом. Если ключу присваивается значение SDBA::DESC_ORDER (по сути, это просто true), то сортировка будет выполнена в порядке убывания. Позже рассмотрим это на примере.
  5. $limit — если не null (по умолчанию), то либо число, обозначающее максимальное число строк, которые надо вернуть, либо массив из этого числа и смещения выборки относительно её начала.
  6. $unique — булево значение. Если true, то все строки будут уникальны. Если false (по умолчанию), то могут встречаться повторы.

Теперь вернёмся к параметру $filter. Самый простой вариант, когда в массиве передаются пары ключ-значение. В таком случае ключ — название поля, и выборка вернёт все строки, где поле содержит указанное значение. То есть проверяется их равенство.
Но ведь есть и другие операции сравнения? Да, есть. Чтобы их использовать, в массив нужно передать строку со знаком требуемой операции перед операндами, которые её используют. Непонятно? Сейчас перейдём к примерам, и я всё наглядно покажу. Но сначала перечислю все возможные операции сравнения: = (задан изначально), <> и !=, <=, <, >=, >, <=>.

Итак, пример:

<?php

// Получаем имена всех пользователей.
$users = SDBA::Select("name", "users");

// А теперь только первого, используя фильтрацию по идентификатору.
$user1 = SDBA::Select("name", "users", ['id' => 1]);

// Берём первую десятку, указывая сортировку по столбцу `id` и ограничивая выборку 10-ю записями.
$users1_10 = SDBA::Select("name", "users", null, "id", 10);

// А теперь возьмём вторую десятку, указав ещё и смещение.
$users10_20 = SDBA::Select("name", "users", null, "id", [10, 10]);

// Отсортируем их в обратном порядке и получим последнюю десятку.
$lastUsers = SDBA::Select("name", "users", null, ['id' => SDBA::DESC_ORDER], 10);

// Вернём список дат регистрации без повторений.
$uniqueUsers = SDBA::Select("registration_date", "users", null, null, null, true);

// Заключительный и самый сложный пример: получаем идентификатор, имя и дату регистрации пользователей,
// которые зарегистрировались не больше, чем месяц назад, отсортировав их от самого последнего
// зарегистрировавшегося до самого "старого". В случае одинаковой даты, отсортировать по имени
// в алфавитном порядке.
$newUsers = SDBA::Select(
    ['id', 'name', 'registration_date'],
    "users",
    [
        '>=',
        'registration_date' => date("Y-m-j H:i:s", time() - 2592000)
    ],
    [
        'registration_date' => SDBA::DESC_ORDER,
        'name'
    ]
);


// Теперь можем по очереди получить все записи, используя стандартные методы MySQLi.
$userList = [];
while ($userList[] = $newUsers->fetch_assoc());

Модификация данных

Для внесения и изменения данных есть 5 методов.

Вставка новых записей

Для внесения данных в базу есть метод Insert($table, array $data).
Он принимает следующие параметры:

  • $table — название таблицы, куда будут добавляться данные.
  • $data — именованный массив, где ключи представляют собой названия полей, а значения — вносимые данные.

Достаточно просто, не так ли? Я не стал делать поддержку добавления анонимных значений, чтоб код был максимально наглядным.

<?php

SDBA::Insert("users", [
    'name'  => 'Леонид Козарин',
    'email' => 'kozalo@nekochan.ru'
]);

Изменение данных

Для модификации значений в базе данных используйте метод Update($table, array $changes, array $filter=null).
Он принимает следующие параметры:

  • $table — название таблицы, которую таблицу будем изменять.
  • $changes — массив, где ключи — названия изменяемых полей, а значения — новые значения для них.
  • $filter (необязательный параметр) — если не null, то содержит массив-фильтр, определяющий, какие записи подвергнутся изменениям. Подробно он был рассмотрен выше в разделе про выборку данных.
<?php

// Изменяем первого пользователя, чтобы им был аккаунт администратора.
SDBA::Update("users", [
    'name'  => 'Admin',
    'email' => 'admin@nekochan.ru'
], [
    'id' => 1
]);

Удаление данных

Для удаления записей предусмотрен метод Delete($table, array $filter=null).
Он принимает следующие параметры:

  • $table — название таблицы, откуда будем удалять данные.
  • $filter (необязательный параметр) — если не null, то содержит массив-фильтр, определяющий, какие записи будут удалены. Подробно он был рассмотрен выше в разделе про выборку данных.
<?php

// Похулиганим и удалим всех пользователей, кроме самого первого.
SDBA::Delete("users", [
    '>',
    'id' => 1
]);

Копирование данных

Для копирования строк из одной таблицы другую (но с такой же структурой!) есть метод Copy($source, $destination, array $filter=null). Он принимает следующие параметры:

  • $source — название таблицы, откуда будем брать данные.
  • $destination — название таблицы, куда будем копировать данные.
  • $filter (необязательный параметр) — если не null, то содержит массив-фильтр, определяющий, какие записи будут скопированы. Подробно он был рассмотрен выше в разделе про выборку данных.
<?php

// Работать будем с первыми 10-ю пользователями.
$filter = [
  '<=',
  'id' => 10
];

// Скопируем их в VIP-таблицу.
SDBA::Copy("users", "vip_users", $filter);

// А теперь удалим из таблицы для простолюдинов.
SDBA::Delete("users", $filter);

Перенос данных

Для перемещения записей между двумя таблицами с одинаковой структурой есть метод Move($source, $destination, array $filter=null). Он принимает следующие параметры:

  • $source — название таблицы, откуда будем удалять данные.
  • $destination — название таблицы, куда будем переносить данные.
  • $filter (необязательный параметр) — если не null, то содержит массив-фильтр, определяющий, какие записи будут перенесены. Подробно он был рассмотрен выше в разделе про выборку данных.
<?php

// Перенесём первые 10 пользователей в VIP-таблицу.
SDBA::Move("users", "vip_users", [
    '<=',
    'id' => 10
]);

// И теперь не нужно никого удалять, как в предыдущем примере!

Выполнение сложных запросов

Я бы настоятельно рекомендовал вместо сложных запросов в клиентском коде по возможности создавать на стороне базы данных триггеры и представления. Однако, если это представляется затруднительным, библиотека позволяет Вам использовать подготовленные запросы, что минифицирует возможность ввода некорректных данных и SQL-инъекций.

Самый простой способ выполнить SQL-запрос — это использовать метод CustomQuery($query, $typesMask=null, $values=null). Параметры у него следующие:

  • $query — любой корректный MySQL-запрос, в котором все вхождения конкретных данных заменены вопросительными знаками (см. пример ниже).
  • $typesMask — может быть равен null только в том случае, если в запросе нет никаких данных! Иначе должен быть строкой с количеством символов, равным количеству значений в следующем параметре (и количеству вопросительных знаков в запросе). Каждый символ представляет собой ожидаемый тип параметра: i — целое число, d - число с плавающей точкой, s - строка. Если не очень понятно, то ниже будет пример.
  • $values — аналогично предыдущему может быть равен null только в том случае, если в запросе нет никаких данных! Представляет собой либо одинокое значение, подставляемое в запрос, либо массив таких значений в соответствии с указанными предыдущим параметром типами.
<?php

// Да, я не придумал ничего лучше простого запроса на изменение данных.
SDBA::CustomQuery(
    "UPDATE `users` SET `name`=?, `email`=? WHERE `id`=?",
    "ssi",
    ['Admin', 'admin@nekochan.ru', 1]
);

Больше о подготовленных запросах

Под капотом CustomQuery() использует два других метода: PrepareQuery($query, $typesMask=null, &$bindings=null) и ExecutePreparedQuery(). Параметры первого очень похожи на параметры уже рассмотренного нами метода. Но что за привязки (bindings)?
Смысл наличия этих открытых методов и вообще подобная организация подготовленных выражений в MySQLi заключается в том, чтоб для различного набора данных можно было скомпилировать запрос только один раз, а затем лишь менять значения переменных и выполнять его. Поэтому при подготовке запроса с данными Вы должны передать третьим параметром в PrepareQuery() переменную, значение которой будете потом менять перед каждым последующим вызовом ExecutePreparedQuery(). Кроме одиночной переменной, разумеется, можно передать и массив таких переменных.

Таким образом, CustomQuery() используется в тех довольно частых случаях, когда нужно выполнить лишь одиночный запрос. Он использует преимущество подготовленных запросов в защите от SQL-инъекций, но не реализует весь их потенциал.
Ну а пара методов PrepareQuery() и ExecutePreparedQuery() используются, когда надо выполнить множество однотипных запросов, но с разными данными.

Транзакции

Класс содержит два простых метода без параметров для управления транзакцией: StartTransaction(), чтобы обозначить её начало, и Commit(), чтобы сохранить внесённые изменения в базе данных.
Если между началом транзакции и её сохранением произойдёт какая-нибудь ошибка, вся транзакция будет отменена, сохраняя все ваши данные в прошлом, целостном, состоянии.

Вспомогательные методы

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

  1. ChangeTimezone($offset)смещает часовой пояс на указанное количество часов.
  2. GetLastInsertedId() — очень полезный метод, возвращающий последний автоматически сгенерированный идентификатор. При использовании механизма транзакций его следует вызывать до сохранения изменений в БД!
  3. GetLastQuery() — просто наиполезнейший метод при отладке и выявлении ошибок! Возвращает итоговый SQL-запрос, который был отправлен к базе данных.
  4. AffectedRows() (начиная с v1.1.0) — возвращает количество строк, которое было затронуто предыдущим запросом.

Автор: Леонид Козарин (kozalo@yandex.ru)
© Kozalo.Ru, 2017

Updated