Using the `Attachable` interface to attach any kind of object to a mail in a Laravel app
Laravel 9 has gained a excellent new way to attach files in mails. Using the Attachable
interface, you can specify what should happen when an object gets used as an attachment.
Using our media library package you can easily associate any file with an Eloquent model. We've added support for Laravel's Attachable
in the latest version of the package.
In this blog post, I'd like to tell you all about it.
Making use of Attachable
Let's look at how you would attach files to Mailables in previous versions of Laravel.
Imagine that your app has a simple Photo
model with a path
property that contains the path to the actual image file. Here is how you could attach the image file in a Mailable.
// in a Mailable
public function __construct(public Photo $photo)
{
}
public function build()
{
return $this
->view('emails.yourView')
->attach($this->photo->path);
}
This is not too bad, but it is strange that the mailable should know that Photo
has a path
property.
In Laravel 9, a new Attachable
interface was introduced. Let's use it in our Photo
model.
namespace App\Models;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Attachment;
class Photo extends Model implements Attachable
{
// ...
public function toMailAttachment(): Attachment
{
return Attachment::fromPath($this->path);
}
}
With this in place, you can pass a Photo
model to attach.
// in a Mailable
public function __construct(public Photo $photo)
{
}
public function build()
{
return $this
->view('emails.orders.shipped')
->attach($this->photo);
}
In this case, we only gained that we didn't have to specify ->path
. In real-world apps, determining the right path could be more complex, and you could, for instance, be using different disks.
Attaching media
Laravel Media Library is our powerhouse package to associate files with Eloquent models. If you aren't familiar with it yet, I can highly encourage going through the docs or watching the free video course.
Imagine that your app has a blog post model that you have prepared to use with media library .
Let's associate an image file to the model and use s3
to store it.
BlogPost::find($blogPostId);
->addMedia($pathToImage)
->toMediaCollection('images', 's3');
Behind the scenes, the media library will create a Media
model that holds all the information regarding the image file (the name, the path, the disk name, the mime type, ...) and a reference to the BlogPost
model.
Let's now attach that image file. Using getFirstMedia
we can get the first Media
model that is associated with the BlogPost
model.
// in a Mailable
public function __construct(public BlogPost $blogPost)
{
}
public function build()
{
return $this
->view('emails.yourView')
->attach($this->blogPost->getFirstMedia());
}
And that's all you need to do to attach that first image. Your mailable is oblivious to the fact that the image is stored on S3.
Let's look at how we use Laravel's attachable Attachable
interface in Media Library's Media
model. Here's the implementation of the toMailAttachment
attachment that the interface requires. As you can see, we use the properties of the Media
model to configure the returned Attachment
.
namespace Spatie\MediaLibrary\MediaCollections\Models;
use Illuminate\Contracts\Mail\Attachable;
class Media extends Model implements Attachable
{
// all other methods...
public function toMailAttachment(): Attachment
{
$path = $this->getUrlGenerator($conversion)->getPathRelativeToRoot();
$attachment = Attachment::fromStorageDisk($this->disk, $path)->as($this->file_name);
if ($this->mime_type) {
$attachment->withMime($this->mime);
}
return $attachment;
}
}
In closing
Tim McDonald did a very nice job adding Attachable
to Laravel. In this blog post, we've used the Attachable
interface on models. But you can add this to any object. Head over to the Laravel docs to know more. There are a few options that we didn't touch on in this blog post.
Media Library can also create thumbnails and any conversion for your files. These converted files can also be used as attachments. Head to the Media Library docs to know more about that.
Be sure also to look at this extensive list of other packages that our team has made. I'm pretty sure there's something there for your next project.