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

Ulid Php Lib Laravel Package

not-empty/ulid-php-lib

A small PHP library for generating ULIDs—universally unique, lexicographically sortable identifiers combining a timestamp with random data. Provides simple APIs for creating, parsing, and validating ULIDs, making them a good fit for database keys and sortable IDs.

Deep Wiki
Context7

Getting Started

Install the package alongside a Laravel app:

composer require not-empty/ulid-php-lib

First places to look:

  1. vendor/not-empty/ulid-php-lib/src/ — the generator class (commonly Ulid) and any factory/parser classes.
  2. README.md in the vendor dir — confirm the exact static method names (generate(), create(), fromString(), etc.), since the package is small and the API is minimal.
  3. composer.json — confirm the PHP version constraint and whether it pulls in ext-* requirements or symfony/uid.

Minimal first use case — treat it as a Str::ulid() substitute generating sortable 26-character IDs:

use NotEmpty\Ulid\Ulid;

$id = (string) Ulid::generate();   // e.g. "01J9Z8K3Q4M5N6P7R8S9T0V1W2"

The most common first application is a database primary key on a new model:

Schema::create('invoices', function (Blueprint $table) {
    $table->ulid('id')->primary();
    $table->timestamps();
});

Then override ID generation on the model (see next section) so inserts actually use this library rather than Laravel’s default.

Implementation Patterns

Model IDs via a custom trait. Laravel 10+ ships HasUniqueStringIds, whose newUniqueId() is the single seam to override. That is the cleanest integration point instead of fighting HasUlids (which hardcodes Str::ulid()):

use Illuminate\Database\Eloquent\Concerns\HasUniqueStringIds;
use NotEmpty\Ulid\Ulid;

trait HasLibUlids
{
    use HasUniqueStringIds;

    public function newUniqueId()
    {
        return (string) Ulid::generate();
    }

    public function uniqueIds()
    {
        return [$this->getKeyName()];
    }
}

Apply it to models with $incrementing = false; protected $keyType = 'string'; (or rely on the trait’s defaults where available).

Route keys. ULIDs work fine with implicit route model binding as long as the column is a string and the route parameter type is not constrained to integer. Normalize case before binding (see Gotchas).

Business references. Prefer this lib for human-facing references where you want a sortable, timestamp-embedded identifier:

$order = Order::create([
    'reference' => (string) Ulid::generate(),
    'status'    => 'pending',
]);

Because the first 10 characters encode the millisecond timestamp, you can bucket records by time without a separate created_at index, useful for cursor pagination: Order::where('reference', '>', $cursor)->orderBy('reference')->limit(50).

Extracting the timestamp. If the parser exposes it (e.g. ->getDateTime() / ->getTimestamp()), use it to audit when a row was created when created_at is missing or in external payloads. Otherwise decode manually: take the first 10 Crockford Base32 chars, convert to an integer, and treat as Unix milliseconds.

Integration with third parties. Validate incoming ULIDs before persisting:

if (! preg_match('/^[0-9A-HJKMNP-TV-Z]{26}$/i', $payload['id'])) {
    abort(422);
}

Store uppercased and canonicalize on input to keep string comparison and indexing consistent.

Service layer / DTOs. For domain objects that need an ID before hitting the DB, generate in a factory or service constructor (new Order(Ulid::generate(), ...)) rather than in an observer, so tests can inject deterministic IDs.

Cursor pagination with ULIDs. Use cursorPaginate() and pass the generated ID as the cursor — since ULIDs are time-ordered, ordering by the ID column approximates ordering by creation time and avoids offset drift.

Gotchas and Tips

  • License: GPL-3.0. This is the biggest real-world concern. GPL-3.0 is copyleft — unlike MIT (Laravel itself, most Laravel packages), it can attach obligations to distributed applications that link against it. Get sign-off before shipping in closed-source products; if that’s a blocker, switch to symfony/uid (MIT), which is what Str::ulid() wraps.
  • Laravel already has ULIDs. Since Laravel 9, Str::ulid() and the HasUlids trait exist and are backed by symfony/uid. Only pull this package in if you specifically need a lighter dependency or a feature it exposes (e.g. custom time/random providers), not just “to get ULIDs”.
  • Case sensitivity. ULIDs are canonically uppercase; the random alphabet is Crockford Base32, which excludes I, L, O, U. If you accept mixed-case input, uppercase it before lookup — WHERE id = '01j…' will not match '01J…' on a default ci collation on MySQL but will on PostgreSQL/bc collation. That mismatch causes “not found after refresh” bugs.
  • String sortability requires fixed width and canonical case. Do not add a prefix or strip leading zeros, or ordering breaks.
  • Same-millisecond ordering is not strictly monotonic unless the library documents a monotonic mode. Two ULIDs generated in the same millisecond sort by their random suffix, not by call order. If you need strict monotonicity (event ordering, ledger sequences), use symfony/uid’s Ulid with a monotonic factory or a different primitive.
  • Clock skew. Multi-node deployments can generate ULIDs “in the past”, breaking assumptions that newer rows always sort after older ones. Keep NTP tight; don’t rely on ULID ordering for correctness — only for pagination heuristics.
  • Storage type. Use char(26) (or binary_sqlite/binary(16) if you decode) rather than varchar(255); on MySQL, varchar + utf8mb4 indexes are wider than necessary and the char(26) column aligns naturally with the sortability guarantee. Always index it.
  • Don’t compare ULIDs as integers. They overflow 64-bit and lose their lexicographic meaning. Treat them as opaque strings everywhere.
  • Testing. If the package supports injecting a clock or random source (many ULID libs do), wire it in tests to assert exact values. If not, assert shape with the regex above rather than exact strings.
  • Debugging. When a generated ID looks wrong (lowercase, I present, 25/27 chars), it almost always means the code is passing a UUID or a random string somewhere — grep for the model factory and migration defaults. A quick sanity check is strlen($id) === 26 and the regex above.
  • Extension point. If you need ULIDs from a specific timestamp (backfilling data, deterministic fixtures), check for a fromTimestamp() / fromDateTime() variant on the generator. Otherwise, decode the timestamp manually and note the library gives you no built-in parser.
  • Small ecosystem. With ~21 stars this is a low-bus-factor dependency; vet the maintainer activity and consider pinning a version in composer.json to avoid surprise API breaks.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource