Laravel 模型用户自定义绑定“/users/me/xxx”

Laravel model user custom binding "/users/me/xxx"

我有像

这样的用户绑定路由
Route::get('users/{user}/posts', [PostController::class, 'index']);
Route::get('users/{user}/comments', [CommentController::class, 'index']);

所以我可以使用 /users/1/posts/users/5/posts 等,并且由于模型绑定,它在控制器中自动可用

public function index(User $user)
{
    dd($user);
}

但是对于当前登录的用户,我希望也可以使用 /me/ 而不是 ID,例如 /users/me/posts

有没有办法在不定义单独的控制器方法的情况下,我必须手动查找用户,并且不复制所有路由?那么是否有可能在全球范围内“扩展”默认 Laravel 模型绑定?

我认为使用像这样的固定路由参数是最可持续的解决方案,尤其是在共享代码库的情况下。它将涉及一些重复的代码,但会立即清楚哪些路由可用。并且可以预定义分组回调,避免重复路由定义。

$routeGroup = function ($r) {
    $r->get('posts', [PostController::class, 'index']);
    $r->get('comments', [CommentController::class, 'index']);
};

Route::prefix('users/{user}')->group($routeGroup);
Route::prefix('users/me')->group($routeGroup);

然后在您的控制器方法中将参数设为可选。

public function index(User $user = null)
{
    $user = $user ?? Auth::user();
    dd($user);
}

另一种可能性是覆盖模型上的 resolveRouteBinding 方法。如果您使用这种方法(或 Patricus 的解决方案),我建议您在路由文件中留下注释,解释您所做的事情。

class User extends Model {
    public function resolveRouteBinding($value, $field = null): ?self
    {
        return $value === 'me'
            ? Auth::user()
            : parent::resolveRouteBinding($value, $field);
    }
}

当然,您只需要使用 explicit route model binding 而不是默认的隐式绑定。无需更改路线或控制器。

在您的 RouteServiceProvider::boot() 方法中,您可以为 user 参数添加以下绑定:

// use App\Models\User;
// use Illuminate\Support\Facades\Auth;
// use Illuminate\Support\Facades\Route;

public function boot()
{
    Route::bind('user', function ($value) {
        if ($value == 'me') {
            return Auth::user();
        }

        return User::findOrFail($value);
    });
}

现在所有定义了 {user} 参数的路由都将使用该函数绑定路由中的 User 模型。

您可能希望更新函数以能够处理区分大小写的问题,或者在以访客身份访问路由时进行处理,但这取决于您的实施细节。