Access ACF values for CPT inside WP_Query

I am trying to query custom post types that have a relationship with the current post and then display ACF values from the queried posts.

I’m not sure if I worded that correctly but basically I have 2 main post types.

commercial_buildings
commercial_listings

I have a relationship ACF that links the two together.

On the commercial_buildings blade template I am querying all commercial_listings that share a relationship with the building.

I have successfully queried all of the correct posts and can display ACF for each listing through get_field( 'acf_field_name', post_id ).

In my single-commercial_buildings.blade.php template I have this code for querying and displaying the content.

<!-- Building Listings WP Query -->
@php

$args = [
  'post_type' =>  'commercial_listings',
  'meta_key' => 'commercial_listing_relationship_building',
  'meta_value' => get_the_ID(),
  'meta_compare' => 'LIKE'
];

$results = new WP_Query($args);
        
@endphp

<!-- Display commercial listings available in this building -->
@foreach( $results->posts as $p)

{{ $p->ID }}
<h1>{{ get_field( 'commercial_listing_info_name', $p->ID) }}</h1>

@endforeach()

<!-- Reset WP query -->
@php wp_reset_postdata() @endphp

The way I have everything setup seems to work but In order to access all of my commercial_listing fields I will have to setup variables inside @php tags. This doesn’t seem like the correct way of doing things.

Can I setup my code to be able to work with the WP_Query results inside a @foreach loop like this?

{{ $p->name }}
{{ $p->address }}
{{ $p->image-url }}

Any help is greatly appreciated! Thank you!

Hello.

First off, I would put WP_Query out of the view template and into the appropriate controller, to better separate logic from view.

Just this might help you properly define your variables/functions to be used as a @foreach.

So, just to clarify: if set up correctly you will be able to use $commercial_listings variable instead of $results->posts as the controller will make it available when defining it as a function, for example:

public function commercial_listings() {
    // your wp_query here + return custom data or just $results 
    // out of my head:
    $args = [
      'post_type' =>  'commercial_listings',
      'meta_key' => 'commercial_listing_relationship_building',
      'meta_value' => get_the_ID(),
      'meta_compare' => 'LIKE'
    ];
   return new WP_Query($args);
}

If you use something like this in a controller for single-commercial_buildings.blade.php then you can just use $commercial_listings variable to access your wp_query result, for example:

@if( $commercial_listings->have_posts() )
    @while( $commercial_listings->have_posts() )
              @php $commercial_listings->the_post(); @endphp
              @include('partials.content-whatever')
     @endwhile
@endif

Be careful when defining the function (static or non-static) as it changes how it is available in the template. If static, you would use something like App::yourFunction() if defined in App Controller.

I hope this was useful? If not, let me know If I can help elsewhere.

1 Like

Hello,

Thank you for your advice.

I was setting up the query logic in a controller but ran into an issue that I can’t figure out.

I setup a controller in \app\Controllers called SingleCommercialBuildings.php

I added the following code to the controller.

<?php

namespace App\Controllers;

use Sober\Controller\Controller;

class SingleCommercialBuildings extends Controller
{

  public function getBuildingListings()
  {
    // setup query arguments
    $args = [
      'post_type' =>  'commercial_listings',
      'meta_key' => 'commercial_listing_relationship_building',
      'meta_value' => get_the_ID(),
      'meta_compare' => 'LIKE'
    ];

    return new WP_Query($args);
  }

  // Testing
  public function sayHello() {
    return 'hello';
  }

}

The problem I am having is that I cannot access data from the controller in my blade template. At first I thought my query was messed up so I tried passing a simple string with the sayHello() function but that doesn’t come through.

I get an Undefined variable error when I try to add it to the blade template with {{ $say_hello }}.
If I add the sayHello() function to the App.php controller it works.

I ran @debug() inside the blade template to see what data was being passed to it.

This is all the data that I get back
App

  • Data
    • postobject
    • commercial_buildingobject
    • site_namestringline 12—15
  • Methods
    • titleline 17—35

commercial_building is being passed in because I set protected $acf = true; in App.php

In my composer.json I have the controller set to this version "soberwp/controller": "~2.1.0"

I read through the soberwp/controllers documentation on github and couldn’t find a solution.

Is there something I’m missing or doing wrong?

It looks like this should work.

I tried it on a local test environment and it works as expected. If I had to guess I would say it’s just a naming issue, try to check again, if the names of these two match:

  • app/Controllers/SingleCommercialBuildings.php
  • resources/views/single-commercial-buildings.blade.php

Perhaps it is the same issue you had with the JS, having “commerical” instead of “commercial” ?

Check body class for single-commercial-buildings exactly. If it’s like that, this should work if the above conditions are met as well.

Your post type is commercial_buildings (with an underscore), but your class is SingleCommercialBuildings (with no underscore). When Controller converts your class name to kebab-case so it can be passed to a filter, it has no capacity to automatically discover that you need an underscore. See the code here: https://github.com/soberwp/controller/blob/0ce075376f1a5cfeb3b41884e0b435cc5e81f071/src/Utils.php#L68-L71

That means that when your existing class is converted to be passed to a filter, it is converted to single-commercial-buildings, which matches nothing. It should be converted to single-commercial_buildings, but in order for that to happen your class must be SingleCommercial_Buildings.

I changed the class name from SingleCommercialBuildings to SingleCommercial_Buildings.

When I just changed the class name I got a Fatal error: Uncaught ReflectionException: Class App\Controllers\SingleCommercialBuildings does not exist in... error.

I fixed this error by also changing the controller filename from SingleCommercialBuildings.php to SingleCommercial_Buildings.php but the data and the sayHello() function doesn’t seem to work on the template.

I used CPT UI to setup my custom post types. Should I set up my post types another way to bypass this issue?

I don’t have any familiarity with CPT UI, so I don’t know if it could be having an effect. I use GitHub - johnbillion/extended-cpts: A library which provides extended functionality to WordPress custom post types and taxonomies., you could always give that a shot and see if it helps.

I only have a small window into your code here so it’s hard to make more guesses, but there are a couple gotchas to check:

  • Your “single” template for the commercial_buildings post type is at [theme]/resources/views/single-commercial_buildings.blade.php.
  • commercial_buildings is the actual name of your post type: image
  • There are only two Controllers that could match, App and SingleCommercial_Buildings. Controller only allows two Controllers to pass data to an endpoint: App and one other. Sometimes another Controller can be “overriding” the one you’re working on.
  • Check the class on your body for the template on the front-end, and make sure that it includes single-commercial_buildings—this value is what Controller is actually matching against.

@withjacoby maybe have some more insight: This is probably about the limit of my familiarity with Controller.

You can also get more or less the same functionality (passing data to templates) through Sage’s filter system or the (currently in development) View Composers, which will be added officially in Sage 10.

Interestingly enough by making my sayHello() function a static function and calling it with {{ SingleCommercial_Building::sayHello() }} in the blade template I actually get the returned value.

Should I just static functions even if they are not within the loop?

Static methods in Controllers are just namespaced functions. If that’s what you want to do you can, but then there’s no real reason to use Controller: you could just put them anywhere.

I think this is what you’d be looking for @Eugene_Grechko.

Basically, if your post type has an underscore, you’ll need to use $template to set which template the Controller is intended for.

protected $template = 'single-commercial_buildings';

3 Likes

@codepuncher

That’s exactly what I was looking for.

I am able to pass data to the blade template now.

Thank you!

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