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 Io Bundle Laravel Package

farmatholin/segment-io-bundle

Segment.io Bundle integrates Segment analytics into Symfony apps, offering a configurable service to identify users and track events and page views through Segment's API, with a simple dependency-injection-friendly wrapper around the Segment PHP library.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it's a Symfony bundle, not a Laravel package. There is no config/bundles.php, no auto-discovery, and no service container integration you get for free. What you actually get is a thin wrapper around segmentio/analytics-php plus a Symfony DI extension. In a Laravel app you will be using the client class the bundle configures, not the bundle itself.

v1.6.0 note: the release adds "PHP Attributes compatibility" — i.e. Symfony's attribute-based DI (#[Autowire], #[AsAlias], #[Autoconfigure], #[TaggedIterator]). This is a Symfony-autowiring improvement only. It changes nothing about how you use the package from Laravel, and it does not add a Laravel service provider. Treat it as a maintenance signal (the package is not abandoned), not a feature for you.

Minimal path:

  1. composer require farmatholin/segment-io-bundle — this pulls the upstream segmentio/analytics-php dependency, which is the piece doing the real work.
  2. Open vendor/farmatholin/segment-io-bundle/ and read two things before writing any code: DependencyInjection/Configuration.php (the config keys — typically write key and a debug flag) and the service definition. Don't assume it's XML/YAML anymore — as of v1.6.0 the wiring may be plain PHP classes using attributes. Grep for the client class name and the service id rather than trusting a file path from a blog post. Class names and ids have changed across versions; do not guess them.
  3. Wire it yourself in a service provider, because Laravel won't instantiate the Symfony extension (attributes or not):
// config/services.php
'segment' => [
    'write_key' => env('SEGMENT_WRITE_KEY'),
],

// app/Providers/SegmentServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Segment\Client::class, fn () => new \Segment\Client(
        config('services.segment.write_key')
    ));
}
  1. First use case — the one that pays for the integration immediately: identify on login and track on the money event.
public function login(Request $request)
{
    // ... authenticate
    $segment = app(\Segment\Client::class);

    $segment->identify([
        'userId' => $request->user()->id,
        'traits' => ['email' => $request->user()->email, 'plan' => $request->user()->plan],
    ]);

    $segment->track([
        'userId' => $request->user()->id,
        'event'  => 'Signed In',
        'properties' => ['method' => 'password'],
    ]);
}

If you're evaluating this for a greenfield Laravel app: skip the bundle and require segmentio/analytics-php directly (or hit https://api.segment.io/v1/track with Http::withBasicAuth($key, '')->post(...)). You lose nothing but a Symfony config layer you can't use — and v1.6.0's attribute support widens that gap rather than closing it.

Implementation Patterns

Bind once, inject everywhere. Register the client as a singleton and type-hint Segment\Client in controllers, jobs, and listeners. Never new it per call — each instance carries its own in-memory queue and its own cURL consumer.

Flush at the end of the request. The PHP library buffers events and flushes on shutdown, which mostly works under php-fpm but is invisible and untestable. Make it explicit with terminable middleware so you control ordering and can log failures:

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

    public function terminate($request, $response): void
    {
        app(\Segment\Client::class)->flush();
    }
}

Push tracking into domain events, not controllers. OrderPlaced, TrialStarted, SubscriptionCancelled → queued listeners that call track(). This keeps the call sites testable and lets you swap destinations later. If the event is revenue-critical, put the listener on a real queue with retries; the Segment client itself gives you no retry semantics.

Pass identity explicitly. Don't rely on the library inferring users. Carry userId from auth()->id() and an anonymousId stored in a cookie or session for pre-login activity, so anonymous → identified stitching works in the Segment UI.

Filter destinations per call. integrations is the lever for cost and noise control — send internal Admin Viewed Report events nowhere, or route a single event to one warehouse:

$segment->track([
    'userId' => $user->id,
    'event'  => 'Order Completed',
    'properties' => ['revenue' => 49.00, 'currency' => 'USD'],
    'integrations' => ['All' => false, 'Mixpanel' => true, 'Amplitude' => true],
]);

Enrich context centrally. A middleware that stamps IP, user agent, locale, and app version onto every call saves you from repeating it and keeps context consistent across web, queue, and console contexts.

Group calls for B2B. group(['groupId' => $team->id, 'traits' => [...]]) alongside identify is the pattern most teams forget, and it's what makes account-level reporting in downstream tools work.

Console and queue workers need their own flush. Any php artisan command or job that emits events must flush before exit — there is no request shutdown to lean on.

Gotchas and Tips

Attributes support is a Symfony win, not a Laravel one — but it moves the goalposts. v1.6.0 may have relocated or rewritten the service definitions you used to find in Resources/config/. If your integration notes say "copy the service id from services.xml", re-verify it after upgrading; the service id and concrete class are still the contract, the file is not. Pin the version you audited.

The version pin is the real risk. This package has ~12 stars, zero dependents, and a long tail of infrequent releases (v1.5.2 in 2024, then v1.6.0). It constrains segmentio/analytics-php; if your app needs a newer upstream (or a newer Guzzle/cURL path), composer will fight you. Check composer why segmentio/analytics-php after install and be prepared to drop the bundle and require the upstream lib directly.

Forked cURL consumers break under Octane and long-running workers. The upstream library's default consumer batch-flushes in a forked process. Under Octane or a persistent queue worker this leaks and can drop events. Either flush explicitly after every batch of work, or swap in a simple synchronous consumer you control.

Failures are silent by default. Segment ingestion failures return nothing useful to your code path. If analytics correctness matters (billing events, funnel-critical steps), wrap the client: log non-2xx responses from a custom consumer, or write your own thin HTTP client with Http::retry(). Do not assume delivery.

Debugging is awkward. The bundle's debug flag likely routes to error_log, which in Laravel means it disappears unless you've pointed PHP's error log at storage/logs. Temporarily set error_log in a local bootstrap, or bind a consumer that writes through Log::debug().

Config caching. Read the write key via config('services.segment.write_key') inside the closure, not env() — env() returns null after php artisan config:cache.

PII and GDPR. The library sends context (including IP) by default. If you operate in the EU, add an explicit allowlist for traits and properties rather than dumping $user->toArray(), and make sure your identify calls don't ship hashed-but-reversible identifiers downstream.

Extension points worth knowing. The consumer is the seam: implement the library's consumer interface to route events to a local queue, add retry, or no-op in tests (Segment\Client with a null consumer is the cheapest way to keep test suites fast and quiet). If the bundle's wiring ever gets in your way, deleting it and writing the ~30-line HTTP wrapper is a legitimate and probably better answer at this maturity level — v1.6.0 adds no Laravel-side capability to change that calculus.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels