Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
GraphQL に 入門 してみた 2024/03/27 第162回 PHP勉強会@東京
Slide 2
Slide 2 text
⾃⼰紹介 ■ ちひろ ■ Twitter: @chiroruxxxx ■ 会社: 株式会社モリサワ
Slide 3
Slide 3 text
話すこと/話さないこと ■ 話すこと – 自分がGraphQL in PHP の勉強をして学んだこと – サンプルコードは chiroruxx/lighthouse-sample に ■ 話さないこと – フロントエンド寄りの話 – 取得以外の処理
Slide 4
Slide 4 text
GraphQL
Slide 5
Slide 5 text
GraphQLの特徴 ■ クライアントが欲しい情報をクエリで送り、サーバがその情報を返す – RESTful API と比べてリクエスト回数を減らせる – 使用しないデータを取得・生成する必要がない ■ HTTPメソッドは基本的にPOSTのみ使用 ■ HTTPステータスコードは基本的に200のみ使用 ■ エンドポイントは基本的に1つのみ使用
Slide 6
Slide 6 text
SELECT id, name FROM users WHERE id = 1; エンジニア SQL データベース query { user(id: 1){ id , name, } } ブラウザ GraphQL Webサーバ
Slide 7
Slide 7 text
リクエストとレスポンス query { user(id: 1){ id, name, } } { "data" : { "user" : { "id" : "1", "name" : "Taro” } } }
Slide 8
Slide 8 text
リクエストとレスポンス query { user(id: 1) { id, name, post(id: 2) { id, title, } } } { "data" : { "user" : { "id" : "1", "name" : "Taro", "post" : { "id" : "2", "title" : "PHP勉強会に参加したよ" } } } }
Slide 9
Slide 9 text
バックエンドの関心事 ■ リクエストボディからクエリを解釈する ■ 必要なデータを取得・生成してレスポンスを返す ライブラリを使う エンジニアが作る
Slide 10
Slide 10 text
lighthouse ■ Laravelのプラグインのひとつ ■ Eloquentと密結合で使うことでほぼコードを書かなくて済む – 今回は理解のためにあえて使用しない
Slide 11
Slide 11 text
今回考える題材 ■ ブログをつくる – ユーザが複数の投稿を持つ – 投稿が複数のコメントを持つ ■ 一度に全部つくらずにインクリメンタルにつくる ■ lighthouseの設定方法は省略
Slide 12
Slide 12 text
ユーザを取得する
Slide 13
Slide 13 text
クエリ query { user(id: 1) { id, name, } }
Slide 14
Slide 14 text
メインのコード ■ App¥GraphQL¥Queries¥User を作成する /** @param array{"id": string} $args */ public function __invoke(null $_, array $args): GraphQLUser { $id = (int)$args['id'] ?? 0; return $this->service->findUser($id); } クエリの引数 (id: 1) DBからデータを取って インスタンスを返す
Slide 15
Slide 15 text
GraphQLUser final readonly class User { public function __construct( public int $id, public string $name, public string $email, ) { } }
Slide 16
Slide 16 text
かんたん!!
Slide 17
Slide 17 text
ユーザの投稿を取得する
Slide 18
Slide 18 text
クエリ query { user(id: 1) { id, name, posts { id, title, content, } } } posts { id, title, content, }
Slide 19
Slide 19 text
元のコード /** @param array{"id": string} $args */ public function __invoke(null $_, array $args): GraphQLUser { $id = (int)$args['id'] ?? 0; return $this->service->findUser($id); } postもJoinして 返せばよい・・︖
Slide 20
Slide 20 text
DBからの取り方 query { user(id: 1) { id, name, } } query { user(id: 1) { id, name, posts { id, title, content, } } } postsテーブルから 取る必要がない postsテーブルからも 取る必要がある クエリによってアクセスする テーブルが変わる Fields の機能を使う
Slide 21
Slide 21 text
Fields ■ 返り値のインスンタンスで追加の属性を取得するロジックを設定できる ■ そのデータが必要な時には呼ばれ、必要ない時には呼ばれない ■ App¥GraphQL¥Types¥User¥Posts クラスを作成してそこに書く
Slide 22
Slide 22 text
Fields public function __invoke(User $user): Collection { return $this->userService->getUserPosts($user); } 最初に書いた処理で 取得したユーザ Where(’user_id’, $user->id) でデータを取ってくる
Slide 23
Slide 23 text
ユーザの投稿のコメントを取得する
Slide 24
Slide 24 text
クエリ query { user(id: 1) { id, name, posts { id, title, content, comments { id, title, } } } } comments { id, title, }
Slide 25
Slide 25 text
メインのロジック ■ 先ほどと同じように Fields を使えば良い? ■ App¥GraphQL¥Types¥Post¥Comments public function __invoke(Post $post): Collection { return $this->service->getComments($post); }
Slide 26
Slide 26 text
ログを見てみると? データの数だけ SQLが発⾏されている (N+1問題)
Slide 27
Slide 27 text
ロジックの流れ 1. ユーザを取得する 2. ユーザの投稿を一括で取得する(投稿A, 投稿B, 投稿C) 3. 投稿Aのコメントを一括で取得する 4. 投稿Bのコメントを一括で取得する 5. 投稿Cのコメントを一括で取得する 投稿の数だけ実⾏される ■ コメントの取得を遅延させて一括で取る必要がある – BatchLoaderを使う
Slide 28
Slide 28 text
元のコード public function __invoke(Post $post): Collection { return $this->service->getComments($post); }
Slide 29
Slide 29 text
BatchLoader
Slide 30
Slide 30 text
BatchLoader public function load(Post $post): Deferred { $this->posts->put($post->id, $post); return new Deferred(function () use ($post): Collection { if (!$this->hasResolved) { $this->resolve(); } return $this->results[$post->id]; }); } WhereIn(’post_id’, $this->posts->pluck(‘id’)) で⼀括取得する
Slide 31
Slide 31 text
ログ N+1問題が解消された︕
Slide 32
Slide 32 text
まとめ
Slide 33
Slide 33 text
まとめ ■ GraphQLはクライアントが欲しい情報をクエリで送り、サーバがその情報を返す ■ PHPでGraphQLを使用するにはlighthouseが便利 ■ クエリに応じて必要な情報だけを取得する ■ ただし、N+1問題が発生しやすい ■ BatchLoaderを使うことで解消できる