В этой статье я собираюсь познакомить вас с Medoo, облегченной библиотекой абстракции базы данных для PHP. Его основные функции включают в себя:
- Поддержка нескольких баз данных — он поддерживает MySQL, MariaDB, Sybase, MS SQL, PostgreSQL и Oracle.
- Безопасный — предотвращает внедрение SQL, использует PDO.
- Прост в использовании — его API очень интуитивно понятен.
Хотя Medoo не является чем-то революционным, и тот факт, что он имеет очень маленький размер файла, имеет мало значения для немногих, это все же интересный проект, который перешел от прямого отклонения к расплывчатому принятию, как видно из этих тем . Он продвигается вверх, и это наша причина взглянуть на это.
Установка
Хотя веб-сайт рекомендует установить его, загрузив файл и включив его в свой проект, вы должны использовать Composer для этого .
Подключение к базе данных
Создание нового экземпляра Medoo требует передачи массива в качестве аргумента. Массив должен содержать следующие элементы:
- тип_базы_данных — тип базы данных, к которой вы хотите подключиться. Это может быть одно из следующих: mysql, mariadb, pgsql, sybase, oracle, mssql, sqlite.
- имя_базы_данных — имя базы данных.
- сервер — имя сервера или IP-адрес.
- username — имя пользователя, использованное для входа в базу данных.
- пароль — пароль пользователя.
$db = new medoo(array( 'database_type' => 'mysql', 'database_name' => 'pokemon', 'server' => 'localhost', 'username' => 'ash_ketchum', 'password' => 'pikachu' ));
Порт по умолчанию, используемый medoo, это порт 3306. Если ваш сервер баз данных использует что-то другое, вы также можете передать port
и назначить правильный порт в качестве значения.
Выбор данных
Первое, что мы собираемся сделать, это выбрать данные из базы данных. Вы можете скачать пример базы данных здесь, если хотите следовать.
Вы можете выбрать данные из определенной таблицы, используя метод select
. Это принимает следующие аргументы:
- название стола
- условие соединения (необязательно)
- поля для выбора
- где условие (необязательно)
В качестве учебного пособия давайте выберем несколько тренеров из базы данных:
$trainers = $db->select( 'trainers', array('id', 'name', 'pokemon_count', 'region') );
Medoo возвращает результаты в виде массива:
Array ( [0] => Array ( [id] => 1 [name] => Brock [pokemon_count] => 7 [region] => Kanto ) [1] => Array ( [id] => 2 [name] => Marshal [pokemon_count] => 8 [region] => Unova ) ...
Запрос выше выбирает всех тренеров. Что если мы хотим выбрать только тренеров из определенного региона? Мы можем сделать это, указав условие where:
$trainers = $db->select( 'trainers', array('id', 'name', 'pokemon_count', 'region'), array('region' => 'Kanto') );
Запрос выше возвращает только тренеров из региона Канто.
Что если мы хотим выбрать только тех тренеров, которые победили определенное количество тренеров из определенного региона? Вот как мы это делаем:
$trainers = $db->select('trainers', array('id', 'name', 'trainers_defeated', 'region'), array('AND' => array('trainers_defeated[>]' => 150, 'region' => 'Johto')) );
Все, что вы должны помнить при использовании условия where, это указать массив полей с соответствующим условием в качестве значения для ключевого слова, которое вы хотите использовать. В этом случае мы хотим, чтобы условие для обоих возвращало true, поэтому мы используем ключевое слово AND
.
Если вам нужно использовать реляционный оператор, отличный от =
, вам нужно указать его после имени поля и заключить в квадратные скобки []
. Вот некоторые примеры:
- Выберите тренеров, которые победили более 150 противников:
'trainers_defeated[>]' => 150
- Выберите тренеров, у которых
badges_count
не равен нулю:
'badges_count[!]' => 0
- Выберите тренеров, которые победили тренеров в диапазоне от 100 до 300:
'trainers_defeated[<>]' => array(100, 300)
- Выберите тренеров, у которых
badges_count
меньше 8:
'badges_count[<]' => 8
Если вернуться назад, то что, если мы хотим выбрать тренеров, принадлежащих к «Kanto» или «Unova» и имеющих badges_count
от 5 и выше или trainers_defeated
от 20 и выше? Вот как мы это делаем:
$trainers = $db->select( 'trainers', array('id', 'name', 'trainers_defeated', 'badges_count', 'region'), array('AND' => array( 'region' => array('Kanto', 'Unova'), 'OR' => array('badges_count[>=]' => 5, 'trainers_defeated[>=]' => 20) )) );
Все, что нам нужно было сделать, это заключить наши условия в ключевое слово AND
. Для первого условия мы перешли в массив. В данном случае это название регионов, которые мы хотим выбрать. Во втором условии мы должны были обернуть его в OR
, это означает, что условия внутри него будут возвращать результаты для любого из них.
Теперь мы хотим знать, какой тренер выловил наибольшее количество покемонов. Мы можем сделать это с помощью метода get
. В отличие от метода select
, он вернет только одну строку из таблицы, из которой мы выбираем.
$most_caught = $db->get( 'trainers', array('name', 'pokemon_count'), array('ORDER' => 'pokemon_count DESC') );
Вы также можете использовать ORDER
с методом select
. Например, мы хотим выбрать всех тренеров и упорядочить их по количеству пойманных ими покемонов.
$trainers_most_caught = $db->select( 'trainers', array('name', 'pokemon_count', 'trainers_defeated'), array('ORDER' => 'pokemon_count DESC') );
Мы также можем заказать несколько полей. Вместо того, чтобы передавать строку для значения ORDER
, мы передаем массив:
$trainers_most_caught = $db->select( 'trainers', array('name', 'pokemon_count', 'trainers_defeated'), array('ORDER' => array('pokemon_count DESC', 'trainers_defeated DESC')) );
Что если мы хотим узнать, сколько инструкторов в каждом регионе? У Medoo пока нет встроенной функциональности такого рода, поэтому нам придется использовать необработанный запрос:
$trainer_count_per_region = $db->query( 'SELECT COUNT(name) as trainers, region FROM trainers GROUP BY region' )->fetchAll(PDO::FETCH_ASSOC);
За кулисами Medoo использует метод извлечения PDO. Это означает, что он также использует стиль выборки по умолчанию, используемый методом выборки PDO. Поэтому мы должны передать стиль выборки в качестве аргумента методу fetchAll
. В этом случае это PDO::FETCH_ASSOC
, это означает, что он будет возвращать только имена полей и их значения. Если мы оставим это поле пустым, он вернет и ассоциативный, и индексированный столбцы. Вот результат, который мы получаем, когда мы используем PDO::FETCH_ASSOC
в качестве стиля выборки:
Array ( [0] => Array ( [trainers] => 2 [region] => Hoenn ) [1] => Array ( [trainers] => 4 [region] => Johto ) [2] => Array ( [trainers] => 2 [region] => Kalos ) [3] => Array ( [trainers] => 1 [region] => Kanto ) [4] => Array ( [trainers] => 3 [region] => Unova ) )
Если мы оставим это поле пустым, результаты будут выглядеть так:
Array ( [0] => Array ( [trainers] => 2 [0] => 2 [region] => Hoenn [1] => Hoenn ) [1] => Array ( [trainers] => 4 [0] => 4 [region] => Johto [1] => Johto ) [2] => Array ( [trainers] => 2 [0] => 2 [region] => Kalos [1] => Kalos ) [3] => Array ( [trainers] => 1 [0] => 1 [region] => Kanto [1] => Kanto ) [4] => Array ( [trainers] => 3 [0] => 3 [region] => Unova [1] => Unova ) )
Наконец, давайте коснемся объединений. Мы можем использовать следующий синтаксис для объединений:
-
>
— левое соединение -
<
— правое соединение -
><
— внутреннее соединение -
<>
— полное присоединение
Вот пример использования внутреннего соединения для выбора всех покемонов и соответствующих им имен типов:
$db->select( 'pokemon', array('[><]types' => array('type_id' => 'id')), array('pokemon.name', 'types.name(type)') );
Как и в обычном методе select
, первый аргумент — это имя таблицы. В этом случае это основная таблица, из которой мы хотим выбрать данные. Второй аргумент — это массив, содержащий условие соединения. Мы предоставляем тип соединения и имя таблицы для соединения в качестве ключа. И значение является массивом с полем в первичной таблице в качестве ключа и полем во вторичной таблице в качестве значения. Третий аргумент — это массив полей, которые вы хотите вернуть. Рекомендуется всегда указывать имя таблицы перед именем поля. В настоящее время medoo не поддерживает псевдонимы таблиц, поэтому нам придется использовать полное имя таблицы. Если имена полей, которые вы хотите выбрать, совпадают, вам нужно использовать псевдоним столбца. Если вы этого не сделаете, будет выбрано только последнее поле с тем же именем, которое вы указали. В нашем примере выше мы указали псевдоним, используя этот синтаксис:
table_name.field_name(alias)
Вставка данных
Далее мы добавляем некоторые данные в таблицу pokemon
. Для этого собирались использовать Pokemon API в качестве нашего источника данных.
Сначала установите guzzle с composer require guzzlehttp/guzzle:~5.0
Создайте новый экземпляр клиента guzzle:
use GuzzleHttp\Client; $client = new Client();
Далее мы выбираем всех тренеров и типы покемонов из базы данных. Мы будем использовать их позже, когда будем вставлять данные в таблицу pokemon
.
$trainers = $db->select('trainers', array('id', 'pokemon_count')); $types = $db->select('types', array('id', 'name'));
Далее мы делаем запрос к ресурсу pokedex в Pokemon API. Это возвращает строку JSON, поэтому мы используем метод json
от json
чтобы преобразовать ее в массив. Ресурс pokedex возвращает массив всех покемонов.
$pokedex_response = $client->get('http://pokeapi.co/api/v1/pokedex/1'); $pokedex_data = $pokedex_response->json(); $pokemon = $pokedex_data['pokemon']; $total_pokemon = count($pokemon) - 1; //were zero-indexed
Затем мы перебираем все тренеры, получаем количество пойманных ими покемонов и затем создаем цикл for на основе этого числа. Ресурс pokedex возвращает имя покемона и URI ресурса, в котором мы можем получить дополнительную информацию о нем. Нам также нужно получить основной тип покемона и список ходов, которые он может сделать, поэтому мы также делаем запрос к ресурсу покемонов. Как только мы вернем данные, нам нужно получить идентификатор типа покемонов. У нас уже есть таблица, в которой есть список всех типов покемонов, поэтому мы просто определяем идентификатор, проходя по нему, и если имя типа, возвращаемое из API, совпадает с одним из имен типов из базы данных, мы просто получаем его идентификатор. После этого мы даем покемону случайный уровень, используя mt_rand
. Затем мы вызываем метод вставки Medoo для вставки данных Pokemon в базу данных. Это принимает имя таблицы в качестве первого аргумента, а затем массив данных в качестве второго аргумента. Наконец, мы назначаем несколько ходов каждому из покемонов. Когда вы вызываете метод insert
, он возвращает последний идентификатор вставки. Мы используем этот последний идентификатор вставки для определения идентификатора покемона, который был назначен базой данных, а затем вставляем его в таблицу pokemon_moves
вместе с именем перемещения.
foreach($trainers as $trainer){ $trainer_id = $trainer['id']; $pokemon_count = $trainer['pokemon_count']; for($x = 0; $x < $pokemon_count; $x++){ $pokemon_id = mt_rand(0, $total_pokemon); $pokemon_name = $pokemon[$pokemon_id]['name']; $pokemon_resource = $pokemon[$pokemon_id]['resource_uri']; $pokemon_response = $client->get('http://pokeapi.co/' . $pokemon_resource); $pokemon_data = $pokemon_response->json(); $pokemon_types = $pokemon_data['types']; //pokemon types in the database starts with a capital letter $type_name = ucfirst($pokemon_types[0]['name']); $type_id = null; foreach($types as $t){ //determine type id if($t['name'] == $type_name){ $type_id = $t['id']; } } $level = mt_rand(1, 100); //give a random level between 1 and 100 $pokemon_db_id = $db->insert( 'pokemon', array( 'name' => $pokemon_name, 'trainer_id' => $trainer_id, 'type_id' => $type_id, 'level' => $level ) ); //assign some moves $pokemon_moves = $pokemon_data['moves']; if(!empty($pokemon_moves)){ $move_count = count($pokemon_moves) - 1; $move_limit = 4; //each pokemon can only have 4 moves for($z = 0; $z < $move_limit; $z++){ $move_id = mt_rand(0, $move_count); $move_name = $pokemon_moves[$move_id]['name']; $db->insert( 'pokemon_moves', array( 'pokemon_id' => $pokemon_db_id, 'move_name' => $move_name ) ); } } } }
После запуска сценария, приведенного выше, у нас должно быть несколько покемонов вместе с их ходами в нашей базе данных.
Допустим, тренер Рокси поймал 3 новых покемонов: Drapion, Toxicroak и Crobat. Как мы можем вставить их всех сразу в нашу таблицу pokemon
? Метод insert
также поддерживает множественные вставки, поэтому мы можем просто предоставить массив, содержащий все строки данных, которые мы хотим вставить.
Сначала мы получаем идентификатор тренера из таблицы trainers
:
$trainer_id = $db->get('trainers', 'id', array('name' => 'Roxie'));
Давайте предположим, что эти 3 покемона имеют один и тот же тип, поэтому мы идем дальше и получаем идентификатор типа из базы данных:
$type_id = $db->get('types', 'id', array('name' => 'Poison'));
В API pokedex
нет метода поиска, поэтому все, что мы можем сделать, это получить доступ к pokedex
напрямую из браузера:
http://pokeapi.co/api/v1/pokedex/1/
И затем ищите resource_uri
который мы хотим получить. Я уже сделал это, чтобы тебе не пришлось. Идентификаторы, которые нам нужны: 452, 454 и 169. Затем мы перебираем их и получаем все необходимые данные. На этот раз, вместо того, чтобы делать вызов insert
на каждой итерации цикла, мы сохраняем его в массиве. Затем мы вызываем метод insert
один раз и предоставляем массив, в котором мы сохранили данные покемонов.
$ids = array(452, 454, 169); $pokemon_caught = array(); foreach($ids as $id){ $response = $client->get('http://pokeapi.co/api/v1/pokemon/' . $id); $data = $response->json(); $name = $data['name']; $pokemon_caught[] = array( 'name' => $name, 'trainer_id' => $trainer_id, 'type_id' => $type_id, 'level' => mt_rand(1, 100) ); } $db->insert('pokemon', $pokemon_caught);
Обновление данных
Теперь у Рокси есть еще 3 покемона, но ее данные тренера еще не были обновлены. Нам нужно добавить 3 к ее текущему pokemon_count
. Для этого мы вызываем метод update
. Это принимает следующие аргументы:
- название стола
- данные
- где условие
Вот как мы делаем обновление:
$db->update( 'trainers', array('pokemon_count[+]' => 3), array('id' => $trainer_id) );
Видишь, что мы там сделали? Medoo поставляется с хорошей утилитой, в которой вы можете выполнять следующие математические операции над конкретным полем, которое вы обновляете:
-
+
— добавить конкретное значение к текущему значению. -
-
вычесть конкретное значение в текущее значение. -
*
— умножить конкретное значение на текущее значение. -
/
— разделить конкретное значение на текущее значение.
Удаление данных
Теперь мы хотим выпустить Drapion, потому что это отстой. Мы можем удалить его из таблицы pokemon
используя метод delete
. Это принимает имя таблицы в качестве первого аргумента и массив условий в качестве второго аргумента.
$db->delete('pokemon', array('name' => 'Drapion'));
Мы также можем удалить на основе 2 условий. Здесь мы хотим удалить всех покемонов, которые имеют тип «Normal» и находятся ниже уровня 60:
$type_id = $db->get('types', 'id', array('name' => 'Normal')); $db->delete( 'pokemon', array('AND' => array('level[<]' => 60, 'type_id' => $type_id)) );
Обратите внимание, что вызов delete не возвращает идентификатор удаленной строки. Это означает, что вам нужно найти способ получить его самостоятельно, если вы хотите использовать этот идентификатор для чего-то.
Агрегатные функции
Medoo также поставляется с некоторыми агрегатными функциями.
Например, мы хотим получить общее количество тренеров из таблицы trainers
. Для этого мы используем метод count
:
$total_trainers = $db->count('trainers');
Если мы хотим узнать тренера с наибольшим / наименьшим количеством покемонов, для этого также есть функция. Чтобы получить максимум, мы используем метод max
. Это займет имя таблицы и поле, которое вы хотите использовать:
$db->max('trainers', 'pokemon_count');
Для получения наименьшего количества мы используем метод min
:
$db->min('trainers', 'pokemon_count');
Мы также можем использовать метод avg
, если мы хотим узнать среднее количество покемонов, которое есть у каждого тренера в таблице trainers
:
$db->avg('trainers', 'pokemon_count');
Мы также можем получить общее количество покемонов, которое есть у всех тренеров, используя метод sum
:
$db->sum('trainers', 'pokemon_count');
Обратите внимание, что с помощью метода count
, min
, max
и sum
мы также можем указать некоторые дополнительные условия. Мы поставляем их в качестве последнего аргумента. Например, мы хотим знать только максимальное количество покемонов, которое имеет дрессировщик в регионе «Hoenn»:
$db->max('trainers', 'pokemon_count', array('region' => 'Hoenn'));
Отладка
Medoo также предоставляет утилиты для проверки ошибок и отладки. Обратите внимание, что medoo не возвращает никаких ошибок, когда вы пытаетесь сделать что-то, что не может привести к успешному результату, поэтому иногда вам приходится явно проверять это.
Например, мы сделали опечатку для таблицы trainers
. Вместо trainers
мы набрали:
$db->update( 'trainerw', array('pokemon_count[-]' => 1), array('id' => 99) );
Выполнение кода выше не заставит меду пожаловаться на это. Просто молча терпит неудачу. Чтобы проверить наличие ошибки, нам нужно вызвать метод error
сразу после кода, вызывающего error
:
$db->error();
Это возвращает массив, который выглядит следующим образом:
Array ( [0] => 42S02 [1] => 1146 [2] => Table 'pokemon.trainerw' doesn't exist )
Первый элемент — это код ошибки, возвращаемый MySQL. Вы можете увидеть список кодов ошибок на этой странице . Второй пункт — это состояние SQL. И третий пункт — удобочитаемое описание ошибки.
Если ошибка является ошибкой с аргументами, переданными определенному методу, который вы используете, medoo возвращает ошибку, поэтому вам не нужно проверять их, если это произойдет.
Если при вызове метода error
не было ошибок, это может быть ошибкой. Вместо этого может быть так, что запрос, сгенерированный medoo, не делает то, что вы хотите. В этих случаях вам нужно использовать метод last_query()
. Как следует из названия, вызов этого метода вернет последний запрос, выполненный medoo. Давайте посмотрим на некоторые примеры:
- Уменьшите
pokemon_count
на 1 для тренера с идентификатором 1:
$db->update( 'trainers', array('pokemon_count[-]' => 1), array('id' => 1) ); echo $db->last_query(); /* returns: UPDATE "trainers" SET "pokemon_count" = "pokemon_count" - 1 WHERE "id" = 1 */
- Выберите покемона с именем ‘virizion’:
$db->get( 'pokemon', array('name', 'level', 'type'), array('name' => 'virizion') ); echo $db->last_query(); /* returns: SELECT "name","level","type" FROM "pokemon" WHERE "name" = 'virizion' LIMIT 1 */
- Выберите всех покемонов с их соответствующим типом:
$db->select( 'pokemon', array('[><]types' => array('type_id' => 'id')), array('pokemon.name', 'types.name(type)') ); echo $db->last_query(); /* returns: SELECT "pokemon"."name","types"."name" AS "type" FROM "pokemon" INNER JOIN "types" ON "pokemon"."type_id" = "types"."id" */
Вывод
Это оно! В этом уроке мы узнали о Medoo, легкой и простой в использовании библиотеке абстракции базы данных для PHP. Мы узнали, как выбирать, обновлять, удалять и вставлять данные в базу данных. Мы также изучили некоторые методы отладки, которые мы можем использовать, если что-то пойдет не так. Если вы хотите узнать больше, ознакомьтесь с официальной документацией meedo .
Вы используете Medoo для чего-либо? Что вы думаете о проекте? Дайте нам знать!