Markus Michalski FAQ

Support Form: Extensibility

Support Form for Sylius - rejecting requests through an event before storing, extending the form with a FormTypeExtension, replaceable services, plus hooks and templates of the admin interface

Deutsche Version

Back to overview

Support Form: Extensibility

Live Demo: Try this and other plugins live — no installation, no risk: demo.markus-michalski.net

The plugin provides two places where your own code can hook in without modifying the plugin. Both are meant for spam protection, but they work for any check before storing.

Rejecting requests before they are stored

Before a request is stored, the processing dispatches the event mmd_sylius_support_form_plugin.support_request.pre_persist. The constant for it is SupportRequestEvents::PRE_PERSIST. This happens on both paths, in the shop and through the Shop API. A listener receives a SupportRequestPrePersistEvent with the request and the current Request and can reject the request.

<?php

declare(strict_types=1);

namespace App\EventListener;

use Mmd\SyliusSupportFormPlugin\Event\SupportRequestEvents;
use Mmd\SyliusSupportFormPlugin\Event\SupportRequestPrePersistEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener(event: SupportRequestEvents::PRE_PERSIST)]
final class RejectBlockedSubjects
{
    public function __invoke(SupportRequestPrePersistEvent $event): void
    {
        $subject = $event->getSupportRequest()->getSubject() ?? '';

        if (str_contains(strtolower($subject), 'casino')) {
            $event->reject('app.support_form.rejected');
        }
    }
}

reject() accepts an optional reason:

Path Effect of reject()
Storefront The customer sees the reason as an error message. The reason is a translation key. Without a reason the default message "Your submission could not be processed. Please try again." appears.
Shop API The API answers with 422. The reason appears unchanged in the error message, without a reason it says "Submission rejected."

If a listener rejects the request, it is neither stored nor sent. This applies to both mails.

The listener runs after the request is valid. It therefore only sees requests that already meet all required fields. Checks on the Request include rate limiting, a honeypot field or evaluating a proof-of-work token.

Extending the form with fields

SupportRequestType is an ordinary Symfony form type and not final. A FormTypeExtension targeting SupportRequestType::class can therefore add its own fields, for example for a spam protection widget.

<?php

declare(strict_types=1);

namespace App\Form\Extension;

use Mmd\SyliusSupportFormPlugin\Form\Type\SupportRequestType;
use Symfony\Component\Form\AbstractTypeExtension;

final class SupportRequestTypeExtension extends AbstractTypeExtension
{
    public static function getExtendedTypes(): iterable
    {
        return [SupportRequestType::class];
    }
}

In addition, the storefront template provides the hook mmd_altcha.widget.support_form before the submit button (see Storefront).

A form type extension only takes effect in the storefront. The Shop API doesn't build any form fields. A check that should apply to both paths therefore belongs in a listener on PRE_PERSIST.

Replaceable services

These services are registered as aliases through an interface. You can replace them in your application with your own implementation by redirecting the alias:

Interface Purpose
SupportRequestSubmissionHandlerInterface Dispatches PRE_PERSIST, stores and notifies
SupportRequestMailerInterface Sends notification and confirmation
SupportFormSubjectResolverInterface Provides the subject choices of a channel and resolves a code into its label
SupportFormPrivacyPolicyLinkProviderInterface Provides the privacy link for the consent
SupportRequestAnswersApplierInterface Validates the Shop API answers and applies them to the request

The entities (SupportRequest, SupportFormField, SupportFormChannelConfiguration, plus SupportFormFieldTranslation for the translations of a field) are Sylius resources. You can replace them with your own classes through the Sylius resource configuration, as with any other Sylius resource.

# config/services.yaml
services:
    Mmd\SyliusSupportFormPlugin\Mailer\SupportRequestMailerInterface:
        alias: App\Mailer\MySupportRequestMailer

Customizing the admin interface

The plugin attaches its admin pages to the Sylius interface through Twig hooks. You can register your own entries on the same hooks or override the templates.

Hooks:

Templates (put a copy under templates/bundles/MmdSyliusSupportFormPlugin/, using the same path without the leading templates/):