Building custom forms

Building a custom form looks like building a node but it is a lot simpler! Let’s have a look at structure image.

../../_images/custom-form.svg

After creating a custom form, you add some question. The questions are the CustomFormField type.

The answer is saved in two entities:
  • in CustomFormAnswer
  • in CustomFormFieldAttribute

The CustomFormAnswer will store the IP and the submitted time. While question answer will be in CustomFormFieldAttribute with the CustomFormAnswer id and the CustomFormField id.

Adding custom form to your theme

If you want to integrate your custom-forms into your theme, you can use Roadiz CustomFormHelper class to generate a standard FormInterface and to create a view into your theme templates.

First you must create a dedicated action for your node or your block if you used {{ nodeSource|render(@AwesomeTheme) }} Twig filter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
 use RZ\Roadiz\Core\Entities\CustomForm;
 use RZ\Roadiz\Core\Exceptions\EntityAlreadyExistsException;
 use RZ\Roadiz\Core\Exceptions\ForceResponseException;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use RZ\Roadiz\Utils\CustomForm\CustomFormHelper;
 use Symfony\Component\Form\FormError;
 use Symfony\Component\HttpFoundation\JsonResponse;

 // …

 /*
  * Get your custom form instance from your node-source
  * only if you added a *custom-form reference field*.
  */
 $customForms = $this->nodeSource->getCustomformReference();
 if (isset($customForms[0]) && $customForms[0] instanceof CustomForm) {
     /** @var CustomForm $customForm */
     $customForm = $customForms[0];

     /*
      * Verify if custom form is still open
      * for answers
      */
     if ($customForm->isFormStillOpen()) {
         /*
          * CustomFormHelper will generate Symfony form against
          * Roadiz custom form entity.
          * You can add a Google Recaptcha passing following options.
          */
         $helper = new CustomFormHelper($this->get('em'), $customForm);
         $form = $helper->getFormFromAnswer($this->get('formFactory'), null, true, [
             'recaptcha_public_key' => $this->get('settingsBag')->get('recaptcha_public_key'),
             'recaptcha_private_key' => $this->get('settingsBag')->get('recaptcha_private_key'),
             'request' => $request,
         ]);
         $form->handleRequest($request);

         if ($form->isSubmitted() && $form->isValid()) {
             try {
                 $answer = $helper->parseAnswerFormData($form, null, $request->getClientIp());

                 if ($request->isXmlHttpRequest()) {
                     $response = new JsonResponse([
                         'message' => $this->getTranslator()->trans('form_has_been_successfully_sent')
                     ]);
                 } else {
                     $this->publishConfirmMessage(
                         $request,
                         $this->getTranslator()->trans('form_has_been_successfully_sent')
                     );
                     $response = $this->redirect($this->generateUrl(
                         RouteObjectInterface::OBJECT_BASED_ROUTE_NAME,
                         [RouteObjectInterface::ROUTE_OBJECT => $this->nodeSource->getParent()]
                     ));
                 }
                 /*
                  * If you are in a BlockController use ForceResponseException
                  */
                 throw new ForceResponseException($response);
                 /*
                  * Or directly return redirect response.
                  */
                 //return $response;
             } catch (EntityAlreadyExistsException $e) {
                 $form->addError(new FormError($e->getMessage()));
             }
         }

         $this->assignation['session']['messages'] = $this->get('session')->getFlashBag()->all();
         $this->assignation['form'] = $form->createView();
     }
 }

If you didn’t do it yet, create a custom form theme in your views/ folder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 {#
  # AwesomeTheme/Resources/views/form.html.twig
  #}
 {% extends "bootstrap_3_layout.html.twig" %}

 {% block form_row -%}
     <div class="form-group form-group-{{ form.vars.block_prefixes[1] }} form-group-{{ form.vars.name }}">
         {% if form.vars.block_prefixes[1] != 'separator' %}
             {{- form_label(form) -}}
         {% endif %}
         {{- form_errors(form) -}}
         {#
          # Render field description inside your form
          #}
         {% if form.vars.attr['data-description'] %}
             <div class="form-description">
                 {{ form.vars.attr['data-description']|markdown }}
             </div>
         {% endif %}
         {{- form_widget(form) -}}
     </div>
 {%- endblock form_row %}

 {% block recaptcha_widget -%}
     <div class="g-recaptcha" data-sitekey="{{ configs.publicKey }}"></div>
 {%- endblock recaptcha_widget %}

In your main view, add your form and use your custom form theme:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 {#
  # AwesomeTheme/Resources/views/form-blocks/customformblock.html.twig
  #}
 {% if form %}
     {% form_theme form '@AwesomeTheme/form.html.twig' %}
     {{ form_start(form) }}
     {{ form_widget(form) }}
     <div class="form-group">
         <button class="btn btn-primary" type="submit">{% trans %}send_form{% endtrans %}</button>
     </div>
     {{ form_end(form) }}
 {% else %}
     <p class="alert alert-warning">{% trans %}form_is_not_available{% endtrans %}</p>
 {% endif %}