在我的 Laravel 项目中,该项目旨在寻找建筑物租金,我使用具有多态关系的 Eloquent 模型来存储用户和租金的资格。 那么,我为这个问题提供了三个相关模型:“用户”、“Alquiler”(租赁)和“Calificacion”(资格),其中定义了多态关系。
在我的“Alquiler”模型中,我定义了:
public function getRouteKeyName()
{
return 'slug';
}
所以当我定义了这样的路线时:
GET|HEAD alquileres/{alquiler} .... alquileres.show › AlquilerController@show
在我的“AlquilerController”中我可以这样做:
public function show(Alquiler $alquiler){
return view('alquileres.show',compact('alquiler'));
}
为了轻松获取与已发布租金相对应的模型实例,请在我的 Blade 视图中:
<a href="{{route('alquileres.show',$alquiler)}}">...</a>
因此,渲染的链接如下所示:
'http://localhost:8000/alquileres/capital-federal-arcos-3130-7-a'
在我的“User”模型中,我没有定义“getRouteKeyName()”方法,所以这只是默认的 ID 字段。
尽管我可以通过这种方式轻松地为“User”和“Alquiler”执行依赖注入,但当必须在控制器方法中接收的模型可以是“User”或“Alquiler”时,我无法实现类似的目标Alquiler',取决于我的路线中定义的第一个参数,所以我编写了以下代码:
web.php:
Route::get('/calificaciones/calificar/{modelo}/{instancia}', [CalificacionesController::class,'calificar'])->name('calificaciones.calificar');
Route::post('/calificaciones/guardarCalificacion/{modelo}/{instancia}', [CalificacionesController::class,'guardarCalificacion'])->name('calificaciones.guardarCalificacion');
CalificacionesController.php:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Alquiler;
use App\Models\Calificacion;
class CalificacionesController extends Controller{
...
public function calificar(Request $request, $modelo=null, $identificador=null) {
$instancia=null;
if($modelo=='usuario'){
$instancia=User::find((int) $identificador);
}
else if($modelo=='alquiler'){
$instancia=Alquiler::where('slug', $identificador)->firstOrFail();
}
return view('calificaciones.calificar',compact(['modelo','instancia']));
}
public function guardarCalificacion(Request $request, $modelo=null, $identificador=null) {
$calificador=auth()->user();
$instancia=null;
if($modelo=='usuario'){
$instancia=User::find((int) $identificador);
}
else if($modelo=='alquiler'){
$instancia=Alquiler::where('slug', $identificador)->firstOrFail();
}
Calificacion::create([
'calificacionable_id' => $instancia->id,
'calificacionable_type' => get_class($instancia),
'puntuacion' => $request->puntuacion,
'comentario' => $request->comentario,
'calificador_id' => $calificador->id
]);
return redirect()->route('calificaciones.calificacionesRealizadas');
}
...
}
所以我得到的一些可能的链接是:
http://localhost:8000/calificaciones/calificar/alquiler/buenos-aires--lomas-del-mirador-liniers-554-12
http://localhost:8000/calificaciones/calificar/usuario/2
通过做
<a href="{{route('calificaciones.calificar',['alquiler',$alquiler])}}"
或做好<a href="{{route('calificaciones.calificar',['usuario',$alquiler->publicador])}}"
我在其他路线和控制器方法中也有类似的东西。
尽管这有效,但我发现它是一种非常丑陋的方法,因为我无法通过执行依赖项注入在我的方法中获取所需的模型实例,但我通过执行 Eloquent 查询来手动获取它。除此之外,如果我在“User”模型中定义了“getRouteKeyName()”方法,或者如果我在“Alquiler”模型中更改了它,则此代码将不再起作用,因此我需要在我的所有模型中编辑它。使用相同方法的方法。
我尝试通过在 RouteServiceProvider 上进行显式路由模型绑定来解决此问题,如官方 Laravel 文档和其他地方所示:
https://laravel.com/docs/10.x/routing#route-model-binding https://www.digitalocean.com/community/tutorials/cleaner-laravel-controllers-with-route-model-binding
但是,我尝试过的这些方法都不起作用。
是否有人可以建议一种更好的方法来实现我想要的结果,通过能够根据我在每种情况下需要的模型执行 DI? (或者至少改进我目前拥有的代码)
如果需要更多信息,或者有不正确理解的地方,请告诉我。
非常感谢!
莱安德罗
编辑:
我几乎找到了一种方法来实现这一点,通过进行上下文绑定:
我写了一个这样的合约(界面):
可校准.php:
<?php
namespace App\Contracts;
interface Calificable {}
在我的模型定义中我已经完成:
use App\Contracts\Calificable;
class Alquiler extends Model implements Calificable {...
还有:
use App\Contracts\Calificable;
class User extends Model implements Calificable {...
然后,在我的 RouteServiceProvider.php 中,我添加了:
....
public function boot() {
...
Route::bind('modelo', function (string $value) {
error_log("Entro al bind de modelo");
if($value=='usuario'){
$this->app->singleton(Calificable::class, User::class);
}
else if($value=='alquiler'){
$this->app->singleton(Calificable::class, Alquiler::class);
}
return $value;
});
....
最后,在我的控制器方法中,我有:
public function calificar(Request $request, string $modelo, Calificable $instancia){
error_log(get_class($instancia));
error_log(json_encode($instancia));
return view('calificaciones.calificar',compact(['modelo','instancia']));
}
当查看“calificar”的第一个“error_log”的结果时,我正确地得到:
应用\模型\用户
或者好吧:
应用\模型\Alquiler
在对应的案例中。
但是,在第二个“error_log”中,我看到一个空模型,而不是我应该得到的模型。 当我在控制器方法上使用“Alquiler”或“User”键入提示我的参数时,不会发生这种情况,从而获得成功的结果。
有什么方法可以通过这种方式完成我想要做的事情,只需编辑一些我当前在 RouteServiceProvider.php 上的内容即可?我认为通过执行该代码我已经差不多完成了。
1-具有更好可维护性的静态方法
考虑到你只有 2 个选择,实现目标最简单、最干净的方法就是像这样分割路线:
web.php
Route::get('/calificaciones/calificar/usuario/{user}', [CalificacionesController::class,'userCalificar']);
Route::get('/calificaciones/calificar/alquiler/{alquiler}', [CalificacionesController::class,'alquilerCalificar']);
CalificacionesController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Alquiler;
use App\Models\Calificacion;
class CalificacionesController extends Controller{
...
public function userCalificar(Request $request, User $instancia) {
return view('calificaciones.calificar',compact(['instancia']));
}
public function alquilerCalificar(Request $request, Alquiler $instancia) {
return view('calificaciones.calificar',compact(['instancia']));
}
...
}
没有魔法绑定使您的代码非常干净且易于理解,适合未来的开发。
2-动态方法
但是,如果您或阅读本文的其他人需要更动态的方法,您可以使用以下方法:
RouteServiceProvider.php
...
Route::bind('instancia', function ($value, $route) {
$model = $route->parameter('modelo');
return match ($model) {
'usuario' => User::where('id', $value)->firstOrFail(),
'alquiler' => Alquiler::where('slug', $value)->firstOrFail(),
default => throw new ModelNotFoundException(),
};
});
...
CalificacionesController.php
...
public function userCalificar(Request $request, string modelo, Model $instancia) {
return view('calificaciones.calificar',compact(['instancia']));
}
...