Делаем GraphQL API на PHP и MySQL. Часть 1: Установка, схема и запросы

Делаем GraphQL API на PHP и MySQL. Часть 1: Установка, схема и запросы

В последнее время я все чаще и чаще слышу про GraphQL. И в интернете уже можно найти немало статей о том как сделать свой GraphQL сервер. Но почти во всех этих статьях в качестве бэкенда используется Node.js.

Я ничего не имею против Node.js и сам с удовольствием использую его, но все-таки большую часть проектов я делаю на PHP. К тому же хостинг с PHP и MySQL гораздо дешевле и доступнее чем хостинг с Node.js. Поэтому мне кажется не справедливым тот факт, что об использовании GraphQL на PHP в интернете практически нет ни слова.

В данной статье я хочу рассказать о том, как сделать свой GraphQL сервер на PHP с помощью библиотеки graphql-php и как с его помощью реализовать простое API для получения данных из MySQL. Я решил отказаться от использования какого-либо конкретного PHP фреймворка в данной статье, но после ее прочтения вам не составит особого труда применить эти знания в своем приложении. Тем более для некоторых фреймворков уже есть свои библиотеки основанные на graphql-php, которые облегчат вашу работу.

Подготовка

В данной статье я не буду делать фронтенд, поэтому для удобного тестирования запросов к GraphQL серверу рекомендую установить GraphiQL-расширение для браузера.

Для Chrome это могут быть:

В таблице «users» будем хранить список пользователей:

А в таблице «friendships» связи типа «многие-ко-многим», которые будут обозначать дружбу между пользователями:

Дамп базы данных, как и весь остальной код, можно взять из репозитория данной статьи на Github.

Hello, GraphQL!

Для начала необходимо установить graphql-php в наш проект. Можно сделать это с помощью composer:

Теперь, по традиции напишем «Hello, World».

Для этого в корне создадим файл graphql.php, который будет служить конечной точкой (endpoint) нашего GraphQL сервера.

В нем подключим автозагрузчик composer:

Чтобы заставить GraphQL выполнить запрос необходимо передать ему сам запрос и схему данных.

Для получения запроса напишем следующий код:

Чтобы создать схему сначала подключим GraphQL\Schema:

Конструктор схемы принимает массив, в котором должен быть указан корневой тип данных Query, который служит для чтения данных вашего API, поэтому сначала создадим этот тип.

В простейшем случае тип данных Query должен быть экземпляром класса ObjectType, а его поля должны быть простых типов (например int или string), поэтому подключим классы предоставляющие эти типы данных в GraphQL:

И создадим тип данных Query:

Как можно заметить тип данных обязательно должен содержать имя (name) и массив полей (fields), а также можно указать необязательное описание (description).

Поля типа данных также должны иметь обязательные свойства «name» и «type». Если свойство «name» не задано, то в качестве имени используется ключ поля (в данном случае «hello»). Также в нашем примере у поля «hello» заданы необязательные свойства «description» — описание и «resolve» — функция возвращающая результат. В этом случае функция «resolve» просто возвращает строку "Привет, GraphQL!", но в более сложной ситуации она может получать какую-либо информацию по API или из БД и обрабатывать ее.

Таким образом, мы создали корневой тип данных «Query», который содержит всего одно поле «hello», возвращающее простую строку текста. Давайте добавим его в схему данных:

А затем выполним запрос GraphQL для получения результата:

Остается только вывести результат в виде JSON и наше приложение готово:

Обернем код в блок try-catch, для обработки ошибок и в итоге код файла graphql.php будет выглядеть так:

Проверим работу GraphQL. Для этого запустим расширение для GraphiQL, установим endpoint (в моем случае это «localhost/graphql.php») и выполним запрос:

Вывод пользователей из БД

Теперь усложним задачу. Выведем список пользователей из базы данных MySQL.

Для этого нам понадобится создать еще один тип данных класса ObjectType. Чтобы не нагромождать код в graphql.php, вынесем все типы данных в отдельные файлы. А чтобы у нас была возможность использовать типы данных внутри самих себя, оформим их в виде классов. Например, чтобы в типе данных «user» можно было добавить поле «friends», которое будет являться массивом пользователей такого же типа «user».

Когда мы оформляем тип данных в виде класса, то не обязательно указывать у него свойство «name», потому что оно по умолчанию берется из названия класса (например у класса QueryType будет имя Query).

Теперь корневой тип данных Query, который был в graphql.php:

Будет находиться в отдельном файле QueryType.php и выглядеть так:

А чтобы в дальнейшем избежать бесконечной рекурсии при определении типов, в свойстве «fields» лучше всегда указывать не массив полей, а анонимную функцию, возвращающую массив полей:

При разработке проекта может появиться очень много типов данных, поэтому для них лучше создать отдельный реестр, который будет служить фабрикой для всех типов данных, в том числе и базовых, используемых в проекте. Давайте создадим папку App, а в ней файл, Types.php, который как раз и будет тем самым реестром для всех типов данных проекта.

Также в папке App создадим подпапку Type, в которой будем хранить все наши типы данных и перенесем в нее QueryType.php.

Теперь добавим пространство имен и заполним реестр Types.php необходимыми типами:

Пока в нашем реестре будет всего 2 типа данных: 1 простой (string) и 1 составной (query).

Теперь во всех остальных файлах вместо:

Подключим наш реестр типов:

И заменим все ранее указанные типы, на типы из реестра.

В QueryType.php вместо:

А схема в graphql.php теперь будет выглядеть так:

Чтобы получить пользователей из базы данных, необходимо обеспечить интерфейс доступа к ней. Получать данные из базы можно любым способом. В каждом фреймворке для этого есть свои инструменты. Для данной статьи я написал простейший интерфейс который может подключаться к MySQL базе данных и выполнять в ней запросы. Так как это не относится к GraphQL, то я не буду объяснять как реализованы методы в данном классе, а просто приведу его код:

В файле graphql.php добавим код для инициализации подключения к БД:

Теперь в папке Type создадим тип данных User, который будет отображать данные о пользователе. Код файла UserType.php будет таким:

Значение полей можно понять из их свойства «description». Свойства «id», «name», «email» и «countFriends» имеют простые типы, а свойство «friends» является списком друзей – таких же пользователей, поэтому имеет тип:

Необходимо также добавить в наш реестр пару базовых типов, которые мы раньше не использовали:

И только, что созданный нами тип User:

Возвращаемые значения (resolve) для свойств «friends» и «countFriends» берутся из базы данных. Анонимная функция в «resolve» первым аргументом получает значение текущего поля ($root), из которого можно узнать id пользователя для вставки его в запрос списка друзей.

В завершении изменим код QueryType.php так, чтобы в API были поля для получения информации о конкретном пользователе по его идентификатору (поле «user»), а также для получения списка всех пользователей (поле «allUsers»):

Тут чтобы узнать идентификатор пользователя, данные которого необходимо получить, у поля «user» мы используем свойство «args», в котором содержится массив аргументов. Массив «args» передается в анонимную функцию «resolve» вторым аргументом, используя который мы узнаем id целевого пользователя.

У аргументов могут быть свои свойства, но в этом случае я использую упрощенную форму записи массива аргументов, при которой ключи массива являются именами, а значения – типами аргументов:

Теперь можно запустить сервер GraphQL и проверить его работу таким запросом:

Или любым другим.

Заключение

На этом все. Читайте документацию. Задавайте вопросы в комментариях. Критикуйте.

📎📎📎📎📎📎📎📎📎📎