Electronic mail is complicated. It's taken for granted by every end user in the world that "it's so simple" and should be free, but they don't want spam and they don't want you to sell their data... but I digress.

Laravel's mail API allows you to build an email using the blade templates you're already familiar with and it handles embedding images, which I'm a big fan of. That way your message is displayed as you planned it to be without having the user have to click on "Allow Images".

The problem we discovered is that when you're using the <img src="{{ $message->embed($pathToImage) }}"> embed functionality, the Mail::->attachFromStorage(); in your build() the attachments will be added to the multipart/related section of the email.

Because you've been studying the inner-workings of email forever, you certainly know that the modern email contains multiple containers separated by boundaries which have headers for each type of data in that container. The overarching email is:

multipart/mixed
  multipart/related
    multipart/alternative
      text/html
      text/plain
    image/png
    image/gif
    image/jpeg
  application/pdf
  application/zip

So the problem is that the Laravel API to Swiftmailer will dump the attachment into the multipart/related block when the embedded images are there which hides the attachments to the user, instead of into the multipart/mixed, where the attachments will be displayed to the user.

To get around this, we can access the underlying Swiftmailer object and add the attachment to that and our resulting email will now have the email attached to the multipart/mixed block of the message so the attachments show up but the embedded images stay where they were and everything works as it should.

App/Mail/PdfNotification.php
/**
 * Build the message.
 *
 * @return $this
 */
public function build()
{
  $data = Storage::get("somegreat.pdf");
  $attachment = (new Swift_Attachment())
    ->setFilename("somebetter.pdf")
    ->setContentType("application/pdf")
    ->setBody($data);

  return $this
    ->view("emails.notification")
    ->withSwiftMessage(function ($message) use ($attachment) {
      $message->attach($attachment);
    });
}

While this is amazing and a great work around, it's worth noting that the Symfony folks are working on a much better than this Swiftmailer. I've been fiddling with the new Symfony Mailer and it resolves all of the issues we have to work around today, but I wasn't able to figure out how to mash their new delivery mechanism into the Laravel SMTP delivery. One day email will work well... one day.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>