Введение: Почему GraphQL и PHP — отличная пара
В мире веб-разработки REST API долгое время был стандартом де-факто. Однако с ростом сложности приложений и появлением мобильных клиентов, недостатки REST стали очевидны: переизбыток или недостаток данных, множество эндпоинтов, сложная версионизация. GraphQL, разработанный Facebook, решает эти проблемы, позволяя клиенту запрашивать только те данные, которые ему нужны, и получать их за один запрос.
PHP, несмотря на свою «старую школу», остается мощным языком для создания серверной логики. Благодаря современным библиотекам, таким как webonyx/graphql-php, реализовать GraphQL API на PHP стало просто и эффективно. В этом туториале мы шаг за шагом создадим полноценный GraphQL сервер, который сможет обрабатывать запросы, мутации и работать с базой данных.
Мы будем использовать PHP 8.0+, Composer для управления зависимостями и встроенный сервер PHP для локального тестирования. К концу статьи вы сможете развернуть собственное API и интегрировать его с любым фронтендом.
1. Установка и настройка окружения
Прежде чем писать код, необходимо подготовить проект. Убедитесь, что у вас установлены PHP 8.0+ и Composer. Создайте новую папку для проекта и перейдите в нее:
mkdir graphql-php-tutorial
cd graphql-php-tutorial
Теперь установите основную библиотеку webonyx/graphql-php с помощью Composer:
composer require webonyx/graphql-php
Для работы с базой данных мы будем использовать PDO, который встроен в PHP. Дополнительно установим библиотеку для удобного парсинга запросов:
composer require graphql-php/graphql-php
Создайте файл index.php — это будет точка входа в наше приложение. Настройте автозагрузку Composer и базовую структуру:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
// Здесь будет наша логика
Также создайте файл schema.graphql для хранения схемы в текстовом формате (опционально, но удобно для больших проектов).
2. Определение схемы и типов данных
Сердце любого GraphQL API — это схема. Она описывает, какие данные доступны, и как к ним обращаться. В нашем примере мы создадим простую модель пользователя (User) и поста (Post).
Начнем с определения типов. В файле index.php добавьте следующие классы:
// Тип User
$userType = new ObjectType([
'name' => 'User',
'fields' => [
'id' => Type::int(),
'name' => Type::string(),
'email' => Type::string(),
'posts' => [
'type' => Type::listOf($postType),
'resolve' => function ($user) use ($db) {
// Функция-резолвер для загрузки постов пользователя
$stmt = $db->prepare('SELECT * FROM posts WHERE user_id = :id');
$stmt->execute([':id' => $user['id']]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
]
]
]);
// Тип Post
$postType = new ObjectType([
'name' => 'Post',
'fields' => [
'id' => Type::int(),
'title' => Type::string(),
'content' => Type::string(),
'author' => [
'type' => $userType,
'resolve' => function ($post) use ($db) {
$stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $post['user_id']]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
]
]
]);
Обратите внимание на резолверы — это функции, которые определяют, как получить данные для каждого поля. В примере выше мы используем PDO для запросов к базе данных.
Теперь создадим корневой тип Query, который будет точкой входа для чтения данных:
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'hello' => [
'type' => Type::string(),
'resolve' => function() {
return 'Hello, GraphQL!';
}
],
'users' => [
'type' => Type::listOf($userType),
'resolve' => function() use ($db) {
$stmt = $db->query('SELECT * FROM users');
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
],
'user' => [
'type' => $userType,
'args' => [
'id' => Type::nonNull(Type::int())
],
'resolve' => function($root, $args) use ($db) {
$stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $args['id']]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
]
]
]);
3. Создание мутаций (Mutation) для изменения данных
GraphQL поддерживает не только чтение, но и запись данных через мутации. Добавим возможность создавать нового пользователя и поста.
$mutationType = new ObjectType([
'name' => 'Mutation',
'fields' => [
'createUser' => [
'type' => $userType,
'args' => [
'name' => Type::nonNull(Type::string()),
'email' => Type::nonNull(Type::string())
],
'resolve' => function($root, $args) use ($db) {
$stmt = $db->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute([':name' => $args['name'], ':email' => $args['email']]);
$id = $db->lastInsertId();
$stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
],
'createPost' => [
'type' => $postType,
'args' => [
'title' => Type::nonNull(Type::string()),
'content' => Type::string(),
'user_id' => Type::nonNull(Type::int())
],
'resolve' => function($root, $args) use ($db) {
$stmt = $db->prepare('INSERT INTO posts (title, content, user_id) VALUES (:title, :content, :user_id)');
$stmt->execute([':title' => $args['title