Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Matomo Tracker Laravel Package

alfrasc/laravel-matomo-tracker

Server-side Matomo analytics for Laravel. Track page views, events, goals, and e-commerce without client-side JavaScript, with a fluent API, configurable sites, and user attribution.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin Laravel service-provider/facade wrapper around matomo/matomo-php-tracker — the official server-side PHP tracker. There is no Eloquent model, migration, route, or JS; it is essentially a configured singleton plus a facade. Treat it as "a way to call Matomo's PHP SDK with Laravel config and DI," not as a full analytics integration.

Install and configure

composer require alfrasc/laravel-matomo-tracker
php artisan vendor:publish --provider="Alfrasc\MatomoTracker\MatomoTrackerServiceProvider"

Auto-discovery registers the provider and MatomoTracker facade alias on Laravel 5.5+. The published config/matomo-tracker.php holds the Matomo base URL, numeric site ID, and token auth. Set them via env:

MATOMO_URL=https://analytics.example.com/
MATOMO_SITE_ID=1
MATOMO_TOKEN_AUTH=xxxxxxxxxxxxxxxx

MATOMO_URL must be the Matomo installation root, including scheme and any subdirectory (https://example.com/matomo/), and MATOMO_SITE_ID must be the integer idsite for the site you configured in Matomo — not the site name.

First use case: track a page view

use Alfrasc\MatomoTracker\Facades\MatomoTracker;

MatomoTracker::doTrackPageView('Dashboard');

Where to look first

  1. vendor/alfrasc/laravel-matomo-tracker/src/ — confirm the facade accessor, config keys actually read by the provider, and how the singleton is constructed.
  2. vendor/matomo/matomo-php-tracker/MatomoTracker.php — this is your real API reference. Every doTrack* and set* method lives here, and the package adds little on top.
  3. The package's composer.json — check the illuminate/* and matomo/matomo-php-tracker constraints against your Laravel version before investing (see Gotchas).

Implementation Patterns

Middleware + terminate() for page views. Don't track inline in controllers. Register a middleware that records the request and sends it after the response:

public function handle($request, Closure $next) { return $next($request); }

public function terminate($request, $response): void
{
    if (app()->isProduction() && ! $request->is('admin/*', 'horizon/*', 'health')) {
        MatomoTracker::setUrl($request->fullUrl())
            ->setUrlReferrer($request->headers->get('referer') ?? '')
            ->setIp($request->ip())
            ->setUserAgent($request->userAgent() ?? '');
        MatomoTracker::doTrackPageView($this->titleFor($request));
    }
}

Listeners for conversions and ecommerce. Track goals and orders from domain events, not from controllers — that keeps analytics out of HTTP code and makes it replayable/queueable:

// OrderPlaced listener
MatomoTracker::doTrackEcommerceOrder(
    $event->order->number,
    $event->order->total,
    $event->order->subtotal,
    $event->order->tax,
    $event->order->shipping,
    $event->order->discount
);

MatomoTracker::doTrackGoal(config('analytics.goal_order'), $event->order->total);

Wrap it in your own service. The package facade is global and stateful. For anything larger than a hobby app, inject a small Analytics class that builds a fresh tracker (or resets the singleton), applies user/custom-dimension context, and can be swapped for a null implementation in tests and local:

final class Analytics
{
    public function __construct(private MatomoTracker $tracker) {}

    public function event(string $category, string $action, ?string $name = null, ?float $value = null): void
    {
        try {
            $this->tracker->doTrackEvent($category, $action, $name, $value);
        } catch (\Throwable $e) {
            report($e); // never let analytics break a request
        }
    }
}

Set identity once per request/job. For logged-in users, setUserId($user->id) gives cross-device attribution in Matomo. Pair it with custom dimensions (setCustomDimension($id, $value)) for plan, tenant, locale — but remember state persists on the singleton within the request (see Gotchas).

Defer or queue the network call. Every doTrack* sends an HTTP request immediately. Either call from a terminate middleware, or dispatch a job/listener that resolves the tracker inside handle():

dispatch(fn () => app(MatomoTracker::class)->doTrackEvent('billing', 'upgrade'))->afterResponse();

For queue workers, resolve a fresh instance per job rather than reusing the singleton, or you'll carry custom variables and visitor IDs between jobs.

Multi-tenancy / multiple Matomo sites. The service provider binds one singleton from one config. For per-tenant site IDs, don't fight the config — construct trackers yourself:

$tracker = new MatomoTracker($tenant->matomo_site_id, config('matomo-tracker.url'));
$tracker->doTrackPageView($title);

Consent, local, and GDPR gating. Server-side tracking bypasses the browser, so your own consent check is the only gate. A simple analytics.enabled (false in local/testing, false until consent recorded) checked inside your wrapper is the cleanest lever. Also call disableCookie()/avoiding visitor-ID persistence and rely on Matomo's IP anonymization settings; if you setIp(), document it in your privacy policy.

Site search and content. doTrackSiteSearch($keyword, $category, $count) from your search endpoint; doTrackContentImpression/doTrackContentInteraction for banners. These are just calls to the SDK — no package-specific plumbing.

Testing. Because it is a facade, MatomoTracker::shouldReceive('doTrackPageView')->once()->with('Dashboard') works. In feature tests, also bind a fake into the container so your wrapper resolves it.

Gotchas and Tips

  • This package is stale. Last release was November 2021 and it is a 20-star wrapper over the SDK. Verify the illuminate/support and matomo/matomo-php-tracker constraints support your Laravel version before adopting it; for Laravel 10/11 you may need to require matomo/matomo-php-tracker directly and write a 20-line service provider yourself. That is a very reasonable alternative.
  • Synchronous by default. Each doTrack* performs an outbound HTTP request from the PHP process. Tracking inside a controller will add Matomo's latency to your response time. Use terminate(), afterResponse(), or a queued job, and set a sane timeout.
  • The singleton is stateful. setUserId, setCustomDimension, setCustomVariable, setIp, setUrl, etc. mutate the instance and leak into subsequent calls in the same request or queue worker. Reset or re-resolve between logical units of work.
  • Config caching. php artisan config:cache freezes the URL/site ID/token. If you read env() at runtime in your own code after caching, it returns null. Keep all Matomo config in the package config file.
  • URL and site ID mistakes are silent. A wrong MATOMO_URL (missing scheme, missing /matomo/ path, missing trailing slash) or a wrong idsite typically produces a 200 with an empty body from Matomo while no data appears in reports. Check the actual request URL first when dashboards are empty.
  • Token auth scope. Normal tracking works without a token; bulk/API operations need one. A token is effectively an API credential — keep it server-side only, never expose it through Inertia/@json/front-end config, and rotate it if it leaks into logs.
  • Double counting. If the site also runs the Matomo JS tracker, adding server-side page views will report roughly double. Use the PHP tracker for things the browser can't see (webhooks, queued jobs, API endpoints, ecommerce server confirmations) and let JS handle normal browsing.
  • Visitor consistency. The PHP tracker does not have the JS tracker's cookie lifecycle. Without setUserId/setVisitorId, server-side hits may be treated as new visitors. Decide explicitly how you correlate them.
  • Network failures. Matomo being down, DNS issues, or TLS problems should never 500 your app. Wrap calls in try/catch and degrade quietly. Conversely, don't swallow errors silently in staging — log them so misconfiguration is caught before launch.
  • Exclude noise. Skip health checks, Horizon/Telescope, admin/*, bots, and /livewire/*; otherwise your reports will be dominated by infrastructure traffic.
  • IPv6 and proxies. $request->ip() respects TrustProxies; if you're behind a proxy without it configured you'll record the load balancer's IP. Test in staging.
  • Extension points are limited. There is no event pipeline, no macroable tracker, and no driver abstraction. The practical extension points are: your own wrapper class, rebinding MatomoTracker::class in the container, and instantiating the SDK directly for multi-site. Plan around that rather than expecting package-level hooks.
  • Look at the SDK source, not just the README. Method names like doTrackEvent, doTrackGoal, doTrackEcommerceOrder, setCustomVariable vs setCustomDimension are easy to mix up, and the wrapper's docs are thin. vendor/matomo/matomo-php-tracker/MatomoTracker.php is the source of truth.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin