-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[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('[email protected]', 'Example Name'), type: 'cc');
$envelope->addRecipient(new Address('[email protected]', '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: ['[email protected]'] + ($production ? ['[email protected]'] : []), |
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('[email protected]', 'Taylor Otwell')] + ($production ? [new Address('[email protected]', 'Second Address')] : []),
tags: [],
metadata: [],
); I'd be more inclined to go $ccRecipients = [new Address('[email protected]', 'Taylor Otwell')];
if($production) {
$ccRecipients[] = new Address('[email protected]', '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('[email protected]', 'Taylor Otwell')],
tags: [],
metadata: [],
));
if($production) {
$envelope->addRecipient(new Address('[email protected]', 'Second Address'), type: 'cc');
}
return $envelope; Or if conditionable then return (new Envelope(
subject: 'Invoice Paid',
cc: [new Address('[email protected]', 'Taylor Otwell')],
tags: [],
metadata: [],
)))->when($production, function(Envelope $envelope) {
$envelope->addRecipient(new Address('[email protected]', 'Second Address'), type: 'cc');
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would indeed be nice to add the fluent methods - have suggested some changes including the tests.
See;
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('[email protected]', 'Example Name'), type: 'cc')
->addRecipient(new Address('[email protected]', '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('[email protected]', 'Example Name'))
->when($production, new Address('[email protected]', 'Example Admin')),
tags: [],
metadata: [],
);
} |
Co-authored-by: Tim MacDonald <[email protected]>
Co-authored-by: Tim MacDonald <[email protected]>
Co-authored-by: Tim MacDonald <[email protected]>
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
build
method. 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
content
method 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
, andattachments
methods 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
headers
method may be defined to add additional headers, including arbitrary text headers to the message:The traditional way of defining mailables using the
build
method will not be removed.