我正在为Symfony开发Form Request,但是我有一个问题。
我使用参数值解析将FormRequest
实例注入到控制器动作中。但是我想向FormRequest
实例注入服务,而不在services.yaml
文件中将其公开。
这是我的解析器:
<?php
namespace App\Resolver;
use App\Exception\FormValidationException;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ControllerRequestResolver implements ArgumentValueResolverInterface
{
/**
* @var SerializerInterface
*/
private $serializer;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* @var DecoderInterface
*/
private $decoder;
/**
* @var ContainerInterface
*/
private $container;
public function __construct(
SerializerInterface $serializer,
ValidatorInterface $validator,
DecoderInterface $decoder,
ContainerInterface $container
) {
$this->serializer = $serializer;
$this->validator = $validator;
$this->decoder = $decoder;
$this->container = $container;
}
/**
* {@inheritdoc}
* @throws FormValidationException
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
$data = $this->decoder->decode($request->getContent(), 'json');
$request->request->replace($data);
$formRequestClass = $argument->getType();
/** @var FormRequest $form */
$form = $this->container->has($formRequestClass)
? $this->container->get($formRequestClass)
: new $formRequestClass();
$form->initialize(
$request->query->all(), $request->request->all(), $request->attributes->all(),
$request->cookies->all(), $request->files->all(), $request->server->all(), $request->getContent()
);
if (!$form->authorize()) {
throw new AccessDeniedHttpException('Access denied.');
}
$violations = $this->validator->validate($data, $form->rules());
if ($violations->count() > 0) {
throw new FormValidationException($violations, 'Validation error.');
}
yield $form;
}
/**
* {@inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return (new \ReflectionClass($argument->getType()))->isSubclassOf(FormRequest::class);
}
}
因此,要在FormRequest
中接收具有所需依赖项的$this->container->get($formRequestClass)
,我需要将其公开。
这是FormRequest:
<?php
namespace App\Request;
use App\Entity\User;
use App\Rule\UniqueConfig;
use App\Service\Randomizer;
use App\Validator\Constraints\UniqueEntity;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Validator\Constraints as Assert;
class UserRequest extends FormRequest
{
/**
* @var null|Randomizer
*/
private $randomizer;
public function __construct(Randomizer $randomizer)
{
$this->randomizer = $randomizer;
}
public function authorize(): bool
{
return $this->randomizer->getNumber() > 0.5;
}
public function rules(): Assert\Collection
{
return new Assert\Collection([
'email' => [
new Assert\NotBlank(),
new Assert\Email(),
new UniqueEntity([
'config' => new UniqueConfig(User::class, 'u', 'email', function ($value, QueryBuilder $qb) {
// You can add here additional filtering
}),
]),
],
'firstName' => [
new Assert\Length(['max' => 255]),
],
'lastName' => [
new Assert\Length(['max' => 255]),
],
]);
}
}
答案 0 :(得分:0)
感谢@Cerad的帮助!这是总结果:
<?php
namespace App\Locator;
use Symfony\Component\DependencyInjection\ServiceLocator;
class FormRequestServiceLocator extends ServiceLocator
{
}
我将(FormRequestServiceLocator
)注入ControllerRequestResolver
<?php
namespace App\DependencyInjection\Compiler;
use App\Locator\FormRequestServiceLocator;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class FormRequestPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(FormRequest::class)) {
return;
}
$taggedServices = $container->findTaggedServiceIds('app.form_request.tag');
$serviceReferences = [];
foreach ($taggedServices as $id => $tags) {
if ($id === FormRequest::class) {
continue;
}
$serviceReferences[$id] = new Reference($id);
}
$driverLocator = $container->getDefinition(FormRequestServiceLocator::class);
$driverLocator->setArguments([$serviceReferences]);
}
}
<?php
namespace App;
// ...
class Kernel extends BaseKernel
{
// ...
protected function build(ContainerBuilder $container)
{
parent::build($container);
$container->registerForAutoconfiguration(FormRequest::class)
->addTag('app.form_request.tag');
$container->addCompilerPass(new FormRequestPass());
}
}