Shopware 6.7 introduces significant improvements to its mail template system, making it more flexible and powerful for developers. This blog post will guide you through the complete process of creating custom mail templates, from basic setup to advanced customization techniques.
Understanding Shopware 6.7 Mail Template Architecture
Before diving into implementation, it's crucial to understand how Shopware 6.7 handles mail templates. The platform now utilizes a more robust template inheritance system with better integration between twig templates and the administration interface. Mail templates are stored in the database and can be managed through both the admin panel and code.
The new architecture separates template content from variables, allowing for more dynamic and reusable templates. This change provides developers with greater control over email composition while maintaining backward compatibility.
Prerequisites and Setup
To create custom mail templates in Shopware 6.7, you'll need:
- A working Shopware 6.7 installation
- Development environment with proper permissions
- Basic understanding of Twig templating
- Knowledge of Shopware's plugin structure
Ensure your plugin is properly registered and that you have the necessary service definitions in your services.xml file.
Step 1: Creating the Plugin Structure
First, create the basic plugin structure for your mail template functionality:
<?php declare(strict_types=1);
use Shopware\Core\Framework\Plugin;
class YourPluginName extends Plugin
{
// Plugin implementation
}
The plugin should include a Resources/config directory where you'll place your configuration files.
Step 2: Defining Mail Template Configuration
Create a new file Resources/config/mail_templates.xml to define your custom mail template:
<?xml version="1.0" encoding="UTF-8"?>
<mail-templates xmlns="http://symfony.com/schema/dic/mail-templates"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/mail-templates https://symfony.com/schema/dic/mail-templates/mail-templates-1.0.xsd">
<mail-template name="custom_order_confirmation">
<subject>Order Confirmation</subject>
<description>Custom order confirmation email template</description>
<sender-name>Shop Name</sender-name>
<sender-email>[email protected]</sender-email>
<content-html>
<![CDATA[
<html>
<head>
<title>Order Confirmation</title>
</head>
<body>
<h1>Thank you for your order!</h1>
<p>Dear {{ customer.firstName }} {{ customer.lastName }},</p>
<p>Your order #{{ order.orderNumber }} has been confirmed.</p>
<table>
<tr>
<td><strong>Order Total:</strong></td>
<td>{{ order.amountTotal|currency }}</td>
</tr>
<tr>
<td><strong>Order Date:</strong></td>
<td>{{ order.orderDate|date }}</td>
</tr>
</table>
</body>
</html>
]]>
</content-html>
<content-plain>
<![CDATA[
Thank you for your order!
Dear {{ customer.firstName }} {{ customer.lastName }},
Your order #{{ order.orderNumber }} has been confirmed.
Order Total: {{ order.amountTotal|currency }}
Order Date: {{ order.orderDate|date }}
]]>
</content-plain>
</mail-template>
</mail-templates>
Step 3: Implementing Service Registration
In your plugin's services.xml file, register the mail template services:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="YourPluginName\MailTemplateService">
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria"/>
<argument type="service" id="Shopware\Core\System\Salutation\SalesChannel\SalutationRoute"/>
</service>
<service id="YourPluginName\MailTemplateSubscriber">
<tag name="kernel.event_subscriber"/>
</service>
</services>
</container>
Step 4: Creating the Mail Template Service
Create a dedicated service class to handle mail template operations:
<?php declare(strict_types=1);
namespace YourPluginName;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\Mail\MailTemplateEntity;
use Shopware\Core\System\Mail\MailTemplateRepository;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class MailTemplateService
{
use ContainerAwareTrait;
private MailTemplateRepository $mailTemplateRepository;
public function __construct(MailTemplateRepository $mailTemplateRepository)
{
$this->mailTemplateRepository = $mailTemplateRepository;
}
public function createCustomMailTemplate(string $templateName, array $data): void
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', $templateName));
$existing = $this->mailTemplateRepository->search($criteria, Context::createDefaultContext());
if ($existing->getTotal() > 0) {
// Update existing template
$this->updateMailTemplate($existing->first(), $data);
} else {
// Create new template
$this->createMailTemplate($templateName, $data);
}
}
private function createMailTemplate(string $templateName, array $data): void
{
$mailTemplate = [
'name' => $templateName,
'subject' => $data['subject'] ?? '',
'description' => $data['description'] ?? '',
'senderName' => $data['senderName'] ?? '',
'senderEmail' => $data['senderEmail'] ?? '',
'contentHtml' => $data['contentHtml'] ?? '',
'contentPlain' => $data['contentPlain'] ?? '',
];
$this->mailTemplateRepository->create([$mailTemplate], Context::createDefaultContext());
}
private function updateMailTemplate(MailTemplateEntity $template, array $data): void
{
$updateData = [
'id' => $template->getId(),
'subject' => $data['subject'] ?? $template->getSubject(),
'description' => $data['description'] ?? $template->getDescription(),
'senderName' => $data['senderName'] ?? $template->getSenderName(),
'senderEmail' => $data['senderEmail'] ?? $template->getSenderEmail(),
'contentHtml' => $data['contentHtml'] ?? $template->getContentHtml(),
'contentPlain' => $data['contentPlain'] ?? $template->getContentPlain(),
];
$this->mailTemplateRepository->update([$updateData], Context::createDefaultContext());
}
}
Step 5: Advanced Template Variables
Shopware 6.7 supports complex variable structures in mail templates. Here's an example of how to handle nested data:
{# Custom order confirmation template with advanced variables #}
<html>
<head>
<title>{{ subject }}</title>
</head>
<body>
<h1>Order Confirmation for {{ customer.firstName }} {{ customer.lastName }}</h1>
<p>Order Number: {{ order.orderNumber }}</p>
<p>Order Date: {{ order.orderDate|date('Y-m-d H:i:s') }}</p>
<p>Total Amount: {{ order.amountTotal|currency }}</p>
{% if order.shippingAddress %}
<h2>Shipping Address</h2>
<p>{{ order.shippingAddress.firstName }} {{ order.shippingAddress.lastName }}</p>
<p>{{ order.shippingAddress.street }}</p>
<p>{{ order.shippingAddress.zipcode }} {{ order.shippingAddress.city }}</p>
{% endif %}
<h2>Order Items</h2>
<table border="1">
<thead>
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{% for lineItem in order.lineItems %}
<tr>
<td>{{ lineItem.label }}</td>
<td>{{ lineItem.quantity }}</td>
<td>{{ lineItem.price.unitPrice|currency }}</td>
<td>{{ lineItem.price.totalPrice|currency }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><strong>Order Total: {{ order.amountTotal|currency }}</strong></p>
</body>
</html>
Step 6: Template Inheritance and Customization
Shopware 6.7 supports template inheritance, allowing you to extend existing templates:
// In your plugin service
public function createInheritedTemplate(string $parentTemplateName, string $customTemplateName): void
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', $parentTemplateName));
$parentTemplate = $this->mailTemplateRepository->search($criteria, Context::createDefaultContext());
if ($parentTemplate->getTotal() > 0) {
$parent = $parentTemplate->first();
$customTemplate = [
'name' => $customTemplateName,
'subject' => $parent->getSubject(),
'description' => 'Custom version of ' . $parentTemplateName,
'senderName' => $parent->getSenderName(),
'senderEmail' => $parent->getSenderEmail(),
'contentHtml' => $this->generateCustomContent($parent->getContentHtml()),
'contentPlain' => $this->generateCustomContent($parent->getContentPlain()),
];
$this->mailTemplateRepository->create([$customTemplate], Context::createDefaultContext());
}
}
private function generateCustomContent(string $baseContent): string
{
// Custom logic to modify content
return str_replace('{{ base_variable }}', '{{ custom_variable }}', $baseContent);
}
Step 7: Testing and Validation
Create a comprehensive test suite for your mail templates:
<?php declare(strict_types=1);
namespace YourPluginName\Test;
use PHPUnit\Framework\TestCase;
use Shopware\Core\System\Mail\MailTemplateEntity;
class MailTemplateTest extends TestCase
{
public function testMailTemplateCreation(): void
{
$mailTemplate = new MailTemplateEntity();
$mailTemplate->setName('test_custom_template');
$mailTemplate->setSubject('Test Subject');
$mailTemplate->setContentHtml('<h1>Test Content</h1>');
$this->assertEquals('test_custom_template', $mailTemplate->getName());
$this->assertEquals('Test Subject', $mailTemplate->getSubject());
$this->assertEquals('<h1>Test Content</h1>', $mailTemplate->getContentHtml());
}
public function testTemplateVariables(): void
{
$templateContent = '{{ customer.firstName }} {{ customer.lastName }}';
$expected = 'John Doe';
// Test variable substitution logic here
$this->assertStringContainsString('customer.firstName', $templateContent);
}
}
Step 8: Performance Considerations
When working with custom mail templates, consider these performance optimizations:
- Caching: Implement proper caching strategies for frequently used templates
- Template Precompilation: Precompile complex templates during plugin installation
- Database Queries: Minimize database calls when retrieving template data
// Example of optimized template retrieval
public function getTemplateWithCache(string $templateName): ?MailTemplateEntity
{
$cacheKey = 'mail_template_' . md5($templateName);
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', $templateName));
$template = $this->mailTemplateRepository->search($criteria, Context::createDefaultContext())->first();
if ($template) {
$this->cache->set($cacheKey, $template, 3600); // Cache for 1 hour
}
return $template;
}
Conclusion
Shopware 6.7's enhanced mail template system provides developers with unprecedented flexibility in creating and managing email communications. By following the steps outlined in this guide, you can create robust, maintainable custom mail templates that integrate seamlessly with your shop's existing functionality.
The key advantages of Shopware 6.7's approach include better template inheritance, improved variable handling, and more granular control over email content generation. Remember to always test your templates thoroughly and consider performance implications when implementing complex templating logic.
With proper implementation, your custom mail templates will not only meet current requirements but also provide a solid foundation for future enhancements and extensions.