【问题标题】:What is the correct (most Laravel) approach to this probem (if any)解决此问题的正确(大多数 Laravel)方法是什么(如果有)
【发布时间】:2021-07-11 12:14:32
【问题描述】:

在我的应用程序中,我有一个像这样名为“Order”的模型,遵循默认的 Laravel 行为,并带有一个名为“hash”的附加唯一字符串属性:

class Order extends Model {
  // $table = 'orders'; 
  // $primaryKey = 'id';
  // $incrementing = true;
  $hash = 'a1b2c3d4e5f6g7h8i9';

  // many related models
}

函数 Model::find 的默认行为是查找主键,这是我绝对想要保留的功能。目前我的 routes/api.php 包含这样的功能:

public function show(Request $request, Order $order) {
  // the order will be loaded via the id
  // if the model is not found an exception is thrown
}

在某些情况下,我现在想注入相同的订单,但通过订单的哈希进行搜索,所以我假设我的 api.php 看起来像这样:

public function show(Request $request, OrderSearchByHash $order) {
  // the order will be loaded via the id
  // if the model is not found an exception is thrown
}

问题来了:这是否可以实现?如果可以,如何实现?我实际上并不需要源代码,而是提示我必须去的方向。我已经阅读了有关 ServiceProviders 和 Facades 的内容,尝试创建扩展模型(但我必须“修补”所有关系,因为数据透视表会有不同的名称)以及 Laravel 文档中的许多其他内容,但我仍然没有甚至确切地知道是否涵盖了我的具体问题?

有人给我提示吗? 提前致谢!

【问题讨论】:

  • 这有点不清楚。为什么show 方法应该知道订单是通过 ID 还是哈希搜索的?会有什么不同?
  • 据我了解文档(在这种情况下我可能完全偏离主题),服务提供商通过类型提示识别我请求的模型并为我加载它。我想要它做的仍然是寻找一个订单模型,但不是通过 ID,而是通过哈希。我希望只更改控制器中的注入类型,检索与通过 ID 搜索时完全相同的模型,并继续使用我当前使用的相同源代码。
  • 是的,但是在这两种情况下,您的控制器方法都应该收到Order。获取它的方式不应该改变它的类型。您可能想要做的是更改到该控制器方法的路由的构造方式,例如route('show', [Order::firstWhere('hash', $hash)])

标签: php laravel type-hinting


【解决方案1】:

您可以使用自定义模型绑定:

您的控制器中的两个方法都将如下所示:

public function show(Request $request, Order $order) {
 
}
Route::get('orders/{order}', 'OrderController@show');
Route::get('orders-by-hash/{order_by_hash}', 'OrderController@show');
Route::bind('order_by_hash', function ($value) {
// Use your logic for finding your order by hash
            return Order::where[...]->first();
});

您也可以授权仅在连接时通过 ID 查找订单:

Route::bind('order', function ($value) {
// Use your logic for finding your order by hash
            return Order::where[...]->when(auth()->check(), function()
               $query->orWhere(function($query){
                      $query->where(order_id, $value)
                            ->where(user_id, auth()->id());
                })
)->first();
});

So when you are connected, IF an order with the id belongs to the user , you can find it that way

This is only relevant if your route using an id and your hash are using the same controller's methods, else I recommend to use 2 different model bindings

【讨论】:

  • 这对于一条路线来说是一个可行的解决方案,但我忘了提到订单模型至少在二十条路线中使用,我必须更改每一条路线。这就是我想改变注射的原因之一,我希望它是最简单的(也是最简单的方式)
  • 这是列表 laravel 方式,因为如果您使用相同的路由模型名称({order}),那么使用 id 或哈希会给您一个模型,我认为您的哈希应该是混淆你的订单的id,这样人们就不能用orders/1找到它,所以最简单的方法是使用{order}为内部使用提供路由(当你登录时,你可以检查你是否可以访问订单) 并使用 {order_by_hash} 为您的公共访问路由。您也可以始终使用您的哈希路由,但如果您已连接,那么您可以使用 ID 找到它,我将编辑我的答案以显示它
  • 我是否正确理解了 Route::bind 改变了任何 URL 中名为 {order} 的变量的解释方式?这就是我可以决定通过 primaryKey 还是哈希搜索模型的方式?
【解决方案2】:

您可以创建一个条件,其中{id} 是 PK 或哈希。然后根据得到的{id}的类型进行查询。

例如:

Route::get('orders/{id}', [OrderController::class, 'show']);
public function show(Request $request, $id)
{
    $type = ctype_alnum($id) ? 'hash' : 'pk'; // or another checking method
    $item = $type == 'pk'
    ? Order::find($id)
    : Order::where('hash', $id)->first();

    ...
}

【讨论】:

    【解决方案3】:

    @Mathieu:感谢 Route::bind 的提示,我最终在 RouteServiceProvider 中得到了这个

    Route::bind('order', function($value) {
      if((int)$value > 0 && env('APP_ENV', 'production') == 'dev') {
        return Order::find($value);
      }
    
      return Order::where('hash', (string)$value)->first();
    });
    

    我允许在开发环境中使用 ID,以便我可以使用 Postman 更轻松地测试 API。

    再次:非常感谢!

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 2010-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多