我需要迭代一定数量的行并根据API调用的结果更新它们:
Item::whereIn('id', $ids)->chunk(5, function ($items) {
$skus = $chunk->pluck('sku')->toArray();
$pricing = $this->api->getPrices($skus); // Requires chunks of 5 SKUs.
foreach ($items as $item) {
if (!empty($pricing[$item->sku])) {
$item->price = $pricing[$item->sku];
$item->save();
}
}
});
但是,如果save()
调用实际上更改了数据库中的一行,那么下一个项目块将包含上一个块中更改的一些项目。
这会导致错过原始查询结果中的某些项目。我可以通过使用Xdebug监视$skus
的内容来看到这一点,它显示后续块获取已更新的SKU($ids
的内容是唯一的)。
例如,如果返回了30个项目,那么您将获得6个5个块。如果5个项目有更改导致数据库更新这些行,那么循环实际上只能处理25个项目。
如果我从回调中删除$item->save()
,那么它完成处理剩余的块,没有SKU旋转到下一个块。但是,无法完全挽救目的。
如果我更改代码以执行此操作,那么我仍然遇到同样的问题:
Item::whereId($item->id)->update($item->getAttributes());
这是使用PostgreSQL 9.6的Laravel 5.3。
据我所知,MySQL不会发生这种情况,因此它似乎是Postgres的怪癖。
我可以通过添加类型$updates
的类var Collection
并将$items->save();
更改为此来解决此问题:
if ($item->isDirty()) {
$this->updates->push($item);
}
然后在完成所有块之后,处理更新,例如分块循环的外部:
if ($this->updates->count()) {
foreach ($this->updates as $update) {
$update->save();
}
}
如果我有一个特别大的结果集需要处理,那么我可以遇到存储一个非常大的集合的内存问题,直到它到达终点。
为什么我不能在不破坏它的情况下更新块中的记录,我该如何(或者我可以)修复它?
根据要求提供$item
的示例内容:
App\Models\Items (22) (
protected table -> string (5) "items"
protected guarded -> array (1) [
string (2) "id"
]
protected connection -> NULL
protected primaryKey -> string (2) "id"
protected keyType -> string (3) "int"
protected perPage -> integer 15
public incrementing -> bool TRUE
public timestamps -> bool TRUE
protected attributes -> array (6) [
'id' => integer 2
'sku' => string (9) "GM-705-24"
'price' => integer 3788
'quantity' => integer 10
'created_at' => string (19) "2016-10-11 01:50:45"
'updated_at' => string (19) "2016-10-11 01:50:45"
]
protected original -> array (6) [
'id' => integer 2
'sku' => string (9) "GM-705-24"
'price' => integer 3788
'quantity' => integer 10
'created_at' => string (19) "2016-10-11 01:50:45"
'updated_at' => string (19) "2016-10-11 01:50:45"
]
protected relations -> array (0)
protected hidden -> array (0)
protected visible -> array (0)
protected appends -> array (0)
protected fillable -> array (0)
protected dateFormat -> NULL
protected casts -> array (0)
protected touches -> array (0)
protected observables -> array (0)
protected with -> array (0)
public exists -> bool TRUE
public wasRecentlyCreated -> bool FALSE
)
$pricing
的内容示例:
array (3) [
'GM-705-24' => float 37.88,
'GM-805-25' => float 25.01,
'GM-715-26' => float 15.05,
]