九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、前回の「axios不要!fetch()でAjax通信する方法」では、私が最近まで知らなかった新しい技術にチャレンジするというコンセプトでお届けしました。
そして、今回もその流れで前からやってみたかった内容をお届けしたいと思います。
それは・・・・・・
GraphQL
です。
GraphQL
とは、簡単にいうと今までのSQL文とは違った形式でDB操作をする技術です。
例えば、これまでのSQL文は次のようなものが一般的でした。
SELECT id, name FROM users
これが、GraphQL
では、次のようなデータを送信することになります。
{ users { id name } }
(おそらくグラフィカルなんでこの名前がついたのかな、と勝手に思っています😊)
また、複数テーブルからデータ取得ができるので、Rest API
では1個ずつ処理していたものが一気に完了します。
{ users { // 👈 usersデータ id name } posts { // 👈 postsデータ id title } }
なお、DB操作したいテーブルだけをschema
と呼ばれるファイルで設定した場合だけ有効になるので、セキュリティ的にも安心です😊✨
・・・ということで今回はLaravel
でGraphQL
を使う方法をご紹介します。
ぜひ楽しみながらやってみましょう❗
「読み方は、(ぐらふきゅーえる)です😊」
開発環境: Laravel 7.x
目次
やりたいこと
今回は各ユーザーが自由に書き込みができるposts
というテーブルを作り、GraphQL
で本人が投稿したデータだけを取得してリスト表示します。
前提として
Laravel
にログイン機能がインストールされ、さらにユーザーデータが用意されていることが前提です。
詳しくは「Laravel6.x以降でログイン機能をインストールする方法」をご覧ください。
パッケージをインストールする
Laravel
でGraphQL
を使えるようにするパッケージをインストールします。
composer require nuwave/lighthouse
準備する
パッケージからLaravel
側にファイルをコピーします。
構成ファイル
まずは構成ファイルです。
以下のコマンドを実行してください。
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
これで、ファイルが作成されました。
/graphql/schema.graphql
そして、このファイルの中で「どんな操作を受け付けるか」、また「どんなデータを返すか」を設定することになります。
では、今回posts
データを取得するための設定をしておきましょう。
/graphql/schema.graphql
"GraphQLのアクセス設定" type Query { posts(page: Int): [Post!]! @paginate(defaultCount: 3) } "postsテーブルの取得内容" type Post { id: ID! user_id: Int! title: String! content: String! created_at: String! updated_at: String! }
この内容を大まかに説明すると、posts
にアクセスすると、posts
のデータを1ページ3件ごとの配列で返すという意味になります。
設定ファイル
次に設定ファイルです。
以下のコマンドを実行してください。
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
これで設定ファイルも作成されました。
/config/lighthouse.php
今回はログイン・ユーザーの情報が必要になるので、ミドルウェアにweb
を追加しておきましょう。
/config/lighthouse.php
'middleware' => [ 'web', // 👈 追加 // 省略 ],
モデル&マイグレーションをつくる
posts
テーブルが使えるように、以下のコマンドでモデルとマイグレーションを作成します。
php artisan make:model Post -m
これで、2つのファイルが作成されました。
それぞれ中身に必要な情報を追加します。
/app/Post.php
<?php namespace App; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Post extends Model { // グローバル・スコープ protected static function booted() { // 👈 追加 static::addGlobalScope('only_user', function (Builder $builder) { $user_id = -1; if(auth()->check()) { $user_id = auth()->user()->id; } $builder->where('user_id', $user_id); }); } }
この中のグローバル・スコープは、ログイン中のユーザーが投稿したデータだけを取得するために追加しています。(つまり、他人のデータを見ることはできなくなります)
続いてマイグレーションです。
今回はテストですのでシンプルなテーブルにします。
/home/sukohi/php-/database/migrations/****_**_**_******_create_posts_table.php
// 省略 public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('user_id'); $table->string('title'); $table->text('content'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); } // 省略
マイグレーションが完了したらテストデータです。
次のコマンドを実行してSeeder
をつくりましょう。
php artisan make:seed PostsTableSeeder
そして、中身を以下のようにします。
/database/seeds/PostsTableSeeder.php
// 省略 public function run() { $user_ids = \App\User::pluck('id'); for($i = 0 ; $i < 100 ; $i++) { $post = new \App\Post(); $post->user_id = $user_ids->random(); $post->title = 'テストタイトル - '. $i; $post->content = "テストコンテンツ\nテストコンテンツ\nテストコンテンツ\n"; $post->save(); } } // 省略
では、以下のコマンドでマイグレーションを実行します。
php artisan migrate:fresh --seed
これで、posts
テーブルは次のようになります。
ルートをつくる
今回はテストですので、簡略して書きますが実際にはPostController
などを作ってください。
/routes/web.php
Route::get('post', function(){ return view('post.index'); });
ビューをつくる
では、実際にGraphQL
でデータ取得するビューをつくりましょう。
<html> <head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> </head> <body> <div id="app"> <!-- リスト表示 --> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>タイトル</th> <th>内容</th> <th>作成日時</th> </tr> </thead> <tbody> <tr v-for="post in posts"> <td v-text="post.id"></td> <td v-text="post.title"></td> <td style="white-space: pre-wrap" v-text="post.content"></td> <td v-text="post.created_at"></td> </tr> </tbody> </table> <!-- ページ移動 --> <div class="p-3"> <ul class="pagination pg-blue"> <li class="page-item" v-for="page in paginatorInfo.lastPage"> <a href="#" class="page-link" v-text="page" @click="movePage(page)"></a> </li> </ul> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script> <script> new Vue({ el: '#app', data: { posts: [], paginatorInfo: {}, page: 1 }, methods: { getPosts() { // 👈 データ取得 ・・・ ① const params = { query: `{ posts(page: ${this.page}) { data { id title content created_at } paginatorInfo { currentPage lastPage } } }` }; axios.post('/graphql', params) .then(response => { const posts = response.data.data.posts; this.posts = posts.data; this.paginatorInfo = posts.paginatorInfo; }); }, movePage(page) { // 👈 ページ移動 ・・・ ② this.page = page; this.getPosts(); } }, mounted() { this.getPosts(); } }); </script> </body> </html>
この中でやっていることは、次のとおりです。
① データ取得
ここでAjax
+ GraphQL
でposts
データを取得しています。
この中で、実際に送信される内容は例えば、次のようなものになります。
{ posts(page: 1) { // 👈 ページは可変 data { id title content created_at } paginatorInfo { currentPage lastPage } } }
また、取得されるデータのサンプルはこのようになります。
"posts": { "data": [ { "id": "2", "title": "テストタイトル - 1", "content": "(長いので省略)", "created_at": "2020-04-04 09:56:12" }, // たくさんあるので省略 ], "paginatorInfo": { "currentPage": 1, "lastPage": 4 } }
また、今回ページ移動の部分は以下2つデータを取得しています。
- currentPage: 現在のページ
- lastPage: 最後のページ
実際にはこのようなデータが取得できます。
paginatorInfo: { currentPage: 1, lastPage: 4 }
② ページ移動
ページ移動は、page
変数の中身を変更しgetPosts()
を実行するだけで完了できます。
テストしてみる
では実際にテストしてみましょう!
まずは「http://******/post」にアクセスしたところです。
データの一覧とページ移動リンクが表示されています。(しかも表示されているのはログインユーザーが投稿したものだけです)
では、2ページ目に移動してみましょう。
はい!
表示内容が入れ替わりました。
成功です😊✨
おまけ:テストに便利なツール
GraphQL
をテストで試すことができる「laravel-graphql-playground」という便利なツールが公開されています。
これは、GraphQL
を書いて送信するだけで「どんな結果になるか❓」がわかるスグレモノです。
インストールは以下のコマンド一発でOKです。
composer require mll-lab/laravel-graphql-playground
なお、419エラーが出る場合は、ミドルウェアでcsrf
を無効にする必要があります。
/app/Http/Middleware/VerifyCsrfToken.php
<?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { protected $except = [ '/graphql' // 👈 追加 ]; }
おわりに
ということで、今回はLaravel
でGraphQL
を使ってみました。
ちなみに、GraphQL
を初めて使ったわけですが、「特にデータ取得に向いている❗」と感じました。
冒頭でも書いたとおり、これまでは1つずつテーブルごとに取得していたのものを一気に取得できるのはとても魅力的です。(実際問題これを解決するために独自パッケージをつくったりもしてました😅)
逆に「うーん・・・💦」となったのは、バリデーションの部分です。もちろんGraphQL
にもバリデーション機能はあるのですが、正直なところ、バリデーションはLaravel
のものをそのまま使うほうが開発効率は高いと感じました。
また、少し複雑なことをしようとするとコードの可読性が一気に悪くなるので、個人的には、以下のような固定されたデータを取得する場合なら使いたいかな、という印象です。
- 都道府県データ
- 事業所データ
- はい/いいえ/未定
などなど。
ただ、今後もGraphQL
には注目していきたいと思います😊✨
ぜひ皆さんも試してみてくださいね。
ではでは〜!
「なぜか、”ぐらふきゅーえる”って、
何回も言いたくなりませんか❓😁」