Using an ACF repeater to set a custom order for global content in WordPress
TLDR: Code tutorial on how to control the order of carousel slides on a per-page basis while content is updated globally through an options page ACF repeater.
What does this code achieve?
Allows a WordPress admin user to display repeater-based content from an ACF options page and select a custom order on each page by using a pre-filled ACF repeater as the ordering control.
Screenshot of the prefilled ACF block used for controlling the order of slides. A bit of dashboard-only CSS was applied to disable editing of field values.
Step 1: Prefill the repeater with global content
To prefill our block we’ll hook into ACF’s load_value filter here passing in the repeater’s ID which in my case is “field_65fc7016b9bc1
“. See my comments in the code snippet below for more details on the functionality.
add_filter('acf/load_value/key=field_65fc7016b9bc1', function($value, $postId, $field){
//Get the slider content from the options page
$slider = get_field('hero_slider', 'options');
/* refer to the field we want to pre-fill as $repeater
and make sure it exists */
$repeater = $value ? $value : [];
/* First compare $repeater's content with $slider's content
and update/create the rows that exist in $slider */
$slidesToUpdate = airtightGetSlidesToUpdate($slider, $repeater);
if (!empty($slidesToUpdate)):
array_map(function($id) use (&$repeater){
/* foreach slide in $slider,
push a new row into to $repeater
$repeater subfields:
- field_65fc7020b9bc2 refers to an image
- field_65fc7cc8ff08e refers to a text field
*/
return array_push($repeater, [
'field_65fc7020b9bc2' => $id,
'field_65fc7cc8ff08e' => $id,
]);
}, $slidesToUpdate);
endif;
//Then check if $repeater has slides that no longer exist in $slider
$slidesToDelete = airtightGetSlidesToDelete($slider, $repeater);
if (!empty($slidesToDelete)):
//to delete, just filter out the slides that shouldn't be there
$repeater = array_filter($repeater, function($row) use ($slidesToDelete){
return !in_array($row['field_65fc7020b9bc2'], $slidesToDelete);
});
endif;
return $repeater;
}, 10, 3);
//returns slides that should be updated
function airtightGetSlidesToUpdate($slider, $repeater = []){
if(!is_array($repeater)) return [];
$slideIds = array_map(function($slide){
return $slide['banner_image']['ID'];
}, $slider);
$savedIds = array_map(function($row){
return intval($row['field_65fc7cc8ff08e']);
}, $repeater);
$notPresent = array_filter($slideIds, function($id) use ($savedIds){
return !in_array($id, $savedIds);
});
return $notPresent;
}
//returns slides that should be deleted
function airtightGetSlidesToDelete($slider, $repeater = []){
if(!is_array($repeater)) return [];
$slideIds = array_map(function($slide){
return $slide['banner_image']['ID'];
}, $slider);
$savedIds = array_map(function($row){
return intval($row['field_65fc7cc8ff08e']);
}, $repeater);
$unwanted = array_filter($savedIds, function($id) use ($slideIds){
return !in_array($id, $slideIds);
});
return $unwanted;
}
Step 2: Apply custom sorting logic
Apply the selected custom order to your content using a custom PHP usort function.
/* using parameters
-$slide: a repeater row from global content as
-$sorter: the order control (repeater field)
- return an integer to indicate posision */
function airtightGetSlidePosition($slide, $sorter){
$postion = array_filter($sorter, function($value, $key) use ($slide){
$imgId = $slide['banner_image']['id'] ?? null;
return $imgId == $value['slide_img']['id'];
}, ARRAY_FILTER_USE_BOTH);
if(!is_array($postion)) return;
$position = array_keys($postion)[0];
return $position;
}
/* just compare order of slides and apply a custom sort
using PHP spaceship <=> operator */
function airtightCustomSortSlider($slider, $sorter){
usort($slider, function($a, $b) use ($sorter){
$aPosition = airtightGetSlidePosition($a, $sorter);
$bPosition = airtightGetSlidePosition($b, $sorter);
return $aPosition <=> $bPosition;
});
return $slider;
}
//template usage example
<div class="airtight-slider">
<?php
$slider = get_field('hero_slider', 'options');
$sorter = get_field('airtight_custom_slide_order');
if(!empty($sorter)):
$slider = airtightCustomSortSlider($slider, $sorter);
endif;
?>
</div>
Step 3: Apply admin CSS
We don’t want the WordPress user to be able to edit the prefilled slides shown within the block aside from drag and drop re-ordering.
Let’s enqueue an admin stylesheet, and knock out the default ACF controls for add, delete, edit remove etc. using pointer-events
and display: none
.
//enqueue a stylesheet for the dashboard only
add_action( 'admin_enqueue_scripts', function(){
wp_enqueue_style( 'airtight-admin-styles', get_template_directory_uri() . "/assets/admin.css", array(), '0.0.2' );
});
.acf-field[data-key="field_65fc7020b9bc2"]{
pointer-events: none;
}
//makes input appear disabled
.acf-field[data-key="field_65fc7cc8ff08e"] input{
pointer-events: none;
opacity: 0.7;
background-color: #dcdcdc;
}
[data-key="field_65fc7016b9bc1"] .acf-actions{
display: none;
}
[data-key="field_65fc7016b9bc1"] .acf-row-handle.remove{
pointer-events: none;
}