Trace every Symfony order to its channel
One bundle prints the tracker in Twig and sends each paid order once the response has gone out.
Package mirafive/sdk-symfony
Set up in 5 steps
Install the bundle
With Symfony Flex the bundle registers itself. Without Flex, add
MiraFive\Symfony\MiraFiveBundle::class => ['all' => true]toconfig/bundles.php. It needs PHP 8.3 or later and Symfony 6.4, 7 or 8.Code for step 1 of the Symfony setup: Install the bundle. composer require mirafive/sdk-symfony
Add the two keys
The secret key of a server source sends events from your app and never reaches a page. The website key of a website source goes into the tracker tag. Both are under Data → Sources. Keep them in
.env.localor your secret store, never in the repository. No configuration file is needed.Code for step 2 of the Symfony setup: Add the two keys. MIRAFIVE_SECRET_KEY=mf_… MIRAFIVE_WEBSITE_KEY=mf_…
Print the tracker in Twig
mirafive_script()prints the two-line tracker with your website key, and nothing without one. It counts every page and starts in consentless mode, with no cookies and nothing stored on the device.Code for step 3 of the Symfony setup: Print the tracker in Twig. <head> {{ mirafive_script() }} </head>
Send the paid order
MiraFive\Mirais autowired, so inject it where your app marks the order as paid.track()only buffers, and the bundle sends the buffer onkernel.terminate, after the response has reached the browser. You never callflush().Code for step 4 of the Symfony setup: Send the paid order. use MiraFive\Mira; // $mira is the autowired MiraFive\Mira, e.g. a controller argument $mira->track('order_paid', userId: (string) $order->getCustomerId(), properties: [ 'revenue' => 328, 'currency' => 'EUR', ]);
Check the setup
The check command sends an install check, which is never stored or billed, and ends with "The secret key and host work. Nothing was stored or billed." Then open a page on its real domain and watch Data → Live.
Code for step 5 of the Symfony setup: Check the setup. bin/console mirafive:check
MIRA FIVE is analytics built around the person. The Symfony bundle wires the PHP SDK into your container:
MiraFive\Mira is autowired, a Twig function prints the tracker, and events leave on kernel.terminate and
console.terminate, so no page waits for analytics.
How does a paid order reach its channel?
Through the person who paid. Switch the website source to Full under Data → Sources, then set the tracker
to full mode and let your banner pass the visitor’s answer with mirafive('consent', …):
# config/packages/mirafive.yaml
mirafive:
script_mode: fullIdentify the signed-in user in a LoginSuccessEvent listener, with the same internal id your server sends. Never
send getUserIdentifier() when it is an email address:
$this->mira->identify((string) $user->getId(), ['plan' => $user->getPlan()]);The paid order then lands on that person, next to the channel of their first visit. Create a purchase goal for
order_paid, and Acquisition shows buyers and revenue per channel and campaign.
How do I make a webhook count once?
send() goes out at once with an idempotency key and returns a MiraFive\Receipt, so a repeat is stored once. It
never goes through Messenger:
$mira->send([
['name' => 'order_paid', 'userId' => (string) $order->getCustomerId(), 'properties' => ['revenue' => 328, 'currency' => 'EUR']],
], idempotencyKey: "order-{$order->getId()}");What else does the bundle handle?
mirafive_script({autocapture: true})adds clicks, form submits and field changes without code. Every option maps to a tracker attribute, and an unknown one throws.enabled: falseunderwhen@devrecords nothing, so local work stays out of your numbers.- Delivery failures never reach your code. They are logged as warnings on the
mirafiveMonolog channel.
The source’s Install tab has Let your coding agent install it, a prompt for Claude Code, Cursor or Codex with every step and the check; for a server source it never contains the secret key. The Symfony SDK docs cover Messenger delivery and every option, and the setup page shows who does what, in order.
Other setups
The same numbers, whichever way your site is built.
Questions and answers
Does tracking slow down my responses?
track() only buffers, and the bundle sends once per request on kernel.terminate, which under PHP-FPM runs after the response has been sent. With messenger: true and a routing entry, a Messenger worker sends instead.How do I test code that sends events?
test: true under when@test in config/packages/mirafive.yaml. Nothing touches the network, and self::mira()->assertTracked('order_paid') from the InteractsWithMira trait checks the event, including events still in the buffer.Does it work with FrankenPHP worker mode or RoadRunner?
messenger:consume sends after each message. Per-request state lives on the request, so nothing leaks into the next one.Which versions are supported?
mirafive/sdk-php.