phpwordpressconditional-statementsadvanced-custom-fields

Use nested while loops with a Advanced Custom Fields repeaster field?


With WordPress, I'm using Advanced Custom Fields with a repeater field https://www.advancedcustomfields.com/resources/repeater/ and I'm trying to work out how to add some conditions to the repeater loop to conditionally display some repeater rows.

Right now, this works and displays all repeater fields:

if (have_rows('repeater')): 

    echo '<div class="container-fluid"><div class="row">';

        while( have_rows('repeater') ): the_row();

            echo '<div class="col">';
            echo '<div class="title">';
            the_sub_field('title');
            echo '</div></div>';
    
        endwhile;

    echo '</div></div>';

 endif;

And that outputs:

<div class="container-fluid">
    <div class="row">

        <div class="col">
            <div class="title">
                The title
            </div>
        </div>

        <div class="col">
            <div class="title">
                Another title
            </div>
        </div>

    </div>
</div>

// and on and on

But what I need to do is display some repeater rows, depending on two conditions: if the user is logged in, and if the repeater subfield show_only_to_logged_in_admins has the value of 1. After any/all of those conditional rows are displayed, I need to continue the repeater loop and display the rest of the repeater rows.

What I have right now, the code below, displays only the rows with the subfield show_only_to_logged_in_admins at the value of 1 to users who are logged-in administrators, and it displays no rwos to non-logged users and when show_only_to_logged_in_admins field value is 0.

if (have_rows('repeater')): 

    echo '<div class="container-fluid"><div class="row">';

    while( have_rows('repeater') ): the_row();

        $show_only = get_sub_field('show_only_to_logged_in_admins');
        $current_user = wp_get_current_user();

        if ((user_can( $current_user, 'administrator' )) & ($show_only == 1)) {

            echo '<div class="col">';
            echo '<div class="title">';
            the_sub_field('title');
            echo '</div></div>';

        }

    endwhile;

    echo '</div></div>';

endif;

But what I need is this:

<div class="container-fluid">
    <div class="row">

// the rows that are shown to logged users and when
// the repeater subfield show_only_to_logged_in_admins
// has the value of 1

        <div class="col">
            <div class="title">
                The title
            </div>
        </div>

// and on and on

// and then the rest of the repeater rows

        <div class="col">
            <div class="title">
                Another title
            </div>
        </div>

    </div>
</div>

How can I display the conditional rows, and then continue the loop for all the rest of the rows?

Would I use a nested while or do-while loop for the conditionals? Nested inside the main repeater loop while( have_rows('repeater') ): the_row(); ?


Solution

  • Update based on the comment:

    I think your best shot is to use the PHP output buffers. So you don't have to repeat the mark up and you can implement what you want with a single while loop.

    Example:

    if (have_rows('repeater')): 
    
        $current_user = wp_get_current_user();
        $adminOnlyContent = '';
        $nonAdminContent = '';
    
        while( have_rows('repeater') ): the_row();
    
            $show_only = get_sub_field('show_only_to_logged_in_admins');
    
            ob_start();
    
            echo '<div class="col">';
            echo '<div class="title">';
            the_sub_field('title');
            echo '</div></div>';
    
            if ((user_can( $current_user, 'administrator' )) & ($show_only == 1)) {
                $adminOnlyContent .= ob_get_clean();
            } else {
                $nonAdminContent .= ob_get_clean();
            }
    
        endwhile;
    
        echo '<div class="container-fluid"><div class="row">';
    
        echo $adminOnlyContent;
    
        echo $nonAdminContent;
    
        echo '</div></div>';
    
    endif;
    

    Another option is implement a separate function which will return the markup.

    Original Answer:

    You cannot use the_row() twice within a loop. So you cannot use the nested loops here. But there are several ways to achieve what you want.

    Here is one option: Store the content for both groups of fields in variables and echo them after the while loop.

    $other_rows = [];
    
    if (have_rows('repeater')): 
    
        $adminFields = '';
        $nonAdminFields = '';
    
        $current_user = wp_get_current_user();
    
        while( have_rows('repeater') ): $row = the_row();
    
            $show_only = get_sub_field('show_only_to_logged_in_admins');
    
            if ((user_can( $current_user, 'administrator' )) & ($show_only == 1)) {
    
                $adminFields .= '<div class="col"><div class="title">';
                $adminFields .= the_sub_field('title');
                $adminFields .= '</div></div>';
    
            } else {
                $nonAdminFields .= '<div class="col"><div class="title">';
                $nonAdminFields .= the_sub_field('title');
                $nonAdminFields .= '</div></div>';
            }
    
        endwhile;
    
        echo '<div class="container-fluid"><div class="row">';
    
        echo $adminFields;
        echo $nonAdminFields;
    
        echo '</div></div>';
    
    endif;
    

    Alternate solution:

    The other options may require at least two loops. You need to use the loops one after the other instead of nested loops.

    ...
        $current_user = wp_get_current_user();
    
        while( have_rows('repeater') ): $row = the_row();
    
            $show_only = get_sub_field('show_only_to_logged_in_admins');
            if ((user_can( $current_user, 'administrator' )) && ($show_only == 1)) {
    
                // Display content
    
            }
    
        endwhile;
    
        while( have_rows('repeater') ): $row = the_row();
    
            $show_only = get_sub_field('show_only_to_logged_in_admins');
            if (! (user_can( $current_user, 'administrator' )) || ($show_only != 1)) {
    
                // Display content
    
            }
    
        endwhile;
    ...
    

    Alternate Solution 2

    Here is another technique: Get all the repeater rows and segregate them to admin and non-admin rows. Then you can loop over them. Here also you need to use two loops.

    // This is just a pseudo-code but you get the idea
    $rows = get_field('repeater');
    $current_user = wp_get_current_user();
    
    $adminOnlyRows = array_filter($rows, function ($row) {
        $show_only = $row['show_only_to_logged_in_admins'];
        return (user_can( $current_user, 'administrator' )) && ($show_only == 1);
    });
    
    $nonAdminOnlyRows = array_filter($rows, function ($row) {
        $show_only = $row['show_only_to_logged_in_admins'];
        return ! (user_can( $current_user, 'administrator' )) || ($show_only != 1);
    });
    
    echo '<div class="container-fluid"><div class="row">';
        foreach ($adminOnlyRows as $row) {
            // Display content
        }
    
        foreach ($nonAdminOnlyRows as $row) {
            // Display content
        }
    echo '</div></div>';