I am writing an artisan console command that loops through all the records in a table and regenerates a field on that table.
The field is a hash
and is generated as an md5()
of a particular string.
Initially my code looked like this:
// Get all recipes
$recipes = Recipe::all();
$hashProgress = $this->output->createProgressBar(count($recipes));
// Loop over each recipe and generate a new hash for it
foreach ($recipes as $recipe)
{
$hashString = '';
$hashString .= $recipe->field1;
$hashString .= $recipe->field2;
$hashString .= $recipe->field3;
$hashString .= $recipe->field4;
$hashString .= $recipe->field5;
$hashString .= $recipe->field6;
$hashString .= $recipe->field7;
$extras1Total = $recipe->extras1->sum('amount');
$hashString .= $recipe->extras1->reduce(function ($str, $item) use ($extras1Total) {
return $str . $item->name . ($extras1Total == 0 ? $item->amount : ($item->amount / $extras1Total * 100));
}, '');
$extras2Total = $recipe->extras2->sum('amount');
$hashString .= $recipe->extras2->reduce(function ($str, $item) use ($extras2Total) {
return $str . $item->name . ($extras2Total == 0 ? $item->amount : ($item->amount / $extras2Total * 100));
}, '');
$extras3Total = $recipe->extras3->sum('amount');
$hashString .= $recipe->extras3->reduce(function ($str, $item) use ($extras3Total) {
return $str . $item->name . ($extras3Total == 0 ? $item->amount : ($item->amount / $extras3Total * 100));
}, '');
$extras4Total = $recipe->extras4->sum('amount');
$hashString .= $recipe->extras4->reduce(function ($str, $item) use ($extras4Total) {
return $str . $item->name . ($extras4Total == 0 ? $item->amount : ($item->amount / $extras4Total * 100));
}, '');
$recipe->update([
'hash' => md5($hashString),
]);
$hashProgress->advance();
}
$hashProgress->finish();
$this->info(' Recipe hashes regenerated.');
After getting to around 10,000 of 28,000 records it would die with a memory exhausted error:
PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 4096 bytes)
I thought chunk
ing this might help:
// Get all recipes
$recipes = Recipe::all();
$hashProgress = $this->output->createProgressBar(count($recipes));
// Loop over each recipe and generate a new hash for it
foreach ($recipes->chunk(1000) as $chunk)
{
foreach ($chunk as $recipe)
{
$hashString = '';
$hashString .= $recipe->field1;
$hashString .= $recipe->field2;
$hashString .= $recipe->field3;
$hashString .= $recipe->field4;
$hashString .= $recipe->field5;
$hashString .= $recipe->field6;
$hashString .= $recipe->field7;
$extras1Total = $recipe->extras1->sum('amount');
$hashString .= $recipe->extras1->reduce(function ($str, $item) use ($extras1Total) {
return $str . $item->name . ($extras1Total == 0 ? $item->amount : ($item->amount / $extras1Total * 100));
}, '');
$extras2Total = $recipe->extras2->sum('amount');
$hashString .= $recipe->extras2->reduce(function ($str, $item) use ($extras2Total) {
return $str . $item->name . ($extras2Total == 0 ? $item->amount : ($item->amount / $extras2Total * 100));
}, '');
$extras3Total = $recipe->extras3->sum('amount');
$hashString .= $recipe->extras3->reduce(function ($str, $item) use ($extras3Total) {
return $str . $item->name . ($extras3Total == 0 ? $item->amount : ($item->amount / $extras3Total * 100));
}, '');
$extras4Total = $recipe->extras4->sum('amount');
$hashString .= $recipe->extras4->reduce(function ($str, $item) use ($extras4Total) {
return $str . $item->name . ($extras4Total == 0 ? $item->amount : ($item->amount / $extras4Total * 100));
}, '');
$recipe->update([
'hash' => md5($hashString),
]);
$hashProgress->advance();
}
}
$hashProgress->finish();
$this->info(' Recipe hashes regenerated.');
But I am still getting a memory exhaustion error.
How can I loop through all these records and achieve what I am after without increasing the memory limit?