Installation:
composer require wnx/laravel-sends
Publish the migration and config:
php artisan vendor:publish --provider="Wnx\LaravelSends\LaravelSendsServiceProvider"
php artisan migrate
Enable Tracking:
Add the HasSends trait to your Eloquent models:
use Wnx\LaravelSends\Concerns\HasSends;
class User extends Model
{
use HasSends;
}
First Use Case: Send an email and automatically associate it with models:
Mail::to($user)->send(new ProductReviewMail($product, $user));
The email will now be tracked and linked to both $product and $user.
config/laravel-sends.php (e.g., table name, model associations).database/migrations/[timestamp]_create_laravel_sends_table.php (customize if needed).Send facade and HasSends trait methods.Associating Models with Emails:
HasSends trait on Eloquent models to query sent emails:
$user->sends()->latest()->take(5)->get(); // Get 5 latest emails for the user
send() method to auto-associate:
Mail::to($user)->send(new OrderConfirmationMail($order, $user));
Querying Sent Emails:
$user->sends()->where('subject', 'like', '%review%')->get();Send::forMailClass(OrderConfirmationMail::class)->where('status', 'sent')->get();
Send::where('recipient', 'user@example.com')->get();
Customizing Email Tracking:
AppServiceProvider:
use Wnx\LaravelSends\Events\EmailSent;
public function boot()
{
EmailSent::listen(function ($event) {
// Custom logic (e.g., log to external service)
});
}
Testing:
$this->assertCount(1, $user->fresh()->sends());
$this->assertEquals(OrderConfirmationMail::class, $user->sends->first()->mailable);
Queue Integration:
If using queues, ensure the EmailSent event fires by adding this to your AppServiceProvider:
Mail::afterSend(function ($message) {
event(new EmailSent($message));
});
Custom Attributes:
Attach metadata to emails via the Send facade:
Mail::to($user)->send(new ProductReviewMail($product, $user))
->withMetadata(['campaign_id' => 123]);
Soft Deletes:
Enable soft deletes in the laravel-sends table by adding SoftDeletes to the Send model and configuring the deleted_at column in the migration.
Event Listener Conflicts:
EmailSent events are fired multiple times (e.g., due to middleware or other packages), deduplicate logic in the listener or use a queue to avoid duplicates.Model Association Limits:
Send pivot model or use custom queries.Queue Delays:
EmailSent event may fire after the mailable is processed. For immediate tracking, use Mail::send() instead of Mail::queue().Recipient Overrides:
recipient field in the laravel_sends table defaults to the to address. Override this in the EmailSent listener if using custom recipient logic.Missing Records:
EmailSent event is firing by adding a dd() in the listener or checking Laravel logs:
tail -f storage/logs/laravel.log | grep "EmailSent"
laravel-sends table exists and is writable.Query Issues:
Send::query()->toSql() to debug raw queries:
$query = Send::forMailClass(OrderConfirmationMail::class);
dd($query->toSql(), $query->getBindings());
Custom Columns:
Add columns to the laravel_sends table via migration and update the Send model:
Schema::table('laravel_sends', function (Blueprint $table) {
$table->string('custom_field')->nullable();
});
Then access via:
$send->custom_field;
Performance:
mailable, recipient):
Schema::table('laravel_sends', function (Blueprint $table) {
$table->index('mailable');
$table->index('recipient');
});
Bulk Operations: Use chunking for large datasets:
$user->sends()->chunk(100, function ($sends) {
foreach ($sends as $send) {
// Process each send
}
});
Testing:
Send::flush() to clear test data between tests:
public function tearDown(): void
{
Send::flush();
parent::tearDown();
}
Extending the Package:
Send model to add custom methods:
namespace App\Models;
use Wnx\LaravelSends\Models\Send as BaseSend;
class Send extends BaseSend
{
public function isPromotional()
{
return $this->mailable === PromotionalMail::class;
}
}
AppServiceProvider:
Send::swap(new \App\Models\Send());
How can I help you explore Laravel packages today?