Как внедрить зависимости в частный сервис, используя контейнер? - PullRequest
2 голосов
/ 14 марта 2019

Я разрабатываю Форма запроса для Symfony, но у меня есть одна проблема.

Я внедряю FormRequest экземпляров в действие контроллера, используя разрешение значения аргумента.Но я бы хотел внедрить сервисы в FormRequest экземпляр, не делая его общедоступным в services.yaml файле.

Вот мой Resolver:

<?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) Мне нужно сделать его общедоступным.

Вот запрос формы:

<?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]),
            ],
        ]);
    }
}

1 Ответ

0 голосов
/ 15 марта 2019

Спасибо @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());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...