ACF Pro Repeater Fields w/ Sage 9

Hi all!

Before I ask my question, I just want to give a huge thanks to everyone on this forum. I’m brand new to WP theme dev and working with Sage, and have been asking frequent questions. Everyone here has been more than helpful, and it is very much appreciated!

That being said, I’m in need of a little more guidance. Recently, I’ve started playing with ACF Pro and the repeater fields. The repeater field group has its own ID (in my case, portfolio_work), and then the subfields have their own name/IDs (portfolio_image, portfolio_description, etc…). How would I go about pulling that data using Controllers?

Thanks again in advance.


There are a lot of ways to answer this question because it’s not really a right or wrong question, it’s more of a stylistic one.

That said, I think the Sageiest way to deal with repeaters and controllers is as follows. This example is adapted from a recent site that needed to list projects:



     * Populate and return the Projects template(s)
    public function projects() {

      // Initialize the return variable
      $return = '';

      // Get the appropriate projects
      $projects = get_field('projects');

      if(is_array($projects)) {

        foreach( $projects as $p ) {

          $return .= \App\template('partials.projects.project-item', [
            'title' => $p['title'],
            'content' => $p['content'],
            'image' => wp_get_attachment_image( $p['image'], 'medium' ),


      // Always return
      return $return;



    <h3>{!! $title !!}</h3>
    {!! $image !!}
    {!! $content !!}

And then just put this wherever you want them to appear:

  {!! $projects !!}

The reason I like this method is that’s it takes every bit of logic out of the templates and puts it into the controller. No ifs, no foreaches, justnice, clean templates.


Worked like a charm. Thank you so much!

Just wanted to add a possible alternative:


public function projects()
   return array_map(function($project) {
      return [
         'title' => $project['title'] ?? null,
         'content' => $project['content'] ?? null,
         'image' => isset($project['title']) ?  wp_get_attachment_image( $project['image'], 'medium' ) : null,
   }, get_field('projects') ?? []);


  <h3>{!! $title !!}</h3>
   {!! $image !!}
   {!! $content !!}

Wherever you want them to appear

   @foreach($projects as $project)
      <li> @include('projects.project-item', $project) </li>

This isn’t any more “correct” than @MWDelaney’s version: It’s just preference. I like to have controllers return only data, and have views communicate structure. Using @include()instead of a compiled string makes it a little clearer to someone reading the Blade what this element is doing, and keeping the <li> out of the partial makes the partial a bit more flexible. Also array_map() is :ok_hand::ok_hand::ok_hand: and has changed my life.


This is good. My way definitely runs the risk of being included without the <ul>.

It’s all personal preference.

I think my way is absolutely preferable if you’re passing instance-specific data, rather than page-specific data, and using a static function. That’s what I’m doing most often and I fell into the habit of doing @foreaches this way from that.

1 Like

Hi guys, thank you so much for helping us to understand how Sage 9 works.

I would like to know, what is the best way to use this example with custom post types and nested repeaters.

I have a custom post type called “books” with an ACF repeater with 3 subfields called “year”, “author” and “shops” (“shops”, is the ACF nested repeater with two fields (“name” and “url”)).

I have several days, trying and I only get one Notice: Undefined variable. :disappointed_relieved:
Can you help me, please?

PS: What a book they recommend for a newbie like me?

Well first of all, are “shops” items ever repeated among books? Do two or more books have the same shop attached to them? If so, I would use a custom post type (and a relationship field), or custom taxonomy (and a taxonomy field) depending on your needs.

That way you can enter the names and URLs once and reuse them wherever you need.

To diagnose the error you’re receiving we’d need to see your code. What is in your template? What’s is in your controller?

Thank you for answering me @MWDelaney .

The links of the stores are not repeated, the name corresponds to the name of the store and the link corresponds to the link of the book for your purchase.

For example

Thanks for helping me with my code, I was able to remove the undefined variable message, but I still can’t get the ACF repeater works. Can you help me?

Field Group

Year = Text Field (year)
Author = Text Field (author)
Stores = Repeater (stores)
    -- Name = Text Field (name)
    -- Url = Url Field (url)


public function books()
    $book_items = get_posts([
        'post_type' => 'book',

    return array_map(function ($post) {
        return [

            // Title, Content and Image
            'title' => apply_filters('the_title', $post->post_title),
            'content' => apply_filters('the_content', $post->post_content),
            'thumbnail' => get_the_post_thumbnail($post->ID, 'large'),

            // ACF Fields
            'acf_year' => get_field('year', $post),
            'acf_author' => get_field('author', $post),

            // Repeater Store Name and Link

    }, $book_items);


    @foreach($books as $book_item)
        {!! $book_item['title'] !!}
        {!! $book_item['acf_year'] !!}
        {!! $book_item['acf_author'] !!}
        {!! $book_item['content'] !!}
        {!! $book_item['thumbnail'] !!}

        // ACF Repeater

        Not found

Thanks Man!
Have a nice day.

It looks like you have @debug set at the top, what does it output? What does or doesn’t work when you use this code?

You could try temporarily changing the @foreach content to @dump($book_item) so you can see if you’re getting all the requested data back properly.

1 Like

This is great. Thank you very much!

Hi - this works great except for when there is no data in the field.

I get this error, on the line that has this:

}, get_field('projects') ?? []);

Warning : array_map(): Argument #2 should be an array

Any ideas?

Yes, the second argument passed to array_map should probably be an array.
In this case, my guess is that get_field() returns false or a falsy value when the field is empty, and because that code is using the null-coalescing operator, the expression resolves to the falsey result of get_field(), not []. If you use ?: instead (which, in all fairness, is how I should have written this in the first place) you should get the result you want.

Hi all, I have been banging my head against a wall trying to figure out how to put a repeater inside a repeater, using this code. Any ideas? Thanks!

public function tickets()
return array_map(function($ticket) {
return [
‘date’ => $ticket[‘date’] ?? null,
‘sub_heading’ => $ticket[‘sub_heading’] ?? null,
‘shows’ => array_map(function($show) {
return [
‘show_time’ => $show[‘show_time’] ?? null,
‘show_title’ => $show[‘show_title’] ?? null,
}, get_sub_field(‘shows’, 11) ?? []),
}, get_field(‘new_date’, 11) ?? []);