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

Segment Laravel Package

alt-three/segment

Laravel integration for Segment analytics that makes it simple to identify users and track events from your application. Includes a service provider, facade, and configuration for sending server-side analytics data to Segment.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and check constraints first. The package is archived (last release July 2020), so treat installation as a compatibility exercise:

composer require alt-three/segment

It pulls in Segment's server-side PHP library and declares Laravel/PHP constraints from the 5.x–7.x era. On modern PHP (8.1+) and Laravel 9/10/11 you will likely need --ignore-platform-reqs or a fork. If Composer resolves cleanly, run the test suite immediately — deprecation notices from the vendored analytics library are the first thing you'll hit.

2. Wire up the provider. On Laravel 5.5+ the service provider is auto-discovered via extra.laravel.providers. On older versions add to config/app.php:

'providers' => [
    AltThree\Segment\SegmentServiceProvider::class,
],
'aliases' => [
    'Segment' => AltThree\Segment\Facades\Segment::class,
],

3. Publish the config and set the write key.

php artisan vendor:publish --provider="AltThree\Segment\SegmentServiceProvider"

This creates config/segment.php. Put the key in .env — never in the config file:

SEGMENT_WRITE_KEY=xxxxxxxxxxxxxxxxxxxxxxxx

Review the consumer and debug keys in that file before going further (see Gotchas).

4. First use case — identify on login, track on the action.

use AltThree\Segment\Facades\Segment;

// After authentication
Segment::identify($user->id, [
    'email'      => $user->email,
    'name'       => $user->name,
    'created_at' => $user->created_at->toIso8601String(),
]);

// Anywhere in the request lifecycle
Segment::track($user->id, 'Order Placed', [
    'order_id' => $order->id,
    'revenue'  => $order->total_cents / 100,
    'currency' => 'USD',
]);

Where to look first: config/segment.php for the consumer/debug knobs, then vendor/alt-three/segment/src/ — the wrapper is thin, so reading the two or three classes gives you the full API surface (identify, track, page, screen, group, alias, flush).


Implementation Patterns

Wrap the facade behind your own interface. Given the package is unmaintained with zero dependents, do not let Segment::track() calls spread across controllers. Define a small contract and bind it:

interface Analytics
{
    public function identify(User $user, array $traits = []): void;
    public function track(?string $userId, string $event, array $props = [], array $options = []): void;
    public function flush(): void;
}

final class SegmentAnalytics implements Analytics
{
    public function track(?string $userId, string $event, array $props = [], array $options = []): void
    {
        Segment::track($userId, $event, $props, $options + ['anonymousId' => request()->cookie('ajs_anonymous_id')]);
    }
}

This gives you one swap point when the package dies (or when you move to segmentio/analytics-php v2 or direct Http::post('https://api.segment.io/v1/track')) and a trivially fakeable dependency in tests.

Drive it from domain events, not controllers. Business events (UserRegistered, SubscriptionCancelled, OrderShipped) already exist in most Laravel apps. Add listeners that translate them into Segment calls. You get a clean tracking plan, and you can queue the listener so analytics never blocks a checkout:

class TrackOrderPlaced implements ShouldQueue
{
    public $afterCommit = true; // don't emit for a transaction that rolled back

    public function handle(OrderPlaced $event): void
    {
        app(Analytics::class)->track((string) $event->order->user_id, 'Order Placed', [...]);
    }
}

afterCommit (or DB::afterCommit(fn () => ...)) is the single most important detail here — firing a Segment call inside a transaction produces events for orders that were later rolled back.

Build context once, in middleware. ip, userAgent, locale, and anonymousId are per-request and easy to forget. Resolve them in a middleware and stash them in a request-scoped context object your Analytics implementation merges into $options:

Segment::track($userId, 'Report Exported', $props, [
    'context' => [
        'ip'        => request()->ip(),
        'userAgent' => request()->userAgent(),
        'locale'    => app()->getLocale(),
    ],
]);

Handle the anonymous → identified transition explicitly. Segment's PHP library does not do this for you. On registration, read ajs_anonymous_id from the cookie, identify() with it, then alias($userId, $anonymousId) so pre-signup activity stitches to the new user:

Segment::identify($anonymousId, [], ['anonymousId' => $anonymousId]);
Segment::alias($user->id, $anonymousId);

Use page() server-side for non-browser clients. Invoices, emails, webhooks, and mobile backends have no analytics.js. A server-side page($userId, 'Invoice View', $props) keeps funnels complete across surfaces.

Flush on long-running processes. Queue workers and Laravel Octane hold the client alive for hours. Register a flush() on the worker's Looping/terminating hook, or every N events, otherwise buffered messages are lost on a hard restart.

Testing. Because it's a facade, Mockery fakes work directly:

Segment::shouldReceive('track')
    ->once()
    ->with($user->id, 'Order Placed', Mockery::on(fn ($p) => $p['order_id'] === $order->id));

$this->post('/orders', [...])->assertOk();

For listener tests, assert on your Analytics interface instead — it survives a package swap.


Gotchas and Tips

  • It's archived. No security patches, no PHP 8.2/8.3 fixes, no Laravel 11 support. Budget for a fork (vendoring or a private VCS repo) or a migration path. Your own Analytics interface is the cheap insurance.
  • Consumer choice bites you in production. Segment's PHP library defaults to a forking/socket consumer that requires ext-pcntl. Under php-fpm, Octane, or inside a queue worker, pcntl_fork is either unavailable or leaks state. Prefer the curl consumer (simple, blocking, works everywhere) or file with an out-of-band flush. Set this in config/segment.php and verify in staging, not production.
  • Failures are silent by default. The library fires and forgets. A wrong write key or a rejected payload produces no exception and no log line — you just get an empty dashboard. Turn on debug locally, and in production monitor for absence of events rather than presence of errors.
  • config:cache freezes the write key. php artisan config:cache during deploy bakes whatever SEGMENT_WRITE_KEY was present at cache time. If you cache before injecting env vars (common with Docker entrypoints), you ship a null key. Re-cache after env injection, and never call env() outside config files.
  • Don't send models. $request->all() or $order->toArray() will blow past Segment's ~32KB per-message limit and leak PII into your warehouse. Explicitly whitelist the handful of properties the tracking plan specifies.
  • Event name typos create silent schema drift. 'Order Placed' vs 'Order placed' vs 'order_placed' are three different events. Centralize names as constants in your Analytics implementation or an enum.
  • Double-counting with analytics.js. If the browser SDK is also loaded, server-side calls duplicate page/track. Use the $options integrations block to suppress specific downstream tools (['integrations' => ['Mixpanel' => false]]), or decide firmly which events are server-authoritative.
  • Backfills need an explicit timestamp. Pass ['timestamp' => $occurredAt->toIso8601String()] in $options; otherwise the event is stamped when the queue drains, which wrecks cohort queries.
  • Gate on consent. Wrap the interface implementation with your consent check (or the context.consent field) so a single policy change doesn't require hunting down every call site.
  • Extension points if you keep it: the container binding (swap in your own subclass of the wrapper), the published config's consumer/debug/write_key, and the facade accessor (override for a test double). Everything else lives in the vendored analytics library — patch there only via a fork you control.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource