Spec-Zone.ru › Phalcon 3

Уровень абстракции базы данных

Phalcon\Db — это компонент, стоящий за Phalcon\Mvc\Model, который управляет слоем модели в фреймворке. Он состоит из независимого уровня абстракции высокого уровня для систем баз данных, полностью написанного на C.

Этот компонент позволяет манипулировать базой данных на более низком уровне, чем с помощью традиционных моделей.

Данное руководство не предназначено для полного описания доступных методов и их аргументов. Для получения полной справки посетите API.

Адаптеры баз данных

Этот компонент использует адаптеры для инкапсуляции деталей конкретной системы баз данных. Phalcon использует PDO для подключения к базам данных. Поддерживаются следующие типы баз данных:

Класс Описание
Phalcon\Db\Adapter\Pdo\Mysql Самая используемая в мире система управления реляционными базами данных (СУБД), работающая как сервер, предоставляющий многопользовательский доступ к ряду баз данных
Phalcon\Db\Adapter\Pdo\Postgresql PostgreSQL — мощная, открытая система реляционных баз данных. Она имеет более 15 лет активного развития и проверенную архитектуру, что обеспечило ей хорошую репутацию в плане надёжности, целостности данных и корректности.
Phalcon\Db\Adapter\Pdo\Sqlite SQLite — программная библиотека, реализующая автономный, бессерверный, безконфигурационный, транзакционный движок SQL базы данных

Реализация собственных адаптеров

Для создания собственных адаптеров баз данных или расширения существующих необходимо реализовать интерфейс Phalcon\Db\AdapterInterface.

Диалекты баз данных

Phalcon инкапсулирует специфические детали каждого движка базы данных в диалектах. Они предоставляют адаптерам общие функции и генератор SQL.

Класс Описание
Phalcon\Db\Dialect\Mysql Специфичный для SQL диалект для системы баз данных MySQL
Phalcon\Db\Dialect\Postgresql Специфичный для SQL диалект для системы баз данных PostgreSQL
Phalcon\Db\Dialect\Sqlite Специфичный для SQL диалект для системы баз данных SQLite

Реализация собственных диалектов

Для создания собственных диалектов баз данных или расширения существующих необходимо реализовать интерфейс Phalcon\Db\DialectInterface.

Подключение к базам данных

Для создания подключения необходимо создать экземпляр класса адаптера. Для этого требуется массив с параметрами подключения. Пример ниже демонстрирует, как создать подключение, передав как обязательные, так и необязательные параметры:

// Required
$config = [
    "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 = [
    "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 = [
    "dbname" => "/path/to/database.db",
];

// Create a connection
$connection = new \Phalcon\Db\Adapter\Pdo\Sqlite($config);

Настройка дополнительных параметров PDO

Вы можете настроить параметры PDO во время подключения, передав параметр ‘options’:

// Create a connection with PDO options
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(
    [
        "host"     => "localhost",
        "username" => "root",
        "password" => "sigma",
        "dbname"   => "test_db",
        "options"  => [
            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,
    [
        "Wall-E",
    ]
);

// Binding with named placeholders
$sql     = "INSERT INTO `robots`(name`, year) VALUES (:name, :year)";
$success = $connection->query(
    $sql,
    [
        "name" => "Astro Boy",
        "year" => 1952,
    ]
);

При использовании числовых плейсхолдеров их необходимо определять как целые числа, т.е. 1 или 2. В этом случае «1» или «2» считаются строками, а не числами, поэтому плейсхолдер не может быть успешно заменён. Во всех адаптерах данные автоматически экранируются с помощью PDO Quote.

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

Кроме того, вы можете передавать параметры напрямую методам execute/query. В этом случае связанные параметры непосредственно передаются в PDO:

// Binding with PDO placeholders
$sql    = "SELECT * FROM robots WHERE name = ? ORDER BY name";
$result = $connection->query(
    $sql,
    [
        1 => "Wall-E",
    ]
);

Вставка/Обновление/Удаление строк

Для вставки, обновления или удаления строк вы можете использовать исходный 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,
    [
        "Astro Boy",
        1952,
    ]
);

// Generating dynamically the necessary SQL
$success = $connection->insert(
    "robots",
    [
        "Astro Boy",
        1952,
    ],
    [
        "name",
        "year",
    ],
);

// Generating dynamically the necessary SQL (another syntax)
$success = $connection->insertAsDict(
    "robots",
    [
        "name" => "Astro Boy",
        "year" => 1952,
    ]
);

// 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,
    [
        "Astro Boy",
        101,
    ]
);

// Generating dynamically the necessary SQL
$success = $connection->update(
    "robots",
    [
        "name",
    ],
    [
        "New Astro Boy",
    ],
    "id = 101" // Warning! In this case values are not escaped
);

// Generating dynamically the necessary SQL (another syntax)
$success = $connection->updateAsDict(
    "robots",
    [
        "name" => "New Astro Boy",
    ],
    "id = 101" // Warning! In this case values are not escaped
);

// With escaping conditions
$success = $connection->update(
    "robots",
    [
        "name",
    ],
    [
        "New Astro Boy",
    ],
    [
        "conditions" => "id = ?",
        "bind"       => [101],
        "bindTypes"  => [PDO::PARAM_INT], // Optional parameter
    ]
);
$success = $connection->updateAsDict(
    "robots",
    [
        "name" => "New Astro Boy",
    ],
    [
        "conditions" => "id = ?",
        "bind"       => [101],
        "bindTypes"  => [PDO::PARAM_INT], // Optional parameter
    ]
);

// 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, [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;
use Phalcon\Db\Adapter\Pdo\Mysql as Connection;

$eventsManager = new EventsManager();

// Listen all the database events
$eventsManager->attach('db', $dbListener);

$connection = new Connection(
    [
        "host"     => "localhost",
        "username" => "root",
        "password" => "secret",
        "dbname"   => "invo",
    ]
);

// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);

Остановка SQL-операций очень полезна, например, если вы хотите реализовать проверку SQL-инъекций на последнем этапе:

use Phalcon\Events\Event;

$eventsManager->attach(
    "db:beforeQuery",
    function (Event $event, $connection) {
        $sql = $connection->getSQLStatement();

        // Check for malicious words in SQL statements
        if (preg_match("/DROP|ALTER/i", $sql)) {
            // 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\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Profiler as DbProfiler;

$eventsManager = new EventsManager();

$profiler = new DbProfiler();

// Listen all the database events
$eventsManager->attach(
    "db",
    function (Event $event, $connection) use ($profiler) {
        if ($event->getType() === "beforeQuery") {
            $sql = $connection->getSQLStatement();

            // Start a profile with the active connection
            $profiler->startProfile($sql);
        }

        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;
use Phalcon\Db\Profiler as Profiler;
use 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 Events Manager
$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;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Logger\Adapter\File as FileLogger;

$eventsManager = new EventsManager();

$logger = new FileLogger("app/logs/db.log");

$eventsManager->attach(
    "db:beforeQuery",
    function (Event $event, $connection) use ($logger) {
        $sql = $connection->getSQLStatement();

        $logger->log($sql, Logger::INFO);
    }
);

// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);

// Execute some SQL statement
$connection->insert(
    "products",
    [
        "Hot pepper",
        3.50,
    ],
    [
        "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 Может ли столбец принимать нулевые значения?

Для каждой поддерживаемой системы баз данных также реализованы методы получения информации о представлениях:

// 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,
    [
       "columns" => [
            new Column(
                "id",
                [
                    "type"          => Column::TYPE_INTEGER,
                    "size"          => 10,
                    "notNull"       => true,
                    "autoIncrement" => true,
                    "primary"       => true,
                ]
            ),
            new Column(
                "name",
                [
                    "type"    => Column::TYPE_VARCHAR,
                    "size"    => 70,
                    "notNull" => true,
                ]
            ),
            new Column(
                "year",
                [
                    "type"    => Column::TYPE_INTEGER,
                    "size"    => 11,
                    "notNull" => true,
                ]
            ),
        ]
    ]
);

Phalcon\Db::createTable() принимает ассоциативный массив, описывающий таблицу. Столбцы определяются с помощью класса Phalcon\Db\Column. Таблица ниже показывает доступные параметры для определения столбца:

Параметр Описание Необязательно
“type” Тип столбца. Должен быть константой Phalcon\Db\Column (см. ниже для списка) Нет
“primary” Истина, если столбец является частью первичного ключа таблицы Да
“size” Некоторые типы столбцов, такие как VARCHAR или INTEGER, могут иметь определённый размер Да
“scale” Столбцы DECIMAL или NUMBER могут иметь масштаб, чтобы указать, сколько десятичных знаков должно храниться Да
“unsigned” Столбцы INTEGER могут быть со знаком или без знака. Этот параметр не относится к другим типам столбцов Да
“notNull” Столбец может хранить нулевые значения? Да
“default” Значение по умолчанию (при использовании с "notNull" => true) Да
“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",
        [
            "type"    => Column::TYPE_VARCHAR,
            "size"    => 32,
            "notNull" => true,
            "after"   => "name",
        ]
    )
);

// Modifying an existing column
$connection->modifyColumn(
    "robots",
    null,
    new Column(
        "name",
        [
            "type"    => Column::TYPE_VARCHAR,
            "size"    => 40,
            "notNull" => true,
        ]
    )
);

// Deleting the column "name"
$connection->dropColumn(
    "robots",
    null,
    "name"
);

Удаление таблиц

Примеры удаления таблиц:

// Drop table robot from active database
$connection->dropTable("robots");

// Drop table robot from database "machines"
$connection->dropTable("robots", "machines");

© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/db.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API