S3みたいなストレージサーバーっぽいものを自前で用意する
こんにちは。のんです。
実はちょっと前からのんラボのリプレースを考えていまして、ブログに載せる画像のアップロード先について考えていました。
これまではLaravelのデフォルト設定であるプロジェクト内のStorageディレクトリ内に保存していました。
ただ、リプレースする際にこれがかなりのネックになっていて、頭を抱えています。
新しいプロジェクトではキチンとストレージサーバーを用意してそこにアップロードするようにしようと思いました。
S3を検討していたのですが、S3使うだけじゃ何のインプットもアウトプットもできないので自前でストレージサーバーを用意しようと思い立ったのがこの記事を書くキッカケです。
今回からそのためのプロジェクトについて記事にしていきます。
一応、連載記事にしようと思っていますが、他にも書きたい内容(Rustとかチームビルディングとか)があるので、とびとびになるかも。
GitHubプロジェクトはこちら
できるだけフルスタックなフレームワークは使わない
- Laravel
- CakePHP
- Symfony
- Slim
などは利用しません。
しかし、自前で用意した生のPHPだけで書くという意味ではなく、laminasのコンポーネントや、leagueのコンポーネントを利用していきます。
ここでの狙いは、
PSR7やPSR15、RFC 7807やRFC 7807などの仕様、動作の勉強
となります。
何を組み合わせればいいのか。どのような実装になっているのか。
LaravelやCakePHPのドキュメントやAPIリファレンスを読んでいるだけでは得ることができない習慣を習得するため
です。
では、早速始めましょう。(今回は触りだけですが...)
まずはディレクトリ構成から
storage/backend
が前提です。
.
├── app
│ ├── Adapter
│ ├── Domain
│ ├── Http
│ ├── Provider
│ └── Strategy
├── bin
├── tests
└── vendor
app/Adapter
主にRepository層の具象クラスをまとめる。
他のユースケース層で書きたくないものを逃がす場としても利用するかも。
ちょっと考え方ガバいけども、今の考え方で大丈夫そう。
app/Domain
主にUseCase層をまとめる。他にはEntities/ValueObjectをまとめます。つまり、レイヤードアーキテクチャでいうDomain層も含んでいるということ。
app/Http
主にHttpControllerをまとめる。MVCモデルのCです。
bin
独自コマンドを実行できるようにする予定。
リッチなMigration機能は求めていないので、単純なSQLを実行するためのコマンドや、定期的にファイルを調整するコマンドを用意するつもり。
と、だらだら書いたけども...
兎にも角にもRoutingとHttp Requestを捌きたい
自前のストレージサーバーと言ってもアップロード用のAPIを公開して受け付けるくらいの機能しかないです。この辺がFW(フレームワーク)を利用しない理由でもあります。
league/route
Route is a fast PSR-7 routing/dispatcher package including PSR-15 middleware implementation that enables you to build well designed performant web apps.
です。この仕様にあるライブラリを探すことになるのですが、このページで書いてある2つのライブラリを利用します。
laminas/laminas-diactoros
If you use Laminas Diactoros project you will also need
composer require laminas/laminas-httphandlerrunner
とあるので、
laminas-httphandlerrunner
も入れます。
これらを利用してテスト用のアクションを用意する
<?php
declare(strict_types=1);
include_once 'vendor/autoload.php';
/**
* Load dotenv.
*/
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
/**
* Load environment.
*/
$env = new Nonz250\Storage\App\Shared\ValueObject\Environment($_ENV['APP_ENV']);
/**
* Create request.
*/
$request = Laminas\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES,
);
/**
* Setting router.
*/
$responseFactory = new Laminas\Diactoros\ResponseFactory();
$strategy = new League\Route\Strategy\JsonStrategy($responseFactory);
$router = new League\Route\Router();
if (Nonz250\Storage\App\Foundation\App::environment(Nonz250\Storage\App\Shared\ValueObject\Environment::LOCAL)) {
$router
->group('test', static function (League\Route\RouteGroup $router) use ($strategy) {
$router->get('/', static function (): array {
return [
'message' => 'test',
];
})->setStrategy($strategy);
$router->get('/hello', static function (): Psr\Http\Message\ResponseInterface {
$response = new Laminas\Diactoros\Response();
$response->getBody()->write('<h1>Hello, World!</h1>');
return $response;
});
$router->get('/action', Nonz250\Storage\App\Http\Test\TestAction::class);
});
}
$response = $router->dispatch($request);
(new Laminas\HttpHandlerRunner\Emitter\SapiEmitter)->emit($response);
最後に
次回はPSR15を参照して、Middlewareを実装した内容を記事にします。
自分のためのAPIなので、認証が必要です。
Basic / Digestなどが一般的ですが、今回はDigest認証をAPI認証として利用します。
OAuth2の実装までやってみようと思いましたが、流石に時間かかりすぎるなと思ったのでやめました。
AuthMiddlewareについてはまた記事にしたいと思います。
そのときはよしなに。
.