Markus Michalski FAQ

Newsletter Management: Storefront integration

Newsletter Management for Sylius in the storefront: signup page, footer form, double opt-in flow, manage and unsubscribe pages, emails, Sylius checkbox, routes and theme overrides

Deutsche Version

Back to overview

Newsletter Management: Storefront integration

This documentation always describes the latest published version.

This page describes what customers see and do in the Twig storefront: the signup page, the footer form, the double opt-in flow, the pages for confirming, managing and unsubscribing, the plugin's emails and the connection to Sylius' own newsletter checkbox. Headless shops get the same flows through the Shop API.

All pages are only reachable for channels where "Newsletter enabled" is switched on (see Configuration). Otherwise the signup page returns 404.

Routes

All routes are defined in config/routes/shop.yaml. Every route name starts with mmd_sylius_newsletter_management_plugin_shop_; the table shows only the end.

Route name (end) Path Methods Purpose
subscribe /newsletter GET, POST Signup page
confirm /newsletter/confirm/{token} GET Confirmation link from the confirmation email
manage /newsletter/manage/{token} GET, POST Manage the subscription (name, categories)
unsubscribe /newsletter/unsubscribe/{token} GET, POST Unsubscribe. GET only shows a button, POST unsubscribes.
track_open /newsletter/track/open/{newsletter}/{signature}.gif GET Counting pixel for opens (see Tracking and statistics)
track_click /newsletter/track/click/{newsletter}/{code}/{signature} GET Redirect that counts clicks (see Tracking and statistics)

{token} is the subscription's secret token, 64 hex characters. The links in the emails contain it, so none of these pages needs a login. The routes for the tracking pixel and the click redirect are signed and cannot be guessed.

Signup page

The /newsletter page contains these fields:

Field Required? Note
Email yes
First name, last name no
Category choice ("What are you interested in?") no Only appears if the channel has enabled categories. Leave empty = receive everything. The category's description is shown below its checkbox.
Tracking consent no Separate, optional checkbox, only in tracking mode "Personal"
Consent to the newsletter yes Links to the privacy policy if the channel has a page entered for it

For a logged-in customer the plugin prefills email address, first and last name from their account. An ?email= parameter in the URL takes precedence for the email address; that is how the footer form hands over its input. A submitted form keeps whatever the visitor typed. The subscription is only linked to the customer account when the logged-in customer enters their own address.

After submitting, the page redirects back to /newsletter and shows a message. The message is always the same, whether the address is new, still unconfirmed or already subscribed ("We have sent you an email ..."). That way the form cannot be used to find out who is on the list. Without double opt-in the message is "Thank you - you are subscribed to our newsletter ..." instead. If an additional check (for example spam protection, see Extending) rejects the signup, an error message appears and nothing is stored.

The footer form is a teaser: an email field and a "Subscribe to newsletter" button that sends the address by GET to the signup page. There the visitor gives consent, name and categories. The form only appears if

It is attached as a Twig hook in the footer:

Hook Name Priority Template
sylius_shop.base.footer.content newsletter 150 shop/footer/widget.html.twig

Priority 150 places the form between the footer's link menu (200) and the payment-methods row (100).

Double opt-in flow

graph TD
    A["Signup via form or Shop API"] --> B{"Newsletter enabled for channel?"}
    B -->|"No"| C["404 / not available"]
    B -->|"Yes"| D{"Additional check rejects?"}
    D -->|"Yes"| E["Error message, nothing is stored"]
    D -->|"No"| F{"Address already confirmed?"}
    F -->|"Yes"| G["Already subscribed mail, nothing changes"]
    F -->|"No"| H{"Double opt-in enabled?"}
    H -->|"Yes"| I["Status pending, confirmation mail"]
    H -->|"No"| J["Status confirmed, welcome mail"]
    I --> K["Click on the confirmation link"]
    K --> L["Status confirmed, welcome mail"]

The flow in detail:

  1. Signup. The plugin creates one subscription per email address and channel. The address is lowercased and trimmed. The selected categories are stored; unavailable ones are ignored.
  2. Confirmation email (with double opt-in). The subscription has the status "Awaiting confirmation". Anyone who signs up again before confirming receives the confirmation email again. Anyone who signs up again after unsubscribing starts over: status "pending" again, new tokens, new confirmation.
  3. Confirmation. Clicking the link opens /newsletter/confirm/{token}, sets the status to "Confirmed" and stores time and IP address. The page shows "Subscription confirmed". A second click on the same link shows the page again without changing anything or sending another email; a link that does not exist (any more) or belongs to an unsubscribed subscription shows "Link invalid or expired" (status 404) with a button to sign up again.
  4. Welcome email. It goes out after the confirmation, or directly at signup if double opt-in is switched off.

Emails triggered by the public form are throttled per subscription: there are at least 10 minutes between two such emails to the same address. This stops anyone from flooding a stranger's inbox through the form. The welcome email right after clicking the confirmation link is exempt. If an email fails to send, the subscription is still saved, and the error is logged without the email address.

"Manage subscription" page

The link is in the welcome and the "already subscribed" email and in the footer of every newsletter. The page is only reachable for confirmed subscriptions; every other token shows "Link invalid". Customers can change here:

The email address is displayed but cannot be changed. It is the basis of the consent proof, the confirmation and the link to the customer account. To use a different address, the customer unsubscribes and signs up again with the new one.

Unsubscribing

The unsubscribe link opens /newsletter/unsubscribe/{token} and shows only a question with an "Unsubscribe" button. Only clicking the button (a POST) unsubscribes. That is deliberate: mail programs and security scanners prefetch links in emails, and that must never unsubscribe anybody.

Unsubscribing

The page afterwards reads "You have been unsubscribed". Anyone who signs up again later goes through the flow from the start. The link in the List-Unsubscribe header of newsletters points to the same route.

"Already subscribed" email

If someone enters an address that is already confirmed in the public form, nothing about that subscription changes: neither categories nor names. The page answers as it would for a new address. Instead, the owner of the address receives the email "You are already subscribed to the {channel name} newsletter" with links to manage or end the subscription. It is subject to the 10-minute throttle.

Sylius' newsletter checkbox

Sylius ships its own newsletter checkbox on the registration form and the profile page, which only sets a field on the customer. The plugin turns it into a real signup with double opt-in:

After that, the plugin keeps the field in sync: it is set to "subscribed" as soon as the customer (matched by email address) is confirmed in any channel, and to "not subscribed" after an unsubscribe as long as they are confirmed nowhere. An unconfirmed signup from another path (such as the footer form) does not touch the field, because it may come from a stranger who merely knows the address.

Sylius' own Shop API for registration (POST /shop/customers) sets the Sylius field without the plugin noticing. Headless clients should therefore subscribe customers through the plugin's endpoint (see Shop API).

For each subscription the plugin stores what you need to show in a dispute. In the admin you see it on the subscriber's detail page.

Item Content
Email and channel exactly one subscription per combination
Source Storefront form, API, Registration or Admin
Language Language at signup (also determines the language of the emails)
Signup Time and IP address
Confirmation Time and IP address
Unsubscribe Time
Tracking consent Time and the wording the subscriber agreed to

How long unsubscribed and never-confirmed subscriptions are kept is set per channel (see Configuration).

Emails to subscribers

The three emails go through the Sylius mailer (email codes under sylius_mailer.emails). They are HTML emails based on Sylius' email layout and arrive in the language of the subscription. You set the sender per channel (see Configuration). The newsletters themselves are a separate mail path, described on Writing and sending newsletters.

Email code When Template Content
newsletter_confirmation New or still unconfirmed signup with double opt-in enabled emails/confirmation.html.twig Confirmation button and the link as plain text
newsletter_welcome After confirmation, or directly at signup if double opt-in is off emails/welcome.html.twig "Manage subscription" button and unsubscribe link
newsletter_already_subscribed Public signup of an already confirmed address emails/already_subscribed.html.twig Notice that nothing changed, with links to manage or end the subscription

The button in all three emails comes from the partial emails/_cta.html.twig. You set its colors in the configuration file (see Configuration).

Customizing templates in your theme

All of the plugin's templates live in the namespace @MmdSyliusNewsletterManagementPlugin. As with any Symfony bundle, you override them by creating a file with the same path under templates/bundles/MmdSyliusNewsletterManagementPlugin/. These are the ones that make practical sense:

Template (under templates/) Purpose
shop/newsletter/subscribe.html.twig Signup page
shop/newsletter/confirm.html.twig Page after clicking the confirmation link
shop/newsletter/manage.html.twig, manage_invalid.html.twig Manage subscription and its error page
shop/newsletter/unsubscribe.html.twig, unsubscribed.html.twig Unsubscribe button and confirmation
shop/footer/widget.html.twig Teaser in the footer
shop/form/theme.html.twig Form theme of the forms (shows the category description below the checkbox)
emails/confirmation.html.twig, welcome.html.twig, already_subscribed.html.twig The three emails
emails/_cta.html.twig Button of the emails

The pages extend @SyliusShop/shared/layout/base.html.twig and fill its content block. The emails extend @SyliusCore/Email/layout.html.twig.

If your theme replaces Sylius' footer template without rendering the hook sylius_shop.base.footer.content, the teaser form does not appear. Link to /newsletter instead.

On the signup page the plugin also renders the hook mmd_altcha.widget.newsletter after the consent checkbox and before the submit button, which the ALTCHA plugin uses to attach its widget. Without that plugin the hook stays empty. How other plugins extend the form is described on the Extending page.