diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 8bbcea505c1..c2ce3ae0486 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -28,13 +28,13 @@ Creating the Transformer First, create an ``IssueToNumberTransformer`` class - this class will be responsible for converting to and from the issue number and the ``Issue`` object:: - // src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformer.php - namespace Acme\TaskBundle\Form\DataTransformer; + // src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php + namespace AppBundle\Form\DataTransformer; + use AppBundle\Entity\Issue; + use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; - use Doctrine\Common\Persistence\ObjectManager; - use Acme\TaskBundle\Entity\Issue; class IssueToNumberTransformer implements DataTransformerInterface { @@ -60,7 +60,7 @@ for converting to and from the issue number and the ``Issue`` object:: public function transform($issue) { if (null === $issue) { - return ""; + return ''; } return $issue->getNumber(); @@ -70,9 +70,7 @@ for converting to and from the issue number and the ``Issue`` object:: * Transforms a string (number) to an object (issue). * * @param string $number - * * @return Issue|null - * * @throws TransformationFailedException if object (issue) is not found. */ public function reverseTransform($number) @@ -82,7 +80,7 @@ for converting to and from the issue number and the ``Issue`` object:: } $issue = $this->om - ->getRepository('AcmeTaskBundle:Issue') + ->getRepository('AppBundle:Issue') ->findOneBy(array('number' => $number)) ; @@ -111,29 +109,116 @@ for converting to and from the issue number and the ``Issue`` object:: Using the Transformer --------------------- -Now that you have the transformer built, you just need to add it to your -issue field in some form. +As seen above our transformer requires an instance of an object manager. While for most +use-cases it is sufficient to use the default entity manager, you will sometimes need +to explicitly choose the one to use. To achieve this, you can use a factory:: + + // src/AppBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php + namespace AppBundle\Form\DataTransformer; + + use Doctrine\Common\Persistence\ManagerRegistry; + + class IssueToNumberTransformerFactory + { + /** + * @var ManagerRegistry + */ + private $registry; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + public function create($om) + { + return new IssueToNumberTransformer($this->registry->getManager($om)); + } + } + +.. configuration-block:: + + .. code-block:: yaml + + services: + app.issue_transformer_factory: + class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory + arguments: ["@doctrine"] + public: false + + app.type.task: + class: AppBundle\Form\TaskType + arguments: ["@app.issue_transformer_factory"] + tag: + - { name: form.type, alias: app_task } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + // ... + + $container + ->setDefinition('app.issue_transformer_factory', new Definition( + 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' + ), array( + new Reference('doctrine'), + )) + ->setPublic(false) + ; + + $container + ->setDefinition('app.type.task', new Definition( + 'AppBundle\Form\TaskType' + ), array( + new Reference('app.issue_transformer_factory'), + )) + ->addTag('form.type', array('alias' => 'app_task')) + ; + +Now that you have the capability to build the transformer with the desired object manager, you +just need to create it from your issue field in some form. You can also use transformers without creating a new custom form type by calling ``addModelTransformer`` (or ``addViewTransformer`` - see `Model and View Transformers`_) on any field builder:: + // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form; + + use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\FormBuilderInterface; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; + use Symfony\Component\OptionsResolver\OptionsResolverInterface; class TaskType extends AbstractType { - public function buildForm(FormBuilderInterface $builder, array $options) + /** + * @var IssueToNumberTransformerFactory + */ + private $factory; + + public function __construct(IssueToNumberTransformerFactory $factory) { - // ... + $this->factory = $factory; + } - // the "em" is an option that you pass when creating your form. Check out - // the 3rd argument to createForm in the next code block to see how this - // is passed to the form (also see setDefaultOptions). - $entityManager = $options['em']; - $transformer = new IssueToNumberTransformer($entityManager); + public function buildForm(FormBuilderInterface $builder, array $options) + { + $transformer = $this->factory->create($options['om']); - // add a normal text field, but add your transformer to it $builder->add( $builder->create('issue', 'text') ->addModelTransformer($transformer) @@ -144,27 +229,19 @@ by calling ``addModelTransformer`` (or ``addViewTransformer`` - see { $resolver ->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', - )) - ->setRequired(array( - 'em', + 'data_class' => 'AppBundle\Entity\Task', )) - ->setAllowedTypes(array( - 'em' => 'Doctrine\Common\Persistence\ObjectManager', - )); - - // ... + ->setRequired(array('om')) + ; } - - // ... } This example requires that you pass in the entity manager as an option when creating your form. Later, you'll learn how you could create a custom ``issue`` field type to avoid needing to do this in your controller:: - $taskForm = $this->createForm(new TaskType(), $task, array( - 'em' => $this->getDoctrine()->getManager(), + $taskForm = $this->createForm('app_task', $task, array( + 'om' => 'default', )); Cool, you're done! Your user will be able to enter an issue number into the @@ -254,33 +331,26 @@ a form that uses the transformer. Because of these, you may choose to :doc:`create a custom field type `. First, create the custom field type class:: - // src/Acme/TaskBundle/Form/Type/IssueSelectorType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/IssueSelectorType.php + namespace AppBundle\Form; + use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; - use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class IssueSelectorType extends AbstractType { - /** - * @var ObjectManager - */ - private $om; - - /** - * @param ObjectManager $om - */ - public function __construct(ObjectManager $om) + private $factory; + + public function __construct(IssueToNumberTransformerFactory $factory) { - $this->om = $om; + $this->factory = $factory; } public function buildForm(FormBuilderInterface $builder, array $options) { - $transformer = new IssueToNumberTransformer($this->om); + $transformer = $this->factory->create($options['om']); $builder->addModelTransformer($transformer); } @@ -288,6 +358,7 @@ First, create the custom field type class:: { $resolver->setDefaults(array( 'invalid_message' => 'The selected issue does not exist', + 'om' => 'default' )); } @@ -310,24 +381,49 @@ it's recognized as a custom field type: .. code-block:: yaml services: - acme_demo.type.issue_selector: - class: Acme\TaskBundle\Form\Type\IssueSelectorType - arguments: ["@doctrine.orm.entity_manager"] + app.issue_transformer_factory: + class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory + arguments: ["@doctrine"] + public: false + app.type.issue_selector: + class: AppBundle\Form\IssueSelectorType + arguments: ["@app.issue_transformer_factory"] tags: - { name: form.type, alias: issue_selector } .. code-block:: xml - - + + + + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + // ... + + $container + ->setDefinition('app.issue_transformer_factory', new Definition( + 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' + ), array( + new Reference('doctrine'), + )) + ->setPublic(false) + ; + $container - ->setDefinition('acme_demo.type.issue_selector', array( - new Reference('doctrine.orm.entity_manager'), + ->setDefinition('app.type.issue_selector', new Definition( + 'AppBundle\Form\IssueSelectorType' + ), array( + new Reference('app.issue_transformer_factory'), )) ->addTag('form.type', array( 'alias' => 'issue_selector', @@ -337,8 +433,8 @@ it's recognized as a custom field type: Now, whenever you need to use your special ``issue_selector`` field type, it's quite easy:: - // src/Acme/TaskBundle/Form/Type/TaskType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface;