はじめに
この記事は前回の記事の続編です。
今回はQiitaのRSSを題材に、Drupalの Controller・API配信・キャッシュ機構 について実装を行います。
Drupalには、Routing機構 / HTTP通信 / キャッシュシステム が標準で組み込まれてます。
なので比較的やりやすいかと思います。
※ RSSがわざわざ提供されているのになんでAPI化するの?
前回の記事でも書いていますが、実務だったらこんなことしません。用途は人それぞれですが、「毎朝Slack通知したい」「フロントエンドフレームワークを使ったPFに使いたい」「独自のAPIを用意して、複数のアプリで使いまわしたい」とか、まぁAPI化すれば色んな場面で役立ちそうですよね。自分の好きなフォーマットで実装できるっていう強みもあります。
なにやるの?
Qiitaが提供しているRSSフィードを解析して自前のAPIを配信します。
それだけだとおもしろくないので、以下の要件を加えてみます。
- QiitaAccountを指定できるようにする
- キャッシュの組み込みを行って都度HTTP通信が発生しないようにする
どうですか?ちょっとやり応えありそうな要件ですよね。
準備
■ Drupal環境の用意
環境構築はこちらを参考に。
■ Drushの導入
composer require drush/drush
Drupal開発を普段から行っている方には説明不要ですね。
Drupalの開発で大活躍のCLIツールです。
■ 再現環境
多分Drupal10x系であれば動くような書き方をしてるので、問題ないかと思いますが一応。
Drupal 10.2.7
Drush 12.4
実装はじめます
001. モジュールのセットアップ
さっそく、モジュールを実装します。とは言ってもDrush
が勝手に作ってくれます。
drush generate controller
コマンドを実行後、いろいろ聞かれるので答えてください。
$ drush generate controller
Welcome to controller generator!
––––––––––––––––––––––––––––––––––
Module machine name:
➤ qiita
Module name [Qiita]:
➤
Class [QiitaController]:
➤
Would you like to inject dependencies? [No]:
➤
Would you like to create a route for this controller? [Yes]:
➤
Route name [qiita.example]:
➤
Route path [/qiita/example]:
➤ /api/qiita/{id}
Route title [Example]:
➤
Route permission [access content]:
➤
いくつかの質問に答えると、Controller付きのModuleが用意されますので有効化してしまいましょう。
drush en qiita
002. Routing設定
Routingの設定を行います。
とは言っても、ほとんどなにもすることありません。
URLや、権限周り特にこだわりがないのであれば以下の感じでいいかと思います。
qiita.example:
path: '/api/qiita/{id}'
defaults:
_controller: '\Drupal\qiita\Controller\QiitaController'
requirements:
access: TRUE
{id}
がポイントですね。Controllerがアカウント名を受け取れるようにできます。
003. Controllerの実装
final class QiitaController extends ControllerBase {
private const ACCOUNT_NAME = 'umekikazuya';
/**
* QiitaのFeedを取得しJSONを返すResponse Method.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* Jsonを返すよ. 処理が上手く以下なかったら 403 を返すよ.
*/
public function __invoke(): JsonResponse {
$res = new JsonResponse();
// URLを設定.
$url = 'https://qiita.com/' . self::ACCOUNT_NAME . '/feed';
try {
// HTTP通信実行.
$http_request = $this->httpClient->get($url);
$feed = $http_request->getBody()->getContents();
if (!$feed) {
throw new \Exception();
}
// 取得情報をXMLデータに変換. (Private関数は前回の記事を見てね)
$data = $this->loadRawFeedData($feed);
if (!$data) {
throw new \Exception();
}
// XMLデータを好みのフォーマットに変換. (Private関数は前回の記事を見てね)
$data = $this->parseXml($data, $rss_url);
// JSONに変換してAPIを提供!!.
return $res->setJson(json_encode($data));
}
catch (\Exception $e) {
// 失敗してたら 403 返すよ.
return $res->setStatusCode(403);
}
}
}
前回の記事で、取得データのXML変換・XMLパースについて触れていますのでそちらもぜひ。
004. ちょっと休憩(一旦動作確認)
ひとまずお疲れ様です。この時点でAPIの実装は完了です。
アクセスしてみてください。
{HOST URL}/api/qiita/umekikazuya
JSON返ってきましたか?
005. URLに応じてアカウントごとのデータ取得を取得
今はどんなURLを投げても同じJSONが返ってくるはずです。
{HOST URL}/api/qiita/umekikazuya
{HOST URL}/api/qiita/hogehoge
{HOST URL}/api/qiita/fugafuga
Routing設定で/api/qiita/{id}
と指定しているので、アカウント名を受け取るよう実装を修正します。
final class QiitaController extends ControllerBase {
- private const ACCOUNT_NAME = 'umekikazuya';
/**
* QiitaのFeedを取得しJSONを返すResponse Method.
*
+ * @param string $id
+ * Qiita accout id.
+ *
* @return \Symfony\Component\HttpFoundation\JsonResponse
* Jsonを返すよ. 処理が上手く以下なかったら 403 を返すよ.
*/
- public function __invoke(): JsonResponse {
+ public function __invoke(string $id): JsonResponse {
$res = new JsonResponse();
- // URLを設定.
+ // ここでアカウント別のHTTPリクエストを行うよ.
$url = 'https://qiita.com/' . id . '/feed';
/** 省略 */
}
}
実装はこれだけです。
{id}
を指定したエンドポイントでアクセスしてみてください。
006. キャッシュの組み込み
このままだと、正直RSS配信されてるのにAPIを提供するって正直意味ないですよね。
キャッシュを効かせます。アカウントごとに。
Drupalのキャッシュはすごいです。
キャッシュ操作
$cache_key = 'api_qiita:' . $id;
$this->cache()->set($cache_key, $data, time() + 8 * 3600);
$cache_key = 'api_qiita:' . $id;
$cache = $this->cache()->get($cache_key);
$id
がUniqueなので、一つのメソッドだけでアカウントごとにキャッシュの保持を行うことができますね。
こちらを__invoke
に組み込めば、キャッシュ機構もばっちしだと思います。
Qiitaサーバーへの負荷も軽減できますね。
まとめ
お疲れ様でした。いかがでしたか?
今回は、Qiita/RSSを題材にDrupalのController・API配信・キャッシュ機構についての実装をやってみました。
RSSを題材にやってみましたが、今回のようなRoutingの設定方法は他にも活かせる場面が多いはず 😎
キャッシュ設定に関しても、DrupalのEntity機構と組み合わせればもっと動的にGUIで設定できたり...。
いろいろできそうですね 😏
実は、このシリーズまだ終わりません。(え〜!!?)
次回は、Next.js でこのAPIを使ってレンダリングをしてみましょう。(え〜!!?)
シリーズ紹介