I'm trying to create a form using the following code:
protected function getSearchForm(){
return $this->getFormFactory()->create('number', null, array('label' => 'code',
'required' => true,
'constraints' => new NotBlank(),
'compound' => true,
))
->add('submit', 'submit');
}
But I get this error, which originates from ->add('submit', 'submit');:
Expected argument of type "object, array or empty", "string" given
However I can create the same form using the following syntax (technically it's not the same, it has a root form of type form added.
protected function getSearchForm(){
return $this->getFormFactory()->createBuilder()->add('code','number', array('label' => 'code',
'required' => true,
'constraints' => new NotBlank(),
))
->add('submit', 'submit')
->getForm();
}
What is the Problem with the first approach?
In Symfony, forms are represented by objects and these objects are built by using a form factory. Building a form factory is simple: This factory can already be used to create basic forms, but it is lacking support for very important features:
Request Handling: Support for request handling and file uploads;
CSRF Protection: Support for protection against Cross-Site-Request-Forgery (CSRF) attacks;
Templating: Integration with a templating layer that allows you to reuse HTML fragments when rendering a form;
Translation: Support for translating error messages, field labels and other strings;
Validation: Integration with a validation library to generate error messages for submitted data.
The Symfony Form component relies on other libraries to solve these problems. Most of the time you will use Twig and the Symfony HttpFoundation, Translation and Validator components, but you can replace any of these with a different library of your choice.
If you're using the Symfony framework, then the form factory is available automatically as a service called form.factory. Also, the default base controller class has a createFormBuilder() method, which is a shortcut to fetch the form factory and call createBuilder on it.
Now that the form has been created, the next step is to render it. This is done by passing a special form "view" object to your template (notice the $form->createView() in the controller above) and using a set of form helper functions:
Read more at : http://symfony.com/doc/current/components/form/introduction.html
Conclusion : You must call createBuilder() to fetch formFactory and you must call createView() to render it to template.
I will make your code easy for you to understand :
protected function getSearchForm(){
$form = $this->getFormFactory()->create('number', null, array('label' => 'code',
'required' => true,
'constraints' => new NotBlank(),
'compound' => true,
));
$form->add('submit', 'submit');
return $form;
}
As you can see in your readable example above, you are adding a field into formFactory before fetched. And you should fetch it first before can adding a field.
protected function getSearchForm(){
$form = $this->getFormFactory()->createBuilder()->add('code','number', array('label' => 'code',
'required' => true,
'constraints' => new NotBlank(),
));
$form->add('submit', 'submit')
->getForm();
return $form;
}
Related
I have an older project which still uses Symfony 2. In it there is a form for editing a client profile. In the controller we have this:
$form = $this->createForm(new ClientProfile($remindTimes), $client);
$form->handleRequest($request);
And in the ClientProfile class we have
class ClientProfile extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('client_name', 'text', array('label' => 'Full name'))
->add('client_address', 'text', array('label' => 'Address', 'required' => false))
->add('client_city', 'text', array('label' => 'City', 'required' => false))
->add('client_post_code', 'text', array('label' => 'Postal index', 'required' => false))
->add('client_email', 'email', array('label' => 'E-mail', 'required' => false));
}
}
... and some other fields, but you get the gist. Then there's also a Twig view which renders the HTML. Standard stuff, as far as I can tell.
Now for my requirement. The client object has two special properties. Let's call them FroobleEnabled and FroobleType. If Frooble is disabled, then the type value has no meaning (can be set to 0). In the UI I want a dropdown with the values:
Disabled
Type 1
Type 2
Type 3
If the user selects Disabled, then FroobleEnabled gets set to false and FroobleType gets set to 0. Otherwise FroobleEnabled gets set to true and FroobleType to 1, 2 or 3 respectively.
How do I do this? The thing which makes this special is that it's not a 1:1 mapping anymore. There are two fields in the model object but just one UI control. I think I could achieve this with the a DataMapper, but I also don't want to manually map all the other fields (though I can, if there's no other option). I also cannot find any decent documentation about DataMapper or any other Symfony Forms features that could help me.
One way to accomplish this is to create a field, let's say, frooble.
In the form class create a frooble Choice field with mapped => false and values 0, 1, 2, 3. Set its choices to strings appropriate to the application.
In the controller, after form submission & validation, include code something like:
...
$frooble = $form->get('frooble')->getData();
if (0 === $frooble) {
$client->setFroobleEnabled(false);
$client->setFroobleType(0);
} else {
$client->setFroobleEnabled(true);
$client->setFroobleType($frooble);
}
Is there any way to validate a BotDetect recaptcha in the symfony form builder?
I have the below form, which lets a user enter their email.:
$form = $this->createFormBuilder()
->add('email', EmailType::class,[
'label' => false,
'attr' => [
'style' => 'text-align:center;',
'value' => $email,
]
])
->add('captchaCode', CaptchaType::class, array(
'captchaConfig' => 'ExampleCaptcha'
))
->add('Do some shiz wif my email bruh.', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->redirect('/unblock/'.$email);
}
The problem with this, is their documentation specifies a validation constraint in an Entity. My application does not have any entities (yet), but I would like to know if anyone has found a way to validate the captcha from the controller?
I'm fine with adding it to my entities when I create them , but I'm wondering how this would be done on an application that doesn't have any entities, or connection to a database.
I ended up using the beelabs google recaptcha bundle from packagist (If anyone is interested) at the link: https://packagist.org/packages/beelab/recaptcha2-bundle
Their documentation tells you pretty much everything you need to know about installation and setup.
The one drawback is that it still lets the form submit even though you havent clicked the I am not a robot checkbox, so you would need to validate whether it's been clicked on the PHP end.
You can use this to get the response, which is usually either a hash, or an empty field.
$recaptcha = $request->get('g-recaptcha-response', '');
use Captcha\Bundle\CaptchaBundle\Validator\Constraints\ValidCaptcha;
$form = $this->createFormBuilder()
->add('captchaCode', CaptchaType::class, [
'captchaConfig' => 'LoginCaptcha',
'constraints' => [
new ValidCaptcha([
'message' => 'CAPTCHA validation failed, try again.'
])
]
]);
I'm trying to dynamically generate a form based on user-provided field definitions. This is fairly straightforward for the basic form:
$builder = $app['form.factory']->createBuilder('form', $my_data);
foreach ($my_user_provided_field_definitions as $field) {
$builder->add($field->handle, $field->type, $field->options);
}
$form = $builder->getForm();
But I also want to have sub-forms using the collection field type, and while it is so easy to dynamically generate the top-level form as I've done above, it does not seem possible to dynamically generate a sub-form for the collection field because all of the docs and sample code I've come across utilize either a single field like so:
$builder->add('my field', 'collection', array(
'type' => 'text',
'allow_add' => true,
));
...or a custom "Type" object, like so:
$builer->add('my field', 'collection', array(
'type' => new TagType(),
'allow_add' => true,
));
The TagType class in the above example must be defined in code (see http://symfony.com/doc/master/cookbook/form/form_collections.html )... but my problem is that I cannot have the class defined in code because these sub-forms are dynamically generated based on user data -- so I do not know what fields the sub-form will contain until run-time!
Is there a way to dynamically generate the sub-forms that can then be passed in to the top-level form, or is this a use case that is not handled by the Symfony FormBuilder?
In my symfony2 project I created a new FormType which is called "ChoiceAndOrTextType" which is a list of choices (checkboxes) but the user can also check the "others" option and write his answer into a textfield.
The code is like this one here in the answer of bschussek:
Use a conditional statement when creating a form
$builder
->add('choice', 'choice', array(
'choices' => $options['choices'] + array('Other' => 'Other'),
'required' => false,
))
->add('text', 'text', array(
'required' => false,
))
->addModelTransformer(new ValueToChoiceOrTextTransformer($options['choices']))
;
Now i want to validate this correctly, so when the user checks "Others" then the textfield needs to be filled out, if "Others" isn't checked it can be blank. (Kind of dependent validation).
How do I do this?
You need to use two validation constraints, for example in validation.yml yaml file
I have a formbuilder where I am adding some values from an entity:
$builder->add('affiliation', 'entity', array(
'class' => 'SciForumVersion2Bundle:UserAffiliation',
'multiple' => true,
'expanded' => true,
'query_builder' => function(EntityRepository $er) use ($author,$user) {
return $er->createQueryBuilder('ua')
->where("ua.user_id = {$user->getId()}")
->andWhere("ua.affiliation_id not in ( select pa.affiliation_id FROM SciForumVersion2Bundle:PersonAffiliation pa where pa.person_id = {$author->getPersonId()} )");
},
'required' => true,
));
In my controller, I would like to check if there is something in my form. If there is something, I will display one view, if there is nothing, I will display another view.
Is this possible and if so, how?
Thank you.
Simply try it:
$data = $form->getData()
function getData() documentation Book
If you want to get the current data (just after rendering the form) in your form type you can use the builder supplied in each form type by standard.
It works exactly as a normal form response, so you can use:
$builder->getData();
and use if clauses to add different fields depending on what you want to generate.