Введение в GraphQL и PHP
GraphQL — это язык запросов для API, разработанный Facebook, который позволяет клиентам запрашивать только те данные, которые им действительно нужны. В отличие от REST, где каждая конечная точка возвращает фиксированную структуру данных, GraphQL даёт гибкость и эффективность. Если вы работаете с PHP и хотите внедрить современный API, этот туториал для вас.
В этом руководстве мы создадим простой, но функциональный GraphQL API с использованием библиотеки webonyx/graphql-php. Вы узнаете, как установить зависимости, определить схему, типы, резолверы и обработать запросы. Примеры кода помогут вам быстро начать.
1. Подготовка окружения и установка зависимостей
Для начала убедитесь, что у вас установлен PHP версии 7.4 или выше и Composer — менеджер зависимостей для PHP. Если Composer ещё не установлен, скачайте его с официального сайта.
Создайте новый проект и установите библиотеку webonyx/graphql-php:
mkdir graphql-php-tutorial
cd graphql-php-tutorial
composer init --name="myapp/graphql-demo" --type="project" --no-interaction
composer require webonyx/graphql-php
После установки в папке проекта появится директория vendor и файл composer.json. Также создайте файл index.php — в нём будет весь наш код.
Дополнительно, для удобства работы с HTTP-запросами, установите laminas/laminas-diactoros (популярная PSR-7 реализация) и laminas/laminas-httphandlerrunner:
composer require laminas/laminas-diactoros
composer require laminas/laminas-httphandlerrunner
2. Определение схемы и типов GraphQL
Сердце любого GraphQL API — это схема (schema). Она описывает, какие данные доступны и как их можно запрашивать. В нашем примере мы создадим простую модель "Пользователь" (User) и "Пост" (Post).
Сначала определим типы с помощью класса GraphQL\Type\Definition\ObjectType. Создадим файл schema.php (или добавьте код прямо в index.php).
<?php
require_once __DIR__ . '/vendor/autoload.php';
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use GraphQL\GraphQL;
// Определяем тип User
$userType = new ObjectType([
'name' => 'User',
'description' => 'Представляет пользователя системы',
'fields' => [
'id' => Type::int(),
'name' => Type::string(),
'email' => Type::string(),
]
]);
// Определяем тип Post
$postType = new ObjectType([
'name' => 'Post',
'description' => 'Пост пользователя',
'fields' => [
'id' => Type::int(),
'title' => Type::string(),
'content' => Type::string(),
'author' => $userType, // Связь с пользователем
]
]);
Обратите внимание: мы используем Type::int() и Type::string() — это встроенные скалярные типы GraphQL. Поле author ссылается на другой объектный тип, что позволяет строить вложенные запросы.
3. Создание резолверов и фиктивных данных
Резолверы (resolvers) — это функции, которые возвращают данные для каждого поля. В простейшем случае мы можем использовать фиктивные данные (массивы).
Добавим резолверы для корневого типа Query. Именно через него клиенты будут получать данные.
// Фиктивные данные
$users = [
1 => ['id' => 1, 'name' => 'Иван Петров', 'email' => 'ivan@example.com'],
2 => ['id' => 2, 'name' => 'Мария Смирнова', 'email' => 'maria@example.com'],
];
$posts = [
1 => ['id' => 1, 'title' => 'Первая запись', 'content' => 'Текст первой записи', 'author_id' => 1],
2 => ['id' => 2, 'title' => 'Вторая запись', 'content' => 'Текст второй записи', 'author_id' => 2],
];
// Определяем корневой тип Query
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'user' => [
'type' => $userType,
'args' => [
'id' => Type::nonNull(Type::int()),
],
'resolve' => function ($root, $args) use ($users) {
return $users[$args['id']] ?? null;
}
],
'users' => [
'type' => Type::listOf($userType),
'resolve' => function () use ($users) {
return array_values($users);
}
],
'post' => [
'type' => $postType,
'args' => [
'id' => Type::nonNull(Type::int()),
],
'resolve' => function ($root, $args) use ($posts) {
return $posts[$args['id']] ?? null;
}
],
]
]);
Теперь нужно связать поле author в типе Post с данными. Добавим резолвер прямо в определение поля:
$postType = new ObjectType([
'name' => 'Post',
'fields' => [
'id' => Type::int(),
'title' => Type::string(),
'content' => Type::string(),
'author' => [
'type' => $userType,
'resolve' => function ($post) use ($users) {
return $users[$post['author_id']] ?? null;
}
],
]
]);
Обратите внимание: резолвер поля author получает родительский объект (пост) в качестве первого аргумента $post.
4. Обработка запроса и запуск API
Теперь соберём всё вместе: создадим схему, обработаем входящий HTTP-