Laravel
Send server-side events from your Laravel app. The customer's IP, user agent and click IDs are added from the current request
On this page
Requires PHP 8.2 or newer and Laravel 12 or 13. Laravel 13 needs PHP 8.3 or newer.
Install#
sfere:install publishes config/sfere.php and prints the environment variables to add:
composer require sfere-io/sfere-laravel
php artisan sfere:installSFERE_HOST=https://events.sfere.io
SFERE_WRITE_KEY=keyId:secret
SFERE_WRITE_KEY must be a server key. A browser key is accepted, but then every event reports your server’s IP instead of your customer’s, and ad platforms stop matching conversions. See Write keys.
Check the setup with a real test event:
php artisan sfere:testSend events#
Inside an HTTP request, the customer’s IP, user agent, page, campaign and click IDs are added automatically. Sfere\Ecommerce\Event has a constant for every GA4 ecommerce event, such as VIEW_ITEM, BEGIN_CHECKOUT, PURCHASE and REFUND.
use Sfere\Facades\Sfere;
use Sfere\Ecommerce\Event;
// Who the customer is
Sfere::identify('10293', ['email' => 'customer@example.com', 'phone' => '+15555550123']);
// What they did
Sfere::track(Event::ADD_TO_CART, [
'currency' => 'USD',
'value' => 149,
'items' => [['item_id' => 'sku_221', 'item_name' => 'Linen Shirt', 'price' => 149, 'quantity' => 1]],
], userId: '10293');
// Or take the ID from the signed-in user
Sfere::forUser(auth()->user())->track(Event::PURCHASE, [
'transaction_id' => 'order_98765',
'currency' => 'USD',
'value' => 313,
'items' => [['item_id' => 'sku_221', 'item_name' => 'Linen Shirt', 'price' => 149, 'quantity' => 1]],
]);Every send method returns the event’s messageId, and track sets properties.sfere_dedup_id to your own value or that messageId. The facade also has page, group and alias, and the scoped clients Sfere::anonymous($id) and Sfere::withContext([...]).
Events outside a customer request#
A payment webhook, a scheduled job or a refund issued by staff has no customer request to read. Save the customer’s context while you have it, and pass it back later:
// At checkout, during the customer's request
$order->sfere_context = Sfere::requestContext(); // for example a JSON column
$order->save();
// Later, in the payment provider's webhook
Sfere::track(Event::PURCHASE, $properties, userId: $order->user_id, context: $order->sfere_context ?? []);Delivery modes#
Set the mode with SFERE_DELIVERY.
| Mode | When events leave | Use for |
|---|---|---|
batch (default) |
In batches of up to 100 events, after the response is sent | Web apps |
sync |
Immediately, one request per event | Console commands and debugging. Retries block your code. |
queue |
One Sfere\Jobs\SendEvents job per batch, on your queue |
When you want your queue’s durability and retries |
The SDK never throws into your request by default. Failures are logged and fire a Sfere\Events\DeliveryFailed event. Set SFERE_THROW=true to throw instead.
Page views from the server#
Sfere\Http\Middleware\TrackPageViews sends a page event for every successful GET request that returns HTML.
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [\Sfere\Http\Middleware\TrackPageViews::class]);
})Test your app#
Sfere::fake() records events in memory and makes no HTTP calls:
public function test_checkout_tracks_a_purchase(): void
{
Sfere::fake();
$this->actingAs($user)->post('/checkout', $payload);
Sfere::assertTracked(Event::PURCHASE);
}Main settings#
| Key | Env | Default | What it does |
|---|---|---|---|
enabled |
SFERE_ENABLED |
true |
false turns every call into a no-op. |
host |
SFERE_HOST |
none | https://events.sfere.io, with no path. |
write_key |
SFERE_WRITE_KEY |
none | Server key, keyId:secret. |
delivery |
SFERE_DELIVERY |
batch |
sync, batch or queue. |
context.trust_proxy |
none | true |
Read the client IP from the left-most X-Forwarded-For. |
throw_exceptions |
SFERE_THROW |
false |
Throw on invalid events and delivery failures instead of logging. |
The full list is in config/sfere.php after sfere:install.