Drupal8.2でREST APIを使ってみる(Guzzle6編)

#はじめに
[前回の投稿](http://qiita.com/mosomoso/items/2548f757ecee584a4e83)からはや丸一年。そして遅刻。
時の流れとは恐ろしいものです。

さて、今回はDrupal8でも採用されているPHPのHTTPクライアント Guzzle を使って、REST APIを叩きます。
（モジュール設定等は前回の記事を参照してください）

また、[本家のドキュメント](https://www.drupal.org/docs/8/core/modules/rest)でもGuzzleを使ったコードが紹介されていますが、jsonとhal+json、basic authとcookieがごちゃまぜになっている気がするので、今回は最小構成で利用できるように心がけます。

#Drupal8.2以降のREST APIの変更点
Drupal8.2では、1年前と比べるとたくさんの変更点がありますが、個人的に使い勝手が大きく向上したと思う2点をご紹介します。

１．気持ち悪かったログイン/CSRFトークンの取得まわりが改善された
　ログイン成功時のレスポンスでCSRFトークンやuidなどがJSON形式で取得できます。
　（ログインエラーなのにステータスコードが「200 OK」な現象がなくなりました）

２．登録・編集したコンテンツのエンティティデータがレスポンス(JSON文字列)として返ってくる
　コンテンツを登録したものの、登録したコンテンツのノードIDがわからないとか、更新した後に再度読み込む2度手間などがなくなりました。
　（以前はレスポンスがNo Contentでした）


#準備

任意のディレクトリでcomposerを使ってguzzleをインストールします。

```bash
composer init
～
composer require guzzlehttp/guzzle:~6.0
```
空のphpファイル(rest_client.php)を作成し、次のコードを追記していきます。

#GuzzleでREST APIを叩いてみる

##初期化
GuzzleのHTTPクライアントのオブジェクトを生成します。
今回はCookieを使うので、 cookiesはtrueにします。

```php:rest_client.php
<?php
require 'vendor/autoload.php';

$base_uri = "https://drupal82.local";

$client = new \GuzzleHttp\Client([
    'base_uri' => $base_uri,
    'cookies'  => true, //必須
]);
```

##記事の取得(GET)
ノードID:1 の記事データを取得してタイトルと本文を変数に格納します。
レスポンスはJSONの文字列として返ってくるので、デコードしたうえで使用します。

```php:記事取得
$response = $client->request("GET", "node/1", [
    "query" => ["_format" => "json"],
]);

$article_entity = json_decode($response->getBody());

//タイトル: My 1st article
$title = $article_entity->title[0]->value;
//本文: Hello Drupal8!
$body  = $article_entity->body[0]->value;
```

下記のようにパスにパラメータを含める、エイリアス名を使うといったことも可能です

```php
//hal+json
$response = $client->request("GET", "node/1?_format=hal_json");
//エイリアス名
$response = $client->request("GET", "my_1st_article?_format=hal_json");
```


##ログイン認証(cookie)およびCSRFトークンの取得
Drupal8.2で改善されたログイン機能を使ってCSRFトークンを取得します。
ログインURLに_formatパラメータを付与したうえで、ユーザ名とパスワードをJSON形式で送信します。

```php:ログイン認証/CSRFトークン取得
//ログイン情報を送信（ application/json )
$response = $client->request('POST', 'user/login', [
    "query" => ["_format" => "json"],
    "json" => [
        "name"    => "rest_user",
        "pass"    => "rest_pass",
    ]
]);

//JSON文字列をデコード(配列にする場合は第2引数をtrue)
$login_info = json_decode($response->getBody());

$uid          = $login_info->current_user->uid;
$name         = $login_info->current_user->name;
$csrf_token   = $login_info->csrf_token;
$logout_token = $login_info->logout_token;
```
##記事の登録(POST)

コンテンツの登録・更新・削除は取得したCSRFトークンを「X-CSRF-Token」ヘッダーに付与して行います。
JSON形式で登録する場合は、type で登録するコンテンツの種類を指定します。 
※配列の階層には要注意

```php:記事登録（JSON)
$article_data = [
    'title' => [['value' => 'REST APIで登録(JSON)']],
    'body'  => [['value' => '本文']],
    'type'  => [['target_id' => 'article']],
];

$response = $client->post('/entity/node', [
    'headers' => [
      'Content-Type' => 'application/json',
      'X-CSRF-Token' => $csrf_token
    ],
    "query" => ["_format" => "json"],
    'json'  => $article_data,
]);

//更新結果がレスポンスに入る
$article_entity = json_decode($response->getBody());

//登録したノードID
$nid = $article_entity->nid[0]->value;
//タイトル: REST APIで登録(JSON)
$title = $article_entity->title[0]->value;
```

HAL+JSON形式で登録する場合は、_links で登録するコンテンツの種類を指定します。 
また、Content-Typeは「application/hal+json」を指定します。

```php:記事登録（HAL+JSON)

$article_data = [
    'title'  => [['value' => 'REST APIで登録(HAL+JSON)']],
    'body'   => [['value' => '本文']],
    '_links' => ['type' => [
        'href' => $base_uri . '/rest/type/node/article'
    ]],
];

$response = $client->post('/entity/node', [
    'headers' => [
      'Content-Type' => 'application/hal+json',
      'X-CSRF-Token' => $csrf_token
    ],
    "query" => ["_format" => "hal_json"],
    'json'  => $article_data,
]);

//更新結果がレスポンスに入る
$article_entity = json_decode($response->getBody());

//登録したノードID
$nid = $article_entity->nid[0]->value;
//タイトル: REST APIで登録(HAL+JSON)
$title = $article_entity->title[0]->value;
```

##記事の更新(PATCH)
先ほど登録した記事を更新します。
レスポンスで取得したノードIDを使い、本文を更新します。
HAL+JSON形式の場合は次のようになります。

```php:記事更新
$article_data = [
    'body'   => [['value' => 'REST APIで本文更新']],
    '_links' => ['type' => [
        'href' => $base_uri . '/rest/type/node/article'
    ]],
];

$response = $client->patch('node/' . $nid, [
    'headers' => [
      'Content-Type' => 'application/hal+json',
      'X-CSRF-Token' => $csrf_token
    ],
    "query" => ["_format" => "hal_json"],
    'json'  => $article_data,
]);

//更新結果がレスポンスに入る
$article_entity = json_decode($response->getBody());

//本文: REST APIで本文更新
$body = $article_entity->body[0]->value;
```

##記事の削除(DELETE)
最後に更新した記事を削除します。

```php:記事削除
$response = $client->delete('node/' . $nid, [
    'headers' => [
      'X-CSRF-Token' => $csrf_token
    ]
]);
```

#さいごに
今回は駆け足で、Guzzleを使った実装についてご紹介しました。
Drupal8.2で痒い所に手が届くようになり、より使いやすくなったREST APIをぜひ使ってみてください。
また、RESTモジュールは現在でも活発に開発が進められているようですので、Drupal8.3でも様々な機能拡張や改善が期待できますね！
（そもそも現行機能もごく一部しか使えていませんが・・・^^;）