Как и почти каждое современное веб-приложение, Cotonti взаимодействует с базой данных, и это взаимодействие реализовано в движке чрезвычайно просто и доступно.
В настоящее время поддерживается версия MySQL 5.1+
Конфигурация подключения к базе данных находится в файле конфигурации вашего приложения datas/config.php. Она формируется автоматически при установке Cotonti на хостинг.
Вы можете получить доступ к объекту БД через класс-фасад Cot. Объект базы данных Cotonti построен поверх PDO и предоставляет метод для выполнения запросов к БД а также методы упрощающие построение запросов INSERT, UPDATE и DELETE.
Вы можете выполнить SQL-запрос, выполнив следующие шаги:
В следующем примере показаны различные способы извлечения данных из базы данных:
// Возвращает набор строк. Каждая строка представляет собой ассоциативный массив имен столбцов
// и значений.
// Если запрос не вернул результатов, возвращается пустой массив
$posts = Cot::$db->query('SELECT * FROM post')->fetchAll();
// Возвращает одну (первую или следующую) строку из набора результатов
// Если запрос не вернул результата, возвращается значение FALSE
$post = Cot::$db->query('SELECT * FROM post WHERE id=?', 1)->fetch();
// Возвращает один столбец из следующей строки набора результатов
// Или FALSE, если строк больше нет.
$titles = Cot::$db->query('SELECT title FROM post')->fetchColumn();
$count = Cot::$db->query('SELECT COUNT(*) FROM post')->fetchColumn();
Для запросов, не возвращающих данные, следует использовать метод PDOStatement::execute(). Например:
Cot::$db->query('UPDATE post SET status=1 WHERE id=1')->execute();
Для запросов INSERT, UPDATE и DELETE вместо написания чистого SQL можно вызвать методы insert(), update(), delete() соответственно, для создания указанных SQL конструкций. Например:
// INSERT (table_name, column_values)
Cot::$db->insert(
'posts',
[
'titlle' => 'Lorem ipsum',
'text' => 'Morbi imperdiet tortor ut nisl ultricies finibus',
]
);
$newPostId = Cot::$db->lastInsertId();
// UPDATE (table_name, column_values, condition, params)
$condition = [
"created_at < '" . date('Y-m-d H:i:s', time() - 3600) . "'",
'category = :category',
];
$updatedRowsCount = Cot::$db->update(
'posts',
['status' => 1],
$condition,
['category' => $category]
);
// DELETE (table_name, condition, params)
$deletedRowsCount = Cot::$db->delete(
'posts',
'status = 0 AND category = ?',
$category
);
Методы insert(), update() и delete() не требуют экранирования названия таблицы. Это будет сделано автоматически.
Также методы insert() и update() не требуют экранирования элементов массива с данными для вставки/обновления.
Методы update() и delete() принимают в качестве условия (condition) строку с SQL-условием или массив строк. В случае с массивом каждая строка будет обернута в скобки и они объединяются оператором AND.
В примере выше:
$condition = [ "created_at < '2024-07-08 12:45:56'", 'category = :category' ];
эквивалентно:
(created_at < '2024-07-08 12:45:56') AND (category = :category)
При создании SQL запроса с параметрами, всегда нужно использовать привязку параметров для предотвращения атак через SQL инъекции. Например:
$post = Cot::$db->query( 'SELECT * FROM post WHERE category = ? AND status = ?', [$category, status] )->fetch();
При выполнении запроса каждый знак вопроса по порядку будет безопасно заменен соотвествующим значением из массива.
Если в запросе всего один параметер, вместо массива можно передать скалярное значение:
$post = Cot::$db->query('SELECT * FROM post WHERE category = ?', $category)->fetch();
Можно использовать именованные параметры:
$post = Cot::$db->query( 'SELECT * FROM post WHERE id = :id AND status = :status', ['id' => $id, ':status' => status] )->fetch();
Cot::$db->query(
'SELECT * FROM ' . Cot::$db->quoteTableName(Cot::$db->pages)
. ' WHERE page_cat = ' . Cot::$db->quote($category)
)->fetchColumn();
Методы для экранирования:
Cot::$db->quoteTableName() или сокращенная версия (Cot::$db->quoteT())Cot::$db->quoteColumnName() или сокращенная версия (Cot::$db->quoteC())Cot::$db->quote()Вы можете использовать метод Cot::$db->transaction() для выполнения нескольких запросов в рамках транзакции. Если при выполнении переданной функции будет выброшено исключение, база данных откатится на состояние, которое было до выполнения запросов, и исключение будет брошено дальше. Если функция-замыкание будет выполнена успешно, транзакция будет зафискирована автоматически. При использовании этого метода нет необходимости заботиться об откате или фискировании транзакции:
Cot::$db->transaction(
function(): void
{
Cot::$db->update('posts', ['status' = 1], 'category = ?', $category);
Cot::$db->delete('posts', 'status = 0']);
// ... executing other SQL statements ...
}
);
Вы можете использовать транзакции вручную, получая больше контроля над фиксацией и откатом транзакции и обработкой ошибок. Пример выше эквивалентен этому:
Cot::$db->beginTransaction();
try {
Cot::$db->update('posts', ['status' = 1], 'category = ?', $category);
Cot::$db->delete('posts', 'status = 0']);
// ... executing other SQL statements ...
Cot::$db->commit();
} catch(\Throwable $e) {
Cot::$db->rollBack();
throw $e;
}
Cotonti поддерживает настройку уровня изоляции для ваших транзакций. По умолчанию, при старте транзакции, будет использован уровень изоляции, настроенный в вашей базе данных. Вы можете переопределить уровень изоляции по умолчанию, как указано ниже:
$isolationLevel = \cot\database\DataBaseDictionary::REPEATABLE_READ;
Cot::$db->transaction(
function(): void
{
....
},
$isolationLevel
);
// или
Cot::$db->beginTransaction($isolationLevel);
Метод Cot::$db->transaction() принимает необязательный третий аргумент, который определяет, сколько раз следует повторять транзакцию при возникновении Deadlock'а. Как только эти попытки будут исчерпаны, будет выдано исключение:
Cot::$db->transaction(
function(): void
{
....
},
$isolationLevel,
5
);
(функционал пополняется)
Объект базы данных Cotonti предоставляет несколько методов для работы со схемой базы данных. Эти методы описаны ниже::
tableExists($tableName) - проверяет существование таблицы
fieldExists($tableName, $fieldName) - проверяет существование поля
indexExists($tableName, $indexName, $indexColumns = []) - проверяет существование индекса
addIndex($tableName, $indexName, $indexColumns = []) - создает индекс, где аргумент $indexColumns это строка, содержащая имя одного поля или массив с одним или множеством имен полей. Если пусто, $indexName будет использован в качестве имени поля.
Метод CotDB::runScript($script, $translaction = false) позволяет выполнять SQL-скрипты (содержимое SQL-файлов). Имена таблиц должны быть экранированы символом экранирования MySQL ` и иметь префикс cot_, который будет заменен на префикс из настроек в datas/config.php.