Laravel 13 GraphQL 集成完全指南
GraphQL 是一种用于 API 的查询语言,提供了比 REST 更灵活的数据获取方式。本文将深入探讨如何在 Laravel 13 中集成和使用 GraphQL。
GraphQL 基础
为什么选择 GraphQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| GET /api/users/1 GET /api/users/1/posts GET /api/users/1/comments
query { user(id: 1) { name email posts { title comments { content } } } }
|
安装 Lighthouse
安装依赖
1 2 3
| composer require nuwave/lighthouse php artisan vendor:publish --tag=lighthouse-schema php artisan vendor:publish --tag=lighthouse-config
|
安装 GraphQL Playground
1
| composer require mll-lab/laravel-graphql-playground
|
配置路由
1 2 3 4 5 6 7 8 9 10
| return [ 'route' => [ 'prefix' => 'graphql', 'middleware' => [ \Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class, \App\Http\Middleware\Authenticate::class, ], ], ];
|
Schema 定义
基础 Schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| type Query { users: [User!]! @all user(id: ID @eq): User @find posts: [Post!]! @all post(id: ID @eq): Post @find }
type User { id: ID! name: String! email: String! posts: [Post!]! @hasMany created_at: DateTime! updated_at: DateTime! }
type Post { id: ID! title: String! content: String! author: User! @belongsTo comments: [Comment!]! @hasMany created_at: DateTime! }
type Comment { id: ID! content: String! user: User! @belongsTo post: Post! @belongsTo created_at: DateTime! }
|
分页查询
1 2 3 4 5 6 7 8 9 10 11
| type Query { users( search: String @where(operator: "like") orderBy: _ @orderBy(columns: ["name", "created_at"]) ): [User!]! @paginate(defaultCount: 15) posts( published: Boolean @eq category_id: ID @eq ): [Post!]! @paginate(defaultCount: 10) }
|
类型定义
标量类型
1 2 3 4
| scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime") scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") scalar JSON @scalar(class: "MLL\\GraphQLScalars\\JSON") scalar Email @scalar(class: "MLL\\GraphQLScalars\\Email")
|
枚举类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| enum UserRole { ADMIN @enum(value: "admin") EDITOR @enum(value: "editor") USER @enum(value: "user") }
enum PostStatus { DRAFT @enum(value: "draft") PUBLISHED @enum(value: "published") ARCHIVED @enum(value: "archived") }
type User { id: ID! name: String! role: UserRole! }
type Post { id: ID! title: String! status: PostStatus! }
|
接口类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface Node { id: ID! created_at: DateTime! }
type User implements Node { id: ID! name: String! email: String! created_at: DateTime! }
type Post implements Node { id: ID! title: String! content: String! created_at: DateTime! }
type Query { node(id: ID!): Node @node }
|
联合类型
1 2 3 4 5
| union SearchResult = User | Post
type Query { search(query: String!): [SearchResult!]! }
|
查询操作
基础查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| query { users { id name email } }
query { user(id: 1) { name posts { title content } } }
|
带参数查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| query GetUsers($search: String, $orderBy: [OrderByClause!]) { users(search: $search, orderBy: $orderBy) { id name email } }
{ "search": "%john%", "orderBy": [ {"field": "name", "order": "ASC"} ] }
|
分页查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| query { users(first: 10, page: 1) { data { id name email } paginatorInfo { currentPage lastPage total perPage } } }
|
嵌套查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| query { user(id: 1) { name posts(first: 5) { data { title comments(first: 3) { data { content user { name } } } } } } }
|
变更操作
定义 Mutation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| type Mutation { createUser(input: CreateUserInput! @spread): User! @create updateUser(id: ID!, input: UpdateUserInput! @spread): User @update deleteUser(id: ID!): User @delete createPost(input: CreatePostInput! @spread): Post! @create updatePost(id: ID!, input: UpdatePostInput! @spread): Post @update deletePost(id: ID!): Post @delete }
input CreateUserInput { name: String! email: String! password: String! @hash }
input UpdateUserInput { name: String email: String password: String @hash }
input CreatePostInput { title: String! content: String! user_id: ID! status: PostStatus }
|
执行 Mutation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } }
{ "input": { "name": "John Doe", "email": "john@example.com", "password": "secret123" } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| mutation UpdateUser($id: ID!, $input: UpdateUserInput!) { updateUser(id: $id, input: $input) { id name email } }
{ "id": "1", "input": { "name": "Jane Doe" } }
|
自定义解析器
创建解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
namespace App\GraphQL\Queries;
use App\Models\User; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
class UserQuery { public function search($root, array $args, GraphQLContext $context) { return User::query() ->where('name', 'like', "%{$args['query']}%") ->orWhere('email', 'like', "%{$args['query']}%") ->get(); }
public function profile($root, array $args, GraphQLContext $context) { return $context->user(); } }
|
注册解析器
1 2 3 4 5 6 7
| type Query { search(query: String!): [User!]! @field(resolver: "App\\GraphQL\\Queries\\UserQuery@search") profile: User! @field(resolver: "App\\GraphQL\\Queries\\UserQuery@profile") }
|
Mutation 解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?php
namespace App\GraphQL\Mutations;
use App\Models\User; use Illuminate\Support\Facades\Hash; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
class UserMutation { public function register($root, array $args, GraphQLContext $context) { $user = User::create([ 'name' => $args['name'], 'email' => $args['email'], 'password' => Hash::make($args['password']), ]);
$token = $user->createToken('auth-token')->plainTextToken;
return [ 'user' => $user, 'token' => $token, ]; }
public function updateProfile($root, array $args, GraphQLContext $context) { $user = $context->user(); $user->update($args['input']); return $user; } }
|
认证与授权
认证中间件
1 2 3 4 5 6 7 8 9 10
| type Query { me: User @auth profile: User @guard }
type Mutation { updateProfile(input: UpdateProfileInput!): User! @guard @field(resolver: "App\\GraphQL\\Mutations\\UserMutation@updateProfile") }
|
授权指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type Query { users: [User!]! @all @can(ability: "viewAny", model: "App\\Models\\User") user(id: ID!): User @find @can(ability: "view", find: "id") }
type Mutation { updateUser(id: ID!, input: UpdateUserInput!): User @update @can(ability: "update", find: "id") deleteUser(id: ID!): User @delete @can(ability: "delete", find: "id") }
|
自定义授权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php
namespace App\GraphQL\Directives;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective; use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
class CanAccessDirective extends BaseDirective implements FieldMiddleware { public static function definition(): string { return <<<'GRAPHQL' directive @canAccess( role: String! ) on FIELD_DEFINITION GRAPHQL; }
public function handleField($field, $fieldArgs, $context, ResolveInfo $resolveInfo, Closure $next) { $user = $context->user(); $requiredRole = $this->directiveArgValue('role');
if (!$user || $user->role !== $requiredRole) { throw new \Exception('Unauthorized access'); }
return $next($field, $fieldArgs, $context, $resolveInfo); } }
|
复杂度控制
配置复杂度
1 2 3 4 5 6 7
| return [ 'security' => [ 'max_query_complexity' => 1000, 'max_query_depth' => 10, ], ];
|
指令控制
1 2 3 4 5 6 7 8
| type Query { users: [User!]! @all @complexity(value: 100) posts: [Post!]! @all @complexity(value: 50) }
type User { posts: [Post!]! @hasMany @complexity(value: 10) }
|
订阅功能
定义订阅
1 2 3 4 5
| type Subscription { postCreated: Post! commentAdded(postId: ID!): Comment! userUpdated(id: ID!): User! }
|
实现订阅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php
namespace App\GraphQL\Subscriptions;
use App\Models\Post; use Nuwave\Lighthouse\Subscriptions\Subscriber; use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
class PostCreated extends GraphQLSubscription { public function authorize(Subscriber $subscriber, $root): bool { return true; }
public function filter(Subscriber $subscriber, $root): bool { return true; }
public function resolve($root, array $args, $context) { return $root; } }
|
触发订阅
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Nuwave\Lighthouse\Subscriptions\Facades\Subscription;
class PostController extends Controller { public function store(Request $request) { $post = Post::create($request->validated()); Subscription::broadcast('postCreated', $post); return $post; } }
|
测试 GraphQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <?php
namespace Tests\Feature\GraphQL;
use Tests\TestCase; use App\Models\User;
class UserGraphQLTest extends TestCase { public function test_query_users() { User::factory()->count(3)->create();
$response = $this->graphQL( ' query { users { id name email } } ');
$response->assertJsonStructure([ 'data' => [ 'users' => [ '*' => ['id', 'name', 'email'] ] ] ]); }
public function test_mutation_create_user() { $response = $this->graphQL( ' mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } } ', [ 'input' => [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'secret123', ] ]);
$response->assertJsonStructure([ 'data' => [ 'createUser' => ['id', 'name', 'email'] ] ]); }
public function test_authenticated_query() { $user = User::factory()->create();
$response = $this->actingAs($user)->graphQL( ' query { me { id name } } ');
$response->assertJson([ 'data' => [ 'me' => [ 'id' => (string) $user->id, 'name' => $user->name, ] ] ]); } }
|
总结
Laravel 13 的 GraphQL 集成提供了:
- 灵活的 Schema 定义
- 强大的查询和变更操作
- 完整的认证授权支持
- 实时订阅功能
- 复杂度控制
- 完善的测试支持
GraphQL 为 API 开发提供了更灵活的选择,特别适合复杂的数据查询场景。