symfony2 form error - symfony

The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess,
but is an instance of class Ecs\CrmBundle\Entity\Customer.
Is the error I get in my browser..
FORM CODE:
<?php
namespace Ecs\CrmBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CustomerDefaultConfigType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('customerStatus')
->add('tags', null, array('multiple' => true, 'property' => 'tag_name'))
;
}
public function getName()
{
return 'ecs_crmbundle_customerdefaultconfigtype';
}
}
and the controller Action:
<?php
namespace Ecs\CrmBundle\Controller;
use Ecs\CrmBundle\Entity\CustomerDefaultConfig;
use Ecs\CrmBundle\Form\CustomerDefaultConfigType;
public function newAction()
{
$entity = new CustomerDefaultConfig();
$form = $this->createForm(new CustomerDefaultConfigType(), $entity);
return $this->render('EcsCrmBundle:CustomerDefaultConfig:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView()
));
}
This is using symfony2.1 with composer... Any ideas on how to get this working?

Since the last form refactoring, you have to specified the data_class in the setDefaultOptions method in your type.
See here (search for data_class).
Edit: Correct link

Related

Add "Validation Constraints" to a form which is not attached to an entity

I created the following fom class wihout using an entity :
<?php
// src/OC/PlatformBundle/Form/AdvertType.php
namespace OC\PlatformBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date', 'date')
->add('title', 'text')
->add('save', 'submit')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array());
}
public function getName()
{
return 'my_form';
}
}
I'd like to add a set of Validation Constraints to validate this form.
You can add constraints like this :
$builder
->add('title', 'text', array(
'constraints' => array(
new \Symfony\Component\Validator\Constraints\NotBlank(['message' => 'Your error message']),
)
));
Doc here : http://symfony.com/doc/current/book/forms.html

Passing options array to EventListerner from a FormType

I have a FormType to be reused for editing & creating records. This form has one entity field which renders into a select populated depending the record id, so I need to skip this field when creating a new record. I read http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html and after changing some roots, everything seems to be fine until I'm trying to edit an existing record when I get stuck with this error:
ContextErrorException: Notice: Undefined variable: options in /Users/a77/Documents/DEV/UVox Com/src/Acme/DemoBundle/EventListener/VenueFieldSubscriber.php line 32
Mi VenuesFormType is :
namespace Acme\DemoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Acme\DemoBundle\EventListener\VenueFieldSubscriber;
class VenuesType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text')
->add('password', 'text')
->add('save', 'submit', array('label' => 'Save', 'attr' => array('data-loading-text' => 'loading', 'class' => "btn btn-primary")))
->addEventSubscriber(new VenueFieldSubscriber());
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Acme\DemoBundle\Entity\Venues'
));
}
/**
* #return string
*/
public function getName() {
return 'acme_demobundle_venues';
}
}
And my VenueFieldSubscriber is :
namespace Acme\DemoBundle\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
class VenueFieldSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
// Tells the dispatcher that you want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
if (!$product || null === $product->getId()) {
// no action for new record
} else {
$form->add('user', 'entity', array(
'class' => 'AcmeDemoBundle:Users',
'property' => 'username',
'query_builder' => function(EntityRepository $er) use ($options) {
return $er->createQueryBuilder('u')
->Where('u.venue=?1')
->andWhere('u.usertype >1')
->orderBy('u.username', 'ASC')
->setParameter(1, $options['attr']['venueid']);
}
));
}
}
Any ideas, what I'm missing ? $options['attr']['venueid'] should give me the id of the record I'm editing... Thanks ;)
I assume that after
$product = $event->getData();
$product is an object of Acme\DemoBundle\Entity\Venues class.
Then instead of
->setParameter(1, $options['attr']['venueid'])
try
->setParameter(1, $product->getId())

How to properly inject container to a form file without breaking the CRUD related actions?

Currently, this is how I injected container into my form (by making it a service):
ApFloorType.php
namespace Techforge\ApartmentBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ApFloorType extends AbstractType
{
//in the controller, they are differently initialised
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function buildForm(FormBuilder $builder, array $options)
{
$bath_choices = $this->container->getParameter('bath_choices');
$bed_choices = $this->container->getParameter('bed_choices');
$builder
->add('baths', 'choice', array('choices' => $bath_choices))
->add('beds', 'choice', array('choices' => $bed_choices))
->add('name')
->add('file')
->add('plan_description', 'textarea')
->add('min_max_feet', 'text')
->add('deposit', 'text')
->add('application_fee')
->add('rental_deposit')
->add('lease_terms', 'textarea')
->add('threshhold_value')
->add('auto_accept')
->add('pending')
->add('apartment', 'hidden', array('property_path' => false))
->add('pre_post_update', 'hidden')
->add('photos', 'collection', array('type' => new ApFloorImageType()))
;
}
public function getName()
{
return 'techforge_apartmentbundle_apfloortype';
}
}
The problem is that the ApFloor CRUD system stoped working after this because, in my controller, I couldn't just write
$form = $this->createForm(new ApFloorType(), $floor);
This would produce the following error:
Catchable Fatal Error: Argument 1 passed to
Techforge\ApartmentBundle\Form\ApFloorType::__construct() must
implement interface
Symfony\Component\DependencyInjection\ContainerInterface, none given,
called in
/home/stormlifter/webapps/pmr/src/Techforge/ApartmentBundle/Controller/Manager/ApFloorController.php
on line 38 and defined in
/home/stormlifter/webapps/pmr/src/Techforge/ApartmentBundle/Form/ApFloorType.php
line 16
I solved this by calling form create method like this;
$form = $this->createForm($this->get('apfloortype'), $floor);
So my question is, is there a workaround this so I don't have to change each createForm() call in my CRUD controller after making it a service?
This is what I did in the FormType:
$data = Class::getYourData();
Because I don;t want (like you) to modify each controller to set extra parameter to the form..

Symfony 2 Form: how to bind data

How to bind data which is gained by using call:
$attributes = $em->getRepository('\OBB\Entity\Attribute')->findAllWithAllRelations($id);
to a Symfony 2 Form
Because according to a manual you need to have a method defined in Entity which is bound to a form.
You should add a form type for editing an individual attribute. This could look something like:
namespace OBB\Form;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'OBB\Entity\Attribute',
);
}
public function getName()
{
return 'obb_attribute';
}
}
Then you can use a collection form to edit a collection of them simultaneously.
$form = $this->createForm('collection', $attributes, array(
'type' => new AttributeType(),
));

Is it possible to have collection field in Symfony2 form with different choices?

I have a collection field with elements of type choice in my Symfony form. Each element should have different list o choices. How can I arrange this in Symfony2? I can't use choices option because every element will have the same choices. I have seen the choice_list option which takes an object that can produce the list of options, but I don't see how it could produce a different choices for different elements in collection.
Any idea how to deal with that?
I think you need form event : http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html.
To change the default way the collection is made.
The main form is simple:
namespace Acme\Bundle\AcmeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Acme\Bundle\AcmeBundle\Form\DescriptorDumpFieldsType;
class TranscodingType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('descriptorDumpFields', 'collection', array('type' => new DescriptorDumpFieldsType()));
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\Bundle\AcmeBundle\Entity\Descriptor',
);
}
public function getName()
{
return 'descriptor';
}
}
Just a simple form with a collection of sub forms.
The second one use a form subscriber who handle the form creation. (using form events)
So the first form is created normaly and add many DescriptorDumpFieldsType wich are dynamicly created.
namespace Acme\Bundle\AcmeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormTypeInterface;
use Acme\Bundle\AcmeBundle\Form\EventListener\TranscodingSubscriber;
class DescriptorDumpFieldsType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$subscriber = new TranscodingSubscriber($builder->getFormFactory());
$builder->addEventSubscriber($subscriber);
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\Bundle\AcmeBundle\Entity\DescriptorDumpField',
);
}
public function getName()
{
return 'desc_dump_field';
}
}
The form subscriber :
namespace Acme\Bundle\AcmeBundle\Form\EventListener;
use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Acme\Bundle\AcmeBundle\Entity\DumpField;
use Acme\Bundle\AcmeBundle\Form\Transcoding\DataTransformer\JsonToHumanDateTransformer;
class TranscodingSubscriber implements EventSubscriberInterface
{
private $factory;
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
public static function getSubscribedEvents()
{
return array(FormEvents::SET_DATA => 'setData');
}
public function setData(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
if (!is_null($data)) {
$this->buildForm($data, $form);
}
}
protected function buildForm($data, $form)
{
switch ($data->getDumpField()->getType()) {
case DumpField::TYPE_ENUM:
$type = 'enum'.ucfirst($data->getDumpField()->getKey());
$class = 'dump_field_'.strtolower($data->getDumpField()->getKey());
$form->add($this->factory->createNamed('collection', 'transcodings', null, array('required' => false, 'type' => $type, 'label' => $data->getDumpField()->getKey(), 'attr' => array('class' => $class))));
break;
case DumpField::TYPE_DATE:
$transformer = new JsonToHumanDateTransformer();
$class = 'dump_field_'.strtolower($data->getDumpField()->getKey());
$builder = $this->factory->createNamedBuilder('human_date', 'params', null, array('label' => $data->getDumpField()->getKey(), 'attr' => array('class' => $class)));
$builder->prependNormTransformer($transformer);
$form->add($builder->getForm());
break;
}
}
}
So you can customize the way you want, each sub-form of the collection in buildForm.

Resources