laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.65k stars 11.04k forks source link

withThrashed() method on routes with explicit model binding does not resolve trashed models #43008

Closed Thomas14 closed 2 years ago

Thomas14 commented 2 years ago

Description:

The withThrashed() method on routes with explicit model binding does not seem to resolve trashed models. Trashed models will produce a not found error; non-trashed models are unaffected. withTrashed() on routes with implicit binding is working as expected.

An issue concerning the exact opposite seems to have been reported some time ago: #39799 - I'm not sure if there's any relation though.

Steps To Reproduce:

Simplified code for reproduction:

The explicit binding part in RouteServiceProvider.php

Route::bind('post', function ($value) {
    return Post::where('id', $value)->firstOrFail();
});

routes\web.php

use App\Models\Post;

Route::get('/post', function () {
    $post = Post::create([]);
    return $post->id;
});

Route::get('/post/{post}', function (Post $post) {
    return $post->id;
});

Route::get('/post/{post}/delete', function (Post $post) {
    $post->delete();
    return 'Deleted';
});

Route::get('/post/{post}/restore', function (Post $post) {
    $post->restore();
    return 'Restored';
})->withTrashed();

app\Models\Post.php

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\MassPrunable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
    use HasFactory, SoftDeletes, MassPrunable;
}

Migration

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->softDeletes();
        $table->timestamps();
    });
}
mateusjunges commented 2 years ago

Hey @Thomas14 when you use the Route::bind method you are telling laravel to resolve your route model binding using whatever callback you provide to that method. You can check line 854 of Illuminate/Routing/Router class:

https://github.com/laravel/framework/blob/95ce178cbde9f9f38013862f677a5314da67af85/src/Illuminate/Routing/Router.php#L852-L861

Basically, if there is a bind for the given key, then laravel will use performBinding method and call the callback you provided with Route::bind to resolve the model:

https://github.com/laravel/framework/blob/95ce178cbde9f9f38013862f677a5314da67af85/src/Illuminate/Routing/Router.php#L887-L890

So, in order to get trashed models, you need to use withTrashed in your query within the provided callback.

If you change your Route bind to to the code below then it will work:

Route::bind('post', function ($value) {
    return Post::withTrashed()->where('id', $value)->firstOrFail();
});

Hope it helps.

Thomas14 commented 2 years ago

Hi @mateusjunges

Whoops... that makes a lot of sense. Intuitively I expected withTrashed on the route to affect the query in the same way it does with implicit binds without giving it any further thought (yikes), I somehow managed to completely overlook the fact that with the callback I am of course building the query myself... oh the simple things I miss sometimes... this was an incredibly silly oversight on my part, sorry!

Thanks for your help :)