I try to save values of one - three checkboxes in field category in database, but i get the error :
Notice: Array to string conversion in /var/www/OnTheWay/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php line 120
The field:
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $category;
Get & Set:
/**
* #return mixed
*/
public function getCategory()
{
return $this->category;
}
/**
* #param $category
*/
public function setCategory($category)
{
$this->category[] = $category;
}
Profile type:
namespace Vputi\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProfileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('fio');
$builder->add('birthDate', null, array('widget' => 'single_text'));
$builder->add('file');
$builder->add('yearOnRoad');
$builder->add('telephone');
$builder->add('contactMail');
$builder->add('role', 'choice', array('choices' => array(1 => 'За рулем') ,'expanded'=>true, 'multiple' => true,));
$builder->add('category', 'choice', array(
'choices' => array('A' => 'Категория А', 'B' => 'Категория B', 'C' => 'Категория C',),
'expanded' => true,
'multiple' => true,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' =>'Vputi\UserBundle\Entity\Profile',
'cascade_validation' => true,
));
}
}
Here is my form type, I hope you help me, and iam ommit getName() method.
The problem is $category is defined as a string but you're using it like an array.
The solution depends on exactly what you want to accomplish. If you want it to be mapped as an array you have to do this:
/**
* #ORM\Column(type="array", nullable=true)
*/
private $category;
When using Doctrine's array type, make sure you take this into account: How to force Doctrine to update array type fields?
Related
Getting below error..
An exception has been thrown during the rendering of a template ("The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class Proxies__CG__\BLA\MyBundle\Entity\TransportType. You can avoid this error by setting the "data_class" option to "Proxies__CG__\BLA\MyBundle\Entity\TransportType" or by adding a view transformer that transforms an instance of class Proxies__CG__\BLA\MyBundle\Entity\TransportType to scalar, array or an instance of \ArrayAccess.") in MyBundle:Shipping:form.html.twig at line 8.
$builder->add('variables','collection', array(
'type' => new AbcType(),
'options' => array(
'required' => true,
),
'constraints' => new NotNull()));
AbcType.php
class AbcType extends AbstractType
{
/**
* Build form
*
* #param FormBuilder $builder
* #param array $options
*
* #return void
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('importance', null, array('empty_value'=>false,'expanded'=>true,
'required'=>true,'multiple'=>false,
'constraints' => new NotNull()))
->add('timeSpent', null, array(
'empty_value'=>false,'expanded'=>true,
'required'=>true,'multiple'=>false,
'constraints' => new NotNull()
)
);
}
/**
* setDefaultOptions set Default values
*
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Demo\MyBundle\Entity\Abc'
));
}
/**
* getName will return Form name
* #return string
*/
public function getName()
{
return 'demo_mybundle_abctype';
}
}
I fixed issue using below link....
https://github.com/symfony/symfony/issues/14877
Make sure you have the 'data_class' default specified in the setDefaultOptions in the AbcType class:
... build form ...
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AbcBundle\Entity\Abc'
));
}
In symfony I have a multi-stage form that embeds other forms. Some of these embedded forms have dynamic validation groups set via the validation_groups callback in the configureOptions (used to be setDefaultOptions) method for the type.
When the form is submitted via a submit that does not have its validation_groups option set, then these callbacks are run and the correct validation groups are used. But when I set the validation_groups option of a submit to a type that has this callback, then the callback is not run and the groups are not set as needed.
Are there any options that need to be set to have this working?
Controller
$form = $this->createForm(new RegistrationType());
$form->add('Submit1', 'submit', array(
'validation_groups' => array('Person'),
))
->add('Submit2', 'submit', array(
'validation_groups' => array('Contact', 'Address'),
))
;
...
Registration type
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RegistrationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('person', new PersonType())
->add('maritalStatus', 'entity', array(
'class' => 'AppBundle:MaritalStatus',
'choice_label' => 'status',
))
->add('spouse', new SpouseType())
->add('homeAddress', new AddressType())
->add('postalAddress', new AddressType())
->add('contact', new ContactType())
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Registration',
'cascade_validation' => true,
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_registration';
}
}
PersonType
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PersonType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('name', 'text')
->add('surname', 'text')
->add('fullName', 'text', array('required' => false))
->add('southAfrican', 'checkbox', array(
'required' => false,
'data' => true,
))
->add('identification', 'text')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Person',
// Setting some groups dynamically based on input data
'validation_groups' => function (FormInterface $form) {
$data = $form->getData();
$groups = array('Person');
// Add southAfrican validation group if is a southAfrican
if ($data->isSouthAfrican() === true) {
$groups[] = 'southAfrican';
}
// Add Married validation group is this is spouse and Married is selected
if ($form->getParent()->getConfig()->getName() === 'spouse') {
if ($form->getParent()->getParent()->getData()->getMaritalStatus()->getStatus() === 'Married') {
$groups[] = 'married';
} elseif (($key = array_search('southAfrican', $groups)) !== false) {
unset($groups[$key]);
}
// If this is not spouse then this is applicant so add its validation group
} else {
$groups[] = 'applicant';
}
return $groups;
},
'cascade_validation' => true,
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_person';
}
}
Submit button validation groups have precedence over Type's validation groups. Form::getClickedButton returns a clicked button that is bound to a current form or its parents.
The case you described can be resolved by passing callbacks instead of plain groups to the submit buttons. E.g.:
$form->add('Submit1', 'submit', array(
'validation_groups' => function (FormInterface $form) {
if ($form->isRoot()) {
return array('Person');
} else {
return ($form->getConfig()->getOptions('validation_groups'))();
}
},
))
How can you have a form type with a collection field of the same form type embedded inside it? I've got the following form type within my Symfony 2 project:
class MenuType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('label')
->add('uri')
// the following does not work
->add('children', 'collection', array(
'type' => new MenuType(),
'allow_add' => true,
'by_reference' => false
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mango\CoreDomain\Model\Menu',
'csrf_protection' => false,
'cascade_validation' => true
));
}
/**
* #return string
*/
public function getName()
{
return 'menu';
}
}
When you run this, it will not work. How would something like this be solved in a clean way?
The class that this form type uses is called Menu and has a property children which is an array of Menu objects.
Thanks!
Hello everybody (please excuse my English).
I want to do an application which needs to allow that the users must fill out on a form their personal data, their children, grandchildren and great-grandchildren (a little family tree).
class Person
{
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $firstname;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $lastname;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime")
*/
private $dateOfBirth;
/**
* #var Person
*
* #ORM\ManyToMany(targetEntity="Person")
*/
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
}
}
In the PersonType class, I do the following:
class PersonType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstname');
$builder->add('lastname');
$builder->add('dateOfBirth');
$builder->add('children', 'collection', array(
'type' => new PersonType(),
'allow_add' => true,
'by_reference' => false,)
);
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person'
));
}
/**
* #return string
*/
public function getName()
{
return 'person';
}
}
In this way, I use the PersonType in the controller as below:
public function newAction()
{
$entity = new Person();
$form = $this->createForm(new PersonType(), $entity, array(
'action' => $this->generateUrl('person_create'),
'method' => 'POST',
));
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
But the problem is when I request the url of this action, and the view of this action has to be rendered, there is a problem because doesn't give a response, because is in a infinite loop (I think that is the reason). I would like to know if is this possible to do using the Symfony forms, or if I have to look at other alternatives. If this was possible, how could I do that and how could I limit the form to only render the four levels that I need (me, my children, my grandchildren and my great-grandchildren)??
I hope that the problem has been understood.
Thanks in advance.
You could add a custom parameter to your form that indicates the current level of recursion.
To archive this you first need to implement a new option:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person',
'recursionLevel' => 4
));
}
Now you update this value in your buildForm method:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
if (--$options['recursionLevel'] > 0) {
$resolver = new OptionsResolver();
$resolver->setDefaults(
$options
);
$childType = new PersonType();
$childType->setDefaultOptions($resolver);
$builder->add('children', 'collection', array(
'type' => $childType,
'allow_add' => true,
'by_reference' => false
));
}
}
This is not tested.
I had the same problem and tried the solutions provided here.
They come with significant drawbacks like a depth limitation and performance overhead - you always create form objects even if there is no data submited.
What I did to overcome this problem was to add a listener for the FormEvents::PRE_SUBMIT event and add the collection type field dynamically if there is data to be parsed.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content');
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$node = $event->getData();
$form = $event->getForm();
if (!$node) {
return;
}
if(sizeof(#$node['children'])){
$form->add('children', CollectionType::class,
array(
'entry_type' => NodeType::class,
'allow_add' => true,
'allow_delete' => true
));
}
});
}
I hope this helps someone that has this issue in the future
Thanks for the answer Ferdynator!!
I didn't solve the problem in the way you proposed, but that approach helped me. I passed the recursion level in the constructor of the Person form, and thus, I could know when I had to stop:
class PersonType extends AbstractType
{
private $recursionLevel;
public function __construct( $recursionLevel ){
$this->recursionLevel = $recursionLevel;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($this->recursionLevel > 0)
{
$builder->add('children', 'collection', array(
'type' => new PersonType(--$this->recursionLevel),
'allow_add' => true,
'by_reference' => false,)
);
}
}
}
Ferdynator, thanks for your answers. And I want to propose my decision based on yours:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person',
'recursionLevel' => 4
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
if (--$options['recursionLevel'] > 0) {
$builder->add('children', 'collection', array(
'type' => $childType,
'allow_add' => true,
'by_reference' => false,
'options' => [
'recursionLevel' => $options['recursionLevel']
],
));
}
}
It solves our problem.
I've these 2 forms:
Lineups form which edits the lineups field of a match entity
<?php
namespace Acme\MatchBundle\Form\Type;
use Acme\UserBundle\Entity\UserRepository;
use Acme\TeamBundle\Entity\TeamRepository;
use Acme\ApiBundle\Listener\PatchSubscriber;
use Acme\CoreBundle\Form\DataTransformer\TimestampToDateTimeTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LineupsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('lineups', 'collection', array(
'type' => new LineupType(),
'allow_add' => true,
'allow_delete' => false
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\MatchBundle\Entity\Match',
'csrf_protection' => false
));
}
public function getName()
{
return 'match';
}
}
Lineup form which creates/edits a lineup entity
<?php
namespace Acme\MatchBundle\Form\Type;
use Acme\PlayerBundle\Entity\PlayerRepository;
use Acme\ApiBundle\Listener\PatchSubscriber;
use Acme\CoreBundle\Form\DataTransformer\TimestampToDateTimeTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LineupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('player', 'entity', array(
'class' => 'AcmePlayerBundle:Player',
'property' => 'id',
'query_builder' => function(PlayerRepository $er) {
$query = $er->createQueryBuilder('p');
return $query;
}
))
->add('status', 'text')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\MatchBundle\Entity\Lineup',
'csrf_protection' => false
));
}
public function getName()
{
return 'match';
}
}
Lineup entity fields
/**
* #var Match $match
*
* #ORM\ManyToOne(targetEntity="Match", inversedBy="lineups")
* #Assert\NotBlank()
*/
private $match;
/**
* #var \Acme\PlayerBundle\Entity\Player $player
*
* #ORM\ManyToOne(targetEntity="Acme\PlayerBundle\Entity\Player", inversedBy="lineups")
* #Assert\NotBlank()
*/
private $player;
/**
* #var string
*
* #ORM\Column(name="status", type="string", length=16)
* #Assert\NotBlank()
*/
private $status;
Now I've successfully got to add/remove Lineup entities to the lineups field, what I want is to set the $match field of lineup entity to the match edited with the lineups form.
Is that possible?
Found by myself that binding to the BIND form event let me do what I've needed:
$builder->addEventListener(
FormEvents::BIND,
function (FormEvent $event) {
$data = $event->getData();
$lineups = $data->getLineups();
foreach ($lineups as &$lineup) {
$lineup->setMatch($data);
}
$event->setData($data);
}
);
Which works fine ;)