6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ray.Di アプリケーションから BEAR.Sunday を呼び出す

Last updated at Posted at 2022-12-25

はじめに

BEAR.Sunday Advent Calendar 2020 で以下の記事を投稿しました。

上記記事中において Front & Backend のケースとして他の PHP アプリケーションから BEAR.Sunday を利用する以下の記事を紹介しました。

今回は Ray.Di for Laravel を使い、既存の Ray.Di アプリケーションから BEAR.Sunday アプリケーションを利用する例を紹介します。

実現すること

以下のようなことを実現します。

  • Ray.Di for Laravel を適用した Laravel アプリケーションから BEAR アプリケーションの Resource を利用できる
  • 上記で利用できるようにしたものとは別の BEAR アプリケーションの Resource を利用できる

リポジトリ

本記事で紹介するコードは下記リポジトリにあるものです。

BEARアプリケーションを実装する

まずは BEAR アプリケーションを実装します。
BEAR アプリケーションといってもルーターなどは不要なので、 BEAR.Resource のみを依存に追加します。

     "license": "MIT",
     "require": {
         "php": "^8.0.2",
+        "bear/resource": "^1.18",
         "guzzlehttp/guzzle": "^7.2",
         "laravel/framework": "^9.19",
         "laravel/sanctum": "^3.0",

今回は以下のような bear-app というディレクトリを作成し、この中に独立したアプリケーションとして実装します。
既存の Laravel アプリケーションに影響を与えることがないので、導入が簡単です。

./bear-app
├── src
│   ├── AppModule.php
│   └── Resource
│       └── Page
│           └── Index.php
└── tests
    └── Resource
        └── Page
            └── IndexTest.php

AppModule では ResourceModule をインストールします。

bear-app/src/AppModule.php
<?php

declare(strict_types=1);

namespace NaokiTsuchiya\MyApp;

use BEAR\Resource\Module\ResourceModule;
use Ray\Di\AbstractModule;

class AppModule extends AbstractModule
{
    /** @inheritDoc */
    protected function configure()
    {
        $this->install(new ResourceModule(__NAMESPACE__));
    }
}

この時点で BEAR アプリケーション内でのテストができます。
必要に応じて Workflow テストなども実装しましょう。

Laravel アプリケーションから Resource を呼び出す

コントローラの依存に ResourceInterface を追加し、作成したリソースへリクエストした結果を Responce オブジェクトに変換します。

app/Http/Controllers/IndexController.php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use BEAR\Resource\ResourceInterface;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Ray\RayDiForLaravel\Attribute\Injectable;

#[Injectable]
class IndexController extends Controller
{
    public function __construct(private readonly ResourceInterface $resource)
    {
    }

    public function __invoke(Request $request): Response
    {
        $name = $request->get('name', 'World');
        $ro = $this->resource->get('page://self/index', ['name' => $name]);

        return new Response((string) $ro, $ro->code, $ro->headers);
    }
}

モジュールをインストールします。

app/RayDi/AppModule.php
    protected function configure(): void
    {
        $this->install(new \NaokiTsuchiya\MyApp\AppModule());
    }

次のような BEAR アプリケーションを呼び出す部分まで含めたテストを実装できます。

tests/Feature/ExampleTest.php
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/?name=Test');

        $response->assertStatus(200);
        $response->assertJson(['greeting' => 'Hello Test'], true);
        $response->assertHeader('content-type', 'application/json');
    }

ここまでで Laravel アプリケーションから BEAR アプリケーションの Resource を利用できる が実現できました。

別の BEAR アプリケーションを追加する

もう 1 つの BEAR アプリケーションを実装する

もう1つ別の BEAR アプリケーションを追加してみましょう。

other-bear-app ディレクトリに先程と同様に BEAR アプリケーションを実装します。

./other-bear-app
├── src
│   ├── AppModule.php
│   └── Resource
│       └── Page
│           └── Index.php
└── tests
    └── Resource
        └── IndexTest.php

アプリケーションをインポートする

bear-app のアプリケーションに other-bear-app のアプリケーションをインポートするために ImportAppModule を利用します。

ここでは、other というホスト名で other-bear-app のリソースをインポートします。

bear-app/src/AppModule.php.diff
     /** @inheritDoc */
     protected function configure()
     {
+        $this->install(
+            new ImportAppModule(
+                [new ImportApp('other', 'NaokiTsuchiya\OtherApp', '')]
+            )
+        );
         $this->install(new ResourceModule(__NAMESPACE__));
     }
 }

インポートしたアプリケーションのリソースを直接 Laravel アプリケーションから呼び出すことが可能ですが、ここでは既存のアプリケーションのみを呼び出すことにしましょう。 以下の Greetingother-bear-app のリソースを呼び出します。

bear-app/src/Resource/Page/Greeting.php
<?php

declare(strict_types=1);

namespace NaokiTsuchiya\MyApp\Resource\Page;

use BEAR\Resource\ResourceInterface;
use BEAR\Resource\ResourceObject;

class Greeting extends ResourceObject
{
    public function __construct(private readonly ResourceInterface $resource)
    {
    }

    public function onGet(string $name = 'other'): static
    {
        $ro = $this->resource->get('page://other/', ['name' => $name]);
        $this->headers = $ro->headers;
        $this->body = $ro->body;

        return $this;
    }
}

Greeting を呼び出すコントローラを実装します。

app/Http/Controllers/GreetingController.php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use BEAR\Resource\ResourceInterface;
use Illuminate\Http\Response;
use Ray\RayDiForLaravel\Attribute\Injectable;

#[Injectable]
class GreetingController extends Controller
{
    public function __construct(private readonly ResourceInterface $resource)
    {
    }

    public function __invoke(): Response
    {
        $ro = $this->resource->get('page://self/greeting');

        return new Response((string) $ro, $ro->code, $ro->headers);
    }
}

テストも同様に実装します。

tests/Feature/GreetingTest.php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class GreetingTest extends TestCase
{
    /**
     * @return void
     */
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/greeting');

        $response->assertStatus(200);
        $response->assertJson(['greeting' => 'Hello other'], true);
        $response->assertHeader('content-type', 'application/json');
    }
}

これで 別の BEAR アプリケーションの Resource を利用できる が実現されました。

おわりに

かなり駆け足でしたが、当初実現しようとしていた以下の2点を実現することができました。

  • Ray.Di for Laravel を適用した Laravel アプリケーションから BEAR アプリケーションの Resource を利用できる
  • 上記で利用できるようにしたものとは別の BEAR アプリケーションの Resource を利用できる

今回の例では、単一のリポジトリ内に複数のアプリケーションを持つようにしましたが、別のリポジトリに切り出し、 Composer の依存として利用することもできます。

注意点として、今回実装した方式では 1 つの Injector によって複数のアプリケーションを跨ぐ巨大なオブジェクトグラフを構築している点が挙げられます。
利用側の束縛のルールによっては意図せず束縛が変わってしまう場合がある点に注意しましょう。1

上記のような煩わしさから開放されるには、依存として使われるアプリケーション側がパッケージとして利用されることを前提とした識別子を使った束縛をすることや、Injector を複数持つような構成を検討しましょう。2

カスタムホストとして別の Injector を利用したい場合は、以下が参考になるかもしれません。

  1. PSR の LoggerInterface に Laravel のロガーが束縛されてしまうなど

  2. 環境変数も気をつける必要があるので重複を避けることを前提とするのが良いかもしれません。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?