michaeldyrynda / laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models
MIT License
453 stars 48 forks source link

Route custom keys & scoping not working with uuid #98

Closed parth391 closed 3 years ago

parth391 commented 4 years ago

Hello,

I am trying to use route custom keys & scoping as bellow, but getting 404.

Route::get('users/{user:uuid}/edit', 'UsersController@edit')->name('users.edit')

Is there any to convert uuid to binary uuid, before type-hitting?

michaeldyrynda commented 4 years ago

I've neither used nor tested this behaviour, so not sure if it will work.

Are you able to see what query is actually being run? I would have thought the custom key casting worked similarly to the getRouteKeyName method on the model, but not sure. I suppose in that scenario it's bypassing the custom cast?

parth391 commented 4 years ago

Query running is as bellow:

select * from 'users' where 'uuid' = '418a5383-6134-42fd-bd5d-4d838c3dfe96' and 'users'.'deleted_at' is null limit 1

parth391 commented 4 years ago

Even after setting getRouteKeyName as uuid its still not working.

My route

Route::get('users/{user}/edit', 'UsersController@edit')->name('users.edit');

My model

public function getRouteKeyName()
{
    return 'uuid';
}

protected $casts = [
    'uuid' => EfficientUuid::class,
];

My controller

public function edit(User $user)
{
     //
}
kevindb commented 4 years ago

@parth391 I think your issue is that the implicit route model binding is taking the UUID string literally, whereas the query needs the binary version of the UUID. I ran into the exact problem you did. The only way I could fix it was by using explicit route model binding. Add this to your app/Providers/RouteServiceProvider@boot (updating to your key name and model, of course):

Route::bind('deal', function ($value) {
    return \App\Models\Deal::whereUuid($value)->first();
});

The downside is that you have to add those three lines for each and every route bound to that Model.


@michaeldyrynda Is there some other way of referencing the uuid attribute that we're not seeing? In my idea world, I'd be able to use the string form of the UUID (418a5383-6134-42fd-bd5d-4d838c3dfe96) in the PHP code and have it automagically cast/convert to the binary form behind the scenes, where I never have to see it.

For example, this does not work, because it is similarly querying with the UUID string instead of the UUID binary.

Deal::whereNotIn('uuid', ['1eaf88f1-dd8d-60ba-9780-080027718adf','906eaba3-1053-43f1-8c04-4be57b08598f'])->get()
[
    "query" => "select * from `deals` where `uuid` not in (?, ?)"
    "bindings" => [
        1 => "1eaf88f1-dd8d-60ba-9780-080027718adf"
        2 => "906eaba3-1053-43f1-8c04-4be57b08598f"
    ]
]

P.S. I came across #89 , and it looks like that issue is very similar to what I'm describing. I do have laravel-efficient-uuid installed, and the Deal model is casting: protected $casts = ['uuid' => EfficientUuid::class];

ArtisanTinkerer commented 4 years ago

This isn't working for me, can you see what is wrong:

Route::patch('/{thingl}'...................etc

Thing.php

public function getRouteKeyName()
    {
        return 'uuid';
    }

RouteServiceProvider

Route::bind('thing', function ($value) {
            return Thing::whereUuid($value)->first();
        });

test

$response = $this->patchJson(route('thing.update', $thing), $thing->toArray());

I get:

"message": "No query results for model [App\Models\Thing] ae92f158-36a4-47c6-8bf4-2308c2a1df04"

ArtisanTinkerer commented 4 years ago

I got my binding working by adding this to the model:

public function resolveRouteBinding($value, $field = null)
    {
        $bytes = Uuid::fromString($value)->getBytes();
        return $this->where('uuid', $bytes)->firstOrFail();
    }