Str::uuid

Generates a random version 4 UUID as a Ramsey UuidInterface object.

Signature
static Str::uuid(): \Ramsey\Uuid\UuidInterface
  • Added in Laravel 5.6.0

Return value

\Ramsey\Uuid\UuidInterfaceA randomly generated version 4 UUID object. Cast it with (string) to get the 36-character canonical form, or call ->toString().

Errors

Raises an Error ("Class \"Ramsey\\Uuid\\Uuid\" not found") when ramsey/uuid is not installed, since illuminate/support only suggests it.

Changelog

  • PHP 5.6.0Str::uuid() and Str::orderedUuid() added to Illuminate\Support\Str.
  • PHP 9.28.0Str::freezeUuids() now restores normal UUID generation even when the callback you pass it throws.
  • PHP 12.34.0Internal type hints corrected so the return value is no longer assumed to be a Stringable; it is a UuidInterface and always has been.

Produces a randomly generated version 4 UUID — 122 random bits — wrapped in a Ramsey\Uuid\UuidInterface. Reach for it when an identifier has to exist before the row does: keys assigned inside a queue job, ids minted in JavaScript, or several databases that cannot share one auto-increment sequence. It is also the usual answer for a public-facing reference you do not want people to guess or count.

The return value is an object

It stringifies, so echo, interpolation and assigning to an Eloquent attribute all behave as expected. So does json_encode(): UuidInterface implements JsonSerializable and encodes as the canonical 36-character string. Strict comparison is the exception — Str::uuid() === $someString is always false — so cast with (string) wherever a real string is required, such as a strict comparison, an in_array() with strict mode or an array key.

Random keys fragment the index

Because the bytes are random, consecutive inserts scatter across a clustered B-tree instead of appending to it. On a write-heavy table prefer Str::orderedUuid() or Str::uuid7(), whose leading bytes encode time, so new rows stay near the right edge of the index.

Making it predictable

Str::createUuidsUsing() installs your own factory, Str::freezeUuids() pins one value for the length of a closure, and Str::createUuidsNormally() restores the default. Any snapshot test or assertion against a generated id needs one of these.

Example

Example · php
<?php

use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

// Pin the generator so this page prints the same thing on every run.
Str::createUuidsUsing(fn () => Uuid::fromString('eadbfeac-5258-45c2-bab7-ccb9b5ef74f9'));

$uuid = Str::uuid();

var_dump($uuid instanceof UuidInterface);   // bool(true)
echo (string) $uuid, PHP_EOL;               // eadbfeac-5258-45c2-bab7-ccb9b5ef74f9
echo $uuid->getVersion(), PHP_EOL;          // 4

Str::createUuidsNormally();

// Back to real random values: only the shape is predictable.
echo strlen((string) Str::uuid()), PHP_EOL; // 36

When to use it

  • Assign a model's primary key inside a queue worker before the insert reaches the database.
  • Mint a public order reference that does not leak how many orders exist.
  • Name an uploaded file so two simultaneous uploads cannot collide.
  • Attach one correlation id to every log line a request produces across services.

Frequently asked questions

Why does Str::uuid() not return a string?

It returns a Ramsey\Uuid\UuidInterface object so you can call methods such as getVersion(), getBytes() or toString() on it. Cast with (string) whenever the value crosses into a query binding, a JSON response, an array key or a strict comparison; echo and interpolation already trigger __toString() for you.

Should I use uuid() or orderedUuid() for a primary key?

Use orderedUuid() (or uuid7()) for anything that will hold many rows, because their leading bytes are time-derived and consecutive inserts stay clustered at the end of the index. Plain uuid() is random, so on InnoDB it scatters writes across the whole B-tree and inflates page splits; keep it for ids that are exposed publicly and should reveal no ordering.

How do I make Str::uuid() return a fixed value in a test?

Wrap the assertion in Str::freezeUuids(fn ($uuid) => ...), which pins one value for the closure and restores normal generation afterwards even if the closure throws. For finer control, Str::createUuidsUsing(fn () => Uuid::fromString('...')) installs any factory you like, and Str::createUuidsNormally() undoes it.

Do I need to install anything to use Str::uuid()?

Yes — ramsey/uuid is a suggested, not required, dependency of illuminate/support, so run composer require ramsey/uuid if you pulled the package in standalone. A full laravel/laravel application already has it, which is why the method appears to work out of the box there.

More examples

Using it as an Eloquent primary key

Both columns are filled before the INSERT is sent, so the application knows the id without a round trip and without reading back an auto-increment value. The cast to string in newUniqueId() matters: the return type is string, and handing back the object there is a TypeError.

Example · php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

class Invoice extends Model
{
    use HasUuids;

    // In Laravel 12 HasUuids generates Str::uuid7() by default;
    // the ordered v4 behaviour now lives in HasVersion4Uuids.
    // Override newUniqueId() when you specifically want a plain v4.
    public function newUniqueId(): string
    {
        return (string) Str::uuid();
    }

    // Every column listed here gets a fresh id on create.
    public function uniqueIds(): array
    {
        return ['id', 'public_reference'];
    }
}

$invoice = Invoice::create(['total' => 4200]);

echo $invoice->id, PHP_EOL;
echo $invoice->public_reference, PHP_EOL;

Freezing the value inside a test

freezeUuids() makes every call inside the closure return the same object and restores normal generation on the way out — including when the closure throws, which has held since 9.28.0. The final var_dump prints bool(false), proving the freeze was lifted. Without the closure form you must call Str::createUuidsNormally() yourself or you leak the frozen factory into the next test.

Example · php
<?php

namespace Tests\Feature;

use Illuminate\Support\Str;
use Tests\TestCase;

class InvoiceTest extends TestCase
{
    public function test_the_reference_appears_on_the_receipt(): void
    {
        Str::freezeUuids(function ($uuid) {
            $this->post('/invoices', ['total' => 4200])
                ->assertSee((string) $uuid);
        });

        // Outside the closure, generation is already back to normal.
        var_dump((string) Str::uuid() === (string) Str::uuid());
    }
}

Stringifying and validating

The first two lines are the bug people actually file: the object and its string form are never identical under ===, so any comparison, in_array() with strict mode, or array key must use the cast value. Str::isUuid() with a version argument is the cheap way to reject a v7 id where a v4 was expected.

Example · php
<?php

use Illuminate\Support\Str;

$uuid = Str::uuid();
$id   = (string) $uuid;

var_dump($uuid === $id);              // bool(false) - object vs string
var_dump($uuid->toString() === $id);  // bool(true)

var_dump(strlen($id));                // int(36)
var_dump(Str::isUuid($id));           // bool(true)
var_dump(Str::isUuid($id, 4));        // bool(true)
var_dump(Str::isUuid($id, 7));        // bool(false)
var_dump(Str::isUuid('not-a-uuid'));  // bool(false)

Discussion

  • Be the first to comment on this lesson.