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.'
])
]
]);
Related
In my Symfony 4.4 application I have the following code inside my controller. I am attempting to pre-populate the form based on previous submissions or data pulled from the database. Importantly, the DetailsType form includes multiple entities so it's not a clean 1 entity per form setup here.
$postVars = $request->request->all();
$formData = [];
if (count($postVars) > 0) {
$formData = $postVars['crmbundle_register_details'];
}
$form = $this->createForm(DetailsType::class, $formData, [
'attr' => ['class' => 'reCaptchaForm'],
]);
$form->setData([
'firstname' => $person->getFirstname(),
'lastname' => $person->getLastname(),
'email' => $person->getEmail(),
'country' => $person->getCountry(),
'timezone' => $person->getTimezone()
]);
My problem is if I try to pre-populate the form with setData above it does not work.
If I do it individually as below it works, but I can't see why. I'd prefer to pass setData an array rather than call setData multiple times.
$form->get('firstname')->setData($user->getFirstname());
$form->get('lastname')->setData($user->getLastname());
$form->get('email')->setData($user->getEmail());
$form->get('country')->setData($user->getCountry());
$form->get('timezone')->setData($user->getTimezone());
If the form contains many entities, it's better to have an embed form for each entity. In this case, you don't need to call the setters at all. The controller action code will be:
$form = $this->createForm(DetailsType::class, $someParentEntity, [
'attr' => ['class' => 'reCaptchaForm'],
]);
$form->handleRequest($request);
I got a problem in my project and dont really know how to fix it. Maybe my approach is completely wrong and I should go another direction.
I have two Entities: Article & ArticleComment
In my ArticleType I add the Comments with a CollectionType (main reason for this is that I also want to modify the Article - basically change Article stuff and also be able to add 1 comment):
//ArticleType.php
$builder->add('articleComments', CollectionType::class, ['label' => false, 'entry_type' => AritcleCommentType::class, 'entry_options' => ['label' => false]]);
My ArticleCommentType is just a textarea field:
//ArticleCommentType.php
$builder->add('text', TextareaType::class, ['label' => 'comment', 'required' => false, 'attr' => ['class' => 'textarea-sm']]);
This solution works perfectly for me.
However, whenever I add a comment, my other comments are also rendered in a textarea field and can be edited, which I do not want. I simple want the user to add 1 comment, reload the page and maybe add another comment thats it.
I already tried to prevent rendering the other comments, but this throws an error, since the element is not in the DOM anymore - but still expected.
Any ideas how to fix this? (Hiding the fields is no valid solution for me)
Update:
My controller:
//ArticleController.php
...
/**
* #Route("/article/{id}", name="app_article", requirements={"id"="\d+"})
*/
public function article(Request $request, Article $article)
{
$articleComment = new ArticleComment();
$article->addArticleComment($articleComment);
$form = $this->createForm(ArticleType::class, $article);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($articleComment); //cascade not active
$em->persist($article);
$em->flush();
$this->addFlash('success', 'entry_saved');
return $this->redirectToRoute('app_article', ['id' => $article->getId()]);
}
return $this->render('article/view.html.twig', ['article' => $article, 'form' => $form->createView()]);
}
...
Changed ArticleType:
$builder->add('articleComments', ArticleCommentType::class, ['label' => false, 'data' => new ArticleComment()]); //changed as suggested
As long as you use
$builder
->add('articleComments', CollectionType::class, [
'label' => false,
'entry_type' => AritcleCommentType::class,
'entry_options' => ['label' => false],
'mapped' => false,
]);
you'll render the whole collection.
To me the best fitting solution is to add directly ArticleCommentType with data attribute sette to new ArticleComment.
If you want to display all the comments, you should do directly in the template passing all existing ArticleComment displaying what you need to display.
Only thing you should take care is to manually add (maybe in a form subscriber) the brand new element (if has any value) to existing collection.
I've got an Integer field in Symfony form, which represents quantity - ofcourse it should be unable to pass the value equal or less than 0, so I've validated form in Entity and Form.
But - It's validated only when form is already sent. Is there any built-in method to validate form on input? I know that propably i would need to write own JS function to provide this, but I don't want to repeat someone others job :)
Found the solution.
To validate Symfony form on HTML side, you can just add an attribute to input field, reference: https://www.w3schools.com/htmL/html_form_attributes.asp
So, you form field should look like:
->add('quantity', IntegerType::class, ['label' => false,
'constraints' => new GreaterThan(array('value' => 0)),
'attr' => array(
'min' => '0',
)
])
Use pattern and min on your input. HTML should prevent form submit as long as the pattern isn't matched. (Careful, isn't doens't prevent submit if you're usin Javascript/Ajax to submit)
$builder->add('input_name', TextType::class, array(
'attr'=>array(
'pattern'=>'^[1-9][0-9]*' // 1 and above
'min'=>'1'
)
));
I'm using a TextType to remove the arrows in the input.
If you want to keep them, use NumberType.
A little addition, if you want to make sure a user can only enter a number you can add this bit of js to your code.
->add('quantity', NumberType::class, [ 'attr' =>
[
'oninput' => "this.value = this.value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');"
] ])
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;
}
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