目次

Slim4 ルーティング

Version 4.5.0

y2sunlight 2020-09-23

Slim に戻る

関連記事

本章は以下のサイトの Routing のセクションを翻訳し若干の補足を加えたのもです。


SlimFramework のルーターは FastRoute コンポーネントの上に構築されており、非常に高速で安定しています。このコンポーネントを使用して全てのルーティングを処理している間は、アプリのコアとそれは完全に分離されており、他のルーティングライブラリを使用をすることを容易にするためにインターフェイスが設置されています。

ルートの作成方法

アプリケーションルートは、Slim\App インスタンス上のプロキシメソッドを使用することにより定義できます。Slim Frameworkは、最も一般的なHTTPメソッドに対するメソッドを提供します。

GET ルート

Slimアプリケーションの get() メソッドを使用して、GET HTTPリクエストのみを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->get('/books/{id}', function ($request, $response, array $args) {
    // Show book identified by $args['id']
});


POST ルート

Slimアプリケーションの post() メソッドを使用して、POST HTTPリクエストのみを処理するルート(route)を追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->post('/books', function ($request, $response, array $args) {
    // Create new book
});


PUT ルート

Slimアプリケーションの put() メソッドを使用して、PUT HTTPリクエストのみを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->put('/books/{id}', function ($request, $response, array $args) {
    // Update book identified by $args['id']
});


DELETE ルート

Slimアプリケーションの delete() メソッドを使用して、DELETE HTTPリクエストのみを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->delete('/books/{id}', function ($request, $response, array $args) {
    // Delete book identified by $args['id']
});


OPTIONS ルート

Slimアプリケーションの options() メソッドを使用して、OPTIONS HTTPリクエストのみを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->options('/books/{id}', function ($request, $response, array $args) {
    // Return response headers
});


PATCH ルート

Slimアプリケーションの patch() メソッドを使用して、PATCH HTTPリクエストのみを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->patch('/books/{id}', function ($request, $response, array $args) {
    // Apply changes to book identified by $args['id']
});


Any ルート

Slimアプリケーションの any() メソッドを使用して、すべてのHTTPリクエストメソッドを処理するルートを追加できます。それは2つの引数を受け入れます:

  1. ルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. ルートコールバック
$app->any('/books/[{id}]', function ($request, $response, array $args) {
    // Apply changes to books or book identified by $args['id'] if specified.
    // To check which method is used: $request->getMethod();
});

2番目のパラメーターはコールバックであることに注意してください。Closureの代わりに __invoke() メソッドを実装するクラスを指定できます。その後、別の場所でマッピングを行うことができます。

$app->any('/user', 'MyRestfulController');


Custom Route

Slimアプリケーションの map() メソッドを使用して、複数のHTTPリクエストメソッドを処理するルートを追加できます。それは次の3つの引数を受け入れます:

  1. HTTPメソッドの配列
  2. ルートのパターン(オプションで名前付きプレースホルダーを含みます)
  3. ルートのコールバック
$app->map(['GET', 'POST'], '/books', function ($request, $response, array $args) {
    // Create new book or list all books
});


ルートコールバック

上記の各ルーティングメソッドは、最後の引数としてコールバックルーチンを受け入れます。この引数は、任意のPHP callable が可能であり、それはデフォルトでは3つの引数を受け入れます。

応答へのコンテンツの書き込み

HTTP応答にコンテンツを書き込む方法は2つあります。まず、ルートコールバックからコンテンツを単に echo() することができます。このコンテンツは、現在のHTTP応答オブジェクトに追加されます。次に、Psr\Http\Message\ResponseInterface オブジェクトを返すことができます。


クロージャバインディング

ルートコールバックとして、依存関係コンテナと Closure インスタンスを使用する場合、クロージャの状態は Container インスタンスにバインドされています。これは、$this キーワードを介して、クロージャー内のDIコンテナーインスタンスにアクセスできることを意味します:

$app->get('/hello/{name}', function ($request, $response, array $args) {
    // Use app HTTP cookie service
    $this->get('cookies')->set('name', [
        'value' => $args['name'],
        'expires' => '7 days'
    ]);
});
注意喚起
Slimは静的クロージャーをサポートしていません。


リダイレクトヘルパー

Slimアプリケーションの redirect() メソッドを使用して、GET HTTPリクエストを別のURLにリダイレクトするルートを追加できます。それは次の3つの引数を受け入れます:

  1. リダイレクト元 from のルートパターン(オプションで名前付きプレースホルダーを含みます)
  2. リダイレクト先 to の場所。文字列または Psr\Http\Message\UriInterface の場合があります。
  3. 使用するHTTPステータスコード(オプション:設定されていない場合は 302
$app->redirect('/books', '/library', 301);

redirect() ルートは、要求されたステータスコードと、2番目の引数に設定された Location ヘッダーで応答します。


ルート戦略

ルートコールバックのシグニチャは、ルート戦略によって決定されます。デフォルトでは、Slimは、ルートコールバックがリクエスト、レスポンス、およびルートプレースホルダー引数の配列を受け入れることを期待しています。これは、RequestResponse 戦略と呼ばれます。 ただし、別の戦略を使用するだけで、期待されるルートコールバックシグニチャを変更できます。例として、Slimは、リクエストとレスポンに加えて個々に分かれた引数として各ルートプレースホルダーを受け入れる RequestResponseArgs と呼ばれる代替戦略を提供します。

この代替戦略の使用例を次に示します:

<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;
 
require __DIR__ . '/../vendor/autoload.php';
 
$app = AppFactory::create();
 
/**
 * Changing the default invocation strategy on the RouteCollector component
 * will change it for every route being defined after this change being applied
 */
$routeCollector = $app->getRouteCollector();
$routeCollector->setDefaultInvocationStrategy(new RequestResponseArgs());
 
$app->get('/hello/{name}', function ($request, $response, $name) {
    $response->getBody()->write($name);
 
    return $response;
});

あるいは、ルートごとに異なる呼び出し戦略を設定することもできます:

<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;
 
require __DIR__ . '/../vendor/autoload.php';
 
$app = AppFactory::create();
$routeCollector = $app->getRouteCollector();
 
$route = $app->get('/hello/{name}', function ($request, $response, $name) {
    $response->getBody()->write($name);
 
    return $response;
});
$route->setInvocationStrategy(new RequestResponseArgs());

Slim\Interfaces\InvocationStrategyInterface を実装することにより、独自のルート戦略を提供できます。


ルートプレースホルダー

上記の各ルーティングメソッドは、現在のHTTPリクエストURIと照合されるURLパターンを受け入れます。ルートパターンは、名前付きプレースホルダーを使用して、HTTPリクエストURIセグメントを動的に照合できます。

フォーマット

ルートパターンのプレースホルダーは { で始まり、プレースホルダー名が続き、} で終わります。以下は、name という名前のプレースホルダーの例です。

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
// ...
 
$app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");
 
    return $response;
});


オプションセグメント

セクションをオプションにするには、角括弧([])で囲みます:

$app->get('/users[/{id}]', function ($request, $response, array $args) {
    // responds to both `/users` and `/users/123`
    // but not to `/users/`
 
    return $response;
});

ネストする事により、複数のオプションパラメータがサポートされます:

$app->get('/news[/{year}[/{month}]]', function ($request, $response, array $args) {
    // reponds to `/news`, `/news/2016` and `/news/2016/03`
    // ...
 
    return $response;
});

オプションパラメータが「無制限」の場合、以下のように行うことができます:

$app->get('/news[/{params:.*}]', function ($request, $response, array $args) {
    // $params is an array of all the optional segments
    $params = explode('/', $args['params']);
    // ...
 
    return $response;
});

上の例では、URIが /news/2016/03/20 の場合、['2016', '03', '20']の3つの要素を含む $params 配列になります。


正規表現マッチング

デフォルトでは、プレースホルダーは {} 内に記述され、任意の値を受け入れることができます。ただし、プレースホルダーは、特定の正規表現に一致するようにHTTPリクエストURIを要求することもできます。現在のHTTPリクエストURIがプレースホルダーの正規表現と一致しない場合、ルートは呼び出されません。以下は、1つ以上の数字を必要とする id という名前のプレースホルダーの例です。

$app->get('/users/{id:[0-9]+}', function ($request, $response, array $args) {
    // Find user identified by $args['id']
    // ...
 
    return $response;
});


ルート名

アプリケーションルートには名前を付けることができます。これは、RouteParser の urlFor() メソッドを使用して特定のルートへのURLをプログラムで生成したい場合に役立ちます。上記の各ルーティングメソッドは Slim\Route オブジェクトを返し、このオブジェクトは setName() メソッドを公開します。

$app->get('/hello/{name}', function ($request, $response, array $args) {
    $response->getBody()->write("Hello, " . $args['name']);
    return $response;
})->setName('hello');

この名前付きルートのURLは、アプリケーション RouteParser の urlFor() メソッドを使用して生成できます。

$routeParser = $app->getRouteCollector()->getRouteParser();
echo $routeParser->urlFor('hello', ['name' => 'Josh'], ['example' => 'name']);
 
// Outputs "/hello/Josh?example=name"

RouteParser の urlFor() メソッドは、次の3つの引数を受け入れます:


ルートグループ

ルートを論理グループに整理するために、Slim\App には group() メソッドも用意されています。各グループのルートパターンは、そのグループに含まれるルートまたはグループの前に付加され、グループパターン内のプレースホルダー引数は、最終的にネストされたルートで使用できるようになります:

use Slim\Routing\RouteCollectorProxy;
// ...
 
$app->group('/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
    $group->map(['GET', 'DELETE', 'PATCH', 'PUT'], '', function ($request, $response, array $args) {
        // Find, delete, patch or replace user identified by $args['id']
        // ...
 
        return $response;
    })->setName('user');
 
    $group->get('/reset-password', function ($request, $response, array $args) {
        // Route for /users/{id:[0-9]+}/reset-password
        // Reset the password for user identified by $args['id']
        // ...
 
        return $response;
    })->setName('user-password-reset');
});

グループパターンは空にすることができ、共通のパターンを共有しないルートの論理グループ化を有効にします。

use Slim\Routing\RouteCollectorProxy;
// ...
 
$app->group('', function (RouteCollectorProxy $group) {
    $group->get('/billing', function ($request, $response, array $args) {
        // Route for /billing
        return $response;
    });
 
    $group->get('/invoice/{id:[0-9]+}', function ($request, $response, array $args) {
        // Route for /invoice/{id:[0-9]+}
        return $response;
    });
})->add(new GroupMiddleware());

グループクロージャー内で、Slimはクロージャーをコンテナーインスタンスにバインドすることに注意してください。


ルートミドルウェア

ミドルウェアを任意のルートまたはルートグループにアタッチすることもできます。

use Slim\Routing\RouteCollectorProxy;
// ...
 
$app->group('/foo', function (RouteCollectorProxy $group) {
    $group->get('/bar', function ($request, $response, array $args) {
        // ...
        return $response;
    })->add(new RouteMiddleware());
})->add(new GroupMiddleware());


ルーターキャッシュ

RouteCollector::setCacheFile() を介してルーターキャッシュを有効にすることができます。以下の例を参照してください:

<?php
use Slim\Factory\AppFactory;
 
require __DIR__ . '/../vendor/autoload.php';
 
$app = AppFactory::create();
 
/**
 * To generate the route cache data, you need to set the file to one that does not exist in a writable directory.
 * After the file is generated on first run, only read permissions for the file are required.
 *
 * You may need to generate this file in a development environment and comitting it to your project before deploying
 * if you don't have write permissions for the directory where the cache file resides on the server it is being deployed to
 */
$routeCollector = $app->getRouteCollector();
$routeCollector->setCacheFile('/path/to/cache.file');


コンテナ―ソリユーション

ルートに対しては関数を定義することだけに限定されません。Slimでは、ルートのアクション関数を定義するいくつかの異なる方法があります。

関数に加えて、次のものを使用できます:

この機能は、Slim の Callable Resolver Class によって有効になります。それは、文字列エントリを関数呼び出しに変換します。例:

$app->get('/', '\HomeController:home');

または、PHPの ::class 演算子を利用することもできます。これは、IDEのルックアップシステムで適切に機能し、同じ結果を生成します:

$app->get('/', \HomeController::class . ':home');

上記のコードでは、/ ルートを定義し、HomeController クラスで home() メソッドを実行するようにSlimに指示しています。

Slimは最初にコンテナ内の HomeController のエントリを探します。見つかった場合はそのインスタンスを使用し、そうでない場合はコンテナを最初の引数としてコンストラクタを呼び出します。クラスのインスタンスが作成されると、定義したストラテジーを使用して、指定されたメソッドが呼び出されます。


コントローラーをコンテナーに登録する

home アクションメソッドを伴うコントローラーを作成します。コンストラクターは、必要な依存関係を受け入れる必要があります。 例えば:

<?php
 
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;
 
class HomeController
{
    private $view;
 
    public function __construct(Twig $view)
    {
        $this->view = $view;
    }
 
    public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
    {
      // your code here
      // use $this->view to render the HTML
      // ...
 
      return $response;
    }
}

依存関係を持つコントローラーをインスタンス化するファクトリをコンテナーに作成します。

use Psr\Container\ContainerInterface;
// ...
 
$container = $app->getContainer();
 
$container->set('HomeController', function (ContainerInterface $container) {
    // retrieve the 'view' from the container
    $view = $container->get('view');
 
    return new HomeController($view);
});

これにより、依存関係の注入にコンテナーを活用できるため、特定の依存関係をコントローラーに注入できます。


Slimがコントローラーをインスタンス化できるようにする

また、クラスがコンテナにエントリされていない場合、Slimはコンテナのインスタンスをそのクラスのコンストラクタに渡します。1つのアクションのみを処理する呼び出し可能なクラスの代わりに、多くのアクションを持つコントローラーを構築できるのです。

<?php
 
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
 
class HomeController
{
   private $container;
 
   // constructor receives container instance
   public function __construct(ContainerInterface $container)
   {
       $this->container = $container;
   }
 
   public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');
 
        return $response;
   }
 
   public function contact(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');
 
        return $response;
   }
}

このコントローラーメソッドは次のように使用できます。

$app->get('/', \HomeController::class . ':home');
$app->get('/contact', \HomeController::class . ':contact');


インボーク可能なアクションクラスの使用

ルート呼び出しのメソッドを指定する必要はなく、次のようなインボーク可能なアクションクラスとして設定するだけです:

<?php
 
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
 
class HomeAction
{
   private $container;
 
   public function __construct(ContainerInterface $container)
   {
       $this->container = $container;
   }
 
   public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');
 
        return $response;
   }
}

このクラスは次のように使用できます。

$app->get('/', \HomeAction::class);

繰り返しになりますが、コントローラーを使った場合と同様に、クラス名をコンテナーに登録すると、ファクトリを作成して、アクションクラスに必要としている特定の依存関係だけを注入できます。