Lighthouse - GraphQL server for Laravel でget相当の機能を実装する

GraphQLのシンプルさはフロントエンドから見るととても魅力的です。
Reactアプリの開発も捗りそうなので、既存のRESTful APIと併設する形で試験実装してみました。
Apacheサーバーで、PHPを動かしている場合でもLighthouseという強力なGraphQLフレームワークがあるのでかなり楽に立ち上げることができました。

ですが、既存のデータベースを対応させようとするとかなり色々設定しなくてはならず、すんなりとは行かなかったので、つまづいた場所を中心に設定箇所をまとめてみました・

##試行環境
| ソフトウェア | バージョン |
|:--|:-:|
| MacOS | 10.10.5 | 
| PHP | 7.2.5 |
| Laravel | 7.0 [^1] |
| Lighthouse | 4.10 [^2] |

サーバの立ち上げはLaravelやLighthouseのセットアップは終わっている状態から始めます。
またデータベースへの接続設定も終わっているものとします。

##準備

プロジェクトの作成と、lighthouseなど各種必要なものをインストールします。

```
$ composer create-project --prefer-dist laravel/laravel graphql
$ cd graphql
$ composer require nuwave/lighthouse
$ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
$ composer require mll-lab/laravel-graphql-playground
$ php artisan serve
```
localサーバーが起動するので、ブラウザからlocalhost:8000にアクセスして開発中の画面を確認します。
また、//localhost:8000/graphql-playground でクエリの実行確認などができます。

##スキーマの定義

webアプリなどから問い合わせる際の形式を定義する所です。
ここで項目の名前やデータの型などを定義していきます。
定義する作業をやってみると、RESTfulでエンドポイントを考えている時と同じ様な感覚でした。

```graphql:schema.graphql
scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")

type Query {
    book(isbn: Int! @eq(key: CODE)): Book @find
}

type Book {
   isbn: Int
   title: String
   unit_price: Int
   released_on: Date
}
```

書籍データの問い合わせを行う形式を定義してみました。

Queryの{}内で問い合わせできるデータの形式や引数を定義しています。
ここではbookという名前でBook形式のデータをisbnという名前のInt型データで一致するものを問い合わせすることを定義しています。
Bookのデータ形式は別途 type Book{} で定義しています。

これで、//localhost:8000/graphql-playground の画面で実際に問い合わせができる様になったはずです。

以下の様なクエリーを投げられる様になっているはずです。

```graphql
query {
   book(isbn: 4150200858){
      title
      unit_price
      released_on
   }
}
```

エラーが出ることなく null データが返ってきたでしょうか？
nullデータが返ってきていれば、スキーマ定義は成功です。

##クエリの実装

次は、実際にデータを返す部分の実装になります。
その前に、実データが入っているデータベースのサンプルを提示しておきます。

table:HDL010

| CODE | NAM | TANKA | YMD |
|:-----|-----|------:|-----|
| 4253903592 | ブラック・ジャック | 10656 | 20030920 |
| 4150200858 | プリンセス・ブライド | 614 | 19860501 |
| 4488725007 | 銀河英雄伝説 | 12848 | 20171012 |

カラム名を isbn, title, unit_priceなどとして欲しいところが、どう言うわけかローマ字まじりの名前になっていてテーブル名も何の略語かわからないというサンプルデータです

まずはLaravelでモデルを作成します

```
php artisan make:model Models/Book
```

app/modelsの中に Book.php というファイルが作成されたはずです。
これで Laravel の Eloquent を継承した Book というモデルが作成されました。
が、参照するべきデータベースのテーブルがLaravelの命名規則に従っていないため、このままではデータを取得できません。
そこで、以下の様にカスタマイズします。

```php:app/Models/Book.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    protected $table = 'HDL010';
    protected $primaryKey = 'CODE';

    public function getIsbnAttribute(){ return $this->attributes['CODE']; }
    public function getTitleAttribute(){ return $this->attributes['NAM']; }
    public function getUnitPriceAttribute(){ return $this->attributes['TANKA']; }
    public function getReleasedOnAttribute(){ return date('Y-m-d', strtotime($this->attributes['YMD']); }
}
```

これで、nullデータしか返ってこなかった問い合わせがデータを伴って返ってくる様になったはずです。

## 最後に
LaravelのEloquentや、それを活用したLighthouseは非常に簡単にGraphQLを実装できるツールですが、既存のデータベースが命名規則に則っていなかったり、データベースの正規化が不十分な場合はすんなりといきません。
データ型によってはリレーションもうまく行かずにデータが正しく取得できない場合もありました。
それでも、Laravelには例外に対するカスタマイズが用意されていて、その設定さえキチンと行えば、Lighthouseがうまく処理をしてくれるので、相当楽にGraphQLサーバを立ち上げることができました。


ただ、この”キチンと設定”というのが、ネットで調べてもなかなかたどり着けない、ちょっとしたコツと言った感じでした。

[^1]: [Laravel The PHP Framework for Web Artisans](https://laravel.com)
[^2]: [Lighthouse GraphQL server for Laravel](https://lighthouse-php.com)