如何强制 Laravel Eloquent 很好地处理 returns 没有结果的查询?

How to force Laravel Eloquent to play nicely with a query which returns no results?

我在我的一个模型中将数据库查询设置为范围查询,在某些情况下可能 returned 没有结果,这样做完全没问题:

public function scopeLastFromLaunchSite($query, $site) {
    return $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name',$site);
    })->orderBy('launch_order_id','DESC')->first();
}

这个函数的调用方式很简单:

Mission::LastFromLaunchSite('someSite');

如果结果中至少有一行,此查询完全正常,但一旦有 none,它 return非常冗长的 Builder 对象,其中充满了递归,这实际上会使我的浏览器崩溃。

我的问题是...

我怎样才能检测到这一点,只需 return 类似 false"No results were found." 的东西?

我试过的...

我试过使用 firstOrFail() 方法代替 first(),但这只是 return 一个 ModelNotFoundException,这给了我两个解决方案:

  1. App:errorglobal.php处理。这是不可取的,因为没有 returned 结果不是错误。只是众多可能结果中的一种。

  2. 在每个方法中捕获异常,导致代码重复。我实际上也无法让它工作。我尝试 using ModelNotFoundException class 并捕获它,但它从未被捕获:

    use Illuminate\Database\Eloquent\ModelNotFoundException;
    
    // ...
    
    try {
        //my query
    } catch (Illuminate\Database\Eloquent\ModelNotFoundException $e) {
        return false;
    }
    
    // otherwise, return as normal
    

当对我的模型 return 的查询没有结果时,我如何简单地 return false 或一些虚假值,而不是简单地打印出这个非常冗长的错误?

我从来没有做过这样的事情,但我首先想到的是……。喜欢:

public function scopeLastFromLaunchSite($query, $site) {
    $retVal = $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name', $site);
    })->orderBy('launch_order_id', 'DESC');

    if ($retVal->count() > 0) {
        return $retVal->first();
    }

    return "No results found";
}

这里的问题是您没有像 Laravel 那样使用范围。 Laravel 实际上并不期望您实际在范围内执行查询。它更像是一个应用过滤器的工具。因此,当您的作用域函数 returns NULL(或任何虚假值)Laravel 无论如何都会 return 当前的 Builder 对象:

Illuminate\Database\Eloquent\Builder

protected function callScope($scope, $parameters)
{
    array_unshift($parameters, $this);

    return call_user_func_array(array($this->model, $scope), $parameters) ?: $this;
}

以下是三种解决方案:

1。使用异常(但在你的控制器中)

我不太喜欢这样,但这是一种可能性。使用 firstOrFail 并在您的控制器中执行此操作:

try {
    $result = Mission::LastFromLaunchSite('someSite');
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e){
    $result = null;
}

2。使用正确的范围

不要在范围内执行查询,而是在您的控制器中执行:

public function scopeLastFromLaunchSite($query, $site) {
    return $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name',$site);
    })->orderBy('launch_order_id','DESC');
}

用法:

Mission::LastFromLaunchSite('someSite')->first();

3。只需向您的模型添加一个具有相同功能的普通函数

public static function LastFromLaunchSite($site){
    return $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name',$site);
    })->orderBy('launch_order_id','DESC')->first();
}

用法:

Mission::LastFromLaunchSite('someSite');

作用域不应该 return 查询结果,这就是为什么你会得到无休止的递归或其他错误,具体取决于你的代码。

将范围更改为:

public function scopeLastFromLaunchSite($query, $site) {
    $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name',$site);
    })->orderBy('launch_order_id','DESC');
}

然后就地执行:

$result = $yourQuery->lastFromLaunchSite($site)->first();

// then do whatever you need with the result
return ($result) ?: 'nothing found!';

说明:范围不需要 return 值。如果 scopeWhatevermethod returns anything that evaluates totrue`,那么它将替换基本查询,否则查询本身是 returned。也就是说,如果您希望 return 编辑单个模型,但没有行匹配条件,那么您会意外地得到查询对象。

你也可以这样做(我不会,但它也会起作用):

public function scopeLastFromLaunchSite($query, $site) {
    $row = $query->whereComplete()->whereHas('launchSite', function($q) use($site) {
        $q->where('name',$site);
       })->orderBy('launch_order_id','DESC')->first();

    return $row ?: 'nothing found!';
}

但是您需要执行 Modelstring 并相应地调整您的代码以避免可能的 Trying to get property of non-object 错误。