Home Blog Using an ACF repeater to set a custom order for global content in WordPress
Web Development

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.

PHP

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.

PHP
/* 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;
}
PHP
//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.

PHP
//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' );
});
CSS
.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;
}
You may also be interested in reading
Shopify Checkout Extensibility: What it means for your analytics
TLDR: Shopify Plus now requires that tracking take place from within a special iFrame. If your tracking scripts currently scrape data directly from the checkout or thank-you pages, you’ll need to update your code to instead...
Continue reading