Hydrahon
🐉 Fast & standalone PHP MySQL Query Builder library.
Hydrahon
Hydrahon is a standalone database / SQL query builder written in PHP. It was built to enhance existing frameworks, libraries and applications that handle the database connection on their own. It does not come with a PDO or mysqli wrapper. The naming is heavily inspired by Eloquent and the Kohana Framework Database component.
What does that mean «Standalone query builder»?
Hydrahon only generates a query string and an array of parameters. On its own, it is not able to execute a query.
# Status
- The Hydrahon MySQL query builder is stable and used in production.
- The Hydrahon AQL (Arango Query Language) query builder is currently in development.
- A builder for Elasticsearch is on my mind but not in development.
# Installation
Hydrahon follows PSR-4 autoloading and can be installed using composer:
$ composer require clancats/hydrahon
# Documentation 💡
The full documentation can be found on clancats.io
# Quick Start (MySQL) ⚡️
Hydrahon is designed to be a pretty generic query builder. So for this quick start, we stick with SQL.
# Create a builder
Again this library is not built as a full database abstraction or ORM, it is only and will always be only a query builder. This means we need to implement the database connection and fetching by ourselves. The Hydrahon constructor therefore requires you to provide a callback function that does this, and returns the results.
In this example, we are going to use PDO
$connection = new PDO('mysql:host=localhost;dbname=my_database;charset=utf8', 'username', 'password'); // create a new mysql query builder $h = new \ClanCats\Hydrahon\Builder('mysql', function($query, $queryString, $queryParameters) use($connection) < $statement = $connection->prepare($queryString); $statement->execute($queryParameters); // when the query is fetchable return all results and let hydrahon do the rest // (there's no results to be fetched for an update-query for example) if ($query instanceof \ClanCats\Hydrahon\Query\Sql\FetchableInterface) < return $statement->fetchAll(\PDO::FETCH_ASSOC); > // when the query is a instance of a insert return the last inserted id elseif($query instanceof \ClanCats\Hydrahon\Query\Sql\Insert) < return $connection->lastInsertId(); > // when the query is not a instance of insert or fetchable then // return the number os rows affected else < return $statement->rowCount(); > >);
And we are ready and set. The variable $h contains now a MySQL query builder.
# Setup a simple table
To continue with our examples, we need to create a simple MySQL table.
CREATE TABLE `people` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT '', `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Inserting
Currently, we do not have any data, to fix this let’s go and insert some.
// In our example we are going to execute multiple operations on the same table, // so instead of loading the table over and over again, we store it in a variable. $people = $h->table('people'); $people->insert( [ ['name' => 'Ray', 'age' => 25], ['name' => 'John', 'age' => 30], ['name' => 'Ali', 'age' => 22], ])->execute();
Will execute the following query:
insert into `people` (`age`, `name`) values (?, ?), (?, ?), (?, ?)
As you can see Hydrahon automatically escapes the parameters.
However, because we are humans that get confused when there are hundreds of thousands of questions marks, I will continue to always display the runnable query:
insert into `people` (`age`, `name`) values (25, Ray), (30, John), (22, Ali)
# Updating
Ah snap, time runs so fast, «Ray» is actually already 26.
$people->update() ->set('age', 26) ->where('name', 'Ray') ->execute();
update `people` set `age` = 26 where `name` = 'Ray'
Currently, you might think: «Well isn’t it much simpler to just write the SQL query? I mean the PHP code is even longer. «.
You have to understand that these are some very very basic examples the Hydrahon query builder starts to shine when things get more complex. However, a «Quick Start» is just the wrong place for complicated stuff, so throw an eye on the full documentation.
# Deleting
$people->delete() ->where('name', 'John') ->execute();
delete from `people` where `name` = 'John'
# Selecting
And finally, fetch the data.
Notice that we use ->get() to actually fetch data, while we used ->execute() for our previous queries (updates, inserts and deletes). See the full documentation for more information about the Hydrahon runners methods.
# Where conditions
For the next few examples, lets assume a larger dataset so that the queries make sense.
Chaining where conditions:
// select * from `people` where `age` = 21 and `name` like 'J%' $people->select() ->where('age', 21) ->where('name', 'like', 'J%') ->get();
Notice how omitting the operator in the first condition ->where(‘age’, 21) makes Hydrahon default to = .
By default all where conditions are defined with the and operator.
Different where operators:
// select * from `people` where `name` like 'J%' or `name` like 'I%' $people->select() ->where('name', 'like', 'J%') ->orWhere('name', 'like', 'I%') ->get();
Please check the relevant section in the full documentation for more where-functions, like
Where scopes
Allowing you to group conditions:
// select * from `people` where ( `age` > 21 and `age` < 99 ) or `group` = admin $people->select() ->where(function($q) < $q->where('age', '>', 21); $q->where('age', ') ->orWhere('group', 'admin') ->get();
# Joins
// select // `people`.`name`, `groups`.`name` as `group_name` // from `people` // left join `groups` on `groups`.`id` = `people`.`group_id` $people->select('people.name, groups.name as group_name') ->join('groups', 'groups.id', '=', 'people.group_id') ->get();
# Grouping
// select * from `people` group by `age` $people->select()->groupBy('age')->get();
# Ordering
// select * from `people` order by `age` desc $people->select()->orderBy('age', 'desc')->get(); // select * from `people` order by `age` desc, `name` asc $people->select()->orderBy(['age' => 'desc', 'name' => 'asc'])->get();
# Limiting data
// select * from `people` limit 0, 10 $people->select()->limit(10)->get(); // select * from `people` limit 100, 10 $people->select()->limit(100, 10)->get(); // select * from `people` limit 100, 10 $people->select()->limit(10)->offset(100)->get(); // select * from `people` limit 150, 30 $people->select()->page(5, 30)->get();
Small reminder this is the quick start, check out the full docs.
# Credits
# License
The MIT License (MIT). Please see License File for more information.
Mysql query library php
За выполнение запросов отвечают функции mysqli::query() , mysqli::real_query() и mysqli::multi_query() . Чаще всего применяется функция mysqli::query() , так как она выполняет сразу две задачи: выполняет запрос и буферизует на клиенте результат этого запроса (если он есть). Вызов mysqli::query() идентичен последовательному вызову функций mysqli::real_query() и mysqli::store_result() .
Пример #1 Выполнение запросов
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «example.com» , «user» , «password» , «database» );
$mysqli -> query ( «DROP TABLE IF EXISTS test» );
$mysqli -> query ( «CREATE TABLE test(id INT)» );
Буферизация результатов запроса
После выполнения запроса результаты можно либо получить сразу, либо считать строку за строкой с сервера. Буферизация набора результатов на стороне клиента позволяет серверу как можно раньше высвободить ресурсы, связанные с результатами запроса. Проще говоря, клиенты медленно используют наборы результатов. Поэтому рекомендуется использовать буферизованные наборы результатов. mysqli::query() объединяет выполнение запроса и буферизацию набора результатов.
PHP-приложения могут свободно оперировать данными внутри буферизованных результирующих наборов. Быстрая навигация по строкам наборов обусловлена тем, что наборы полностью располагаются в памяти клиента. Следует помнить, что зачастую обработка результатов на клиенте проще, нежели средствами сервера.
Пример #2 Навигация по строкам буферизованной результирующей таблицы
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «example.com» , «user» , «password» , «database» );
$mysqli -> query ( «DROP TABLE IF EXISTS test» );
$mysqli -> query ( «CREATE TABLE test(id INT)» );
$mysqli -> query ( «INSERT INTO test(id) VALUES (1), (2), (3)» );
$result = $mysqli -> query ( «SELECT id FROM test ORDER BY id ASC» );
echo «Обратный порядок. \n» ;
for ( $row_no = $result -> num_rows — 1 ; $row_no >= 0 ; $row_no —) $result -> data_seek ( $row_no );
$row = $result -> fetch_assoc ();
echo » id color: #007700″>. $row [ ‘id’ ] . «\n» ;
>
echo «Исходный порядок строк. \n» ;
foreach ( $result as $row ) echo » id color: #007700″>. $row [ ‘id’ ] . «\n» ;
>
Результат выполнения данного примера:
Обратный порядок. порядок строк.
Небуферизованные результирующие наборы
Если клиентские ресурсы ограничены, и в тоже время не требуется поддерживать низкую нагрузку на сервер, можно использовать не буферизованные результирующие наборы. Навигация по таким таблицам невозможна, потому что так или иначе должны быть обработаны все строки набора.
Пример #3 Навигация по строкам небуферизованной результирующей таблицы
$mysqli -> real_query ( «SELECT id FROM test ORDER BY id ASC» );
$result = $mysqli -> use_result ();
?php
echo «Порядок строк в результирующем наборе. \n» ;
foreach ( $result as $row ) echo » id color: #007700″>. $row [ ‘id’ ] . «\n» ;
>
Типы данных значений в результирующей таблице
Функции mysqli::query() , mysqli::real_query() и mysqli::multi_query() предназначены для выполнения не подготавливаемых запросов. На уровне протокола клиент-серверного взаимодействия MySQL за выполнение запросов отвечают команда COM_QUERY и текстовый протокол. Когда используется текстовый протокол, сервер MySQL перед отправкой клиенту преобразует все данные в результирующем наборе в текстовые строки. Это преобразование выполняется вне зависимости от типа данных SQL-столбца результирующей таблицы. Клиентские библиотеки mysql, в свою очередь, получают все данные, принимая их за строки. На клиенте не проводится никакого обратного преобразования к исходным типам, все данные, полученные приложением остаются PHP строками.
Пример #4 Текстовый протокол по умолчанию возвращает строки
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «example.com» , «user» , «password» , «database» );
$mysqli -> query ( «DROP TABLE IF EXISTS test» );
$mysqli -> query ( «CREATE TABLE test(id INT, label CHAR(1))» );
$mysqli -> query ( «INSERT INTO test(id, label) VALUES (1, ‘a’)» );
$result = $mysqli -> query ( «SELECT id, label FROM test WHERE > );
$row = $result -> fetch_assoc ();
printf ( «id = %s (%s)\n» , $row [ ‘id’ ], gettype ( $row [ ‘id’ ]));
printf ( «label = %s (%s)\n» , $row [ ‘label’ ], gettype ( $row [ ‘label’ ]));
Результат выполнения данного примера:
id = 1 (string) label = a (string)
Если используется библиотека mysqlnd, можно включить преобразование целочисленных значений и чисел с плавающей точкой из столбцов таблицы в PHP числа. Делается это заданием настройки подключения MYSQLI_OPT_INT_AND_FLOAT_NATIVE . В таком случае mysqlnd будет проверять метаданные столбцов и преобразовывать SQL-числа этих полей в PHP-числа, если эти значения не выходят за рамки допустимых диапазонов типов данных PHP. То есть, например, SQL INT число попадёт в PHP приложение в виде целого (integer).
Пример #5 Получение исходных типов данных в приложении
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ();
$mysqli -> options ( MYSQLI_OPT_INT_AND_FLOAT_NATIVE , 1 );
$mysqli -> real_connect ( «example.com» , «user» , «password» , «database» );
$mysqli -> query ( «DROP TABLE IF EXISTS test» );
$mysqli -> query ( «CREATE TABLE test(id INT, label CHAR(1))» );
$mysqli -> query ( «INSERT INTO test(id, label) VALUES (1, ‘a’)» );
$result = $mysqli -> query ( «SELECT id, label FROM test WHERE > );
$row = $result -> fetch_assoc ();
printf ( «id = %s (%s)\n» , $row [ ‘id’ ], gettype ( $row [ ‘id’ ]));
printf ( «label = %s (%s)\n» , $row [ ‘label’ ], gettype ( $row [ ‘label’ ]));
Результат выполнения данного примера:
id = 1 (integer) label = a (string)