Extending Action Argument Resolving

In the controller guide, you've learned that you can get the Request object via an argument in your controller. This argument has to be type-hinted by the Request class in order to be recognized. This is done via the ArgumentResolver. By creating and registering custom value resolvers, you can extend this functionality.

コントローラー ガイドでは、コントローラーの引数を介して Request オブジェクトを取得できることを学びました。この引数は、認識されるために Request クラスによってタイプヒントが与えられている必要があります。これは、ArgumentResolver を介して行われます。カスタム値リゾルバーを作成して登録することで、この機能を拡張できます。

Built-In Value Resolvers

Symfony ships with the following value resolvers in the HttpKernel component:

symfony は、HttpKernel コンポーネントに以下の値リゾルバーを同梱しています:
BackedEnumValueResolver

Attempts to resolve a backed enum case from a route path parameter that matches the name of the argument. Leads to a 404 Not Found response if the value isn't a valid backing value for the enum type.

引数の名前に一致するルート パス パラメーターから、裏付けられた列挙型のケースを解決しようとします。値が列挙型の有効な裏付け値でない場合は、404 Not Found 応答につながります。

For example, if your backed enum is:

たとえば、バックアップされた列挙型が次の場合:
1
2
3
4
5
6
7
8
9
namespace App\Model;

enum Suit: string
{
    case Hearts = 'H';
    case Diamonds = 'D';
    case Clubs = 'C';
    case Spades = 'S';
}

And your controller contains the following:

また、コントローラーには次のものが含まれています。
1
2
3
4
5
6
7
8
9
10
class CardController
{
    #[Route('/cards/{suit}')]
    public function list(Suit $suit): Response
    {
        // ...
    }

    // ...
}

When requesting the /cards/H URL, the $suit variable will store the Suit::Hearts case.

/cards/H URL を要求すると、$suit 変数は theSuit::Hearts ケースを格納します。

Furthermore, you can limit route parameter's allowed values to only one (or more) with EnumRequirement:

さらに、EnumRequirement を使用して、ルート パラメーターの許容値を 1 つ (または複数) のみに制限できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Symfony\Component\Routing\Requirement\EnumRequirement;

// ...

class CardController
{
    #[Route('/cards/{suit}', requirements: [
        // this allows all values defined in the Enum
        'suit' => new EnumRequirement(Suit::class),
        // this restricts the possible values to the Enum values listed here
        'suit' => new EnumRequirement([Suit::Diamonds, Suit::Spades]),
    ])]
    public function list(Suit $suit): Response
    {
        // ...
    }

    // ...
}

The example above allows requesting only /cards/D and /cards/S URLs and leads to 404 Not Found response in two other cases.

上記の例では、/cards/D と /cards/SURLs のみを要求でき、他の 2 つのケースでは 404 Not Found 応答が返されます。

6.1

6.1

The BackedEnumValueResolver and EnumRequirement were introduced in Symfony 6.1.

BackedEnumValueResolver と EnumRequirement は Symfony 6.1 で導入されました。
RequestAttributeValueResolver
Attempts to find a request attribute that matches the name of the argument.
引数の名前に一致するリクエスト属性を見つけようとします。
DateTimeValueResolver

Attempts to find a request attribute that matches the name of the argument and injects a DateTimeInterface object if type-hinted with a class extending DateTimeInterface.

引数の名前に一致するリクエスト属性を見つけようとし、DateTimeInterface を拡張するクラスでタイプヒントされている場合は DateTimeInterface オブジェクトを挿入します。

By default any input that can be parsed as a date string by PHP is accepted. You can restrict how the input can be formatted with the MapDateTime attribute.

デフォルトでは、PHP によって日付文字列として解析できるすべての入力が受け入れられます。MapDateTime 属性を使用して、入力のフォーマット方法を制限できます。

6.1

6.1

The DateTimeValueResolver was introduced in Symfony 6.1.

DateTimeValueResolver は Symfony 6.1 で導入されました。
RequestValueResolver
Injects the current Request if type-hinted with Request or a class extending Request.
Request または classextending Request で型ヒンティングされている場合、現在の Request を注入します。
ServiceValueResolver
Injects a service if type-hinted with a valid service class or interface. This works like autowiring.
有効なサービス クラスまたはインターフェイスでタイプ ヒンティングされている場合は、サービスを注入します。これは自動配線のように機能します。
SessionValueResolver
Injects the configured session class implementing SessionInterface if type-hinted with SessionInterface or a class implementing SessionInterface.
SessionInterface または SessionInterface を実装するクラスで型ヒントされた場合に、SessionInterface を実装する構成済みのセッション クラスを注入します。
DefaultValueResolver
Will set the default value of the argument if present and the argument is optional.
引数が存在し、引数がオプションの場合、引数のデフォルト値を設定します。
UidValueResolver

Attempts to convert any UID values from a route path parameter into UID objects. Leads to a 404 Not Found response if the value isn't a valid UID.

ルート パス パラメーターの UID 値を UID オブジェクトに変換しようとします。値が有効な UID でない場合は、404 Not Found 応答が返されます。

For example, the following will convert the token parameter into a UuidV4 object:

たとえば、次の例ではトークン パラメータが UuidV4 オブジェクトに変換されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Uid\UuidV4;

class DefaultController
{
    #[Route('/share/{token}')]
    public function share(UuidV4 $token): Response
    {
        // ...
    }
}

6.1

6.1

The UidValueResolver was introduced in Symfony 6.1.

UidValueResolver は Symfony 6.1 で導入されました。
VariadicValueResolver
Verifies if the request data is an array and will add all of them to the argument list. When the action is called, the last (variadic) argument will contain all the values of this array.
リクエスト データが配列であるかどうかを確認し、それらすべてを引数リストに追加します。アクションが呼び出されると、最後の (可変個の) 引数には、この配列のすべての値が含まれます。

In addition, some components, bridges and official bundles provide other value resolvers:

さらに、一部のコンポーネント、ブリッジ、および公式バンドルは、他の値リゾルバーを提供します。
UserValueResolver

Injects the object that represents the current logged in user if type-hinted with UserInterface. You can also type-hint your own User class but you must then add the #[CurrentUser] attribute to the argument. Default value can be set to null in case the controller can be accessed by anonymous users. It requires installing the SecurityBundle.

UserInterface で型ヒンティングされている場合、現在ログインしているユーザーを表すオブジェクトを挿入します。独自の User クラスをタイプヒントすることもできますが、その場合は #[CurrentUser] 属性を引数に追加する必要があります。匿名ユーザーがコントローラーにアクセスできる場合は、デフォルト値を null に設定できます。 SecurityBundle をインストールする必要があります。

If the argument is not nullable and there is no logged in user or the logged in user has a user class not matching the type-hinted class, an AccessDeniedException is thrown by the resolver to prevent access to the controller.

引数が null 可能ではなく、ログインしているユーザーがいない場合、またはログインしているユーザーのユーザー クラスが型ヒント クラスと一致しない場合、リゾルバーによって AccessDeniedException がスローされ、コントローラーへのアクセスが防止されます。
EntityValueResolver

Automatically query for an entity and pass it as an argument to your controller.

エンティティを自動的にクエリし、それを引数としてコントローラーに渡します。

For example, the following will query the Product entity which has {id} as primary key:

たとえば、次のクエリは、主キーとして {id} を持つ Product エンティティを照会します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController
{
    #[Route('/product/{id}')]
    public function share(Product $product): Response
    {
        // ...
    }
}

To learn more about the use of the EntityValueResolver, see the dedicated section Automatically Fetching Objects.

EntityValueResolver の使用の詳細については、専用のセクション「オブジェクトの自動取得」を参照してください。

6.2

6.2

The EntityValueResolver was introduced in Symfony 6.2.

EntityValueResolver は Symfony 6.2 で導入されました。
PSR-7 Objects Resolver:
Injects a Symfony HttpFoundation Request object created from a PSR-7 object of type ServerRequestInterface, RequestInterface or MessageInterface. It requires installing the PSR-7 Bridge component.
タイプ ServerRequestInterface、RequestInterface、または MessageInterface の PSR-7 オブジェクトから作成された Symfony HttpFoundation Request オブジェクトを挿入します。PSR-7 Bridge コンポーネントをインストールする必要があります。

Adding a Custom Value Resolver

In the next example, you'll create a value resolver to inject an ID value object whenever a controller argument has a type implementing IdentifierInterface (e.g. BookingId):

次の例では、コントローラの引数が implementationIdentifierInterface (BookingId など) の型を持つ場合は常に ID 値オブジェクトを挿入する値リゾルバーを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
// src/Controller/BookingController.php
namespace App\Controller;

use App\Reservation\BookingId;
use Symfony\Component\HttpFoundation\Response;

class BookingController
{
    public function index(BookingId $id): Response
    {
        // ... do something with $id
    }
}

6.2

6.2

The ValueResolverInterface was introduced in Symfony 6.2. Prior to 6.2, you had to use the ArgumentValueResolverInterface, which defines different methods.

ValueResolverInterface は Symfony 6.2 で導入されました。 6.2 より前では、さまざまなメソッドを定義する ArgumentValueResolverInterface を使用する必要がありました。

Adding a new value resolver requires creating a class that implements ValueResolverInterface and defining a service for it.

新しい値リゾルバーを追加するには、ValueResolverInterface を実装するクラスを作成し、そのサービスを定義する必要があります。

This interface contains a resolve() method, which is called for each argument of the controller. It receives the current Request object and an ArgumentMetadata instance, which contains all information from the method signature.

このインターフェースには、コントローラーの各引数に対して呼び出される resolve() メソッドが含まれています。現在の Request オブジェクトと、メソッド シグネチャからのすべての情報を含む ArgumentMetadata インスタンスを受け取ります。

The resolve() method should return either an empty array (if it cannot resolve this argument) or an array with the resolved value(s). Usually arguments are resolved as a single value, but variadic arguments require resolving multiple values. That's why you must always return an array, even for single values:

resolve() メソッドは、空の配列 (この引数を解決できない場合) または解決された値を含む配列を返す必要があります。通常、引数は単一の値として解決されますが、可変引数は複数の値を解決する必要があります。そのため、単一の値であっても、常に配列を返す必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// src/ValueResolver/IdentifierValueResolver.php
namespace App\ValueResolver;

use App\IdentifierInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

class BookingIdValueResolver implements ValueResolverInterface
{
    public function resolve(Request $request, ArgumentMetadata $argument): array
    {
        // get the argument type (e.g. BookingId)
        $argumentType = $argument->getType();
        if (
            !$argumentType
            || !is_subclass_of($argumentType, IdentifierInterface::class, true)
        ) {
            return [];
        }

        // get the value from the request, based on the argument name
        $value = $request->attributes->get($argument->getName());
        if (!is_string($value)) {
            return [];
        }

        // create and return the value object
        return [$argumentType::fromString($value)];
    }
}

This method first checks whether it can resolve the value:

このメソッドは、最初に値を解決できるかどうかを確認します。
  • The argument must be type-hinted with a class implementing a custom IdentifierInterface;
    引数は、カスタム IdentifierInterface を実装するクラスで型ヒントを指定する必要があります。
  • The argument name (e.g. $id) must match the name of a request attribute (e.g. using a /booking/{id} route placeholder).
    引数名 (例: $id) は requestattribute の名前と一致する必要があります (例: /booking/{id} ルート プレースホルダーを使用)。

When those requirements are met, the method creates a new instance of the custom value object and returns it as the value for this argument.

これらの要件が満たされると、メソッドはカスタム値オブジェクトの新しいインスタンスを作成し、それをこの引数の値として返します。

That's it! Now all you have to do is add the configuration for the service container. This can be done by tagging the service with controller.argument_value_resolver and adding a priority:

それでおしまい!あとは、サービス コンテナーの構成を追加するだけです。これは、サービスに controller.argument_value_resolver をタグ付けし、優先順位を追加することで実行できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# config/services.yaml
services:
    _defaults:
        # ... be sure autowiring is enabled
        autowire: true
    # ...

    App\ValueResolver\BookingIdValueResolver:
        tags:
            - { name: controller.argument_value_resolver, priority: 150 }

While adding a priority is optional, it's recommended to add one to make sure the expected value is injected. The built-in RequestAttributeValueResolver, which fetches attributes from the Request, has a priority of 100. If your resolver also fetches Request attributes, set a priority of 100 or more. Otherwise, set a priority lower than 100 to make sure the argument resolver is not triggered when the Request attribute is present.

優先度の追加はオプションですが、期待値が確実に挿入されるように追加することをお勧めします。リクエストから属性を取得する組み込みの RequestAttributeValueResolver の優先度は 100 です。リゾルバーがリクエスト属性も取得する場合は、優先度を 100 以上に設定します。それ以外の場合は、優先度を 100 未満に設定して、引数リゾルバがトリガーされないようにします。 Request 属性が存在する場合。

To ensure your resolvers are added in the right position you can run the following command to see which argument resolvers are present and in which order they run:

リゾルバーが正しい位置に追加されるようにするには、次のコマンドを実行して、どの引数リゾルバーが存在し、どの順序で実行されるかを確認します。
1
$ php bin/console debug:container debug.argument_resolver.inner --show-arguments