Hello @mpociot, as promised, here comes the big change PR. Take a look and feel free to comment. Independently of the outcome, I will continue to support my version since I need it anyways. I understand if this is too much of a change to accept, and that is fine as well. Thanks in advance.
Change log
Concept
The main entity is a MailboxGroup
. What it enables you to do is to have multiple Mailboxes
, each with its own set of patterns and actions to execute.
Refactor has been done so that Mailbox
provides a fluent API to define same set of features as before with some nice additions.
Example:
$mailbox = new Mailbox();
$mailbox
->from('{user}@gmail.com')
->to('{user2}@gmail.com')
->subject('Subject to match')
->where('user', '.*')
->where('user2', '.*')
->action(function (InboundEmail $email) {
Log::info("Mail received");
})
->matchEither()
->priority(10);
The concept now is that each mailbox has a single or multiple rules it can match, however it can have only one action to execute. Reason being to enable matching all or either of the rules imposed.
By default, all patterns need to be matched in order for action to be executed. So if you provide from
and to
, they both need to match. This can be changed by including a matchEither()
function which will transform this logic to matching at least one.
Due to the fact that multiple mailboxes can exist now, MailboxGroup
is responsible for running them. Executing MailboxGroup::run($email)
will run all the mailboxes, but will before order them by given priority()
. Mailbox
has a default priority of 0
. The bigger the number, the sooner it is executed.
Group example:
$group = new MailboxGroup();
$group
->add($mailbox)
->add($mailbox2)
->add($mailbox3)
->fallback(function (InboundEmail $email) {
Log::info("Fell back");
})
->continuousMatching()
->run($email);
You are responsible for adding mailboxes to the group (be it in the service provider or the controller itself). Fallback is pulled out to MailboxGroup
for logical reasons. If none of the mailboxes match, fallback will be executed. A new feature here is that mailboxes will, by default, stop at first match. Meaning, if out of 5 mailboxes, second one matches the incoming mail, execution will stop. This can be overridden including the continuousMatching()
function which will run through all mailboxes. So if match is found in 3/5 mailboxes, those 3 callbacks will be executed.
Dependencies
PHP version has been bumped to 7.4
. Breaking changes are introduced, 7.3
is no longer supported, and in the next few days I will complete all type hints and return types, together with strict modes included.
Support for 6.x
Laravel has been dropped. composer install
requires a ton of memory with it, breaking most of the times if you don't give more memory to it. This is also the reason Scrutinizer checks break.
Drivers
I have removed drivers all together. Reason being that they were (at least for me) ambiguous, and their only purpose was exposing routes behind the scenes. Current setup also doesn't limit you in using a single driver.
I like my code to be explicit, so currently in order to set up the package you need to:
- Set up endpoint manually (I like single action controllers for these purposes, but not mandatory):
Route::post('receive', ReceiveEmailController::class);
- Add a
MailboxRequest
form request validation, or extend it if needed.
public function __invoke(MailboxRequest $request)
{
...
}
- Define mailboxes and run them:
public function __invoke(MailboxRequest $request)
{
$mailbox = (new Mailbox())->from(...)->action(...);
MailboxGroup::add($mailbox);
MailboxGroup::run($request->email());
}
- Alternatively, you can define mailboxes in your service provider boot method, leaving the controller clean:
// App service provider
public function boot()
{
$mailbox = (new Mailbox())->from(...)->action(...);
MailboxGroup::add($mailbox);
}
// Controller
public function __invoke(MailboxRequest $request)
{
MailboxGroup::run($request->email());
}
- Alternatively, option I prefer is to pull out mailbox definition to a separate class and include it within the controller so that it doesn't overburden the rest of API calls.
public function __invoke(MailboxRequest $request)
{
// Factory leading to defining mailboxes, and ultimately running them.
MailboxService::receiveEmail($request->email());
}
6.. Controllers being used as driver callbacks are still there for your convenience, so you can point the routes to different variants of those:
Route::post('receive/mailgun', MailgunController::class);
Route::post('receive/mailcare', MailCareController::class);
Route::post('receive/postmark', PostmarkController::class);
Route::post('receive/sendgrid', SendGridController::class);
Dev naming
Some concepts, variables etc. have been renamed to be more intuitive.
Tests
Tests which made sense were modified to pass, other were deleted as things like Route
and RouteCollection
no longer exist. I am willing to support new features with tests.
Readme
I think readme is a tad outdated and unnecessarily long. I will probably rewrite it.