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
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 |
|---|---|---|
| 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.
Footer form
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
- the newsletter is enabled for the channel,
- "Signup form in the footer" is switched on, and
- the current page is not one of the plugin's own newsletter pages (there it would duplicate the form, or invite another signup right after a confirmation).
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:
- 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.
- 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.
- 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. - 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:
- first and last name,
- the selected categories, and
- in tracking mode "Personal", the optional tracking consent. Withdrawing it immediately deletes this subscriber's individual tracking data.
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
- sets the status to "Unsubscribed" and stores the time,
- withdraws a tracking consent and deletes the individual tracking data, and
- counts as an unsubscribe of the newsletter the link came from, if it came from a newsletter.
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:
- Registration and profile. If customers tick the checkbox, the plugin creates a subscription with the source "Registration". With double opt-in the customer receives the confirmation email, and the Sylius field is reset to "not subscribed" until the link has been clicked.
- Only a real tick counts. On a profile update only a change from "off" to "on" is evaluated. An existing customer whose field was already "on" (legacy data, an import) does not lose it and gets no email just because they changed their phone number.
- Saves by an administrator are ignored. They are not the customer's consent.
- Newsletter disabled for the channel: the plugin leaves the Sylius field unchanged.
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).
Stored proof of consent
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/newsletterinstead.
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.