bredabeds / magento-email-attachments
Attach files to Magento emails, including the emails Magento sends itself (Magento 2.4.9+, Symfony Mailer)
Package info
github.com/BredaBeds/magento-email-attachments
Type:magento2-module
pkg:composer/bredabeds/magento-email-attachments
Requires
- php: ~8.3.0 || ~8.4.0 || ~8.5.0
- magento/framework: ^103.0.9
- magento/module-email: ^101.1.9
- symfony/mime: ^7.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Attach files to any email Magento sends, including the emails Magento sends itself (order, invoice and shipment confirmations), without replacing Magento's mail classes.
Supports Magento 2.4.9+ (Symfony Mailer, symfony/mime 7). Version 1.x built attachments with
Laminas\Mime, which Magento 2.4.8 removed from the mail stack: on 2.4.8+ 1.x attachments no longer work.
Install
composer require bredabeds/magento-email-attachments:^2.0
bin/magento module:enable BredaBeds_EmailAttachments
bin/magento setup:upgrade
bin/magento setup:di:compile
The attachment directory
Attachments are regular files in var/email-attachments/. Any other path is refused: a template variable
must never be able to mail out an arbitrary file of the installation. Write files with Storage::put(),
which takes a plain relative path (letters, digits, ., _, -; sub-directories are created) and returns
the absolute path to hand to the module:
public function __construct(private readonly \BredaBeds\EmailAttachments\Model\Storage $storage) {} $path = $this->storage->put('invoices/invoice_100001.pdf', $pdfBytes);
The directory is a staging area, not an archive: a daily cron job (bredabeds_email_attachments_cleanup)
deletes files older than 30 days. Keep your own copy if you need one.
Paths are checked on their real location: .., symlinks that point outside the directory, directories,
missing files and stream wrappers (php://…) are all refused.
Attach from an observer (template variable)
Code that does not hold the transport builder, for example an observer of
email_order_set_template_vars_before, adds the template variable Attachment::TEMPLATE_VAR
(bredabeds_email_attachments). The module removes it from the variables before the template is rendered.
use BredaBeds\EmailAttachments\Model\Attachment; public function execute(\Magento\Framework\Event\Observer $observer): void { $transportObject = $observer->getData('transportObject'); $vars = $transportObject->getData(); $vars[Attachment::TEMPLATE_VAR] = [ ['path' => $path, 'filename' => 'Invoice.pdf', 'type' => 'application/pdf'], ]; $transportObject->setData($vars); }
Three shapes are accepted:
$vars[Attachment::TEMPLATE_VAR] = $path; // one path $vars[Attachment::TEMPLATE_VAR] = ['path' => $path, 'filename' => 'A.pdf']; // one file with options $vars[Attachment::TEMPLATE_VAR] = [$path1, ['path' => $path2, 'type' => 'text/csv']]; // a list of either
filename defaults to the file's own name, type to the MIME type detected from the content.
The same key works with any TransportBuilder::setTemplateVars() call.
Attach from code that holds the transport
use BredaBeds\EmailAttachments\Model\Attacher; use BredaBeds\EmailAttachments\Model\AttachmentFactory; $transport = $this->transportBuilder ->setTemplateIdentifier('my_template') ->setTemplateOptions(['area' => 'frontend', 'store' => $storeId]) ->setTemplateVars($vars) ->setFromByScope('general', $storeId) ->addTo($email) ->getTransport(); $this->attacher->attach($transport->getMessage(), $this->attachmentFactory->fromFile($path, 'Invoice.pdf')); $transport->sendMessage();
Public API (@api): Model\Attachment, Model\AttachmentFactory, Model\Storage, Model\Attacher.
Limits and failures
- At most 15 MiB of attachments per email (about 20.6 MB once base64-encoded), empty files are refused.
- An invalid attachment (outside the directory, missing, empty, too large, bad MIME type, wrong shape)
fails that email:
getTransport()throws aLocalizedException. For Magento's sales emails the sender logs it and the email is not sent (queued emails are retried persales_email/general/async_sending_attempts); other emails are unaffected. Nothing is skipped silently. - Error messages name the file or the type, never a path outside the directory or the content.
To verify, send a test email through an SMTP log (for example a mail-log module) or a mail catcher and check the attachment part.
Changelog
2.0.0
- Magento 2.4.9+ only: attachments are Symfony Mime parts.
- Plugins on
TransportBuilderinstead of a preference; the core builder is no longer replaced. - Attachments must live in
var/email-attachments/(Storage::put()); daily cleanup after 30 days. - Template variable renamed from
attachmentstobredabeds_email_attachments(Attachment::TEMPLATE_VAR). TransportBuilder::addAttachment()replaced byAttacher::attach().- Removed: the Browserless PDF helper, the basic mailer helper and its
simple_text_emailtemplate, thebredabeds:email-attachments:email-confirmationcommand.