PHPackages                             wnx/laravel-sends - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. wnx/laravel-sends

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

wnx/laravel-sends
=================

Keep track of outgoing emails in your Laravel application.

v2.8.1(2mo ago)200427.3k—9.6%10MITPHPPHP ^8.3CI passing

Since Dec 3Pushed 2mo ago2 watchersCompare

[ Source](https://github.com/stefanzweifel/laravel-sends)[ Packagist](https://packagist.org/packages/wnx/laravel-sends)[ Docs](https://github.com/stefanzweifel/laravel-sends)[ GitHub Sponsors](https://github.com/stefanzweifel)[ RSS](/packages/wnx-laravel-sends/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (16)Versions (20)Used By (0)

Keep track of outgoing emails and associate sent emails with Eloquent models
============================================================================

[](#keep-track-of-outgoing-emails-and-associate-sent-emails-with-eloquent-models)

[![Latest Version on Packagist](https://camo.githubusercontent.com/9f47017259e1779362536c96f15063a546a0d0b1ee4c5e19fb41dba7bcd8fad9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f776e782f6c61726176656c2d73656e64732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/wnx/laravel-sends)[![run-tests](https://github.com/stefanzweifel/laravel-sends/actions/workflows/run-tests.yml/badge.svg)](https://github.com/stefanzweifel/laravel-sends/actions/workflows/run-tests.yml)[![Check & fix styling](https://github.com/stefanzweifel/laravel-sends/actions/workflows/php-cs-fixer.yml/badge.svg)](https://github.com/stefanzweifel/laravel-sends/actions/workflows/php-cs-fixer.yml)[![Total Downloads](https://camo.githubusercontent.com/f40ddb05b66447f9eb92af89eaa9423395a8ba7ddff901c247e2ec452338add3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f776e782f6c61726176656c2d73656e64732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/wnx/laravel-sends)

This package helps you to keep track of outgoing emails in your Laravel application. In addition, you can associate Models to the sent out emails.

Here are a few short examples what you can do:

```
// An email is sent to $user. The passed $product and $user models will be
// associated with the sent out email generated by the `ProductReviewMail` mailable.
Mail::to($user)->send(new ProductReviewMail($product, $user));
```

In your application, you can then fetch all sent out emails for the user or the product.

```
$user->sends()->get();
$product->sends()->get();
```

Or you can fetch all sent out emails for the given Mailable class.

```
Send::forMailClass(ProductReviewMail::class)->get();
```

Each `Send`-model holds the following information:

- FQN of mailable class
- subject
- from address
- reply to address
- to address
- cc adresses
- bcc adresses

Additionally, the `sends`-table has the following columns which can be filled by your own application ([learn more](#further-usage-of-the-sends-table)).

- `delivered_at`
- `last_opened_at`
- `opens`
- `clicks`
- `last_clicked_at`
- `complained_at`
- `bounced_at`
- `permanent_bounced_at`
- `rejected_at`

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require wnx/laravel-sends
```

Then, publish and run the migrations:

```
php artisan vendor:publish --tag="sends-migrations"
php artisan migrate
```

Optionally, you can publish the config file with:

```
php artisan vendor:publish --tag="sends-config"
```

This is the contents of the published config file:

```
return [
    /*
     * The fully qualified class name of the `Send` model.
     */
    'send_model' => \Wnx\Sends\Models\Send::class,

    /**
     * If set to true, the content of sent mails is saved to the database.
     */
    'store_content' => env('SENDS_STORE_CONTENT', false),

    'headers' => [
        /**
         * Header containing the encrypted FQN of the mailable class.
         */
        'mail_class' => env('SENDS_HEADERS_MAIL_CLASS', 'X-Laravel-Mail-Class'),

        /**
         * Header containing an encrypted JSON object with information which
         * Eloquent models are associated with the mailable class.
         */
        'models' => env('SENDS_HEADERS_MAIL_MODELS', 'X-Laravel-Mail-Models'),

        /**
         * Header containing unique ID of the sent out mailable class.
         * Set this to `Message-ID`, if you want to use the Message ID as the unique ID identifing the sent mail.
         */
        'send_uuid' => env('SENDS_HEADERS_SEND_UUID', 'X-Laravel-Send-UUID'),
    ],
];
```

Usage
-----

[](#usage)

After the installation is done, update your applications `EventServiceProvider` to listen to the `MessageSent` event. Add the `StoreOutgoingMailListener`-class as a listener.

```
// app/Providers/EventServiceProvider.php
protected $listen = [
    // ...
    \Illuminate\Mail\Events\MessageSent::class => [
        \Wnx\Sends\Listeners\StoreOutgoingMailListener::class,
    ],
]
```

The metadata of **all** outgoing emails created by mailables or notifications is now being stored in the `sends`-table. (Note that you can only associate models to mailables; but not to notifiations)

Read further to learn how to store the name and how to associate models with a Mailable class.

### Store Mailable class name on Send Model

[](#store-mailable-class-name-on-send-model)

By default the Event Listener stores the mails subject and the recipient adresses. That's nice, but we can do better. It can be beneficial for your application to know which Mailable class triggered the sent email.

To store this information, add the `StoreMailables`-trait to your Mailable classes like below. You now have access to a couple of helper methods.

Depending on how you write Mailables, there a different ways to use these new methods. Call the `storeClassName`-method inside the `build`-method of your Mailable.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(public User $user, public Product $product)
    {
    }

    public function build()
    {
        return $this
            ->storeClassName()
            ->view('emails.products.review')
            ->subject("$this->product->name waits for your review");
    }
}
```

If you use the Mailable syntax introduced in Laravel v9.35, you can either use `$this->storeClassName()` in the `headers`-method or pass `$this->getMailClassHeader()->toArray()` to the `Header` object.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(
        public User $user,
        public Product $product
    ) { }

    // ...

    /**
     * @return \Illuminate\Mail\Mailables\Headers
     */
    public function headers()
    {
        // Call storeClassName() and let the package take care of adding the
        // header to the outgoing mail.
        $this->storeClassName();

        // Or – if you want more control – use the getMailClassHeader() method
        // to get a Header instance and merge it with your own headers.
        return new Headers(
            text: [
                'X-Custom-Header' => 'Custom Value',
                ...$this->getMailClassHeader()->toArray(),
            ],
        );
    }
}
```

The method will add a `X-Laravel-Mail-Class`-header to the outgoing email containing the fully qualified name (FQN) of the Mailable class as an encrypted string. (eg. `App\Mails\ProductReviewMail`). Update the `SENDS_HEADERS_MAIL_CLASS`-env variable to adjust the header name. (See config for details).

The package's event listener will then look for the header, decrypt the value and store it in the database.

### Associate Sends with Related Models

[](#associate-sends-with-related-models)

Now that you already keep track of all outgoing emails, wouldn't it be great if you could access the records from an associated Model? In the example above we send a `ProductReviewMail` to a user. Wouldn't it be great to see how many emails you have sent for a given `Product`-model?

To achieve this, you have to add the `HasSends`-interface and the `HasSendsTrait` trait to your models.

```
use Wnx\Sends\Contracts\HasSends;
use Wnx\Sends\Support\HasSendsTrait;

class Product extends Model implements HasSends
{
    use HasSendsTrait;
}
```

You can now call the `associateWith()`-method within the `build()`-method. Pass the Models you want to associate with the Mailable class to the method as arguments. Instead of passing the Models as arguments, you can also pass them as an array.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(
        private User $user,
        private Product $product
    ) { }

    public function build()
    {
        return $this
            ->storeClassName()
            ->associateWith($this->product)
            // ->associateWith([$this->product])
            ->view('emails.products.review')
            ->subject("$this->user->name, $this->product->name waits for your review");
    }
}
```

If you're using the Mailable syntax introduced in Laravel v9.35, you can call `associateWith()` or `getMailModelsHeader()` in the `headers`-method too.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(
        private User $user,
        private Product $product
    ) { }

    // ...

    /**
     * @return \Illuminate\Mail\Mailables\Headers
     */
    public function headers()
    {
        // Call associateWith() and the package automatically associates the public
        // properties with this mailable.
        $this->associateWith($this->user);
        $this->associateWith([$this->product]);

        // Or – if you want more control – use the getMailModelsHeader() method
        // to get a Header instance and merge it with your own headers.
        return new Headers(
            text: [
                'X-Custom-Header' => 'Custom Value',
                ...$this->getMailModelsHeader($this->user)->toArray(),
                ...$this->getMailModelsHeader([$this->product])->toArray(),
            ],
        );
    }
}
```

You can now access the sent out emails from the product's `sends`-relationship.

```
$product->sends()->get();
```

#### Automatically associate Models with Mailables

[](#automatically-associate-models-with-mailables)

If you do not pass an argument to the `associateWith`-method, the package will automatically associate all public properties which implement the `HasSends`-interface with the Mailable class. 🪄

In the example below, the `ProductReviewMail`-Mailable will automatically be associated with the `$product` Model, as `Product` implements the `HasSends` interface. The `$user` model will be ignored, as it's declared as a private property.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(
        private User $user,
        public Product $product
    ) { }

    public function build()
    {
        return $this
            ->associateWith()
            ->view('emails.products.review')
            ->subject("$this->user->name, $this->product->name waits for your review");
    }
}
```

If you're using the Mailable syntax introduced in Laravel v9.35, you can call `associateWith()` or `getMailModelsHeader()` in the `headers`-method.

```
class ProductReviewMail extends Mailable
{
    use SerializesModels;
    use StoreMailables;

    public function __construct(
        private User $user,
        public Product $product
    ) { }

    // ...

    /**
     * @return \Illuminate\Mail\Mailables\Headers
     */
    public function headers()
    {
        // Call associateWith() and the package automatically associates the public
        // properties with this mailable.
        $this->associateWith();

        // Or – if you want more control – use the getMailModelsHeader() method
        // to get a Header instance and merge it with your own headers.
        return new Headers(
            text: [
                'X-Custom-Header' => 'Custom Value',
                ...$this->getMailModelsHeader()->toArray(),
            ],
        );
    }
}
```

### Attach custom Message ID to Mails

[](#attach-custom-message-id-to-mails)

If you're sending emails through AWS SES or a similar service, you might want to identify the sent email in the future (for example when a webhook for the "Delivered"-event is sent to your application).

The package comes with an event listener helping you here. Update the EventServiceProvider to listen to the `MessageSending` event and add the `AttachSendUuidListener` as a listener. A `X-Laravel-Message-UUID` header will be attached to all outgoing emails. The header contains a UUID value. (This value can not be compared to the `Message-ID` defined in [RFC 2392](https://datatracker.ietf.org/doc/html/rfc2392))
You can then use the value of `X-Laravel-Message-UUID` or `$send->uuid` later in your application.

```
// app/Providers/EventServiceProvider.php
protected $listen = [
    // ...
    \Illuminate\Mail\Events\MessageSending::class => [
        \Wnx\Sends\Listeners\AttachSendUuidListener::class,
    ],
]
```

(If you want to store the value of `Message-ID` in your database, do not add the event listener but update the `SENDS_HEADERS_SEND_UUID`-env variable to `Message-ID`. The `StoreOutgoingMailListener` will then store the `Message-ID` in the database.)

### Store Content of Mails

[](#store-content-of-mails)

By default, the package does not store the content of sent out emails. By setting the `sends.store_content` configuration value to `true`, the body of all outgoing mails is stored in the `content`-column in the `sends` database table.

```
/**
 * If set to true, the contet of sent mails is saved to the database.
 */
'store_content' => true,
```

```
SENDS_STORE_CONTENT=true
```

### Customize Attributes stored in Send Models

[](#customize-attributes-stored-in-send-models)

If you need to store more attributes with your `Send`-model, you can extend the `StoreOutgoingMailListener` and override the `getSendAttributes`-method.

For example, let's say we would like to store an `audience`-value with each sent out email. We create a new Event Listener called `CustomStoreOutgoingMailListener` and use the class as Listener to the `MessageSent`-event.

Our `EventServiceProvider` would look like this.

```
// app/Providers/EventServiceProvider.php
protected $listen = [
    // ...
    \Illuminate\Mail\Events\MessageSent::class => [
        \App\Listeners\CustomStoreOutgoingMailListener::class,
    ],
]
```

The Listener itself would look like the code below. We extend `Wnx\Sends\Listeners\StoreOutgoingMailListener` and override `getSendAttributes`. We merge the `$defaultAttributes` with our custom attributes we want to store. In the example below we store an `audience` value.

```
