tourze/symfony-aop-async-bundle
A Symfony bundle that adds AOP-driven asynchronous execution, letting you offload selected service methods to run in the background via simple attributes instead of hand-written messaging code. Integrates with Symfony's DI container for minimal setup.
This is a Symfony bundle (tourze/symfony-aop-async-bundle), not a Laravel package — there is no service provider, no facade, and no config/aop-async.php. Treat it as a reference implementation / dependency you pull into a Symfony app, and read it as a pattern before adopting it in Laravel.
composer require tourze/symfony-aop-async-bundle, then add the bundle to config/bundles.php (Symfony Flex will usually do this). It sits on top of the Tourze AOP core bundle plus a Messenger transport, so verify those are installed and that framework.messenger has at least one transport configured — without a transport the interceptor has nowhere to hand work off.src/ and find, in this order: the attribute class (the thing you put on a method), the interceptor/aspect class (what actually intercepts the call), and config/services.php (how the interceptor is tagged and wired). Those three files tell you the real API in about five minutes; the README of Tourze bundles is typically thin.new — sending a notification, calling a webhook, generating a thumbnail, writing an audit row. Annotate that one method and confirm the HTTP response returns before the work completes (check the transport table / log, not just the response time).Concretely, the shape is roughly:
#[Async] // exact attribute name lives in src/, verify before use
public function sendReceipt(OrderId $orderId): void
{
// slow I/O here
}
Callers keep calling $service->sendReceipt($id) and get an immediate return.
$this->sendReceipt() from inside the same class, new Service(), or a static call all bypass it silently.Order entity with lazy relations and an open EntityManager will not survive the round trip. Redesign the signature to take scalars or small readonly DTOs, and re-fetch inside the async handler.void (or a handle). There is no result to give back — the call has already returned by the time the work runs. Any method whose caller depends on the return value is a bad candidate. If you need "started" semantics, return a correlation ID you generate yourself before dispatch.failure_transport, delays, and queue names belong in messenger.yaml. The attribute should stay a one-liner; if you find yourself adding options to it, you probably want a Messenger middleware instead.test env, route the transport to in-memory:// (or sync:// where you want deterministic assertions) and assert on the dispatched message rather than on side effects. $this->assertCount(1, $transport->getSent()) is the fast, reliable assertion; sleeping to wait for a worker is not.DispatchAfterResponse job or a queued listener plus a small decorator. Copy the idea (attribute + interceptor + bus dispatch), not the wiring.1.0.0 and 1.0.1. That is consistent with a patch bump, but it is not evidence of "no behaviour change." Before bumping, run git diff 1.0.0..1.0.1 -- src/ yourself — Tourze ships fast and often, and patch releases have historically included quiet signature tweaks. Pin to 1.0.0 if you need stability and diff before moving.final classes or methods, and never sees private or static calls. This is the single most common "the attribute does nothing" cause — there's no error, the method just runs synchronously.after_commit/DispatchAfterCurrentBusMiddleware, or dispatch from an event fired post-commit. Otherwise the handler races the INSERT and finds nothing.messenger:failed:show and messenger:failed:retry are your first stop; check the messenger_messages table to confirm the message was even enqueued. If it was never enqueued, the problem is interception/serialisation, not the worker. The Symfony profiler's Messenger panel shows dispatched messages per request.messenger.yaml. If you need conditional async (async only above a payload size), write a thin wrapper method that decides, rather than adding branching to the attribute.How can I help you explore Laravel packages today?