Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

madprabh's avatar

Does Str::uuid() always generate unique id?

Hey Folks,

So I just want to know if the below code always generate a unique id per record in a table?

\Str::uuid()

I want to use this to avoid exposing the autoincrement ids in the url to prevent users from doing funny things such as changing the id in the url.

0 likes
19 replies
jlrdw's avatar

Unless laravel uses the version with a timestamp, it is possible to get a duplicate, but the chance is very small.

Why not regular id field with big int?

prevent users from doing funny things such as changing the id in the url.

Don't show it in the url.

kokoshneta's avatar

@jlrdw The question explicitly says they are using an auto-incrementing ID, they just don’t want to expose that to the user. If permalinks to individual records are required, “don’t show it in the url” is hardly useful. Using UUIDs for URLs is a very reasonable approach to do that.

kevinbui's avatar

Laravel is using uuid v4. And this is from wikipedia:

For example, the number of random version-4 UUIDs which need to be generated in order to have a 50% probability of at least one collision is 2.71 quintillion.
...
This number is equivalent to generating 1 billion UUIDs per second for about 86 years. A file containing this many UUIDs, at 16 bytes per UUID, would be about 45 exabytes.
...
Thus, the probability to find a duplicate within 103 trillion version-4 UUIDs is one in a billion.

Does that assure you?

madprabh's avatar

@kevinbui lol Let me first get 100 users for my app. This is reassuring, thanks a ton for the information.

1 like
jlrdw's avatar

But a ULID is unique, but only in the system that generated it, same for UUID.

It's not your system that you will get a bogus UUID, a hacker can try a bunch of UUID's generated on another system.

If any of this is left out of a url, then it's secure. A hacker has no query string to try and mess with.

Ask yourself, how did @jeffreyway show the "my participation" section without an id, or uuid, or ulid.

That's my point here. Only non secure items belong in a url.

The "my participation" section was just an example.

Edit:

Or a trusted (FBI background checked) admin who is editing data, and they are the only person in the World who can see some data (like employee data) in the url, meaning employee_id for example.

1 like
LaryAI's avatar
Level 58

Yes, the Str::uuid() method in Laravel generates a UUID (Universally Unique Identifier) which is highly unlikely to be duplicated. It uses the ramsey/uuid package under the hood, which is a PHP library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).

Here's why UUIDs generated by Str::uuid() are considered unique:

  1. Version 4 UUIDs are randomly generated from the PHP's random bytes generator. The chance of generating a duplicate is statistically negligible (theoretically, the probability is 1 in 2^122 for a given UUID to have a duplicate).

  2. Collision Resistance: UUIDs are designed to be unique across space and time, which makes them suitable for use across distributed systems without the need for a central coordinating entity to avoid collisions.

  3. Large Namespace: The size of the UUID space per variant is 2^122 (since 6 bits are fixed in the variant and version fields). This is an astronomically high number, which ensures that the possibility of duplication is extremely low.

However, it's important to note that while the probability of generating a duplicate UUID is not zero, it is close enough to zero to consider it "practically unique" for most applications.

Here's an example of how you might use Str::uuid() in a Laravel application to set a UUID when creating a new record:

use Illuminate\Support\Str;
use App\Models\YourModel;

$record = new YourModel;
$record->uuid = (string) Str::uuid(); // Cast to string to ensure it's stored as text in the database
$record->save();

Make sure your database has a column to store the UUID and that it's set to a string type that can accommodate a UUID (which is a 36-character string including hyphens).

When using UUIDs, you should also ensure that the UUID column in your database is indexed, as searching for records by UUID can be slower than using integer-based primary keys if the column is not properly indexed.

Remember to use route model binding with caution if you're using UUIDs instead of auto-incrementing IDs, as you'll need to adjust your route model binding to key on the UUID instead of the primary key.

Snapey's avatar

@amitsolanki24_ you can but its slower to index and search, and if the only requirement is to hide incremental ids then its not necessary to change primary keys

1 like
madprabh's avatar

@snapey I don't want to change the primary key. I just want the UUID generated for the records so I can use them in the permalinks. Is this approach ok?

min-mahatara's avatar

Note: I used ChatGPT to help structure parts of this explanation, but the approach described is from my actual production experience.

Laravel 12.x supports UUIDv7 out of the box (and Laravel 11.x introduced it). However, if you want to use both UUID7 and ULID together on the same model, you'll need a custom solution.

✅ Internal vs. External Identifiers

Using UUIDv7 for primary keys (e.g., id) is an excellent choice—especially for scalable applications. UUIDs are globally unique and make tasks like data merging and system interoperability much safer by minimizing the risk of key collisions.

One of the key advantages of using UUIDs as internal identifiers is that you can perform direct database operations without relying solely on APIs. This encourages cleaner, more decoupled API design.

However, UUIDs (especially v7) are long and not user-friendly. Therefore, I strongly recommend not exposing UUIDs to end users. Instead, use ULIDs for public-facing IDs.

For example:

  • Use id (UUIDv7) internally for primary keys, foreign keys, and unique constraints.
  • Use public_id (ULID) externally in URLs, APIs, and UI displays.

This separation protects internal infrastructure and keeps your system's internals abstracted from external consumers.

🛠 CRUD Operations and Routing

Always expose and interact with the public_id for all CRUD operations. This adds a slight abstraction layer but helps shield internal identifiers and improves system design.

Laravel allows you to customize route model binding to use public_id:

public function getRouteKeyName(): string
{
    return 'public_id';
}

⚖️ Performance Considerations

It's true that UUIDs and ULIDs can be slightly slower than auto-incrementing integers due to their size and index performance. However, the benefits—global uniqueness, scalability, and flexibility—far outweigh these drawbacks, especially for distributed systems.

UUIDv7 offers the additional benefit of lexical sortability. Because it encodes a timestamp in milliseconds, newly generated UUIDs are naturally ordered. This improves index locality and query performance compared to UUIDv4.

🔄 Data Portability

Another major benefit of UUIDs: they're ideal for syncing data across environments or services. Since UUIDs are unique across systems, there's very little risk of collision when importing or migrating data.


✅ Recommended Strategy

  • Internal IDs (id, FK, PK, UK): Use UUIDv7. Never expose.
  • External IDs (public_id): Use ULID. Always expose.

🧩 Custom Trait: HasUuidsAndPublicId

Since Laravel does not currently support using both HasUuids and HasUlids traits together in a single model, you can define a custom trait to achieve this:

Use this trait on your models:

class User extends Model
{
    use HasUuidsAndPublicId;

    protected $table = 'users';
}

⚠️ Note: Trying to use both HasUuids and HasUlids traits simultaneously, like this:

use HasUuids, HasUlids;

will not work, as Laravel does not currently support combining both traits on the same model.


Final Thoughts

For scalable Laravel applications:

  • Use UUIDv7 for internal keys.
  • Use ULIDs for external identifiers.
  • Encapsulate this logic in custom traits like HasUuidsAndPublicId.

This strategy balances performance, security, and maintainability, and is already adopted by many large-scale systems.

min-mahatara's avatar

@martinbean, Honestly tried to help people struggling with UUIDs, especially when IDs are separated by internal and external UUIDs. Whatever, I did use ChatGPT to make the solution simpler and easier to understand, but I didn't just copy-paste, as I currently use the above approach with many projects.

Please don't hate people who are using AI, that's also an important skill nowadays and forever.

Snapey's avatar

@min-mahatara then start your response with "According to chatGPT....."

its also dangerous to rely on AI generated responses when you don't know what prompt was given.

1 like
min-mahatara's avatar

@gazchap @martinbean @Snapey

You're absolutely right, and I apologize for the confusion.

Let me clarify:

First, @gazchap is correct - Laravel 12.x does support UUIDv7 out of the box. I misstated that and should have verified before posting.

Second, @Snapey and @martinbean, you're right about being upfront. I did use ChatGPT to help structure parts of my explanation, and I should have mentioned that upfront.

Third, I understand bumping old threads can be disruptive. I was trying to share a dual-UUID approach I actually use in production, but I could have created a new thread or blog post instead.

My core point stands - I do use UUID7 internally and ULID externally in my projects for the scalability reasons mentioned. But I should have presented it better and fact-checked more carefully.

Thanks for keeping the community standards high. I'll be more careful going forward.

Bye.

Please or to participate in this conversation.