将不同可能的 Eloquent 模型的条件依赖注入到控制器方法中

问题描述 投票:0回答:1

在我的 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 上的内容即可?我认为通过执行该代码我已经差不多完成了。

php laravel eloquent lamp
1个回答
0
投票

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']));
    }
    
    ...
© www.soinside.com 2019 - 2024. All rights reserved.