1

I've inherited some code which uses Symfony (v3.3) to generate forms. Some elements are being created with no space between the element type and the auto-generated ID. This means the element doesn't display:

<selectid="someID">
...
</selectid="someID">

This is happening on select elements and textarea elements.

I'm not familiar with Symfony so don't know how to troubleshoot this... any help is much appreciated!

Edit: added code as requested. The problem is I don't know where the issue lies and there are a lot of classes.

Twig template

<form action="" method="post" name="callback" id="request-callback" class="contact-form">
    <input type="hidden" name="form-type" value="callback">
    {#<input type="hidden" name="mc4wp-subscribe" value="1">#}
    <div{% if form.name.vars.errors | length > 0 %} class="form-error"{% endif %}>
        {{ form_label(form.name) }} {{ form_errors(form.name) }}
        {{ form_widget(form.name) }}
    </div>
    <div{% if form.phone_number.vars.errors | length > 0 %} class="form-error"{% endif %}>
        {{ form_label(form.phone_number) }} {{ form_errors(form.phone_number) }}
        {{ form_widget(form.phone_number) }}
    </div>
    <div{% if form.email.vars.errors | length > 0 %} class="form-error"{% endif %}>
        {{ form_label(form.email) }} {{ form_errors(form.email) }}
        {{ form_widget(form.email) }}
    </div>
    <div{% if form.treatment.vars.errors | length > 0 %} class="form-error"{% endif %}>
        {{ form_label(form.treatment) }} {{ form_errors(form.treatment) }}
        {{ form_widget(form.treatment) }}
    </div>
    <div class="text-center">
        <button class="button bg-darkblue" type="submit" id="contact_send" name="contact[send]">Send My Request</button>
    </div>
</form>

Form class

<?php

namespace REDACTED;

use DrewM\MailChimp\MailChimp;
use GuzzleHttp\Exception\ConnectException;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\Translator;
use Symfony\Bridge\Twig\Extension\FormExtension;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Bridge\Twig\Form\TwigRendererEngine;
use Symfony\Bridge\Twig\Form\TwigRenderer;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Validator\Validation;
use GuzzleHttp\Client;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;

abstract class Form
{
    /**
     * Recaptcha endpoint
     */
    const RECAPTCHA_VERIFY = 'https://www.google.com/recaptcha/api/siteverify';

    /**
     * Default from name
     */
    const EMAIL_FROMNAME = '';

    /**
     * @var \Twig_Environment
     */
    protected $twig;

    /**
     * @var \Symfony\Component\Form\FormInterface
     */
    protected $form;

    /**
     * @var \Symfony\Component\HttpFoundation\Request
     */
    private $request;

    /**
     * Capture failed
     *
     * @var bool
     */
    protected $captchaFailed = false;

    /**
     * @var string
     */
    protected $template;

    /**
     * @var string
     */
    protected $messageTemplate;

    /**
     * @var string
     */
    protected $subject;

    /**
     * @var string
     */
    protected $emailTo;

    /**
     * @var string
     */
    protected $emailFromName;

    /**
     * @var array
     */
    protected $params = [];

    protected $mailchimpList;

    private $mailchimpApiKey = '6542760048f1c73d69df8f552d4a2b87-us18';

    public $mailerError;

    public $redirectTo;

    /**
     * SunstoneForm constructor
     *
     * @param Request $request
     * @param $emailTo
     * @param $emailFromName
     * @param array $params
     */
    private function __construct(
        Request $request = null,
        $emailTo = null,
        $emailFromName = null,
        array $params = []
    ) {
        $this->request = $request;
        $this->emailTo = $emailTo;
        $this->emailFromName = $emailFromName;
        $this->params = $params;
    }

    /**
     * Make the contact form
     *
     * @param Request $request
     * @param string $emailTo
     * @param string $emailFromName
     * @param array $params
     * @return static
     */
    public static function make(
        Request $request = null,
        $emailTo = null,
        $emailFromName = self::EMAIL_FROMNAME,
        array $params = []
    ) {
        return (new static($request, $emailTo, $emailFromName, $params))
            ->twig()
            ->form();
    }

    /**
     * Render the form
     *
     * @return string
     */
    public function renderForm()
    {
        return $this->twig->render($this->template, [
            'form' => $this->form->createView(),
            'captchaFailed' => $this->captchaFailed,
        ]);
    }

    /**
     * Handle a form submission and check form is valid
     *
     * @return bool
     */
    public function handleRequest()
    {
        $this->form->handleRequest($this->request);
        if ($this->form->isSubmitted() && $this->form->isValid()) {
            // send the message
            return $this->process();
        }

        return false;
    }

    /**
     * Instantiate Twig
     *
     * @return $this
     */
    protected function twig()
    {
        // instantiate twig
        $translator = new Translator('en');
        $loader = new \Twig_Loader_Filesystem([
            TWIG_TEMPLATE_DIR,
            ABSPATH.'vendor/symfony/twig-bridge/Resources/views/Form',
        ]);
        $twig = new \Twig_Environment($loader, [
            'debug' => WP_DEBUG,
        ]);
        $twig->addExtension(new FormExtension());
        $twig->addExtension(new TranslationExtension($translator));
        if (WP_DEBUG) {
            $twig->addExtension(new \Twig_Extension_Debug);
        }

        // get form engine
        $formEngine = new TwigRendererEngine(['form_div_layout.html.twig'], $twig);
        $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader([
            TwigRenderer::class => function() use ($formEngine) {
                return new TwigRenderer($formEngine);
            },
        ]));

        $this->twig = $twig;

        return $this;
    }

    public function getForm()
    {
        return $this->form;
    }

    public function getSubmissionComplete()
    {
        return sprintf('<div class="form-sent">%s</div>',
            get_field('form_submitted_content', 'options')
        );
    }

    /**
     * Generate the form
     *
     * @return $this
     */
    protected function form()
    {
        $this->form = $this->formFields(
            Forms::createFormFactoryBuilder()
                ->addExtension(new HttpFoundationExtension)
                ->addExtension(new ValidatorExtension(Validation::createValidator()))
                ->getFormFactory()
            )
            ->getForm();

        return $this;
    }

    /**
     * @param array $additionalData
     * @return bool
     */
    protected function process(array $additionalData = [])
    {
        $data = $this->form->getData();

        $mailer = new \PHPMailer(true);
        $mailer->addAddress($this->emailTo);
        if (WP_DEBUG && defined('DEBUG_BCC')) {
            $mailer->addBCC(DEBUG_BCC);
        }
        $mailer->From = $this->emailTo;
        $mailer->FromName = 'drpuneetgupta.co.uk';
        $mailer->Subject = $this->subject;
        $mailer->Body = $this->twig->render($this->messageTemplate, [
            'data' => $data + $additionalData,
        ]);
        $mailer->isHTML(true);
        
        if ($this->mailchimpList) {
            try {
                $mailchimp = new MailChimp($this->mailchimpApiKey);
                $mailchimp->post("lists/{$this->mailchimpList}/members", [
                    'email_address' => $data['email'],
                    'status' => 'subscribed',
                ]);
            } catch (\Exception $e) {}
        }

        try {
            return $mailer->send();
        } catch (\phpmailerException $e) {
            $this->mailerError = $e->getMessage();
        }
        return false;
    }

    /**
     * Define form fields
     *
     * @param FormFactoryInterface $formFactory
     * @return mixed
     */
    abstract protected function formFields(FormFactoryInterface $formFactory);
}

RequestCallback extends Form class

<?php

namespace REDACTED;

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
use DrewM\MailChimp\MailChimp;

class RequestCallback extends Form
{
    protected $template = 'request-callback.twig';

    protected $messageTemplate = 'email-callback.twig';

    protected $mailchimpList = 'REDACTED';

    protected $subject = 'Callback request';

    /**
     * @param FormFactoryInterface $formFactory
     * @return FormBuilderInterface
     */
    protected function formFields(FormFactoryInterface $formFactory)
    {
        return $formFactory->createNamedBuilder('request_callback', FormType::class, null, [
                'allow_extra_fields' => true,
            ])
            ->add('mc4wp-subscribe', HiddenType::class, [
                'data' => 1,
            ])
            ->add('name', TextType::class, [
                'required' => true,
                'label' => 'Your Name',
                'attr' => [
                    'placeholder' => 'Your Name',
                ],
                'label_attr' => [
                    'class' => 'sr-only',
                ],
                'constraints' => [
                    new NotBlank(['message' => 'Please enter your name']),
                ],
            ])
            ->add('phone_number', TextType::class, [
                'required' => true,
                'label' => 'Phone Number',
                'attr' => [
                    'placeholder' => 'Phone Number',
                ],
                'label_attr' => [
                    'class' => 'sr-only',
                ],
                'constraints' => [
                    new NotBlank(['message' => 'Please enter your phone number']),
                ],
            ])
            ->add('email', EmailType::class, [
                'required' => true,
                'label' => 'Your email address',
                'attr' => [
                    'placeholder' => 'Email address',
                ],
                'label_attr' => [
                    'class' => 'sr-only',
                ],
                'constraints' => [
                    new NotBlank(['message' => 'Please enter your email address']),
                    new Email(['message' => 'Please enter a valid email address']),
                ],
            ])
            ->add('treatment', ChoiceType::class, [
                'required' => true,
                'label' => 'Which treatment would you like to discuss?',
                'label_attr' => [
                    'class' => 'sr-only',
                ],
                'constraints' => [
                    new NotBlank(['message' => 'Please select a treatment']),
                ],
                'choices' => [
                    'Which treatment would you like to discuss?' => '',
                    'Liposuction' => 'Liposuction',
                    'Lipoedema' => 'Lipoedema',
                    'Breast reduction' => 'Breast reduction',
                    'Male chest reduction' => 'Male chest reduction',
                ],
            ]);
    }
}
Stephan Vierkant
  • 9,674
  • 8
  • 61
  • 97
  • can u share the code ? – Maatoug Omar Oct 25 '20 at 19:23
  • @MaatougOmar I've edited the post to include links to some of the code. – Dave McCourt Oct 25 '20 at 22:46
  • @DarkBee added but as I mention I don't really know where the issue is and there are a lot of classes and includes. – Dave McCourt Oct 26 '20 at 10:41
  • Does this answer your question? [PHP 7.4 trimming whitespace between string variables](https://stackoverflow.com/questions/61609531/php-7-4-trimming-whitespace-between-string-variables) – DarkBee Oct 26 '20 at 10:44
  • Nothing in the posted code contains any reference to a ` – Nico Haase Oct 26 '20 at 10:50
  • As a hint: usually, these form IDs (or some surrounding attributes) correspond to the fields you are using – Nico Haase Oct 26 '20 at 10:51
  • To answer your comment, you have to update your twig as it's a core issue adding/removing whitespace where it shoudln't – DarkBee Oct 26 '20 at 11:00
  • Thanks @DarkBee I downgraded the PHP version to 7.3 and this has fixed it. – Dave McCourt Oct 26 '20 at 11:05
  • @NicoHaase the ChoiceType class generates the – Dave McCourt Oct 26 '20 at 11:07

1 Answers1

0

I thought I'll create an answer for this as finding the right answer in a comment is not straightforward.

As @DarkBee mentions in one of the question comments the fix on the question PHP 7.4 trimming whitespace between string variables solves this issue.

There is a fix in Twig that prevents the whitespace from being trimmed so updating to a recent Twig version fixes the issue: composer require "twig/twig:^2.0"

Titi
  • 1,126
  • 1
  • 10
  • 18