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

Symfony Snowflake Bundle Laravel Package

tourze/symfony-snowflake-bundle

Symfony bundle that integrates Snowflake-style distributed ID generation. It wires the generator into Symfony's service container, lets you configure worker and datacenter identifiers, and provides unique, time-sortable IDs for entities and services. A lightweight integration for scalable identifier...

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a Symfony bundle (tourze/symfony-snowflake-bundle), not a Laravel package. The first thing to understand is that you are not getting a service provider, a facade, or a publishable config file — you are getting a DI extension that registers a Snowflake ID generator service inside a Symfony kernel. In a Laravel app you will almost always consume the underlying generator, not the bundle itself.

Minimal path:

  1. composer require tourze/symfony-snowflake-bundle and immediately inspect what it pulls in (composer show --tree tourze/symfony-snowflake-bundle). The bundle itself is thin; the real behaviour lives in its snowflake dependency.
  2. Read these files first, in this order:
    • vendor/tourze/symfony-snowflake-bundle/src/*Bundle.php — confirms the service ids and whether anything is registered conditionally.
    • vendor/tourze/symfony-snowflake-bundle/src/DependencyInjection/*Extension.php and .../Configuration.php — this is the closest thing to "documentation" for worker/datacenter/epoch parameters.
    • vendor/tourze/symfony-snowflake-bundle/src/Resources/config/* — the service definitions you'll be mirroring.
  3. In Laravel, register the generator yourself in a service provider (see below) rather than trying to boot the bundle.
  4. First real use case: an append-only table (orders, events, outbox rows, job payload ids) where you want a monotonically-increasing-by-time, non-enumerable 64-bit primary key without a round trip to the DB.

If you are actually running Symfony (e.g. a shared package across both), register the bundle in config/bundles.php and configure the worker id via the bundle's config key — then the rest of this assessment's patterns still apply.

Implementation Patterns

Bind the generator as a singleton. Snowflake generators are stateful — they hold last-timestamp and sequence counters. Creating one per request or, worse, per call, guarantees collisions.

$this->app->singleton(SnowflakeGenerator::class, fn () => new SnowflakeGenerator(
    workerId:     (int) config('snowflake.worker_id'),
    datacenterId: (int) config('snowflake.datacenter_id'),
    // epoch is a decision you make once; never change it after data exists
));

Inject it by constructor wherever you need it; use app(...) only in legacy code.

Model primary keys. Keep $incrementing = false and $keyType = 'string' (or int if you're sure nothing serialises it to JSON), and generate in the creating hook — before the insert, so you can reference the id in related writes in the same transaction:

protected static function booted(): void
{
    static::creating(function (self $model) {
        $model->{$model->getKeyName()} ??= app(SnowflakeGenerator::class)->nextId();
    });
}

Prefer this over HasUniqueStringIds — you want a BIGINT UNSIGNED, not a UUID-shaped string, and you want the generator injectable/testable.

Assign worker ids deliberately. The generator only guarantees uniqueness per (datacenter, worker) pair. Derive the id from something stable per process: SNOWFLAKE_WORKER_ID injected by your deploy manifest, the pod ordinal from a StatefulSet, or HOSTNAME hashed into the allowed bit-space — and fail loudly at boot if it isn't set. Never leave it defaulted to 0 in a multi-replica deployment.

Cast at the API boundary. Any id crossing into JavaScript must be a string. Use API Resources with (string) $model->id, and be aware json_encode on a raw int will silently truncate past 2^53.

Exploit the time-ordering. Snowflake ids sort by creation time, so ->latest('id') is a valid, index-friendly substitute for created_at in cursor pagination, and ranges of ids map to ranges of time. That's the main day-to-day payoff over UUIDv4.

Deterministic tests. Bind a fake sequence generator in the test container so assertions don't depend on wall-clock time or worker id.

Gotchas and Tips

  • Symfony bundle ≠ Laravel package. There is no package discovery. If you add it to config/bundles.php nothing happens — Laravel never reads that file. Expect to write a ~20-line service provider, and treat the bundle's Configuration.php as the spec for parameter names and bit-widths.
  • Symfony component version conflicts. The bundle's symfony/dependency-injection / symfony/framework-bundle constraints can fight with the Symfony components Laravel already pins. Check composer why symfony/framework-bundle after install; if it's being added solely for the bundle, drop the bundle and use the underlying generator directly.
  • Clock going backwards. Snowflake implementations either block, throw, or (worst case) emit duplicates when the system clock moves back — a real risk with NTP in containers. Find out which behaviour this version has, and wrap nextId() so a backwards-clock exception is logged with the worker id attached.
  • Duplicate ids from stale worker ids. The most common production failure is a rolling deploy where two pods briefly share a worker id. Enforce uniqueness at the orchestration layer, and add a unique index on the column so the failure is a loud QueryException rather than quiet data corruption.
  • Never change the epoch after you have rows; it silently changes the timestamp embedded in every id.
  • Postgres/SQLite. Use unsignedBigInteger for MySQL, but note Postgres has no unsigned 64-bit integer — the high bit is unused by Snowflake anyway, so bigInteger is fine there. SQLite will store it as an integer but has no true 64-bit signed guarantee at the edges; fine for tests, not for production.
  • Extension points. Rather than editing the bundle, decorate it: define your own IdGenerator interface, bind the bundle's service as the implementation, and wrap it with logging/metrics. That's also the seam that lets you swap in a ULID or a test double later.
  • Debugging tip. In local/staging, boot-log the resolved worker id and datacenter id once per process (Log::info('snowflake', [...])) — a surprising share of "duplicate key" reports are just a mis-set env var.
  • About 1.0.2 specifically. The release notes contain only a compare link (1.0.1...1.0.2) with no changelog entries, so there is nothing documented to assess for breaking changes. Before bumping, run the diff yourself (git diff 1.0.1 1.0.2 or the GitHub compare view) and focus on the generator dependency, since a patch bump there can change clock-rollback behaviour. If you need a known-good baseline, stay on 1.0.1 and pin it until the diff is reviewed. Because the package is pre-2.0 and the notes are empty, treat every patch release as potentially behaviour-changing and lock it in composer.lock with a deliberate review step.
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