ルーティング

Slim Framework のルーターは Fast Route コンポーネント上に構築されており、非常に高速で安定しています。このコンポーネントを使用してすべてのルーティングを行っていますが、アプリケーションのコアは完全に分離されており、他のルーティングライブラリを使用できるようにインターフェースが用意されています。

ルートの作成方法

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 リクエストのみを処理するルートを追加できます。 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']
});

すべてのルート

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 番目のパラメータはコールバックであることに注意してください。 クロージャの代わりに、__invoke() メソッドを実装するクラスを指定できます。 その後、他の場所でマッピングを行うことができます

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

カスタムルート

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 コールバックにすることができ、デフォルトでは 3 つの引数を受け入れます。
  • Request 最初の引数は、現在の HTTP リクエストを表す Psr\Http\Message\ServerRequestInterface オブジェクトです。
  • Response 2 番目の引数は、現在の HTTP レスポンスを表す Psr\Http\Message\ResponseInterface オブジェクトです。

Arguments 3 番目の引数は、現在のルートの名前付きプレースホルダーの値を含む連想配列です。

レスポンスへのコンテンツの書き込み

  1. HTTP レスポンスにコンテンツを書き込むには、2 つの方法があります

  2. Response オブジェクトの $response->getBody()->write('my content'); メソッドを使用する。

ルートコールバックから単に echo() コンテンツを出力する。 出力バッファリングミドルウェア を追加すると、このコンテンツは現在の HTTP レスポンスオブジェクトに追加または先行して追加されます。

Slim 4 以降、Psr\Http\Message\ResponseInterface オブジェクトを返す必要があることに注意してください。

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

$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'
    ]);
});
依存性注入コンテナClosure インスタンスをルートコールバックとして使用する場合、クロージャの状態は Container インスタンスにバインドされます。 つまり、$this キーワードを介してクロージャ *内* から DI コンテナインスタンスにアクセスできます

注意!

Slim は static クロージャをサポートしていません。

リダイレクトヘルパー

  1. Slim アプリケーションの redirect() メソッドを使用して、GET HTTP リクエストを別の URL にリダイレクトするルートを追加できます。 3 つの引数を受け入れます
  2. リダイレクト *元* のルートパターン(名前付きプレースホルダーはオプション)
  3. リダイレクト *先* の場所。 string または Psr\Http\Message\UriInterface です。
$app->redirect('/books', '/library', 301);

使用する HTTP ステータスコード(オプション、設定されていない場合は 302

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 セグメントを動的に一致させることができます。

フォーマット

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;
});

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

オプションのセグメント

$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) {
    // responds 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;
});

「無制限の」オプションパラメータの場合は、これを行うことができます

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

正規表現マッチング

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

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

ルート名

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

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

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

// Outputs "/hello/Josh?example=name"

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

  • RouteParser の urlFor() メソッドは 3 つの引数を受け入れます
  • $routeName ルート名。 ルートの名前は $route->setName('name') を介して設定できます。 ルートマッピングメソッドは Route のインスタンスを返すため、ルートをマッピングした直後に名前を設定できます。 例:$app->get('/', function () {...})->setName('name')
  • $data ルートパターンプレースホルダーと置換値の連想配列。

$queryParams 生成された URL に追加されるクエリパラメータの連想配列。

ルートグループ

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');
});

ルートを論理グループに編成するのを助けるために、Slim\Appgroup() メソッドも提供します。 各グループのルートパターンは、その中に含まれるルートまたはグループの前に追加され、グループパターン内のプレースホルダー引数は、最終的にネストされたルートで使用できるようになります

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はクロージャをコンテナインスタンスにバインドします。

  • ルートクロージャ内では、$thisPsr\Container\ContainerInterface のインスタンスにバインドされます。

ルートミドルウェア

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

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 committing 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では、ルートアクション関数を定義する方法はいくつかあります。

関数の他に、以下を使用できます。

  • container_key:method
  • Class:method
  • __invoke() メソッドを実装するクラス
  • container_key

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

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

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

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

配列を渡すこともできます。最初の要素にはクラス名、2番目の要素には呼び出されるメソッド名が含まれます。

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

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

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::class, 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);

コントローラーと同様に、クラス名をコンテナに登録すれば、ファクトリを作成し、必要な特定の依存関係のみをアクションクラスに注入できます。

ルートオブジェクト

ミドルウェアでルートのパラメータが必要になる場合があります。

この例では、まずユーザーがログインしていること、次にユーザーが視聴しようとしている特定のビデオを視聴する権限を持っていることを確認しています。

$app->get('/course/{id}', Video::class . ':watch')
    ->add(PermissionMiddleware::class);
<?php

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;

class PermissionMiddleware
{
    public function __invoke(Request $request, RequestHandler $handler)
    {
        $routeContext = RouteContext::fromRequest($request);
        $route = $routeContext->getRoute();
        
        $courseId = $route->getArgument('id');
        
        // do permission logic...
        
        return $handler->handle($request);
    }
}

ルート内からベースパスを取得する

ルート内からベースパスを取得するには、以下のようにします。

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteContext;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/', function(Request $request, Response $response) {
    $routeContext = RouteContext::fromRequest($request);
    $basePath = $routeContext->getBasePath();
    // ...
    
    return $response;
});