[9.x] Alternative Mailable Syntax - #44462
Conversation
|
I really like the syntax, but I have a suggestion to ?maybe? improve readability, of course you could write it with the constructor arguments, but what about methods too? public function envelope()
{
$envelope = new Envelope('Invoice Paid');
// addRecipient ($address, $type = 'to')
$envelope->addRecipient(new Address('foo@example.com', 'Example Name'), type: 'cc');
$envelope->addRecipient(new Address('foo@example.com', 'Example Name'), type: 'bcc');
$envelope->addTags([]);
$envelope->addMetadata([]);
return $envelope;
} |
|
In fact methods will be really helpful to conditionally apply logic like, if in production always CC the sales email address. |
|
Syntax looks great |
So perhaps as well as the methods we could add the |
cc: ['taylor@example.com'] + ($production ? ['second@example.com'] : []), |
Sure that's possible but it's not exactly nice to scan, especially if you're using the full address syntax? return new Envelope(
subject: 'Invoice Paid',
cc: [new Address('taylor@example.com', 'Taylor Otwell')] + ($production ? [new Address('second@example.com', 'Second Address')] : []),
tags: [],
metadata: [],
);I'd be more inclined to go $ccRecipients = [new Address('taylor@example.com', 'Taylor Otwell')];
if($production) {
$ccRecipients[] = new Address('second@example.com', 'Second Address');
}
return new Envelope(
subject: 'Invoice Paid',
cc: $ccRecipients,
tags: [],
metadata: [],
);But isn't it easier to understand if it's just $envelope = new Envelope(
subject: 'Invoice Paid',
cc: [new Address('taylor@example.com', 'Taylor Otwell')],
tags: [],
metadata: [],
));
if($production) {
$envelope->addRecipient(new Address('second@example.com', 'Second Address'), type: 'cc');
}
return $envelope;Or if conditionable then return (new Envelope(
subject: 'Invoice Paid',
cc: [new Address('taylor@example.com', 'Taylor Otwell')],
tags: [],
metadata: [],
)))->when($production, function(Envelope $envelope) {
$envelope->addRecipient(new Address('second@example.com', 'Second Address'), type: 'cc');
}); |
There was a problem hiding this comment.
Suggesting we explicitly mark constructors with named argument support.
This is a custom docblock, similar to what is seen in PHPUnit (however they are opting out of named argument support).
As Laravel does not support named arguments globally, this will serve as a marker for developers and maintainers that the feature is supported here.
|
For me personally, explicitly passing constructor properties is redundant and ugly. So agree with @Sammyjo20 for adding methods. It is also necessary to be able to use beautiful code. For example: <?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class InvoicePaid extends Mailable
{
use Queueable, SerializesModels;
public function envelope()
{
return (new Envelope())
->setSubject('Invoice Paid')
->addRecipient(new Address('foo@example.com', 'Example Name'), type: 'cc')
->addRecipient(new Address('foo@example.com', 'Example Name'), type: 'bcc')
->addTags(['foo', 'bar'])
->addMetadata([]);
}
public function content()
{
return new Content(
view: 'html-view-name',
text: 'text-view-name',
);
}
} |
|
Just another possible syntax. public function envelope()
{
return new Envelope(
subject: 'Invoice Paid',
cc: Envelope\CC::make()
->add(new Address('foo@example.com', 'Example Name'))
->when($production, new Address('admin@example.com', 'Example Admin')),
tags: [],
metadata: [],
);
} |
Co-authored-by: Tim MacDonald <hello@timacdonald.me>
Co-authored-by: Tim MacDonald <hello@timacdonald.me>
Co-authored-by: Tim MacDonald <hello@timacdonald.me>
|
Added methods and conditionable trait to Envelope. |
|
Methods look good! |
|
Documentation: laravel/docs#8282 |
|
How can we use this syntax into APIs? |
Currently, mailables are "configured" via a
buildmethod. Within this method, various methods on the parent mailable class are invoked to specify the structure of the mailable:While this works fine, over the last couple of years it has always rubbed me the wrong way. It feels a lot like an initialization method or a two-step constructor. The mailable object can't be inspected in any useful way until that method is invoked. There aren't really any other types of objects in Laravel user-land that work this way or follow this pattern.
This PR offers an alternative syntax for defining mailables in which several discrete methods may be defined that each return configuration style objects that describe the structure of the mailable:
Note: Only the
contentmethod is really required when defining mailables in this fashion.A more minimal example would look like the following:
The DX of this feature relies heavily on PHP 8 named arguments to provide a nice, clean experience when defining the configuration of the mailable object.
While the resulting mailable does end up having more lines of code overall, it feels a bit more like a real object and not a weird, two-step constructed thing. The
envelope,content, andattachmentsmethods may all be invoked as many times as needed and their results inspected like normal objects.A few new methods have also been added to inspect mailable instances:
In addition, an optional
headersmethod may be defined to add additional headers, including arbitrary text headers to the message:The traditional way of defining mailables using the
buildmethod will not be removed.