WordPress systems / Case 24
Preventing ACF repeater data from crashing a production WordPress template
A template assumed an ACF repeater always returned the same nested array shape. One missing or legacy value was enough to turn a content problem into a PHP fatal error.
Plain-English summary
What this solved for the business or user
An editor should be able to leave an optional content group empty without taking down the page. The fix made the template tolerant of incomplete content while still rendering the full component when valid rows are available.
01 / Real situation
What was happening
A production page loaded an ACF field used for a question carousel and immediately accessed nested array values and called count() on them. On some content states the field returned false or a nested value was null. The page therefore failed before WordPress could finish rendering, even though the missing content itself was optional.
02 / Constraint
Why the obvious solution was not enough
The component already worked for populated pages, so replacing the content model was unnecessary. The safer repair was to make the rendering contract explicit: optional ACF data can be absent, malformed, or left over from an older field structure, and the template must handle those states before iterating or counting anything.
03 / Implementation
How the solution works
- Retrieve the field once and inspect the actual value before rendering the component. Do not assume an optional repeater is always an array.
- Return early or render a safe empty state when the parent value is false, null, or an empty array.
- Validate nested values independently. A valid repeater row can still contain a subfield that is missing or stored in an older shape.
- Use is_array() before count(), foreach, or array-offset access when the field contract allows an empty value.
- For repeater rendering, prefer ACF's documented have_rows()/the_row()/get_sub_field() pattern when that matches the field structure. It makes the row context explicit and avoids mixing parent-field and subfield access.
- Keep the defensive checks close to the content boundary instead of scattering warnings suppression throughout the template.
- Test empty, one-row, multi-row, partially populated, and legacy records before considering the component safe.
$rows = get_field('question_carousel');
if (!is_array($rows) || empty($rows)) {
return;
}
foreach ($rows as $row) {
$toggles = $row['toggle'] ?? [];
$toggles = is_array($toggles) ? $toggles : [];
if (!$toggles) {
continue;
}
foreach ($toggles as $toggle) {
// Render only validated content.
}
}04 / Release checks
What should be verified before shipping
- Save the page with the entire repeater empty and confirm the template still returns a normal response.
- Create a row with one optional nested field missing and confirm no warning or fatal error is emitted.
- Test existing records created before the current ACF field structure was introduced.
- Confirm populated rows still render in the expected order after adding the guards.
- Review PHP error logs after release instead of relying only on the browser output.
05 / Result
What changed
Optional content stopped being a page-level reliability risk. The template could tolerate missing ACF data while preserving the same output for valid content.
Reusable lessonCMS data is external input to the template. Treat its shape as something to validate, not something to assume, especially when editors and migrations can create legitimate empty states.
References
