Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

前回の投稿からはや丸一年。そして遅刻。
時の流れとは恐ろしいものです。

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

また、本家のドキュメントでもGuzzleを使ったコードが紹介されていますが、jsonとhal+json、basic authとcookieがごちゃまぜになっている気がするので、今回は最小構成で利用できるように心がけます。

Drupal8.2以降のREST APIの変更点

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

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

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

準備

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

composer init
~
composer require guzzlehttp/guzzle:~6.0

空のphpファイル(rest_client.php)を作成し、次のコードを追記していきます。

GuzzleでREST APIを叩いてみる

初期化

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

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の文字列として返ってくるので、デコードしたうえで使用します。

記事取得
$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;

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

//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形式で送信します。

ログイン認証/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 で登録するコンテンツの種類を指定します。
※配列の階層には要注意

記事登録(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」を指定します。

記事登録(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形式の場合は次のようになります。

記事更新
$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)

最後に更新した記事を削除します。

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

さいごに

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

mosomoso
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした