Markus Michalski FAQ

Support Form: Technical Details

Support Form for Sylius - flow of a request as a diagram, dual mode at a glance, plugin structure, the six tables, migrations and details of the admin interface

Deutsche Version

Back to overview

Support Form: Technical Details

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

Flow of a request

Storefront and Shop API run into the same processing. There the event is dispatched first, and the request only continues if no listener rejects it.

graph TD
    A["Customer: POST /support"] --> B{"Form valid?"}
    H["Client: POST /shop/support-requests"] --> I["Entity validation"]
    I --> J["Check subject against select list"]
    J --> K["Validate answers with form rules"]
    B -->|Yes| M["Derive answers and e-mail address"]
    K --> M
    M --> N["Resolve subject code into label"]
    N --> O["PRE_PERSIST event"]
    O --> Q{"Rejected by a listener?"}
    Q -->|Yes| U["Storefront: error message, API: 422"]
    Q -->|No| R["Store request"]
    R --> S["Mail to the operator"]
    R --> T["Confirmation to the customer"]
    S --> V["Storefront: success message, API: 201"]
    T --> V

The processing (SupportRequestSubmissionHandler) is the only place where requests are stored and notifications are sent. Controller and API processor only call it. Each of the two mails has its own error guard: if one fails, the request is still stored and the other one goes out. Only the exception class and code are logged, never the message, because it can contain personal data.

Dual mode at a glance

Capability Storefront Shop API
Submit a request GET/POST /support POST /shop/support-requests
Extra fields In the form as field_<ID> GET /shop/support-form-fields
Answers Input fields field_<ID> Object answers, keyed by the ID of the field
Subject choices "Subject" field in the form GET /shop/support-form-subject-choices
Confirmation to the customer If the request has an address If answers contains an answered e-mail
View requests /admin/support-requests GET /admin/support-requests

Plugin structure

src/
β”œβ”€β”€ Api/                  # Shop API processor and provider, answer validation, subject choices as a DTO
β”œβ”€β”€ Command/              # SetupCommand, PurgeSupportRequestsCommand
β”œβ”€β”€ Controller/           # SupportRequestController (storefront)
β”œβ”€β”€ DependencyInjection/  # Bundle extension (empty configuration)
β”œβ”€β”€ Entity/               # SupportRequest, SupportFormField (+ translation), SupportFormChannelConfiguration, enums
β”œβ”€β”€ Event/                # SupportRequestEvents, SupportRequestPrePersistEvent
β”œβ”€β”€ Factory/              # SupportFormFieldFactory
β”œβ”€β”€ Form/Type/            # SupportRequestType and the admin forms
β”œβ”€β”€ Handler/              # SupportRequestSubmissionHandler
β”œβ”€β”€ Mailer/               # SupportRequestMailer, Emails
β”œβ”€β”€ Menu/                 # Admin sidebar entry in the "MMKreativ Plugins" group
β”œβ”€β”€ Migrations/           # Three Doctrine migrations
β”œβ”€β”€ Repository/           # Repositories of the three resources
β”œβ”€β”€ Resolver/             # Subject choices, answer collector, privacy link
└── Twig/                 # PluginVersionExtension
config/
β”œβ”€β”€ grids/                # Three admin grids
β”œβ”€β”€ routes/               # admin.yaml, shop.yaml
β”œβ”€β”€ twig_hooks/           # admin.yaml, shop.yaml
└── mailer.yaml           # Two e-mail templates
templates/
β”œβ”€β”€ admin/                # Title blocks and forms, detail view
β”œβ”€β”€ emails/               # Notification and confirmation
└── shop/                 # Form and footer link
translations/             # messages, validators, flashes in de and en

Tables and migrations

The plugin creates six tables.

graph LR
    A["support_request"] -->|"channel_id, SET NULL"| C["sylius_channel"]
    B["support_form_channel_configuration"] -->|"channel_id, CASCADE"| C
    B --- D["configuration_product"]
    B --- E["configuration_taxon"]
    F["support_form_field"] --- G["support_form_field_translation"]
Table Content
mmd_sylius_support_form_support_request Incoming requests: created_at, email, subject, consent, channel_id, field_values
mmd_sylius_support_form_field Extra fields: field_type, required, position, limits, select_options
mmd_sylius_support_form_field_translation Label and placeholder per language
mmd_sylius_support_form_channel_configuration One row per channel: subject_mode, recipient_email, privacy_policy_page
mmd_sylius_support_form_channel_configuration_product Assigned products
mmd_sylius_support_form_channel_configuration_taxon Assigned categories

The answers to the extra fields are stored as a JSON list of label and value in the field_values column of the request, not in a table of their own. The plugin never reads them individually, only together with the request. Because each answer carries its label, it stays readable when you later change or delete the field.

The channel_id column of a request is set to NULL when a channel is deleted, whereas the channel's configuration is deleted with it.

Migration Change
Version20260928095114 Creates all six tables
Version20260929120000 Adds recipient_email to the channel configuration
Version20260929140000 Adds privacy_policy_page to the channel configuration

Admin details