Install the Package (Symfony-only):
composer require beelab/recaptcha2-bundle
Note: Not directly usable in Laravel without adaptation.
Configure Environment Variables (.env):
APP_RECAPTCHA_SITE_KEY=your_site_key
APP_RECAPTCHA_SECRET=your_secret_key
Add Bundle Configuration (config/packages/beelab_recaptcha2.yaml):
beelab_recaptcha2:
site_key: '%env(APP_RECAPTCHA_SITE_KEY)%'
secret: '%env(APP_RECAPTCHA_SECRET)%'
Disable in Tests (config/packages/test/beelab_recaptcha2.yaml):
beelab_recaptcha2:
enabled: false
First Use Case:
Add the RecaptchaType to a Symfony form (e.g., RegistrationType):
use Beelab\Recaptcha2Bundle\Form\Type\RecaptchaType;
use Beelab\Recaptcha2Bundle\Validator\Constraints\Recaptcha2;
$builder->add('captcha', RecaptchaType::class, [
'constraints' => new Recaptcha2(['groups' => ['registration']]),
]);
Include Script in Layout (e.g., base.html.twig):
<script src="//www.google.com/recaptcha/api.js?hl={{ app.request.locale }}"></script>
$builder->add('captcha', RecaptchaType::class, [
'label' => 'Verify you are human',
'mapped' => false, // Optional: if not storing the token
]);
$builder->add('captcha', RecaptchaSubmitType::class, [
'label' => 'Submit',
]);
Use Symfony’s validation groups to scope reCAPTCHA to specific actions:
new Recaptcha2(['groups' => ['registration', 'contact']])
Override validation messages in config/validator/validation.yaml:
Beelab\Recaptcha2Bundle\Validator\Constraints\Recaptcha2:
message: "Invalid CAPTCHA. Please try again."
Pass the site key dynamically in templates:
{% set siteKey = app.parameters.beelab_recaptcha2.site_key %}
<div class="g-recaptcha" data-sitekey="{{ siteKey }}"></div>
Use Symfony’s HTTP Client for requests (requires symfony/http-client):
beelab_recaptcha2:
request_method: http_client
Extract Core Logic:
Use the underlying google/recaptcha library directly in Laravel:
use Google\Recaptcha\ReCaptcha;
use Google\Recaptcha\Response;
$recaptcha = new ReCaptcha($secretKey);
$response = $recaptcha->verify($token, $remoteIp);
Create a Validator:
namespace App\Validators;
use Google\Recaptcha\ReCaptcha;
use Illuminate\Contracts\Validation\Rule;
class ReCaptchaValidator implements Rule {
public function passes($attribute, $value) {
$recaptcha = new ReCaptcha(config('services.recaptcha.secret'));
$response = $recaptcha->verify($value, request()->ip());
return $response->success();
}
public function message() {
return 'The CAPTCHA verification failed.';
}
}
Integrate with Form Requests:
use App\Validators\ReCaptchaValidator;
public function rules() {
return [
'captcha' => ['required', new ReCaptchaValidator],
];
}
Listen to validation failures for analytics/logging:
use Beelab\Recaptcha2Bundle\Event\RecaptchaEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RecaptchaSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
RecaptchaEvent::ON_RECAPTCHA_FAIL => 'onRecaptchaFail',
];
}
public function onRecaptchaFail(RecaptchaEvent $event) {
// Log failed attempts
\Log::warning('Failed reCAPTCHA', [
'error' => $event->getErrorCode(),
'ip' => $event->getRequest()->getClientIp(),
]);
}
}
Theme Overrides:
Customize the reCAPTCHA widget in form_themes/_form_theme.html.twig:
{% block beelab_recaptcha2_widget %}
<div class="g-recaptcha"
data-sitekey="{{ site_key }}"
data-size="compact"
data-theme="dark">
</div>
{% endblock %}
Multi-Environment Keys:
Use environment-specific keys (e.g., APP_RECAPTCHA_SITE_KEY_STAGING):
beelab_recaptcha2:
site_key: '%env(APP_RECAPTCHA_SITE_KEY_%kernel.environment%)%'
Caching Responses: Cache reCAPTCHA verification responses (e.g., for high-traffic forms):
$response = $recaptcha->verify($token, $ip);
if ($response->success()) {
Cache::put('recaptcha_'.$token, true, now()->addMinutes(5));
}
Service Provider Setup:
Register the validator in AppServiceProvider:
Validator::extend('recaptcha', function ($attribute, $value, $parameters, $validator) {
return (new ReCaptchaValidator)->passes($attribute, $value);
});
Blade Template Integration:
<div class="g-recaptcha"
data-sitekey="{{ config('services.recaptcha.site_key') }}"
data-callback="onSubmit">
</div>
@push('scripts')
<script>
function onSubmit(token) {
document.getElementById('captcha_token').value = token;
}
</script>
@endpush
Duplicate Service Configuration:
google/recaptcha recipe separately. This bundle already provides the service.recaptcha.client service if it conflicts.Test Environment Issues:
enabled: false). If tests still fail, explicitly mock the RecaptchaValidator:
$this->get('validator')->disableConstraintsFor('captcha');
Non-Scalar Token Errors:
$builder->add('captcha', TextType::class, [
'mapped' => false,
]);
HTTP Request Method Failures:
file_get_contents() is disabled (e.g., in shared hosting), use curl_post or http_client:
beelab_recaptcha2:
request_method: curl_post
http_client requires symfony/http-client (composer require symfony/http-client).Locale Mismatches:
hl parameter in the reCAPTCHA script must match the user’s locale. If using app.request.locale, ensure it returns a valid ISO 639-1 code (e.g., en, fr).Symfony 8+ Message Customization:
config/packages/validator.yaml:
Beelab\Recaptcha2Bundle\Validator\Constraints\Recaptcha2:
message: "recaptcha.invalid"
translations/messages.en.yaml:
recaptcha:
invalid: "The CAPTCHA verification failed. Please try again."
How can I help you explore Laravel packages today?