Pull to refresh

DBX: попытка избавиться от составления MySQL запросов

Reading time4 min
Views6.4K
Давным давно, когда я только начинал изучать PHP и тонкости составления запросов MySQL (2011 год) у меня возникла мысль написать обертку для MySQLi наподобие Doctrine для упрощения синтаксиса обращения к базе данных. На дворе уже 2019 и решил поделиться своим велосипедом на тему ORM.

И так, это DBX — движок базы данных MySQL для PHP на основе библиотеки MySQLi основанный на запросах вида структурного описания таблиц и полей базы данных в виде обычного массива с функцией статического кэширования запросов и автообновления хэша статики.

Вопреки всеобщей любви к PDO, была выбрана именно легковесная MySQLi функциональность, которая мягче и проще программируется и более лаконично вписывается в предложенное API DBX. Я даже не использовал composer, потому что у меня нет ни одного проекта, где я использую сторонние зависимости кроме самописных.

Собственно, рассмотрим пример объявления структуры простейшей тестовой базы данных и ее таблиц. Для начала подключим саму библиотеку с классом и отправим данные авторизации с хостом базы данных:

require_once './DBX.php';
$dbx_data  = ['localhost', 'root', 'root', 'dbx_test', '8889'];

Теперь представим, что нам нужно описать и создать таблицу в БД используя синтаксис обычного массива PHP:

<?php
/**
  * CREATE TABLE EXAMPLE
  */
$table_1 = 'example'; // table name
$query_1 = 'c'; // create table sql
$fields_1 = [
    'field_id' => [
        'type'   => 'num', // int
        'auto'   => true,  // auto increment
        'length' => 255,
        'fill'   => true   // not null
    ],
    'field_text' => [
        'type'   => 'text', // varchar
        'length' => 255,
        'fill'   => true
    ],
    'field_date' => [
        'type'   => 'time', // TIMESTAMP
        'value'  => date('Y-m-d')
    ]
];

?>

Здесь синтаксис простой. Я не использовал строгую типизацию и выбрал просто значения TEXT, NUMBER и TYMESTAMP. Конечно же, для более «рульной» структуры таблицы используется ключевое поле ID с автоинкрементом (не встречал случаев в реальных проектах, когда задание автоинкремента и ключа мешает проектированию запросов и связей таблиц). Для указания может ли поле быть пустым указывается параметр fill => true.

Типы запросов DataBaseX очень просты и содержат основные методы вроде select, insert, delete, update, drop, truncate и так далее.

К примеру запрос для создания таблицы:

// perform queries
$dbx::query("c", $table_1, $fields_1);

Или запрос добавления данных и структуры столбцов может выглядеть вот так:

// fields values for table_1 example
$fields_2 = [
    'field_id' => [
        'value' => 456
    ],
    'field_text' => [
        'value' => 'I have to add into my table'
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('i', $table_1, $fields_2);

Структура массива позволяет однажды описать базу данных и в последующем лишь манипулировать полями value для изменения данных, которые используются для формирования запросов.

Вот пример update инструкции, которую я упраздню в следующем примере:

// fields values for table_1 example
$fields_3 = [
    'field_id' => [
        'value' => 456
    ],
    'field_text' => [
        
        'new_value' => 'I was updated',
        'criterion_field' => 'field_id',
        'criterion_value' => 456
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('u', $table_1, $fields_3);


Мне показалось, что использование полей criterion_field и criterion_value усложняет систему, поэтому я создал запрос, который автоматически либо добавляет данные в БД, если они еще не были созданы, либо обновляет существующие данные. Этот запрос я назвал INJECT QUERY и вызывается он префиксом «in»:


// fields values for table_1 example
$fields_2 = [
    'field_id' => [
        'value' => 0
    ],
    'field_text' => [
        'value' => 'Yo if field_id = 0 it\'s an insert or if id exists it\'s an update'
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('in', $table_1, $fields_2);


Работает это на низком уровне так:

INSERT INTO `revolver__comments` (`field_id`, `field_content`) 
VALUES ('5', 'TEST UPDATE') 
ON DUPLICATE KEY UPDATE `field_id`='5', `field_content`='TEST UPDATE';

Все остальные запросы простейшие и описывать их не имеет смысла(смотрите примеры в файле тестов index.php), но я не могу не показать как работает запрос SELECT:

<?php 
// perform queries
$dbx::query('s|field_id|asc|100|0', $table_1, $fields_1);
?>

<?php
    // print structure
    print '<h2>DBX STRUCTURE</h2>';
    print '<pre><code>';
    print_r( $fields_1 );
    print '</code></pre>'; 
    // print result
    print '<h2>DBX QUERY RESULT</h2>';
    print '<pre><code>';
    print_r( $dbx::$result );
    print '</code><pre><hr />';
?>


Здесь с использованием префикса «s» можно сгруппировать параметры запроса s|field_id(order)|asc(direction)|100(limit)|0(offset).

Кроме прочего DBX обладает такой отличительностью, как встроенный файловый кэш на базе JSON статики. Каждый запрос изменения (INSERT, UPDATE, DELETE, TRUNCATE) вызывает пресчёт хэша в таблице хэшей кэша и автоматически обновляет статический кэш, что позволяет не думать о нагрузке на запросы SELECT.

В будущем я планирую до развить движок DBX и расширить возможности конструктора SQL запросов системой конфигурации UNION и JOIN, а также добавить новую из MySQL 8 поддержку базы данных на типе JSON структуры.

Сейчас DBX используется в моей системе управления контентом RevolveR и показывает неплохие характеристики (весь сайт при условии наличия обновленного кэша инициализируется одним запросом к БД и потребляет около 0.7 Mb оперативной памяти интерпретатора). А также мне очень удобно видеть всю структуру БД в одном отдельном файле, что в разы ускоряет наращивание и проектирование новых модулей.

Репозиторий проекта: DBX v1.1.1 на GitHub.
Tags:
Hubs:
Total votes 25: ↑5 and ↓20-15
Comments63

Articles