Уровень абстракции базы данных
Phalcon\Db — это компонент, стоящий за Phalcon\Mvc\Model, который обеспечивает работу слоя моделей в фреймворке. Он состоит из независимого высокоуровневого слоя абстракции для систем баз данных, полностью написанного на языке C.
Этот компонент позволяет осуществлять манипуляции с базой данных на более низком уровне, чем с использованием традиционных моделей.
Это руководство не является полным описанием доступных методов и их аргументов. Для получения полной справки, пожалуйста, посетите API.
Адаптеры баз данных
Этот компонент использует адаптеры для инкапсуляции деталей конкретной системы управления базой данных. Phalcon использует PDO для подключения к базам данных. Поддерживаются следующие типы баз данных:
| Имя | Описание | API |
|---|---|---|
| MySQL | Самая используемая в мире реляционная система управления базами данных (СУБД), работающая как сервер, предоставляющий многопользовательский доступ к нескольким базам данных. | Phalcon\Db\Adapter\Pdo\Mysql |
| PostgreSQL | PostgreSQL — мощная, свободная реляционная система управления базами данных. Она имеет более чем 15 лет активного развития и проверенную архитектуру, что обеспечило ей сильную репутацию надежности, целостности данных и корректности. | Phalcon\Db\Adapter\Pdo\Postgresql |
| SQLite | SQLite — библиотека программного обеспечения, которая реализует автономный, бессерверный, безконфигурационный транзакционный движок SQL-базы данных. | Phalcon\Db\Adapter\Pdo\Sqlite |
| Oracle | Oracle — объектно-реляционная система управления базами данных, разработанная и продаваемая компанией Oracle Corporation. | Phalcon\Db\Adapter\Pdo\Oracle |
Реализация собственных адаптеров
Для создания собственных адаптеров баз данных или расширения существующих необходимо реализовать интерфейс Phalcon\Db\AdapterInterface.
Диалекты баз данных
Phalcon инкапсулирует специфические детали каждого движка базы данных в диалектах. Они предоставляют общие функции и генератор SQL адаптерам.
| Имя | Описание | API |
|---|---|---|
| MySQL | Диалект SQL для системы баз данных MySQL. | Phalcon\Db\Dialect\Mysql |
| PostgreSQL | Диалект SQL для системы баз данных PostgreSQL. | Phalcon\Db\Dialect\Postgresql |
| SQLite | Диалект SQL для системы баз данных SQLite. | Phalcon\Db\Dialect\Sqlite |
| Oracle | Диалект SQL для системы баз данных Oracle. | Phalcon\Db\Dialect\Oracle |
Реализация собственных диалектов
Для создания собственных диалектов баз данных или расширения существующих необходимо реализовать интерфейс Phalcon\Db\DialectInterface.
Подключение к базам данных
Для создания подключения необходимо создать экземпляр класса адаптера. Требуется массив с параметрами подключения. Приведенный ниже пример демонстрирует создание подключения, передавая как обязательные, так и необязательные параметры:
// Required
$config = array(
"host" => "127.0.0.1",
"username" => "mike",
"password" => "sigma",
"dbname" => "test_db"
);
// Optional
$config["persistent"] = false;
// Create a connection
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql($config);
// Required
$config = array(
"host" => "localhost",
"username" => "postgres",
"password" => "secret1",
"dbname" => "template"
);
// Optional
$config["schema"] = "public";
// Create a connection
$connection = new \Phalcon\Db\Adapter\Pdo\Postgresql($config);
// Required
$config = array(
"dbname" => "/path/to/database.db"
);
// Create a connection
$connection = new \Phalcon\Db\Adapter\Pdo\Sqlite($config);
// Basic configuration
$config = array(
'username' => 'scott',
'password' => 'tiger',
'dbname' => '192.168.10.145/orcl',
);
// Advanced configuration
$config = array(
'dbname' => '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=xe)(FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRIES=20)(DELAY=5))))',
'username' => 'scott',
'password' => 'tiger',
'charset' => 'AL32UTF8',
);
// Create a connection
$connection = new \Phalcon\Db\Adapter\Pdo\Oracle($config);
Настройка дополнительных параметров PDO
Вы можете настроить параметры PDO во время подключения, передавая параметры «options»:
// Create a connection with PDO options
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => "localhost",
"username" => "root",
"password" => "sigma",
"dbname" => "test_db",
"options" => array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES \'UTF8\'",
PDO::ATTR_CASE => PDO::CASE_LOWER
)
));
Поиск строк
Phalcon\Db предоставляет несколько методов для запроса строк из таблиц. В этом случае требуется конкретная синтаксическая конструкция SQL целевого движка базы данных:
$sql = "SELECT id, name FROM robots ORDER BY name";
// Send a SQL statement to the database system
$result = $connection->query($sql);
// Print each robot name
while ($robot = $result->fetch()) {
echo $robot["name"];
}
// Get all rows in an array
$robots = $connection->fetchAll($sql);
foreach ($robots as $robot) {
echo $robot["name"];
}
// Get only the first row
$robot = $connection->fetchOne($sql);
По умолчанию эти вызовы создают массивы с ассоциативными и числовыми индексами. Вы можете изменить это поведение, используя Phalcon\Db\Result::setFetchMode(). Этот метод получает константу, определяющую, какой тип индексов требуется.
| Константа | Описание |
|---|---|
| Phalcon\Db::FETCH_NUM | Возвращает массив с числовыми индексами |
| Phalcon\Db::FETCH_ASSOC | Возвращает массив с ассоциативными индексами |
| Phalcon\Db::FETCH_BOTH | Возвращает массив с ассоциативными и числовыми индексами |
| Phalcon\Db::FETCH_OBJ | Возвращает объект вместо массива |
$sql = "SELECT id, name FROM robots ORDER BY name";
$result = $connection->query($sql);
$result->setFetchMode(Phalcon\Db::FETCH_NUM);
while ($robot = $result->fetch()) {
echo $robot[0];
}
Метод Phalcon\Db::query() возвращает экземпляр Phalcon\Db\Result\Pdo. Эти объекты инкапсулируют всю функциональность, связанную с возвращаемым набором результатов, т.е. проход, поиск конкретных записей, подсчёт и т.д.
$sql = "SELECT id, name FROM robots";
$result = $connection->query($sql);
// Traverse the resultset
while ($robot = $result->fetch()) {
echo $robot["name"];
}
// Seek to the third row
$result->seek(2);
$robot = $result->fetch();
// Count the resultset
echo $result->numRows();
Связывание параметров
Связывание параметров также поддерживается в Phalcon\Db. Хотя использование связанных параметров имеет минимальное влияние на производительность, рекомендуется использовать этот метод, чтобы избежать возможности использования уязвимостей SQL-инъекции. Поддерживаются как строковые, так и позиционные плейсхолдеры. Связывание параметров может быть реализовано следующим образом:
// Binding with numeric placeholders
$sql = "SELECT * FROM robots WHERE name = ? ORDER BY name";
$result = $connection->query($sql, array("Wall-E"));
// Binding with named placeholders
$sql = "INSERT INTO `robots`(name`, year) VALUES (:name, :year)";
$success = $connection->query($sql, array("name" => "Astro Boy", "year" => 1952));
Вставка/Обновление/Удаление строк
Для вставки, обновления или удаления строк можно использовать исходный SQL-запрос или использовать предопределённые функции класса:
// Inserting data with a raw SQL statement
$sql = "INSERT INTO `robots`(`name`, `year`) VALUES ('Astro Boy', 1952)";
$success = $connection->execute($sql);
//With placeholders
$sql = "INSERT INTO `robots`(`name`, `year`) VALUES (?, ?)";
$success = $connection->execute($sql, array('Astro Boy', 1952));
// Generating dynamically the necessary SQL
$success = $connection->insert(
"robots",
array("Astro Boy", 1952),
array("name", "year")
);
// Updating data with a raw SQL statement
$sql = "UPDATE `robots` SET `name` = 'Astro boy' WHERE `id` = 101";
$success = $connection->execute($sql);
//With placeholders
$sql = "UPDATE `robots` SET `name` = ? WHERE `id` = ?";
$success = $connection->execute($sql, array('Astro Boy', 101));
// Generating dynamically the necessary SQL
$success = $connection->update(
"robots",
array("name"),
array("New Astro Boy"),
"id = 101"
);
// Deleting data with a raw SQL statement
$sql = "DELETE `robots` WHERE `id` = 101";
$success = $connection->execute($sql);
//With placeholders
$sql = "DELETE `robots` WHERE `id` = ?";
$success = $connection->execute($sql, array(101));
// Generating dynamically the necessary SQL
$success = $connection->delete("robots", "id = 101");
Транзакции и вложенные транзакции
Работа с транзакциями поддерживается, как и в PDO. Выполнение манипуляций с данными внутри транзакций часто увеличивает производительность в большинстве систем баз данных:
try {
//Start a transaction
$connection->begin();
//Execute some SQL statements
$connection->execute("DELETE `robots` WHERE `id` = 101");
$connection->execute("DELETE `robots` WHERE `id` = 102");
$connection->execute("DELETE `robots` WHERE `id` = 103");
//Commit if everything goes well
$connection->commit();
} catch(Exception $e) {
//An exception has occurred rollback the transaction
$connection->rollback();
}
В дополнение к стандартным транзакциям, Phalcon\Db предоставляет встроенную поддержку вложенных транзакций (если используемая система баз данных их поддерживает). При повторном вызове begin() создаётся вложенная транзакция:
try {
//Start a transaction
$connection->begin();
//Execute some SQL statements
$connection->execute("DELETE `robots` WHERE `id` = 101");
try {
//Start a nested transaction
$connection->begin();
//Execute these SQL statements into the nested transaction
$connection->execute("DELETE `robots` WHERE `id` = 102");
$connection->execute("DELETE `robots` WHERE `id` = 103");
//Create a save point
$connection->commit();
} catch(Exception $e) {
//An error has occurred, release the nested transaction
$connection->rollback();
}
//Continue, executing more SQL statements
$connection->execute("DELETE `robots` WHERE `id` = 104");
//Commit if everything goes well
$connection->commit();
} catch(Exception $e) {
//An exception has occurred rollback the transaction
$connection->rollback();
}
События базы данных
Phalcon\Db может отправлять события в EventsManager, если он присутствует. Некоторые события, возвращающие false, могут остановить активную операцию. Поддерживаются следующие события:
| Имя события | Вызывается | Может остановить операцию? | |
|---|---|---|---|
| afterConnect | После успешного подключения к системе баз данных | Нет | |
| beforeQuery | Перед отправкой SQL-запроса в систему баз данных | Да | |
| afterQuery | После отправки SQL-запроса в систему баз данных | Нет | |
| beforeDisconnect | Перед закрытием временного подключения к базе данных | Нет | |
| beginTransaction | Перед началом транзакции | Нет | |
| rollbackTransaction | Перед откат транзакции | Нет | |
| commitTransaction | Перед фиксацией транзакции | Нет | ||
Привязка EventsManager к подключению проста, Phalcon\Db будет вызывать события с типом «db»:
use Phalcon\Events\Manager as EventsManager,
\Phalcon\Db\Adapter\Pdo\Mysql as Connection;
$eventsManager = new EventsManager();
//Listen all the database events
$eventsManager->attach('db', $dbListener);
$connection = new Connection(array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
));
//Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
Остановка SQL-операций очень полезна, если, например, требуется реализовать проверку инъекций SQL в качестве последнего средства:
$eventsManager->attach('db:beforeQuery', function($event, $connection) {
//Check for malicious words in SQL statements
if (preg_match('/DROP|ALTER/i', $connection->getSQLStatement())) {
// DROP/ALTER operations aren't allowed in the application,
// this must be a SQL injection!
return false;
}
//It's ok
return true;
});
Профилирование SQL-запросов
Phalcon\Db включает компонент профилирования под названием Phalcon\Db\Profiler, который используется для анализа производительности операций с базой данных, чтобы диагностировать проблемы производительности и найти узкие места.
Профилирование баз данных очень просто с помощью Phalcon\Db\Profiler:
use Phalcon\Events\Manager as EventsManager,
Phalcon\Db\Profiler as DbProfiler;
$eventsManager = new EventsManager();
$profiler = new DbProfiler();
//Listen all the database events
$eventsManager->attach('db', function($event, $connection) use ($profiler) {
if ($event->getType() == 'beforeQuery') {
//Start a profile with the active connection
$profiler->startProfile($connection->getSQLStatement());
}
if ($event->getType() == 'afterQuery') {
//Stop the active profile
$profiler->stopProfile();
}
});
//Assign the events manager to the connection
$connection->setEventsManager($eventsManager);
$sql = "SELECT buyer_name, quantity, product_name "
. "FROM buyers "
. "LEFT JOIN products ON buyers.pid = products.id";
// Execute a SQL statement
$connection->query($sql);
// Get the last profile in the profiler
$profile = $profiler->getLastProfile();
echo "SQL Statement: ", $profile->getSQLStatement(), "\n";
echo "Start Time: ", $profile->getInitialTime(), "\n";
echo "Final Time: ", $profile->getFinalTime(), "\n";
echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n";
Вы также можете создать собственный класс профиля на основе Phalcon\Db\Profiler для записи статистических данных в реальном времени о запросах, отправленных в систему баз данных:
use Phalcon\Events\Manager as EventsManager,
Phalcon\Db\Profiler as Profiler,
Phalcon\Db\Profiler\Item as Item;
class DbProfiler extends Profiler
{
/**
* Executed before the SQL statement will sent to the db server
*/
public function beforeStartProfile(Item $profile)
{
echo $profile->getSQLStatement();
}
/**
* Executed after the SQL statement was sent to the db server
*/
public function afterEndProfile(Item $profile)
{
echo $profile->getTotalElapsedSeconds();
}
}
//Create an EventsManager
$eventsManager = new EventsManager();
//Create a listener
$dbProfiler = new DbProfiler();
//Attach the listener listening for all database events
$eventsManager->attach('db', $dbProfiler);
Ведение журнала SQL-запросов
Использование высокоуровневых компонентов абстракции, таких как Phalcon\Db для доступа к базе данных, затрудняет понимание отправляемых запросов. Phalcon\Logger взаимодействует с Phalcon\Db, обеспечивая возможности ведения журнала на уровне абстракции базы данных.
use Phalcon\Logger,
Phalcon\Events\Manager as EventsManager,
Phalcon\Logger\Adapter\File as FileLogger;
$eventsManager = new EventsManager();
$logger = new FileLogger("app/logs/db.log");
//Listen all the database events
$eventsManager->attach('db', function($event, $connection) use ($logger) {
if ($event->getType() == 'beforeQuery') {
$logger->log($connection->getSQLStatement(), Logger::INFO);
}
});
//Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
//Execute some SQL statement
$connection->insert(
"products",
array("Hot pepper", 3.50),
array("name", "price")
);
Как и выше, файл app/logs/db.log будет содержать что-то вроде этого:
[Sun, 29 Apr 12 22:35:26 -0500][DEBUG][Resource Id #77] INSERT INTO products
(name, price) VALUES ('Hot pepper', 3.50)
Реализация собственного логгера
Вы можете реализовать собственный класс логирования для запросов к базе данных, создав класс, реализующий единственный метод с именем «log». Метод должен принимать строку в качестве первого аргумента. Затем вы можете передать свой объект логирования в Phalcon\Db::setLogger(), и с этого момента любой SQL-запрос, выполняемый в базе данных, будет вызывать этот метод для записи результатов.
Описание таблиц/видов
Phalcon\Db также предоставляет методы для получения подробной информации о таблицах и представлениях:
// Get tables on the test_db database
$tables = $connection->listTables("test_db");
// Is there a table 'robots' in the database?
$exists = $connection->tableExists("robots");
// Get name, data types and special features of 'robots' fields
$fields = $connection->describeColumns("robots");
foreach ($fields as $field) {
echo "Column Type: ", $field["Type"];
}
// Get indexes on the 'robots' table
$indexes = $connection->describeIndexes("robots");
foreach ($indexes as $index) {
print_r($index->getColumns());
}
// Get foreign keys on the 'robots' table
$references = $connection->describeReferences("robots");
foreach ($references as $reference) {
// Print referenced columns
print_r($reference->getReferencedColumns());
}
Описание таблицы очень похоже на команду MySQL describe, оно содержит следующую информацию:
| Индекс | Описание |
|---|---|
| Поле | Имя поля |
| Тип | Тип столбца |
| Ключ | Является ли столбец частью первичного ключа или индекса? |
| Null | Разрешает ли столбец значения null? |
Для каждой поддерживаемой системы баз данных также реализованы методы получения информации о представлениях:
// Get views on the test_db database
$tables = $connection->listViews("test_db");
// Is there a view 'robots' in the database?
$exists = $connection->viewExists("robots");
Создание/изменение/удаление таблиц
Разные системы баз данных (MySQL, Postgresql и т.д.) предлагают возможность создавать, изменять или удалять таблицы с помощью команд, таких как CREATE, ALTER или DROP. Синтаксис SQL отличается в зависимости от используемой системы баз данных. Phalcon\Db предлагает унифицированный интерфейс для изменения таблиц без необходимости различать синтаксис SQL в зависимости от целевой системы хранения.
Создание таблиц
Следующий пример демонстрирует создание таблицы:
use \Phalcon\Db\Column as Column;
$connection->createTable(
"robots",
null,
array(
"columns" => array(
new Column("id",
array(
"type" => Column::TYPE_INTEGER,
"size" => 10,
"notNull" => true,
"autoIncrement" => true,
)
),
new Column("name",
array(
"type" => Column::TYPE_VARCHAR,
"size" => 70,
"notNull" => true,
)
),
new Column("year",
array(
"type" => Column::TYPE_INTEGER,
"size" => 11,
"notNull" => true,
)
)
)
)
);
Phalcon\Db::createTable() принимает ассоциативный массив, описывающий таблицу. Столбцы определяются с помощью класса Phalcon\Db\Column. Таблица ниже отображает доступные параметры для определения столбца:
| Параметр | Описание | Необязательно |
|---|---|---|
| “type” | Тип столбца. Должен быть константой Phalcon\Db\Column (см. список ниже) | Нет |
| “primary” | True, если столбец является частью первичного ключа таблицы | Да |
| “size” | Для некоторых типов столбцов, таких как VARCHAR или INTEGER, может быть указан размер | Да |
| “scale” | Столбцы DECIMAL или NUMBER могут иметь масштаб, определяющий количество десятичных знаков | Да |
| “unsigned” | Столбцы INTEGER могут быть со знаком или без знака. Это свойство не применяется к другим типам столбцов | Да |
| “notNull” | Столбец может хранить значения null? | Да |
| “autoIncrement” | С этим атрибутом столбец будет автоматически заполняться целым числом с автоинкрементом. Только один столбец в таблице может иметь этот атрибут. | Да |
| “bind” | Одна из констант BIND_TYPE_*, указывающая, как столбец должен быть связан перед сохранением | Да |
| “first” | Столбец должен быть размещен на первом месте в порядке столбцов | Да |
| “after” | Столбец должен быть размещен после указанного столбца | Да |
Phalcon\Db поддерживает следующие типы столбцов баз данных:
- Phalcon\Db\Column::TYPE_INTEGER
- Phalcon\Db\Column::TYPE_DATE
- Phalcon\Db\Column::TYPE_VARCHAR
- Phalcon\Db\Column::TYPE_DECIMAL
- Phalcon\Db\Column::TYPE_DATETIME
- Phalcon\Db\Column::TYPE_CHAR
- Phalcon\Db\Column::TYPE_TEXT
Ассоциативный массив, передаваемый в Phalcon\Db::createTable(), может иметь следующие возможные ключи:
| Индекс | Описание | Необязательно |
|---|---|---|
| “columns” | Массив с набором столбцов таблицы, определённых с помощью Phalcon\Db\Column | Нет |
| “indexes” | Массив с набором индексов таблицы, определённых с помощью Phalcon\Db\Index | Да |
| “references” | Массив с набором ссылок таблицы (внешних ключей), определённых с помощью Phalcon\Db\Reference | Да |
| “options” | Массив с набором параметров создания таблицы. Эти параметры часто связаны с системой баз данных, в которой была сгенерирована миграция. | Да |
Изменение таблиц
По мере роста вашего приложения вам, возможно, потребуется изменить свою базу данных, например, в процессе рефакторинга или добавления новых функций. Не все системы баз данных позволяют изменять существующие столбцы или добавлять столбцы между двумя существующими. Phalcon\Db ограничен этими ограничениями.
use Phalcon\Db\Column as Column;
// Adding a new column
$connection->addColumn("robots", null,
new Column("robot_type", array(
"type" => Column::TYPE_VARCHAR,
"size" => 32,
"notNull" => true,
"after" => "name"
))
);
// Modifying an existing column
$connection->modifyColumn("robots", null, new Column("name", array(
"type" => Column::TYPE_VARCHAR,
"size" => 40,
"notNull" => true,
)));
// Deleting the column "name"
$connection->deleteColumn("robots", null, "name");
Удаление таблиц
Примеры удаления таблиц:
// Drop table robot from active database
$connection->dropTable("robots");
//Drop table robot from database "machines"
$connection->dropTable("robots", "machines");
© 2011–2016 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/2.0.0/reference/db.html