Короткий рассказ:
Мне нужно получить сервис Request из класса, который не наследуется от класса Controller (это DataTransformer, который, как правило, реализует DataTransformerInterface).
Длинная история:
У меня встроенная форма с полем электронной почты. Если пользователь вводит электронное письмо, которое не существует в моей таблице базы данных пользователей, я хочу создать нового пользователя с этим электронным письмом.
Для этого мне нужно установить свой IP-адрес, поэтому я следил за учебником встроенных форм и рецептом трансформатора данных , но, наконец, я понятия не имею, где я могу вставить экземпляр запроса в свой конструктор DataTransformer или что-то еще.
Если бы я был в классе, расширяющем форму Контроллера, это было бы так же просто, как:
$this->container->get('request')->getClientIp()
Вы можете сделать это с помощью «Реферирование (инжекция)» . В вашем случае вы хотите ввести запрос, который является сервисом из более узкой области .
Если вы используете трансформаторы, вы, вероятно, уже используете пользовательский тип формы и создаете экземпляр Data Transformer в вашем методе формы формы FormForm , см. Здесь для получения дополнительной информации.
Вы хотите ввести объект Request в пользовательский тип формы, затем он может передаваться в Data Transformer в качестве параметра конструктора.
Для этого измените файл services.yml
в своем пакете и добавьте конструктор к типу пользовательской формы и настраиваемому преобразователю данных следующим образом:
// src/Acme/HelloBundle/Resources/config/services.yml parameters: // ... services: acme.type.custom_type: class: Acme\HelloBundle\Form\Type\CustomType scope: request arguments: ["@doctrine.odm.mongodb.document_manager", "@request"] tags: - { name: form.type, alias: custom_type }
Обновите класс CustomType следующим образом:
<?php // src/Acme/HelloBundle/Form/Type/CustomType.php namespace Acme\HelloBundle\Form\Type; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Doctrine\ODM\MongoDB\DocumentManager; use Acme\HelloBundle\Form\DataTransformer\CustomDataTransformer; class CustomType extends AbstractType { private $request; private $dm; public function __construct(DocumentManager $dm, Request $request) { $this->dm = $dm; $this->request = $request; } public function buildForm(FormBuilderInterface $builder, array $options) { // Create a new Data Transformer that will be able to use the Request Object! $transformer = new CustomDataTransformer($this->dm, $this->request); $builder->addModelTransformer($transformer); } // ... }
и, наконец, добавить конструктор в трансформатор, аналогичный тому, который был добавлен в Тип формы:
<?php // src/Acme/HelloBundle/Form/DataTransformer/CustomDataTransformer.php namespace Acme\HelloBundle\Form\DataTransformer; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Form\DataTransformerInterface; use Doctrine\ODM\MongoDB\DocumentManager; class CustomDataTransformer implements DataTransformerInterface { private $request; private $dm; public function __construct(DocumentManager $dm, Request $request) { $this->dm = $dm; $this->request = $request; } // ... }
Обратите внимание, что вместе с запросом я ввел MongoDB DocumentManager, это означает, что можно вводить несколько объектов.
Хорошо, это просто: в моем вопросе я предполагал, что DataTransformer будет вызван «магически», но он создается при создании формы, поэтому, если это кому-то помогает, вот оно:
В классе DataTransformer (реализация DataTransformerInterface):
Определите новые атрибуты класса, чтобы удерживать инъекцию зависимостей:
/** * @var EntityManager */ private $entityManager; /** * @var \Symfony\Component\DependencyInjection\Container */ private $container;
Определите конструктор как:
public function __construct( EntityManager $entityManager, Container $container ) { $this->entityManager = $entityManager; $this->container = $container; }
В вашем классе формы (реализация AbstractType)
Добавьте следующие методы метода setDefaultOptions :
$resolver->setRequired( array( 'em', 'container' ) ); $resolver->setAllowedTypes( array( 'em' => 'Doctrine\Common\Persistence\ObjectManager', 'container' => 'appDevDebugProjectContainer', ) );
В методе buildForm примените трансформатор, как определено в рецепте трансформатора, но введите его как:
$entityManager = $options['em']; $container = $options['container']; $transformer = new FantasticTransformer( $entityManager, $container );
В вашем контроллере, когда вы вызываете метод createForm , можно ли вводить экземпляры EntityManager и Container, просто добавляя их следующим образом:
$form = $this->createForm( 'your_form', $lookup, array( 'action' => $this->generateUrl( 'your_action_url' ), 'em' => $this->getDoctrine()->getManager(), 'container' => $this->container ) );
Теперь вы можете, наконец, получить IP-адрес клиента из службы запросов, вызывающего контейнер, определенный в конструкторе вашего класса DataTransformer:
$ip = $this->container->get('request')->getClientIp();
Обратите внимание, что мы вводим контейнер вместо экземпляра запроса, это связано с областями Symfony .