From 15daca3ebe0d4954690096b13f2f4e713c926622 Mon Sep 17 00:00:00 2001 From: daraghscopey Date: Tue, 28 Jul 2026 11:34:45 +0100 Subject: [PATCH] fix: read attachments from the path they were written to The storage path accessor looked up `filament-better-mails.logging.attachments.root`, but the shipped config only defines that key under `mails.`. The lookup returned null, so the prefix vanished and every attachment resolved to `/{id}/{filename}` instead of `mails/attachments/{id}/{filename}`. Downloads threw UnableToRetrieveMetadata, and ResendMailAction failed the same way through the file_data accessor. On PHP 8.4 the null also raised a deprecation in rtrim(). The reader now uses the same key and default as the writer. The writer in turn drops its own inline path construction and uses the accessor, since the record is created before the file is written. Two independent lookups of one key is what let the two sides drift; now there is a single derivation. The disk comes off the stored record for the same reason. Stored files need no migration; they were always written to the right path. Fixes #11 --- .../Listeners/BeforeSendingMailListener.php | 6 +- src/Core/Models/BetterEmailAttachment.php | 4 +- .../Core/Models/BetterEmailAttachmentTest.php | 74 +++++++++++++++++++ 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 tests/Feature/Core/Models/BetterEmailAttachmentTest.php diff --git a/src/Core/Listeners/BeforeSendingMailListener.php b/src/Core/Listeners/BeforeSendingMailListener.php index 26a1867..d310bc3 100644 --- a/src/Core/Listeners/BeforeSendingMailListener.php +++ b/src/Core/Listeners/BeforeSendingMailListener.php @@ -49,7 +49,6 @@ private function storeAttachments(array $attachments, BetterEmail $mail): void } $disk = config('filament-better-mails.mails.logging.attachments.disk', 'local'); - $root = config('filament-better-mails.mails.logging.attachments.root', 'mails/attachments'); foreach ($attachments as $part) { $filename = $part->getFilename() ?? 'attachment'; @@ -64,10 +63,7 @@ private function storeAttachments(array $attachments, BetterEmail $mail): void 'size' => strlen($content), ]); - Storage::disk($disk)->put( - $root.'/'.$attachment->getKey().'/'.$filename, - $content, - ); + Storage::disk($attachment->disk)->put($attachment->storage_path, $content); } } } diff --git a/src/Core/Models/BetterEmailAttachment.php b/src/Core/Models/BetterEmailAttachment.php index 4ad2648..ebdaca0 100644 --- a/src/Core/Models/BetterEmailAttachment.php +++ b/src/Core/Models/BetterEmailAttachment.php @@ -56,7 +56,9 @@ public function mail(): BelongsTo public function getStoragePathAttribute(): string { - return rtrim(config('filament-better-mails.logging.attachments.root'), '/').'/'.$this->getKey().'/'.$this->filename; + $root = config('filament-better-mails.mails.logging.attachments.root', 'mails/attachments'); + + return rtrim($root, '/').'/'.$this->getKey().'/'.$this->filename; } public function getFileDataAttribute(): string diff --git a/tests/Feature/Core/Models/BetterEmailAttachmentTest.php b/tests/Feature/Core/Models/BetterEmailAttachmentTest.php new file mode 100644 index 0000000..fa2a0d0 --- /dev/null +++ b/tests/Feature/Core/Models/BetterEmailAttachmentTest.php @@ -0,0 +1,74 @@ +subject('Test Subject') + ->from('from@example.com') + ->to('richard@3points.com') + ->html('

Test HTML

') + ->attach($content, $filename, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + + (new BeforeSendingMailListener)->handle(new MessageSending($email, [ + 'mailer' => 'log', + '__laravel_mailable' => FakeMail::class, + ])); + + return BetterEmailAttachment::query()->latest('id')->firstOrFail(); +} + +it('reads an attachment back from the path it was written to', function () { + Storage::fake('local'); + + $attachment = sendMailWithAttachment(); + + Storage::disk('local')->assertExists($attachment->storage_path); + expect($attachment->file_data)->toBe('file contents'); +}); + +it('prefixes the storage path with the configured attachment root', function () { + Storage::fake('local'); + + $attachment = sendMailWithAttachment(); + + expect($attachment->storage_path) + ->toBe('mails/attachments/'.$attachment->getKey().'/report.docx'); +}); + +it('honours a custom attachment root on both write and read', function () { + Storage::fake('local'); + config()->set('filament-better-mails.mails.logging.attachments.root', 'custom/root/'); + + $attachment = sendMailWithAttachment(); + + expect($attachment->storage_path)->toBe('custom/root/'.$attachment->getKey().'/report.docx'); + Storage::disk('local')->assertExists($attachment->storage_path); +}); + +it('falls back to the default root when the config key is absent', function () { + Storage::fake('local'); + + $attachment = sendMailWithAttachment(); + + config()->set('filament-better-mails.mails.logging.attachments', null); + + expect($attachment->fresh()->storage_path) + ->toBe('mails/attachments/'.$attachment->getKey().'/report.docx'); +}); + +it('can stream the attachment as a download', function () { + Storage::fake('local'); + + $attachment = sendMailWithAttachment(); + + $response = $attachment->downloadFileFromStorage(); + + expect($response->getStatusCode())->toBe(200); +});