開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. [RadarPHP]ADRフレームワークRadarのインストール
no-image

[RadarPHP]ADRフレームワークRadarのインストール

ADRパターンを紹介した前回に引き続き、PSR-7準拠のADRフレームワーク、Radarの紹介です。 今回はRadarをインストールしてHello Worldのレスポンスを返すまでを辿ります。

Radarのインストール

radarphpには2つのリポジトリがあります。

Radar.Projectを利用します。

Composerでプロジェクトを作成し、

> composer create-project -s dev radar/project example-project
> cd example-project

composer.jsonでオートロードを設定しておきます。

    "autoload": {
        "psr-4": {
            "MyVendor\\MyProject\\": "src/"
        }
    },
> composer dump-autoload
ビルトインウェブサーバーで起動します。
> php -S localhost:8080 -t web/

http://localhost:8080/ にアクセスするとJSONが出力されます。

{"phrase":"Hello world"}

アプリケーションのエントリポイントとルーティング

web/index.phpがエントリポイントであり、オートローダの読み込み、設定の読み込み、コアのブートストラップ、HTTPミドルウェアの設定、ルーティング、レスポンスのハンドルが記述されています。

/**
 * Routes
 */
$adr->get('Hello', '/{name}?', function (array $input) {
        $payload = new Aura\Payload\Payload();
        return $payload
            ->setStatus(Aura\Payload_Interface\PayloadStatus::SUCCESS)
            ->setOutput([
                'phrase' => 'Hello ' . $input['name']
            ]);
    })
    ->defaults(['name' => 'world']);

デフォルトのルーティングは良くあるマイクロフレームワーク形式のインライン記述になっています。 マジックメソッド__invoke()を定義した任意のcallableなクラスに処理を割り当てることもできます。

/**
 * Routes
 */
$adr->get('Hello', '/{name}?', \MyVendor\MyProject\Hello::class)
    ->defaults(['name' => 'world']);

src/Hello.php

<?php

namespace MyVendor\MyProject;

use Aura\Payload\Payload;
use Aura\Payload_Interface\PayloadStatus;

class Hello
{
    /**
     * @param array $input
     * @return Payload
     */
    public function __invoke(array $input)
    {
        $payload = new Payload();
        return $payload
            ->setStatus(PayloadStatus::SUCCESS)
            ->setOutput([
                'phrase' => 'Hello ' . $input['name']
            ]);
    }
}

Domainに専念するためのフレームワーク

ルーティングで設定したインラインの無名関数、もしくはcallableなクラスは Action-Domain-Responderの区分のうち、Domainのサービス(もしくはユースケース)という位置付けです。

初見だとMVCのControllerに対応するActionだと勘違いしがちなので注意が必要です。

それではADRのActionとResponderはどこへいったのでしょうか?

Actionの責務であるHTTPリクエストの入力を変換してDomainに受け渡し、Domainの出力をResponderに受け渡す処理は、 Radar.Adr(とその中のarbiter/arbiter)によって行われています。

  • HTTPの入力→Domainの入力(Inputクラスが担当)
  • Domainの出力→HTTPの出力(Responderクラスが担当)

の変換処理もデフォルトが用意されているため、試すだけなら意識する必要がほとんどありません。

Domain Payload オブジェクト

RadarではDomainの出力(先ほどの無名関数や__invoke()の返り値)はDomain Payloadと呼ばれるオブジェクトにする必要があります。

もちろんこれには理由があります。

例えばリポジトリからオブジェクトを探す処理で、見つからない場合はnull、見つかった場合はオブジェクトを返すとします。 そのデータをResponderに渡し、HTTPレスポンスを生成することを考えます。

※以下は擬似的なコードです

//Actionの処理

$article = $articleRepository->find($id);

$domainOutput = [
    ‘id’ => $id,
    ‘article’ => $article,
];

$responder = new Responder();
$httpResponse = $responder($request, new HttpResponse(), $domainOutput);


//Responderクラス
class Responder {

    public function __invoke($request, $response, array $domainOutput)
    {
        $article = $domainOutput[‘article’];
        
        //↓nullであるという情報からオブジェクトが見つからなかったという結果を解釈している
        if($article === null) {
             $content = $this->templateRenderer->render(‘not_found’, [‘id’ => $id]);
             return $response
                 ->setStatus(404)
                 ->setContent($content);
        }

        $content = $this->templateRenderer->render(‘show’, [‘article’ => $article]);
        return $response
            ->setStatus(200)
            ->setContent($content);        
    }
}

シンプルにデータをまとめた連想配列を渡すだけだと、データの「意味」をResponderが再解釈する必要が出てきます。

Domainの処理で目的のオブジェクトが見つからなかったことはすでにわかっているはずなのにもかかわらず、です。

さらに、エンティティは見つかったがクライアントにアクセス権がなかった場合、nullだけじゃ表現しきれない……などと考えるとより辛みが増します。

「Domainの処理の結果を明確に示すオブジェクトをDomainの出力とする」ことでこの辛さを解消することができます。

PayloadオブジェクトはDomainへの入力、Domainからの出力、処理の結果を表すステータス、処理の結果に関するメッセージ、+αを保持します。

ステータスはSUCCESS、NOT_FOUND、CREATEDなどなど、HTTPのステータスにマップできる分は揃っています。 ただ、Aura\Payload\Payloadはあくまで実装例であり、自身が設計するDomainに応じた独自のステータスを増やすことも可能です。

まとめ

今回はRadarを試しに使う上で最低限知っておきたい情報を紹介しました。 RadarによってDomainの入出力がHTTPの入出力にマッピングされるため、 HTTPの事情を分けて考え、Domainに専念することが可能になる。それがRadarの魅力だと思います。

TOPに戻る