我的数据库中有一个名为tallies
的表,用于跟踪特定实体的count
。表中的两个关键字段是:
- 类型:实体的名称 计数
- :实体的计数
现在在表中我有两条记录,第一条记录具有hardDrives
类型,第二条记录具有monitors
类型。
我的存储库中有一个方法,用于增加tallies
表中特定记录的计数:
public function decreaseCountBy($type, $number){
$countCollection = $this->tally->where('type', '=', $type )->firstOrFail();
$record = $countCollection->first();
// Troubleshooting output
die(var_dump([$type, $record->toArray()]));
// The rest of the method
$record->count -= $number;
$result = $record->save();
if(!$result){
throw new Exception('Error saving decrementation');
}
return $record->count;
}
当我发送请求以递增monitors
并查看此方法中故障排除模具和转储的输出并得到以下输出时:
array (size=2)
0 => string 'monitors' (length=8)
1 =>
array (size=5)
'id' => int 4
'type' => string 'hardDrives' (length=10)
'count' => int 15
'created_at' => string '2014-12-21 03:50:04' (length=19)
'updated_at' => string '2014-12-21 14:35:28' (length=19)
即使我在查询中使用 monitors
作为$type
的值,我也会获得 hardDrives
的记录。
在此之后,我尝试更改方法来触发查询:
$countCollection = $this->tally->where('type', $type )->get();
然后我得到正确的结果:
array (size=2)
0 => string 'monitors' (length=8)
1 =>
array (size=5)
'id' => int 5
'type' => string 'monitors' (length=8)
'count' => int 3
'created_at' => string '2014-12-21 03:50:04' (length=19)
'updated_at' => string '2014-12-21 03:50:04' (length=19)
如果查找记录时出错,我可以在这里停下来并添加我自己的异常抛出,但是当我阅读 Builder 类方法的 API 文档时firstOrFail()
(抱歉我无法直接链接到它(,该方法描述为:
执行查询并获取第一个结果或引发异常。
我想使用内置的 Laravel 异常,当找不到记录时会抛出它,而不是使用我自己的。
我在这里缺少什么吗?当我在 laravel Eloquent 文档中查找其他示例时,看起来我正在正确构建查询。
最重要的是,我想知道为什么它会失败,而不仅仅是解决方法。
分辨率
这是该方法的最终版本,只是为了向大家展示它是如何结束的:
public function decreaseCountBy($type, $number){
$record = $this->tally->where('type', '=', $type )->firstOrFail();
$record->count -= $number;
$result = $record->save();
if(!$result){
throw new Exception('Error saving decrementation');
}
return $record->count;
}
通常,当您执行->get();
来检索数据时,结果是一个包含多个记录的雄辩Collection
实例。从那里,如果您只想检索第一条记录,则可以使用 Collection 类的 ->first()
方法来获取包含该记录信息的雄辩Model
类实例。
在 firstOrFail()
的情况下,您告诉查询生成器的是,如果找到第一条记录,您只需要第一条记录。由于您只会收到一条记录的数据,因此 eloquent 会跳过集合并返回一个模型实例。
在上面的代码中,我删除了"抓取第一条记录的模型"的行,即 $record = $countCollection->first();
,并重命名变量以更好地拟合预期结果,即 $record
代替$countCollection
。
已经调用firstOrFail()
之后,无需调用first()
。 firstOrFail(( 已经返回单个模型而不是集合,在模型上调用first()
会触发一个全新的 select 语句(这次没有where
(
正如 @Jarek Tkaczyk 在下面指出的那样,使用您的代码,两个查询将针对数据库运行
-
select * from tallies where type = ?
-
select * from tallies
这意味着在您的情况下,第一个查询的结果会被第二个查询覆盖。
一些背景资料
firstOrFail()
只执行任何其他操作,只是调用first()
,然后在first()
返回时引发异常null
public function firstOrFail($columns = array('*'))
{
if ( ! is_null($model = $this->first($columns))) return $model;
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}