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

santosh_kancharla's avatar

Route Model binding not working when id is of type uuid

I have a model which as the primaryKey id which is of type UUID protected $primaryKey = 'id'; protected $keyType = 'string';

code in migration file:
$table->uuid('id')->primary();

The route model binding is not working.. Iam i missing any thing here? "laravel/framework": "^11.9",

0 likes
7 replies
Tray2's avatar

I would suggest not using uuid as primary or foreign keys, they are quite a bit slower than ints. If you still want to hide your primary keys behind a uuid then you can specify the uuid column in your route.

Route::get('/posts/{post:uuid}', [PostController::class, 'show'])
    ->name('posts.show');
santosh_kancharla's avatar

Thanks @tray2 .. Iam fine with having uuid as my primary key. /posts/{post:uuid} will work when the primary key is uuid but in my case the primary id. Thanks!

jamesbuch79's avatar

What exactly are you doing? What errors are you getting, if any, what database are you using, and what messages or logs are being produced? You shouldn't have to do much to switch to UUIDs if you really want to.

What I did when using UUIDs was adding the HasUuids Eloquent Concern, change the migrations to use $table->uuid('id')->primary->default(DB::raw('the_database_method()')) and $table->foreignUuid('something_id')->constrained('something')->cascadeOnDelete(), add those relationships in models, and generate the UUID with a raw database method.

There's nothing else you must do unless you're also dealing with starter kits and need to modify the migrations, maybe add your PersonalAccessToken if using Sanctum and put this in your AppServiceProvider's boot method. See for example: https://dev.to/adnanbabakan/implement-uuid-primary-key-in-laravel-and-its-benefits-55o3. Not everything in here is required, but it's a decent guide.

In migrations, this example is for Postgres, MySQL and others do it differently:

$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));

In your model, for example:

// Add related models and imports
use App\Models\Post;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Concerns\HasUuids;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable, HasUuids;  // HasUuids

    // For example...
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

In something related, in a migration:

$table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
$table->string('title');
$table->string('description');
$table->string('slug');
$table->text('content');
$table->foreignUuid('user_id')->constrained('users')->cascadeOnDelete();
$table->timestamps();

In related Model, for example:

Example routes:

use App\Models\Post;
use Illuminate\Support\Facades\Route;

use App\Http\Controllers\PostController;
use App\Http\Controllers\UserController;

// Loop over posts in welcome page
Route::get('/', function () {
    $posts = Post::with('user')->get();
    return view('welcome', ['posts' => $posts]);
});

// Specific routes to users and posts
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::get('/users/{user}', [UserController::class, 'show'])->name('users.show');

// You can also do this:
Route::get('/posts/{id}', [PostController::class, 'showByUuid']);

// Or this:
Route::get('/posts/{id}', function (string $id) {
    // ...
})->whereUuid('id');

// Or this:
Route::get('/posts/{id}', function (Request $request, string $id) {
    //
});

Use .env.testing for example:

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=testdb
DB_USERNAME=james
DB_PASSWORD=james

Create the database and make sure the user specified can do things with it.

APP_ENV=testing php artisan migrate

And run:

APP_ENV=testing php artisan serve

And that's all you have to do. You don't need $keyType = 'string'; or $incrementing = false; because Laravel is smart enough to know this from $table->uuid().

Putting uuids on models behind serial id types for public routes as Tray2 said is an option, for example if you don't want to expose incrementing id's in a simple ReST API or in any other public route. Index them in that case, make them a bit more suitable for indexing with uuid v7 types which are time ordered, almost sequential, and index locality problems are taken care of. You can add a loadByUuid() method on the models or similar. If you do that, you can also add them using the Str::uuid or a better one that generates time ordered v7 uuids after the model has booted, so if the uuid doesn't exist, make one and assign it.

santosh_kancharla's avatar

@jamesbuch79 cau also please post the code in your PostController.

hit route.. Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

show method should be something like

public function show(Post $post) { dd($post); } what the value dd returned by dd();

Please or to participate in this conversation.