New to Blade: Issue with Array Map and CPT

We have a custom post type called our_models to display models from a series of a product.

We’re still learning and trying to wrap our heads around Blade Controllers, etc.

So we have a controllers setup to work with ACF in App.php:

public function models()
{
    return array_map(function($model) {
        return [
            'title' => $model['model_type_name'] ?? null,
            'description' => $model['short_description'] ?? null,
            'image' => $model['image'],
            'aluminum' => $model['aluminum_colors'] ?? null,
            'poly' => $model['poly_roof_colors'] ?? null,
            'heat' => $model['heat_poly_roof_colors'] ?? null,
            'wind' => $model['wind_pressure'] ?? null,
            'snow' => $model['snow_load'] ?? null,
        ];
    }, get_field('model_series') ?? []);
}

This works great for individual posts under our post type without error and displays all the data just fine but when we’re visiting the archive for for our_models, we receive the following PHP error:

Warning: array_map(): Expected parameter 2 to be an array, bool given in /srv/www/website.com/current/web/app/themes/website/app/Controllers/App.php on line 89

which focuses on this line:

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

Adding a 2nd parameter like, 'options' or 'dog' or anything else, removes the PHP error but then also removes the display of all the ACF field data on each post in the custom post type single pages.

We’re trying to understand what we’re doing wrong here as none of the archive pages: 'archive-our_models.php', 'content-our_models' or similar files that are used in developing the archive page are at all referencing the controller, as far as we know or can tell.

A controller applies to the entire endpoint it sits on top of. In this case, the controller for an archive sits on the archive endpoint.

get_field() accepts two arguments; the field name, and the post ID (or options if it’s a site option). If you call it without the second argument, it attempts to get the ID of the current post. It can only do this if there is a current post.

When you call get_field() without an ID argument on an archive controller, there is no “current post” for it to get, because there is no post ID associated with an archive endpoint. That means it returns false (or null, or some other error return value), which is obviously not the array that array_map() expects.

What you are doing wrong is calling get_field() on an archive, instead of calling it on each post that is displayed on the archive.

One way to do this is to generate an array in your archive controller for each post on the current archive page, and then do all the logic you need to on each post at that time. I’ve posted an example of that technique here: The right way to run multiple WordPress loops

Thanks for pointing me in the right direction. We’re still having trouble wrapping this up. Apologies for any ignorance.

We have this in our filters.php file:

/** @var $wp_query \WP_Query */
 global $wp_query;

 $model['our_models'] = false;
 if ($wp_query->post_count > 0) {
     $model['our_models'] = array_map(function ($post) {
         /** @var $post \WP_Post */
         return array(
             'title' => $model['model_type_name'],
             'description' => $model['short_description'],
             'image' => $model['image'],
             'aluminum' => $model['aluminum_colors'],
             'poly' => $model['poly_roof_colors'],
             'heat' => $model['heat_poly_roof_colors'],
             'wind' => $model['wind_pressure'],
             'snow' => $model['snow_load'],
         );
     }, $wp_query->posts);
 }

Our archive-our_models.blade.php file:

@extends('layouts.archive')

@section('content')
    @if($posts)
        <ul>
            @foreach($posts as $post)
                @include('partials.content-'.get_post_type(), $post)
            @endforeach
        </ul>
    @endif
@endsection

Our content-our_models.blade.php file:

<li>
    <div>
        <h3>{{$title}}</h3>
    </div>
</li>

Currently just trying to return one field at this time. No more PHP errors but also nothing being displayed on the archive page.

Deeply appreciate any further help and guidance.

It’s not necessary to use filters.php if you’d prefer to use Controller (as you seemed to be initially). As I said in my linked post:

I’m using Sage’s filters for my controller instead of soberwp/controller , but the principle is more or less the same

If you do want to use filters instead of Controller, then you need to wrap your logic in the appropriate filter do get your data to your template. See the appropriate section of the documentation.

If you want to keep using Controller, then just put that logic you put in filters.php in your controller, i.e.:

public function selectedposts() {
   // return array
}

Regardless of your choice for how to return your data, the code you posted won’t do anything: You pass $post to the anonymous function you pass to array_map(), but then the $post variable is never used within that function: You try and use a variable called $model which isn’t set in that scope. You need to actually get your meta fields from the $post.

Thank you for your help! Still battling with getting the data to pass through. Does anyone have any specific examples used with ACF? That would be really helpful.

There’s nothing special about ACF fields. They’re just fields on a post object. Here’s a very minimal example that should work:

// this is the controller
// Archive.php
public function SomePosts()
{
 if ($wp_query->post_count > 0) {
     return array_map(function ($post) {
         return [ 
            'field_one' => get_field('an_acf_field', $post->ID) ,
            'field_two' => get_field('another_acf_field', $post->ID) ,
         ];
     }, $wp_query->posts);
 }
 return false;
}
// this is the blade
// archive.blade.php

// it is import here to **not** use $posts because that would try to
// overwrite the $posts global that WordPress archives use, and that
// is kind of asking for trouble; hence $some_posts.

@if($some_posts)
   // it is important here to **not** use $post because that would try
   // and overwrite the global $post that WordPress uses, and that kind
   // is asking for trouble, hence $this_post.
   @foreach($some_posts as $this_post)
      <h1>{{ $this_post['field_one'] }}</h1>
      <p>{{ $this_post['field_two'] }}</p>
   @endforeach
@endif
1 Like

Thank you, I appreciate your response in trying to help here. I tried your code for a FAQ page that simply returns a question and an answer but no go. It won’t return any data. I do apologize for my lack of understanding and I will figure it out at some point.

Can you post your code?

I literally took the code you provided and swapped in my ACF field names but I’ve since removed the code.

I added the controller code to my App.php file.

I created a page called page-faq.blade.php which extends layouts.app and included a partial called content-faq.blade.php and that is where I added the loop.

Is your example only intended to work with an archive file?

Are you talking about this code?

That is intended to be used on an archive page, because it’s iterating over $wp_query->posts. The example I gave was for an archive because your initial question specifically called out archives as the problem area. It wasn’t really intended to be copied and pasted anyway; I was just trying to illustrated how the principle operated so you could write your own code.

If you’re having trouble getting data from your controller to your blades, then I’d recommending trying to debug where in that pipeline the problem is happening. There are a lot of ways to do that, but I usually just start where var_dumping stuff at different stages to see where the data changes or goes away. For instance, if you var_dump and die in your controller, can you access the data you want? If that spits out data, put the result of your array_map into a variable, then var_dump and die that. Keep doing that all the way down the line, and eventually you’ll figure out where the problem is.

1 Like

This topic was automatically closed after 42 days. New replies are no longer allowed.