Custom Post Types in loop

If you’re using my controller code, you’d just need to access those things in a way that allows you to access them directly instead of using functions that assume the availability of $post (as the_post_thumbnail() does). For WordPress content, many functions have a get_ variant that allows you to pass a post ID to specify the post you want to get data from. For instance, the_post_thumbnail() has get_the_post_thumbnail().

To use it, you could modify the code I posted to something similar to this following:

// controllers/FrontPage.php
public function galleryLoop()
{
    $gallery_items = get_posts([
        'post_type' => 'gallery',
        'posts_per_page'=>'10',
    ]);

    return array_map(function ($post) {
        return [
            'content' => apply_filters('the_content', $post->post_content),
            'thumbnail' => get_the_post_thumbnail($post->ID, 'large'),
        ];
    }, $gallery_items);
}

// views/front-page.blade.php
@foreach($gallery_loop as $gallery_item)
    {!! $gallery_item['content'] !!}
    {!! $gallery_item['thumbnail'] !!}
@endforeach

Custom fields you can access similarly, depending on what you’re using to create them. In general, they can be accessed with get_post_meta(). Some frameworks give you other functions to access the custom fields they create—i.e. ACF offers get_field().

10 Likes

Now it is working perfectly, thank you guys very much! I think it should be in the documentation…)

Hey man!

I’m kinda new with sage, and with blade, so I’m using this and is actually working great!

But i’m having a curiosity: based in this custom loop I need to return in the gallery item the categories in which my custom post is listed. Is there any way to do this with this code?

Yes, you’d just need to add a value to the array returned by the anonymous function inside array_map that contains your category information. Something like 'categories' => get_the_terms($post, 'whatever_your_taxonomy_is_called').

Hello. I just migrated from Sage8 to Sage9 and I am trying to understand Blade as much as I can and I love it so far. Now I have created a custom post type in my “app/setup.php” file and I added 10 posts in the backend. I now want to do a custom WP Query as I usually do but using Blade so I tried to add the following to “app/controllers/app.php”:

    public function projectsLoop()
{
    $projects_items = get_posts([
        'post_type' => 'ctp_projects',
        'posts_per_page'=>'6',
    ]);

    return array_map(function ($post) {
        return apply_filters('the_content', $post->post_content);
    }, $projects_items);
}

this of course is inside class App extends Controller {

and then inside “views/partials/homepage/homepage-projects.blade.php” I have added the following code:

        @foreach($projectsLoop as $project_item)
            {!! $project_item !!}
        @endforeach

I get this error:

Undefined variable: projectsLoop in /srv/www/michelec/current/web/app/uploads/cache/d8946328bcc3eb4c6204474ad8a86a6bd66b21d4.php on line 5

It’s worth to mention that I have done the following changes in this project which shouldn’t affect anything I assume:

I have created a custom “app-custom.blade.php” file inside the layouts folder which I have cloned from the original one and slightly changed to my needs:

<!doctype html>
<html @php(language_attributes())>
  @include('partials.head')
  <body @php(body_class())>
    @php(do_action('get_header'))
    @include('partials.header')
    <div class="wrap" role="document">
      <div class="content">
        <main class="main">
          @yield('content')
        </main>
      </div>
    </div>
    @php(do_action('get_footer'))
    @include('partials.footer')
    @php(wp_footer())
  </body>
</html>

Created a custom template for the homepage called “template-homepage.blade.php”:

@extends('layouts.app-custom')

@section('content')
  @while(have_posts()) @php(the_post())
    @include('partials.content-homepage')
  @endwhile
@endsection

And then inside the “partials” folder I have the “content-homepage.blade.php” that includes each and every section that I need to build the long homepage:

@include('partials.homepage.homepage-intro')
@include('partials.homepage.homepage-about')
@include('partials.homepage.homepage-projects')

Reading this post I am starting to understand how to create controllers but I have some questions. I see that there is a controller for FrontPage.php. What if I want to create a new controller just for my custom template (template-homepage.blade.php). Because I don’t have a “front-page.php” file I have put mine in “controllers/app.php”. Is that wrong?
Also I am going to add ACF soon so I might have some questions but I see that Advanced Custom Fields is already discussed in other topics so I will try to read them first after I solve this issue.

Sorry for the long post and thanks for the time.

Hi @michelecocuccio - try $projects_loop in your Blade template instead of $projectsLoop. Your controller method names are converted to snake case when they are made available in your templates.

  • Create methods within the Controller Class;
    • Use public function to return data to the Blade views/s
      • The method name becomes the variable name in Blade
      • Camel case is converted to snake case. public function ExampleForUser in the Controller becomes $example_for_user in the Blade template

Edit: regarding your second question, if you want to add a new controller for a certain template, just create a file in the controllers folder with the same name as the template (minus .blade). If your template is views/template-homepage.blade.php, then your controller would be controllers/template-homepage.php.

1 Like

I feel so stupid sometimes… Thank you very much and so sorry for this simple mistake. I didn’t noticed despite the fact that I knew about it.

Thank you very much!

1 Like

No problem! I also edited my post to respond to your second question (missed it at first).

1 Like

Hey thanks for the great explanations. Huge help.

Can the ACF get_field() usage be explained further?

I’ve got the CPT post title displaying nicely so I know things are working, but I’m struggling to get my ACF fields to show.

//controllers/FrontPage.php
...
'title' => apply_filters('the_title', $post->post_title),
'acf_field' => get_field('acf_field'),
...

// views/front-page.blade.php
@foreach($cpt_loop as $cpt_item)
    {!! $cpt_item['title'] !!}
    {!! $cpt_item['acf_field'] !!}
@endforeach

I’ve got ACF fields working elsewhere, just not in this CPT loop. Using Controller 2.0.1 and I’ve got the protected $acf = true; line up top as well, although not sure if this is related. Just trying to wrap my head around all this. Again, thanks so much :+1:

Edit: Came back to it and figured it out:

'acf_field' => get_field('acf_field', $post),

( Missing $post )

Thanks all for all the related questions and well written answers. This entire thread has all the information I was looking for.

I plan to use this to create four custom loops on the static homepage template (each one for a different custom post type or category), but none of the documentation that I’ve been able to find has covered the topic.

I’ll let you know if it works.

It worked. Thanks for all the good replies and documentation. Here is what I did for one of my custom loops. I basically created a loop for the “news” category like so:

In app.php (inside of Controller)

public function newsLoop()
  {
    $news_items = get_posts([
      'post_type' => 'post',
      'category_name' => 'news',
      'posts_per_page' => '3'
    ]);
    return array_map(function ($post) {
      $text = strip_shortcodes( $post->post_content );
      $text = apply_filters( 'the_content', $text );
      $text = str_replace(']]>', ']]&gt;', $text);
      $excerpt_length = apply_filters( 'excerpt_length', 55 );
      $excerpt_more = apply_filters( 'excerpt_more', ' ' . '[&hellip;]' );
      $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
      return [
        'title' => apply_filters('the_title', $post->post_title),
        'link' => '<a href="'.get_the_permalink($post->ID).'">',
        'excerpt' => $text,
      ];
  }, $news_items);
}

In partials folder:
I inserted the following into a UL element:
<ul class="list-group list-group-flush">
@foreach($news_loop as $news_item)
<li class="list-group-item">{!! $news_item['link'] !!}{!! $news_item['title'] !!}</a></li>
@endforeach
</ul>

Again, thanks.

1 Like

Hi, thanks for all this informations.

I have followed all this instructions for my custom post type and everything works well with my template… on local server.
But after uploading the files on my production server, the loop don’t displaying well anymore. I get the following WP_DEBUG message :

<b>Notice</b>:  Undefined variable: chantier_loop in <b>[...]/www/wp-content/uploads/cache/94dc7946fb3c7b085fa4250d7d8114045355fb05.php</b> on line <b>2</b><br>
<br>
<b>Warning</b>:  Invalid argument supplied for foreach() in <b>[...]/www/wp-content/uploads/cache/94dc7946fb3c7b085fa4250d7d8114045355fb05.php</b> on line <b>2</b>

I think all the required files have been uploaded :

├── app/
├── config/
├── dist/
├── resources/                 
│  ├── views/                  
│  ├── functions.php
│  ├── index.php
│  ├── screenshot.jpg
│  └── style.css
├── vendor/                    
└── LICENSE.md   

Can’t figure why this works well on my local server but not on my production server.
Any idea ?

I solved this problem putting my public function chantierLoop() in App.php instead of FrontPage.php (inside of Controller).
Still can’t figure why this public function in FrontPage.php works well on local server and not on production server.

Are there other methods in FrontPage.php that work fine and only this one fails? Or do any methods in FrontPage.php fail? If the later, you may be getting thrown off by Controller’s inheritance pattern—specifically that (unless you enable tree) only two controllers can apply at the same time: App, and whatever one matches your current endpoint first. More in-depth explaination here:

Thanks alwaysblank, I understand my mistake now :
Controllers are linked to the wordpress template Hierarchy and this is where I had to take a look.
I forgot the FrontPage.php is used to render the site’s front page. Actually my production website is hide behind an “on construction” index.html webpage, so the webpage I tested was not set as a front page in wordpress. This is why on production server, FrontPage.php fail.

P.S : Thanks again for your attention and your patience because I’m not sure to be always very clear when I’m writing in english. :confused:

Glad you figured it out.

I don’t use this trick much anymore because I’m not usually working on “under construction” sites, but you can use server redirects to redirect every IP that’s not yours somewhere else—say, a static file that says “under construction.” This is one example, using .htaccess (i.e. if you’re on Apache): https://stackoverflow.com/questions/8985502/htaccess-redirect-to-all-ips-but-mine With this approach you don’t need to worry about the kind of dev/prod parity issues you ran into here, because only you have access to the actual production server, negating the need to have WordPress itself in a different state on the production server.

Hi there! Now i have 2 questions about passing data from controller to views:

  1. Is it possible to make pagination on custom template with public function loop? Or we have to use archive-template and use standard wordpress loop and pagination?

  2. WPML plugin doesn’t include translation in database, instead it duplicates posts on both languages. So the question is: how to tell to loop that i want posts on particular language? I have carousel on all pages and after i use wpml plugin carousel shows duplicates posts with both languages.

Thank you!

1 Like

This is more of a WordPress general question, than a Sage-specific one. Here’s a possible solution from Google:

1 Like

Ooooh, now i understand where to dig. Thank you sir!

Might be worth looking at Timber’s implementation . I think they’ve restructured it to do it automatically, but you had to run query_posts

I’ll see if I can find the old link
https://timber.github.io/docs/guides/pagination/