[11.x] HasMiddlewareHooks trait - Adds job middleware before, after, onFail Hooks #53764
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR provides a trait that allows for hooks into existing job middleware at the point of failure or success with a simple api. It allows for modification of the Job itself before the middleware is executed, after it is successful (before
$next($job)
is called, or if it failed (failed or released). Not only does this allow for modifications, but also provides the needed ability to determine which middleware released/failed the job, allowing for enhanced monitoring and better job handling. It also provides the ability to skip the middleware if a condition is met.Example Usage:
Background and usage:
This came about when trying to (1) diagnose and report which middleware was releasing jobs and (2) then trying to modify the job if it was released for a specific reason.
Some of the jobs were leveraging several middlewares, including RateLimited and WithoutOverlapping. When the jobs would release, there is no easy way to figure out why it was released unless we extended every middleware we were using and write a wrapper for the
handle()
method.The thought occurred that it would be much simpler to handle these random scenarios with something like:
(new RateLimitedWithRedis('rate-limiter'))->onFail(fn ($job) => $job->failReason = 'Rate Limiting')
"Quick example showcasing the issue:
When
Job::dispatch()
is called, there is a 50% chance it passesMiddlewareA
, and if it passes, another 50% chance it passesMiddlewareB
. As it stands, there is no way to know whether it wasMiddlewareA
orMiddlewareB
or the job handle itself that calledrelease()
.The current solution is to extend the middleware, but that doesn't feel right, especially when different jobs might want to do different things with the same middleware.
Order of operations:
Logs when both middlewares pass:
Logs when MiddlewareA fails:
Logs when MiddlewareB fails:
Additional Notes
If the
before()
hook releases the job, fails the job, or returnsfalse
, the middleware is aborted before it is ever executed. Using the example above, and changing MiddlewareA'sbefore()
to:before(fn ($job) => false)
, the logs would only show:The
skipWhen
orskipUnless
will skip thehandle()
of the middleware and simply call$next($job)
, but it will not skip initialization of the middleware. So queries and modifications in the__construct()
would still be executed.