Reputation: 273
I am trying to get a dataTransformer to work on an entity field in symfony 2.
context:
form displays sails that user can select (checkboxes)
this is the first step in a multi-step sail ordering process (later steps display options available for each sail, colors, etc)
This is my form type class:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Co\QuoteBundle\Form\DataTransformer\SailCollectionToStringsTransformer;
class PartsTypeStep1 extends AbstractType {
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Co\QuoteBundle\Entity\Parts',));
$resolver->setRequired(array('sailsAvailable', 'em'));
}
public function buildForm(FormBuilderInterface $formBuilder, array $options)
{
$transformer = new SailCollectionToStringsTransformer($options['em']);
$formBuilder->add(
$formBuilder->create('mainsailparts', 'entity', array(
'class' => 'CoQuoteBundle:Mainsail',
'choices' => $options['sailsAvailable']['mains'],
'multiple' => true,
'expanded' => true,
'label' => 'Mainsails',))
->addModelTransformer($transformer)); //line 58
}
public function getName() {
return 'partsStep1';
}
}
The above works with no errors, but does not display the transformed data. The view is:
__ Race main
__ Cruising main
(__
stands for checkbox)
However, the view I want is:
__ Race main ($1400)
__ Cruising main ($800)
The transformer I have is:
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Co\QuoteBundle\Entity\Sail;
use Doctrine\Common\Collections\ArrayCollection;
class SailCollectionToStringsTransformer implements DataTransformerInterface
{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms a collection of sails to a collection of strings.
* @param ISail|null $sail
* @return string
*/
public function transform($sailCollection)
{
if (null === $sailCollection) {
return null;
}
$labels = new ArrayCollection();
foreach($sailCollection as $sail){
$labels[] = $sail->getName().' ($'.$sail->getBuildPrice().')';
}
return $labels;
}
//reverse transformer... not the issue (yet) because the forward transformer doesn't work
}
When running this through the netbeans debugger, an empty array is passed to the transformer. However, if I change line 58 to ->addViewTransformer($transformer));
and debug, it correctly passes two booleans with the sail id's as the array keys to the transformer. Unfortunately, I can't use the ViewTransformer
because that no longer contains the original strings to change.
Why does the ArrayCollection that should contain the main sails get passed to the transformer as an empty ArrayCollection? The function returns an empty $labels
collection.
I'm not sure what I am doing wrong... Help is much appreciated!!!! Thanks.
Upvotes: 2
Views: 2937
Reputation: 273
I never did find a way how to implement what I was attempting to do. However, the workaround I used is described below.
for the form type class, I used a form event (symfony2 book), and I saved the boatId (that the sails correspond to) in the parts object in the controller, like so:
$partsObject = new Parts($boat->getId());
$form = $this->createForm(new PartsTypeStep1(), $partsObject, array(
'em' => $this->getDoctrine()->getManager()));
The form type class now looks like this:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Doctrine\ORM\EntityRepository;
class PartsTypeStep1 extends AbstractType {
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Co\QuoteBundle\Entity\Parts',));
$resolver->setRequired(array('em'));
}
public function buildForm(FormBuilderInterface $formBuilder, array $options)
{
$factory = $formBuilder->getFormFactory();
$em = $options['em'];
$formBuilder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use($factory, $em){
$form = $event->getForm();
$data = $event->getData();
if (!$data || !$data->getDateTime()) {
return;
}
else {
$boatClass = $data->getBoatId();
$formOptions = array(
'class' => 'CoQuoteBundle:Mainsail',
'multiple' => true,
'expanded' => true,
'property' => 'displayString',
'label' => 'Mainsails',
'query_builder' => function(EntityRepository $er) use ($boatClass) {
return $er->createQueryBuilder('m')
->where('m.boatType = :boatClass')
->setParameter('boatClass', $boatClass);
},
);
$form->add($factory->createNamed('mainsailparts', 'entity', null, $formOptions));
}
}
);
}
public function getName() {
return 'partsStep1';
}
I also needed to add the displayString
property in the Mainsail
class (I only added a getter, not an actual variable for the string). So the Mainsail
class now has this:
public function getDisplayString(){
return $this->name . ' - ' . $this->descr . ' ($' . $this->buildPrice . ')';
}
The only issue I ran into with this workaround is what happens if the query returns an empty result, because twig will automatically render the form label ('Mainsails') whether or not it has any checkboxes to render. I got around that issue like this:
{% if form.mainsailparts|length > 0 %}
<div class="groupHeading">{{ form_label(form.mainsailparts) }}</div>
{% for child in form.mainsailparts %}
{# render each checkbox .... #}
{% endfor %}
{% else %}
{% do form.mainsailparts.setRendered %}
{% endif %}
I don't know if this is the recommended solution in this case, but it does work with form validation (at least disallowing progression if no sails are selected, I don't need anything more rigorous).
I'm not going to mark this as the answer since it doesn't answer the question (how to apply transformer to entity field), but it is a workaround for anyone dealing with the same problem.
Upvotes: 1