Roots Discourse

Issue passing data to a foreach loop in a gutenberg block

I have a function set up in my App.php controller which I’m looking to pass to a Gutenberg block (its using sage-acf-wp-blocks), but unfortunately its showing an error:

"Undefined variable: testimonial_loop in on line : invalid argument supplied for foreach() in on line"

As screenshot shows, I’ve added the same function and outputted through a non-Gutenberg blade template which works fine. Any idea why the block may be showing this error? Thanks in advance for any advice on this.

Heres my function from App.php:

    public function testimonialLoop()
{
  $testimonial_items = get_posts([
    'post_type' => 'testimonial',
    'posts_per_page' => '-1'
  ]);
  return array_map(function ($post) {
    return [
      'title' => apply_filters('the_title', $post->post_title),
      'content' => apply_filters('the_content', $post->post_content),
      'thumbnail' => get_the_post_thumbnail($post->ID, 'large'),
      'by' => get_field('testimonial_by', $post->ID),
    ];
  }, $testimonial_items);
}

And heres is my block code:

{{--

Title: Testimonial
Description: Customer testimonial
Category: formatting
Icon: admin-comments
Keywords: testimonial quote
Mode: edit
Align: left
PostTypes: page post
SupportsAlign: left right
SupportsMode: false
SupportsMultiple: false
EnqueueStyle: styles/style.css
EnqueueScript: scripts/script.js
EnqueueAssets: path/to/asset
–}}

@foreach($testimonial_loop as $item)

<div class="crest">{!! $item['thumbnail'] !!}</div>
<p>{!! $item['content'] !!}</p>
<p class="name"><strong>{!! $item['by'] !!}</strong></p>

@php wp_reset_postdata() @endphp
@endforeach

I’m not familiar w/ the specific package, but based on a quick look at the code, you’re getting an error because block templates aren’t executed within the context of your normal blades, and therefore aren’t getting data passed to them by the controller: https://github.com/MWDelaney/sage-acf-wp-blocks/blob/cb481330cfa3f34b730fa46d618a77996a00dad4/sage-acf-gutenberg-blocks.php#L172-L178

The block template only appears to receive data in two ways:

  1. Data that is “on” that block
  2. Data you pass to it manually with a filter

My understanding is that block HTML is generated before any data even reaches your templates: You could treat your block HTML in your main blade template as essentially just a big string, not PHP that will be executing in the context of that template.

Thanks for that info. So essentially, should I just be adding the function to the block itself rather than via the controller method?

If the function isn’t needed outside the block, yes. If the function is needed elsewhere, I’d refactor it out of the controller: Then you can call the same function (and get the same logic and data) in the controller and easily pass it to the block filter.

1 Like

That makes sense. The function isn’t needed anywhere so I’ll just add the query to the block itself.

Thanks :+1:

So I refactored the query out of the controller as suggested which does indeed work within the new gutenberg block, but for some reason it refuses to show an ACF field ($testimonial_by) from a custom post type:

{{--
  Title: Testimonial Block
  Description: Test block
  Category: formatting
  Icon: admin-comments
  Keywords: testimonial quote
  Mode: edit
  Align: left
  PostTypes: page post
  SupportsAlign: left right
  SupportsMode: false
  SupportsMultiple: false
--}}

<section>
  <?php
  $the_query = new WP_Query(array(
    'post_type'			=> 'testimonial'
  ));
  ?>
  <?php if( $the_query->have_posts() ): ?>
    <?php while( $the_query->have_posts() ) : $the_query->the_post(); ?>
      <h2>@title</h2>
      <p>{{ $testimonial_by }}</p>
    <?php endwhile; ?>
  <?php endif; ?>
  <?php wp_reset_query();	?>
</section>

Heres the CPT:

add_action( 'init', function() {
	register_extended_post_type( 'testimonial', [
    'show_in_feed' => true,
    'menu_icon'    => 'dashicons-format-chat',
		# Show all posts on the post type archive:
		'archive' => [
			'nopaging' => true,
		],
	], [
		# Override the base names used for labels:
		'singular' => 'Testimonial',
		'plural'   => 'Testimonials',
		'slug'     => 'testimonials',
	] );
} );

Any idea why this may be happening?

Thanks

The variable $testimonial_by probably isn’t available in that context. Where are you setting it?

Sorry I’ve tried so many versions of this now its getting a bit confusing, but here is what I had a bit earlier with variable:

  <?php

  $by = get_field('testimonial_by');

  // args
  $args = array(
    'numberposts'	=> -1,
    'post_type'		=> 'testimonial'
  );
  
  // query
  $the_query = new WP_Query( $args );
  
  ?>
  <?php if( $the_query->have_posts() ): ?>
    <ul>
    <?php while( $the_query->have_posts() ) : $the_query->the_post(); ?>
      <li>
        <a href="<?php the_permalink(); ?>">
          {{ $by }}
        </a>
      </li>
    <?php endwhile; ?>
    </ul>
  <?php endif; ?>
  
  <?php wp_reset_query();	?>

Is this the blade for your block?

$by = get_field('testimonial_by');

You aren’t giving this a second argument to tell it where to find the field. My guess would be that when the block blade executes it’s not in a context where ACF can derive an post ID to use to look up the field, so this returns false or null or whatever.

Yeah, this is the Blade file within the Blocks folder under Views:

I’m close now. So I’ve it passed the post_id to the object, which now works, but if I add:

the_content();

it breaks:

<?php

  $args = array(
    'post_type' => 'testimonial',
    'posts_per_page' => -1,
    'fields' => 'ids',
  );
  $query = new WP_Query( $args );

  if( $query->have_posts() ){

    echo '<ul>';

    foreach( $query->posts as $id ){
      the_content();
      echo '<li>' . get_field( 'testimonial_by', $id ) . '</li>';
    }

    echo '</ul>';

  } else {
    echo 'There are no testimonials';
  }
  
?>

Any idea why this might happen?

Thank you

the_content() has to be used inside of The Loop. You are not inside of The Loop when you call it.

1 Like

So eventually I got it all hooked up (after another slight bit of refactoring), so for anyone that may be having same issue, here is my final code for ref - I was able to echo the custom field with the get_the_id() argument to make it work successfully.

      <?php

      $the_query = new WP_Query(array(
        'post_type' => 'testimonial'
      ));

      ?>

      <?php if( $the_query->have_posts() ): ?>

        <?php while( $the_query->have_posts() ) : $the_query->the_post(); ?>

          <?php echo get_field( 'testimonial_by', get_the_ID()); ?>

        <?php endwhile; ?>

      <?php endif; ?>

      <?php wp_reset_query();	?>