Finding the first, last and in-between elements of a ACF flexible repeater array

Hello, I’m trying to target the first, last and in-between elements of a repeater array that is the sub-field of a flexible content layout to display previous, next or close buttons in the footer of an image modal based on its position in the array.

The tile_column in the controller is the repeater field that has a sub-field for [‘images’], which will pop-up in a modal when clicked. In the footer of each of these modals I want to display previous, back or close buttons depending on their position in the array.

Here is the controller

<?php

namespace App\Controllers\Partials;

trait FlexibleContent
{
    public function flexible_layouts()
    {

        // Get all page builder fields
        $flexible_layouts = get_field('flexible_layouts');

        // Set up array
        $data = [];
        

        // Loop through each block
        if ($flexible_layouts) {
            foreach ($flexible_layouts as $flexibleLayouts) {


                if ($flexibleLayouts['acf_fc_layout'] == 'two_column_layout_with_video_left') {

                    // Do any logic for this component here

                    $this_block = (object) [
                        'block_type' => $flexibleLayouts['acf_fc_layout'],
                        'title_and_copy_column' => $flexibleLayouts['title_and_copy_column'],
                        'video_column' => $flexibleLayouts['video_column'],
                        'background_color_layout_1' => $flexibleLayouts['background_color_layout_1'],
                        'background_image_layout_1' => $flexibleLayouts['background_image_layout_1'],
                    ];

                    array_push($data, $this_block);

                } elseif ($flexibleLayouts['acf_fc_layout'] == 'two_column_tile_layout_with_modal') {

                    $this_block = (object) [
                        'block_type' => $flexibleLayouts['acf_fc_layout'],
                        'title_column' => $flexibleLayouts['title_column'],
                        'tile_column' => $flexibleLayouts['tile_column'],
                        'button_column' => $flexibleLayouts['button_column'],
                        'background_color_layout_2' => $flexibleLayouts['background_color_layout_2'],
                        'background_image_layout_2' => $flexibleLayouts['background_image_layout_2'],
                    ];

                    array_push($data, $this_block);
                }
                
            }

                $data = (object) $data;

                return $data;
        }
    }
}

Partial

@if($flexible_layouts)
@foreach($flexible_layouts as $flexibleLayouts)

@if($flexibleLayouts->block_type == 'two_column_layout_with_video_left')
    @include('flexible.two-column-layout-video-left')

@elseif($flexibleLayouts->block_type == 'two_column_tile_layout_with_modal')
    @include('flexible.two-column-tile-modal')

@endif

@endforeach
@endif

Tile View

@php $counter = 0 @endphp
@php $image_count = count($flexibleLayouts->tile_column) @endphp
@if($flexibleLayouts->tile_column)
@foreach($flexibleLayouts->tile_column as $tile)
@if($tile['title'] || $tile['content'])
<div class="uk-padding-small">
    <div class="compose-card-step uk-card uk-card-small uk-text-left">
        <div class="uk-card-body">
            <h3 class="uk-heading-small uk-text-normal">{!! $tile['title'] !!}</h3>
            <p class="liqid-card-copy uk-flex-1 uk-margin-remove-top uk-text-large">{!! $tile['content'] !!}</p>
        </div>
    </div>
</div>



@elseif($tile['image'])
<?php $image_array = $tile['image'] ?>
<div class="uk-padding-small">
    <div class="compose-card-image uk-card uk-card-small uk-card-hover">
        <a href="#{{ $tile['current_image_number'] }}" uk-toggle>
            <div class="uk-card-body">
                <img style class="" src="{!! $tile['image'] !!}">
            </div>
        </a>
    </div>
</div>
<div id="{{ $tile['current_image_number'] }}" class="" uk-modal>
    <div class="compose-modal-image uk-card uk-card-small uk-flex uk-flex-column uk-width-xlarge uk-margin-auto-vertical uk-modal-dialog">
        <button class="uk-modal-close-default" type="button" uk-close></button>
        <div class="uk-card-body">
            <img style class="" src="{!! $tile['image'] !!}">
        </div>
        <div class="uk-card-footer">
            @if($counter == 0)
            @include('flexible.two-column-tile-modal.tile-footer-1')
            @elseif($counter > 0 && $counter < $image_count - 1)
            @include('flexible.two-column-tile-modal.tile-footer-2')
            @elseif($counter == $image_count - 1)
            @include('flexible.two-column-tile-modal.tile-footer-3')
            @endif
        </div>
    </div>
</div>
@php $counter++ @endphp
@endif
@endforeach
@endif

As you can see I’ve tried the counter approach, but its not targeting correctly. Maybe my controller should be setup differently to store the array images in a separate array? Any help is greatly appreciated. Thanks.

Blade loops have a $loop variable that can tell you first, last, etc: Blade Templates - Laravel - The PHP Framework For Web Artisans

Yeah I’ve tried this and its not working correctly. It gives me undefined property for some of the properties. And the ones that do work for instance the $loop->first or $loop->last, it’s not targeting neither the first or last instance of the loop. All the image modals end up getting the same footer.

I don’t totally understand what this design is intended to accomplish, but your blade for “Tile View” seems to complicated—I feel like a lot of this juggling and manipulation could be handled in the Composer. Ideally you want a little logic in your views as you can get away with. I don’t know if that complexity is the cause of your problems, but refactoring to keep logic in the Composer might help you track down the issue.

Your “Tile View” is also relying on data to cascade down to it (specifically the $flexibleLayouts variable) which IMO is not a great practice. When including partials you should explicitly pass the data you way to them, i.e.:

@include( 'partial.filename', [ 'data' => $some_array ] )

This can make debugging (and avoiding) scope issues a lot easier.

I can’t really guess why this is happening without seeing your code.

As an example of simplifying your logic, you could so the following:

// Composer
            foreach ($flexible_layouts as $flexibleLayouts) {
                if ($flexibleLayouts['acf_fc_layout'] == 'two_column_layout_with_video_left') {
                    $this_block = (object) [
                        'view' => 'flexible.two-column-layout-video-left',
                        // ...
                    ];

                    array_push($data, $this_block);
                } elseif ($flexibleLayouts['acf_fc_layout'] == 'two_column_tile_layout_with_modal') {
                    $this_block = (object) [
                        'view' => 'flexible.two-column-tile-modal',
                        // ...
                    ];
                    array_push($data, $this_block);
                }
            }
// Blade
@foreach( ($flexible_layouts || []) as $layout )
  @include( $layout['view'], ['flexibleLayouts' => $layout ] )
@endforeach

Thanks for your feedback.

I’ve tried your code, but it throws an error for the 1st line of your blade code…

Warning : Invalid argument supplied for foreach()

It’s either the array bracket or how its pulling in my tile view. Not sure how this code changes that view.

    @if($flexibleLayouts->tile_column)
    @foreach($flexibleLayouts->tile_column as $tile)
    @if($tile['title'] || $tile['content'])
    <div class="uk-padding-small">
        <div class="compose-card-step uk-card uk-card-small uk-text-left">
            <div class="uk-card-body">
                <h3 class="uk-heading-small uk-text-normal">{!! $tile['title'] !!}</h3>
                <p class="liqid-card-copy uk-flex-1 uk-margin-remove-top uk-text-large">{!! $tile['content'] !!}</p>
            </div>
        </div>
    </div>



    @elseif($tile['image'])
    <div class="uk-padding-small">
        <div class="compose-card-image uk-card uk-card-small uk-card-hover">
            <a href="#{{ $tile['current_image_number'] }}" uk-toggle>
                <div class="uk-card-body">
                    <img style class="" src="{!! $tile['image'] !!}">
                </div>
            </a>
        </div>
    </div>
    <div id="{{ $tile['current_image_number'] }}" class="" uk-modal>
        <div class="compose-modal-image uk-card uk-card-small uk-flex uk-flex-column uk-width-xlarge uk-margin-auto-vertical uk-modal-dialog">
            <button class="uk-modal-close-default" type="button" uk-close></button>
            <div class="uk-card-body">
                <img style class="" src="{!! $tile['image'] !!}">
            </div>
            <div class="uk-card-footer">

               @if($loop->first)
                @include('flexible.two-column-tile-modal.tile-footer-1')
                @elseif($loop->remaining)
                @include('flexible.two-column-tile-modal.tile-footer-2')
                @elseif($loop->last)
                @include('flexible.two-column-tile-modal.tile-footer-3')
                @endif
               
            </div>
        </div>
    </div>
  @endif
    @endforeach
    @endif

I decided to go back to my original logic and was able to resolve it by targeting the array in my if statements:

 <div class="uk-card-footer">
                @if($tiles <= $flexibleLayouts->tile_column[1]) 
                    @include('flexible.two-column-tile-modal.tile-footer-1') 
                @elseif($tiles > $flexibleLayouts->tile_column[1] && $tiles < end($flexibleLayouts->tile_column)) 
                    @include('flexible.two-column-tile-modal.tile-footer-2') 
                @elseif($tiles === end($flexibleLayouts->tile_column)) 
                    @include('flexible.two-column-tile-modal.tile-footer-3') 
                @endif 
            </div>