Skip to content

Installation

  • PHP 8.2+
  • Symfony 6.4, 7.x or 8.x, with symfony/twig-bundle to render the widget
  • A Cap Standalone server reachable from your visitors’ browsers, and a site key created in its dashboard: you get a site key and a secret key
Terminal window
composer require silarhi/cap-bundle

Register the bundle if Flex did not:

config/bundles.php
return [
// ...
Silarhi\CapBundle\SilarhiCapBundle::class => ['all' => true],
];

Copy its script to public/bundles/silarhicap/ (Flex runs this command after every composer install already):

Terminal window
php bin/console assets:install

The bundle reads the URL of your Cap server and the secret key from two env vars:

.env
CAP_ENDPOINT=https://cap.example.com/<site-key>/
# .env.local, or your secrets vault
CAP_SECRET_KEY=<the secret key of the site key>

CAP_ENDPOINT is the URL the browser uses: your Cap instance followed by the site key, as in Cap’s data-cap-api-endpoint. The secret key is the one of the site key, not the ADMIN_KEY of the dashboard.

Disable the bundle in the test environment:

config/packages/silarhi_cap.yaml
when@test:
silarhi_cap:
# No widget, every submission accepted: see the testing guide
enabled: false

Every option, other env var names included, is in the configuration reference.

use Silarhi\CapBundle\Form\CapType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
final class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class)
->add('message', TextareaType::class)
->add('cap', CapType::class);
}
}

That’s it:

  • {{ form(form) }} (or {{ form_row(form.cap) }}) renders Cap’s checkbox and the script of the bundle;
  • the widget solves the challenge and posts the token as contact[cap];
  • $form->isValid() redeems the token against your Cap server. A missing or rejected token is an error on the field: “Please verify you are human.” or “The human verification failed, please try again.”

The field is not mapped: it never touches your entity or DTO.