How do you validate the new Google reCaptcha using volt and phalcon techniques?
(Just wanted to share what I did to make it work, see answer below, hope it helps...)
How do you validate the new Google reCaptcha using volt and phalcon techniques?
(Just wanted to share what I did to make it work, see answer below, hope it helps...)
Form
implementationController
implementation (actually no changes needed here, but for completeness...)View
implementationThe validator is the most crucial part of this, all other things are rather intuitive ...
use \Phalcon\Validation\Validator;
use \Phalcon\Validation\ValidatorInterface;
use \Phalcon\Validation\Message;
class RecaptchaValidator extends Validator implements ValidatorInterface
{
public function validate(\Phalcon\Validation $validation, $attribute)
{
if (!$this->isValid($validation)) {
$message = $this->getOption('message');
if ($message) { // Add the custom message defined in the "Form" class
$validation->appendMessage(new Message($message, $attribute, 'Recaptcha'));
}
return false;
}
return true;
}
/********************************************
* isValid - Return Values
* =======================
* true .... Ok
* false ... Not Ok
* null .... Error
*/
public function isValid($validation)
{
try {
$config = $validation->config->recaptcha; // not needed if you don't use a config
$value = $validation->getValue('g-recaptcha-response');
$ip = $validation->request->getClientAddress();
$url = $config->verifyUrl; // or 'https://www.google.com/recaptcha/api/siteverify'; without config
$data = ['secret' => $config->secretKey, // or your secret key directly without using the config
'response' => $value,
'remoteip' => $ip,
];
// Prepare POST request
$options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data),
],
];
// Make POST request and evaluate the response
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
return json_decode($result)->success;
}
catch (Exception $e) {
return null;
}
}
}
class SignupForm extends Form
{
public function initialize($entity = null, $options = null)
{
// Name (just as an example of other form fields)
$name = new Text('name');
$name->setLabel('Username');
$name->addValidators(array(
new PresenceOf(array(
'message' => 'Please enter your name'
))
));
$this->add($name);
// Google Recaptcha v2
$recaptcha = new Check('recaptcha');
$recaptcha->addValidator(new RecaptchaValidator([
'message' => 'Please confirm that you are human'
]));
$this->add($recaptcha);
// Other form fields...
}
Even though the controller is the same as with every other form, for completeness sake here an example...
class SessionController extends \Phalcon\Mvc\Controller
{
public function signupAction()
{
$form = new SignupForm();
if ($this->request->isPost()) {
if ($form->isValid($this->request->getPost()) != false)
{
// Add user to database, do other checks, etc.
// ...
}
}
$this->view->form = $form;
}
}
For the view you can either just put the html there or let it be rendered by the engine. If you want it rendered instead (with e.g. {{ form.render('recaptcha') }}
), you will have to also create an Recaptcha
Element instead of using one of the defaults (see last point in this answer for that).
...
{{ form('class':'signupForm') }}
<fieldset>
<div>{{ form.label('name') }}</div>
{{ form.render('name') }}
{{ form.messages('name') }}
<!-- other elements here -->
<!-- ... -->
<div class="g-recaptcha" data-sitekey="{{ this.config.recaptcha.publicKey }}"></div>
{{ form.messages('recaptcha') }}
If you don't wanna use a config for your public key (next section), just set the value of the data-sitekey to your personal (Google reCaptcha) public key.
Also don't forget to include the script (<script src='https://www.google.com/recaptcha/api.js'></script>
) somewhere (e.g. in your html head section).
If you wanna use the config to store your recaptcha keys, also add the following to your config/config.php
...
// config/config.php
return new \Phalcon\Config([
'application' => [
'controllersDir' => __DIR__ . '/../../app/controllers/',
'modelsDir' => __DIR__ . '/../../app/models/',
'formsDir' => __DIR__ . '/../../app/forms/',
'viewsDir' => __DIR__ . '/../../app/views/',
'pluginsDir' => __DIR__ . '/../../app/plugins/',
'libraryDir' => __DIR__ . '/../../app/library/',
'cacheDir' => __DIR__ . '/../../app/cache/',
'baseUri' => '/',
],
// other configs here
// ...
'recaptcha' => [
'publicKey' => 'your public key',
'secretKey' => 'your private key',
'verifyUrl' => 'https://www.google.com/recaptcha/api/siteverify',
],
]);
To be able to access the config in your view, you might also need to add $di->set('config', $config);
to your dependency injector (typically within config/services.php
).
If you want your recaptcha to be rendered for you (instead of you putting the div
directly in the view), you will need a separate \Phalcon\Forms\Element\
...
class Recaptcha extends \Phalcon\Forms\Element
{
public function render($attributes = null) {
return '<div class="g-recaptcha" data-sitekey="'
.$this->config->recaptcha->publicKey
.'"></div>';
}
}
You will also have to change your Form
accordingly:
// ...
$recaptcha = new Recaptcha('recaptcha');
$recaptcha->addValidator(new RecaptchaValidator([
'message' => '...'
]));
// ...
And finally also your View
:
{{ form.render('recaptcha') }}