Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update data_transformers.rst #5307

Merged
merged 4 commits into from
Jun 27, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 152 additions & 56 deletions cookbook/form/data_transformers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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();
Expand All @@ -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)
Expand All @@ -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))
;

Expand Down Expand Up @@ -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"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make the transformer factory private.

public: false

app.type.task:
class: AppBundle\Form\TaskType
arguments: ["@app.issue_transformer_factory"]
tag:
- { name: form.type, alias: app_task }

.. code-block:: xml

<service id="app.issue_transformer_factory"
class="AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory" public="false">
<argument type="service" id="doctrine"/>
</service>

<service id="app.type.task"
class="AppBundle\Form\TaskType">
<argument type="service" id="app.issue_transformer_factory"/>
<tag name="form.type" alias="app_task"/>
</service>

.. 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)
;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is invalid (same below). It should be something like:

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
// ...

$definition = new Definition(
    'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory',
    array(new Reference('doctrine'))
);
$container->setDefinition('app.factory.issue_transformer', $definition);


$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)
Expand All @@ -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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be:

$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
Expand Down Expand Up @@ -254,40 +331,34 @@ a form that uses the transformer.
Because of these, you may choose to :doc:`create a custom field type </cookbook/form/create_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);
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'invalid_message' => 'The selected issue does not exist',
'om' => 'default'
));
}

Expand All @@ -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

<service id="acme_demo.type.issue_selector" class="Acme\TaskBundle\Form\Type\IssueSelectorType">
<argument type="service" id="doctrine.orm.entity_manager"/>
<service id="app.issue_transformer_factory"
class="AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory" public="false">
<argument type="service" id="doctrine"/>
</service>

<service id="app.type.issue_selector"
class="AppBundle\Form\IssueSelectorType">
<argument type="service" id="app.issue_transformer_factory"/>
<tag name="form.type" alias="issue_selector" />
</service>

.. 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',
Expand All @@ -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;
Expand Down